This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Specify the search path for DllImport in .NET
I have an unmanaged DLL. For clearance, its a C++ dll that I want to use in my c# code.
The problem is that its a windows app and user can install it in any directory of their choice. So I can't reference it with a static path and I couldn't find a way to give a relative path.
Here is the code I tried :
[DllImport("mydll.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
public void SetDllDirectory(string lpPathName)
{
try
{
bool r = SetDllDirectory(lpPathName);
}
catch (Exception ex)
{
throw ex;
}
}
public void SetDirectoryPath()
{
try
{
DirectoryInfo directory = new DirectoryInfo(System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath));
if (directory.Exists)
SetDllDirectory(directory.ToString() + "\\mydll.dll");
}
catch (Exception ex)
{
throw ex;
}
}
And below is the error I am receiving.
Unable to find an entry point named 'SetDllDirectory' in DLL 'mydll.dll'.
This question could be a replica of
"C++ unmanaged DLL in c#"
"Relative Path to DLL in Platform Invoke Statement"
Sorry for that but I didn't find any solution in these references.
If you are trying to pinvoke into an unmanaged library that is in a non-standard location you need to add this location to the dll search path that windows uses to look for dll files.
If your users can tell the program where the c/c++ library file is, you can load it manually when they choose it.
public class UnsafeNativeMethods {
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int GetDllDirectory(int bufsize, StringBuilder buf);
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string librayName);
[DllImport("mylibrary")]
public static extern void InitMyLibrary();
}
You can call the above in some form of preload routine like so:
public void LoadDllFile( string dllfolder, string libname ) {
var currentpath = new StringBuilder(255);
UnsafeNativeMethods.GetDllDirectory( currentpath.Length, currentpath );
// use new path
UnsafeNativeMethods.SetDllDirectory( dllfolder );
UnsafeNativeMethods.LoadLibrary( libname );
// restore old path
UnsafeNativeMethods.SetDllDirectory( currentpath );
}
You might then call it like so:
LoadDllFile( "c:\whatever", "mylibrary.dll" );
This error message:
Unable to find an entry point named 'SetDllDirectory' in DLL 'mydll.dll'.
says nothing about not being able to find the DLL. It says that your P/Invoke definition is incorrect. There is no function named SetDllDirectory in your DLL (mydll.dll). And why should there be? The only functions that are exported from DLLs are those that you explicitly code and export. Since you didn't write code for a SetDllDirectory and indicate that it should be exported from mydll.dll, it doesn't exist in the DLL.
Beyond that, this question is unanswerable unless you update your question with real code. You need to post, at minimum, the signature for the function you're trying to call from the unmanaged DLL, along with the P/Invoke code that you've tried on the C# (managed) side.
The problem is that its a windows app and user can install it in any directory of their choice. So I can't reference it with a static path and I couldn't find a way to give a relative path.
This is not a real problem. Relative paths work just fine, and they work in exactly the way that your question indicates that you've already tried. You just include the name of the DLL in the P/Invoke definition. The default DLL search order takes care of everything else. If the managed EXE is in the same directory as the unmanaged DLL that you want to P/Invoke, everything will work seamlessly.
It doesn't matter where the user chooses to install the app, so long as the DLL and the EXE are located in the same folder. And that's the job of your installer.
And so long as you use relative paths, you absolutely do not need to use the SetDllDirectory function from the Win32 API (which is actually defined in kernel32.dll).
Related
I trying to use a method in a dll written in C in a C# console app project.
this is the method signature in h file I got :
DLL_PUBLIC sint AshrAPI_TranRecToRefunISO(char* TranRec , byte* RefunISO , usint* ISOLength);
I used P/Invoker to create the corresponding method in C#:
[DllImport(#"Ashrait.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
[return: MarshalAs(UnmanagedType.I2)]
internal static extern short AshrAPI_TranRecToRefunISO([MarshalAs(UnmanagedType.LPStr)] string TranRec, [MarshalAs(UnmanagedType.LPArray)] byte[] RefunISO, ushort ISOLength);
but no matter which UnmanagedType I try to use, I always get the same exception :
Unable to find an entry point named 'XXX' in DLL 'XXX.dll
can anyone see something wrong ?
so found out the issue! since I loaded the dll to project as an item and not as a reference, I've put it in resources folder inside the project.
once I moved the dll to root project folder everything started working :)
In most cases, VB6 DLLs and OCXs must be registered before use. In Visual Basic Project (VBP) files the objects referenced are listed as follows:
Object={70031B70-1070-0D70-AC0E-B049A0701010}#1.0#0; Component.ocx
Reference=*\G{000CD090-0D00-4F07-0707-80040E010704}#1.0#0#..\References\GeneralLibrary.dll#General Purpose Libraries
When these files are updated, I need to change all of the references in my VB6 project. Since VBP files are just text files, these references can be updated using text parsing. The caveat is, I need to know the GUIDs of each component and version ahead of time.
After some searching I found that the oleaut32.dll library in Windows can be used to gather information from a DLL. This can be leveraged through C# using ITypeLib and ITypeInfo.
I was able to scour a method for using C# to read the DLL file in question to obtain GUID and version information. However, the documentation available for the ITypeLib and ITypeInfo doesn't have any examples. I also wasn't able to find any examples online.
Sample Code:
class Program
{
[DllImport("oleaut32.dll", PreserveSig=false)]
public static extern ITypeLib LoadTypeLib([In, MarshalAs(UnmanagedType.LPWStr)] string typelib);
static void Main(string[] args)
{
ITypeLib dllLibrary;
ITypeInfo information;
dllLibrary = LoadTypeLib("C:\\common_files\\Common.dll");
dllLibrary.GetTypeInfo(0, out information);
Console.ReadLine();
}
}
Question
How do I use ITypeLib and ITypeInfo to get the GUID and version information of a DLL or OCX?
Caveat: Because of the way our system is deployed and the fact that there are a number of old DLL artifacts in the registry with the same name, I would like to avoid registering the DLLs and then reading what was registered from the registry.
I was also unable to find the DLL used for getting version information as stated in this question.
The following code will load a tlb in C# - i've not tried a DLL or OCX with an embedded tlb, but it may work for those. If not, you'll need to extract the tlb from the embedded resources (yay! more PInvoke) and then run on the tlb directly. This is a full command line app - you can copy and paste this into a new source file and compile and test on the command line.
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
public class TypeLibTest
{
[DllImport("oleaut32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)]
private static extern IntPtr LoadTypeLib(string fileName, out System.Runtime.InteropServices.ComTypes.ITypeLib typeLib);
public static void Main(string[] args)
{
System.Runtime.InteropServices.ComTypes.ITypeLib typeLib = null;
IntPtr ptr = IntPtr.Zero;
string file = arg[0];
try
{
LoadTypeLib(file, out typeLib);
typeLib.GetLibAttr(out ptr);
var typeAttr = (System.Runtime.InteropServices.ComTypes.TYPELIBATTR) Marshal.PtrToStructure(ptr, typeof(System.Runtime.InteropServices.ComTypes.TYPELIBATTR));
Console.WriteLine("{0}.{1}", typeAttr.wMajorVerNum, typeAttr.wMinorVerNum);
}
catch (COMException ex)
{
Console.WriteLine("Error: " + ex.Message);
}
finally
{
if (typeLib != null && ptr != IntPtr.Zero)
{ typeLib.ReleaseTLibAttr(ptr); }
}
}
}
I am building C# library (.dll), where some classes use native Windows API functions. To be specific, functions GetPrivateProfileString and WritePrivateProfileString, like:
static class NativeMethods
{
// external functions for working with ini files
[DllImport("kernel32.DLL", EntryPoint = "GetPrivateProfileStringW",
SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
public static extern int GetPrivateProfileString(string lpAppName, string lpKeyName,
string lpDefault,
string lpReturnString,
int nSize,
string lpFilename);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool WritePrivateProfileString(string lpAppName,
string lpKeyName,
string lpString,
string lpFileName);
}
And using them:
NativeMethods.GetPrivateProfileString(section, key, "", result, 127, this.path);
NativeMethods.WritePrivateProfileString(section, key, " " + value, this.path);
When I am using this class directly in another project (without using it from dll), it works without problem. But when I build it into dll, then reference this dll in another project, then these functions aren't working. They simply do nothing.
Your pinvoke for GetPrivateProfileString is wrong as Matthew says. You need to use StringBuilder when passing string data back to the caller.
You are not checking for errors. So it's entirely plausible that the functions are failing without you knowing that.
In order to know why your calls are failing you'd first need to fix these issues. Then you can look at the return values. Knowledge also of the values of the parameters that you are using would help. But only you have the ability to do that, at present.
The bigger problem is that you are not meant to call these functions at all. The documentation says:
This function is provided only for compatibility with 16-bit Windows-based applications.
These functions contain numerous compatibility shims that make using them a complete minefield. Don't go there. Find a decent third party native C~ ini file class and pretend you never heard of GetPrivateProfileString and WritePrivateProfileString.
Oh, I found error. I forced to find ini file path in current directory (in my DLL class code). As I call it from DLL, it was automatically looking for file in C:\Windows. After using Directory.GetCurrentDirectory() + iniFile , it works.
This question already has answers here:
Change C# DllImport target code depending on x64/x86
(2 answers)
Closed 8 years ago.
I try to develope an C# Interface for using an USB Hardware Device. I access the API DLL Via PInvoke pattern from the manufacturer.
There are two DLLs with the same name. But one is for 32Bit Systems and the other one for 64Bit Systems.
I want that my application uses the right API for each system.
So i start checking wich platfrom is in use:
bool is64Bit = System.Environment.Is64BitOperatingSystem
I defined a string variable and set the name of recommend .DLL.
like this:
string dll;
if (is64bit)
{
dll = "APINAME64.DLL";
}
else
{
dll = "APINAME32.DLL"
}
[DllImport(dll, SetLastError=true)]
public static extern bool ImmConfigureIME();
But this is still not working. The Compiler wants an const string for Pinvoke.
Does anybody has an idea how to solve that?
You can declare both DLLs inside your code and give them different names. Then, use the EntryName property to make sure they have the right entry point for the native dll:
[DllImport("APINAME64.dll", EntryName="ImmConfigureIME" SetLastError=true)]
public static extern bool ImmConfigureIME64();
[DllImport("APINAME32.dll", EntryName="ImmConfigureIME" SetLastError=true)]
public static extern bool ImmConfigureIME32();
bool is64Bit = System.Environment.Is64BitOperatingSystem;
if (is64bit)
{
ImmConfigureIME64();
}
else
{
ImmConfigureIME32();
}
I'm writing a cross-platform .NET library that uses some unmanaged code. In the static constructor of my class, the platform is detected and the appropriate unmanaged library is extracted from an embedded resource and saved to a temp directory, similar to the code given in another stackoverflow answer.
So that the library can be found when it isn't in the PATH, I explicitly load it after it is saved to the temp file. On windows, this works fine with LoadLibrary from kernel32.dll. I'm trying to do the same with dlopen on Linux, but I get a DllNotFoundException when it comes to loading the P/Invoke methods later on.
I have verified that the library "libindexfile.so" is successfully saved to the temp directory and that the call to dlopen succeeds. I delved into the mono source to try figure out what is going on, and I think it might boil down to whether or not a subsequent call to dlopen will just reuse a previously loaded library. (Of course assuming that my naïve swoop through the mono source drew the correct conclusions).
Here is the shape of what I'm trying to do:
// actual function that we're going to p/invoke to
[DllImport("indexfile")]
private static extern IntPtr openIndex(string pathname);
const int RTLD_NOW = 2; // for dlopen's flags
const int RTLD_GLOBAL = 8;
// its okay to have imports for the wrong platforms here
// because nothing will complain until I try to use the
// function
[DllImport("libdl.so")]
static extern IntPtr dlopen(string filename, int flags);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string filename);
static IndexFile()
{
string libName = "";
if (IsLinux)
libName += "libindexfile.so";
else
libName += "indexfile.dll";
// [snip] -- save embedded resource to temp dir
IntPtr handle = IntPtr.Zero;
if (IsLinux)
handle = dlopen(libPath, RTLD_NOW|RTLD_GLOBAL);
else
handle = LoadLibrary(libPath);
if (handle == IntPtr.Zero)
throw new InvalidOperationException("Couldn't load the unmanaged library");
}
public IndexFile(String path)
{
// P/Invoke to the unmanaged function
// currently on Linux this throws a DllNotFoundException
// works on Windows
IntPtr ptr = openIndex(path);
}
Update:
It would appear that subsequent calls to LoadLibrary on windows look to see if a dll of the same name has already been loaded, and then uses that path. For example, in the following code, both calls to LoadLibrary will return a valid handle:
int _tmain(int argc, _TCHAR* argv[])
{
LPCTSTR libpath = L"D:\\some\\path\\to\\library.dll";
HMODULE handle1 = LoadLibrary(libpath);
printf("Handle: %x\n", handle1);
HMODULE handle2 = LoadLibrary(L"library.dll");
printf("Handle: %x\n", handle2);
return 0;
}
If the same is attempted with dlopen on Linux, the second call will fail, as it doesn't assume that a library with the same name will be at the same path. Is there any way round this?
After much searching and head-scratching, I've discovered a solution. Full control can be exercised over the P/Invoke process by using dynamic P/Invoke to tell the runtime exactly where to find the code.
Edit:
Windows solution
You need these imports:
[DllImport("kernel32.dll")]
protected static extern IntPtr LoadLibrary(string filename);
[DllImport("kernel32.dll")]
protected static extern IntPtr GetProcAddress(IntPtr hModule, string procname);
The unmanaged library should be loaded by calling LoadLibrary:
IntPtr moduleHandle = LoadLibrary("path/to/library.dll");
Get a pointer to a function in the dll by calling GetProcAddress:
IntPtr ptr = GetProcAddress(moduleHandle, methodName);
Cast this ptr to a delegate of type TDelegate:
TDelegate func = Marshal.GetDelegateForFunctionPointer(
ptr, typeof(TDelegate)) as TDelegate;
Linux Solution
Use these imports:
[DllImport("libdl.so")]
protected static extern IntPtr dlopen(string filename, int flags);
[DllImport("libdl.so")]
protected static extern IntPtr dlsym(IntPtr handle, string symbol);
const int RTLD_NOW = 2; // for dlopen's flags
Load the library:
IntPtr moduleHandle = dlopen(modulePath, RTLD_NOW);
Get the function pointer:
IntPtr ptr = dlsym(moduleHandle, methodName);
Cast it to a delegate as before:
TDelegate func = Marshal.GetDelegateForFunctionPointer(
ptr, typeof(TDelegate)) as TDelegate;
For a helper library that I wrote, see my GitHub.
Try running it like this from a terminal:
export MONO_LOG_LEVEL=debug
export MONO_LOG_MASK=dll
mono --debug yourapp.exe
Now every library lookup will be printed to the terminal, so you'll be able to find out what's going wrong.
I needed to load a native library extracted to a temporary location, and I almost found a solution. I've checked Mono's source code and figured out a way:
[DllImport("__Internal", CharSet = CharSet.Ansi)]
private static extern void mono_dllmap_insert(IntPtr assembly, string dll, string func, string tdll, string tfunc);
// and then somewhere:
mono_dllmap_insert(IntPtr.Zero, "somelib", null, "/path/to/libsomelib.so", null);
This kind of works. The problem is, you cannot allow Mono's stupid JIT compiler to catch a whiff of any DllImported method referring this library before calling mono_dllmap_insert().
Because if it does, strange things will happen:
Mono: DllImport searching in: '/tmp/yc1ja5g7.emu/libsomelib.so' ('/tmp/yc1ja5g7.emu/libsomelib.so').
Mono: Searching for 'someGreatFunc'.
Mono: Probing 'someGreatFunc'.
Mono: Found as 'someGreatFunc'.
Error. ex=System.DllNotFoundException: somelib
So now that I'm calling my native someGreatFunc(), Mono is able to find the library and load it (I checked), it is able to find the symbol (I checked), but because somewhen in the past when it was doing JIT it was not able to load that library, it decides to throw DllNotFoundException anyway. I guess the generated code contains a hardcoded throw statement or something :-O
When you call another native function from the same library that happens not to have been JITted before you called mono_dllmap_insert(), it will work.
So you can either use the manual solution added by #gordonmleigh or you must tell Mono where the library is BEFORE it JITs any of these imports. Reflection may help there.
Not sure why you think this is related to mono, since the issue you're having is not about mono's dynamic loading facilities.
If your updated sample works, it just means that LoadLibrary() on windows has different semantics than dlopen() on Linux: as such you either have to live with the difference or implement your own abstraction that deals with the directory issue (my hunch is that it's not the directory that is retained, but windows simply looks to see if a library with the same name was already loaded and it reuses that).