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.
Related
I just encountered with strange behaviour of DllImport in C#, which I can't explain. I want to know how It is possible and where I can read about It. Case is that via DllImport one can call function that doesn't really exported form dll. In my case It is kernel32.dll and function ZeroMemory (but with Copy/Move/Fill memory such behavior). So, my code:
[DllImport("kernel32", EntryPoint = "LoadLibraryW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string libName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr module, string procName);
//WTF???
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ZeroMemory(IntPtr address, int size);
static void TestMemory()
{
IntPtr mem = Marshal.AllocHGlobal(100); //Allocate memory block of 100 bytes size
Marshal.WriteByte(mem, 55); //Write some value in the first byte
ZeroMemory(mem, 100); //Clearing block of memory
byte firstByte = Marshal.ReadByte(mem); //Read the first byte of memory block
Console.WriteLine(firstByte); //Output 0 (not 55) - ZeroMemory is working
//Getting address of ZeroMemory from kernel32.dll
IntPtr kernelHandle = LoadLibrary("kernel32.dll");
IntPtr address = GetProcAddress(kernelHandle, "ZeroMemory");
Console.WriteLine(address.ToString("X")); //Output 0 - Library kernel32.dll DOESN'T export function ZeroMemory!!!
//Testing GetProcAddress via getting address of some exported function
Console.WriteLine(GetProcAddress(kernelHandle, "AllocConsole").ToString("X")); //Output some address value - all is OK.
}
No EntryPointNotFoundException is thrown - code works fine. If change name of ZeroMemory to ZeroMemory1 or something like that - exception will be thrown. But in export table of kernel32.dll we see:
There is NO ZeroMemory function at all!
If we look in msdn, we read that ZeroMemory is just a macro in WinBase.h header file for C++. Inside that we see:
#define RtlMoveMemory memmove
#define RtlCopyMemory memcpy
#define RtlFillMemory(d,l,f) memset((d), (f), (l))
#define RtlZeroMemory(d,l) RtlFillMemory((d),(l),0)
#define MoveMemory RtlMoveMemory
#define CopyMemory RtlCopyMemory
#define FillMemory RtlFillMemory
#define ZeroMemory RtlZeroMemory
Obviously, that in C++ ZeroMemory actually works through RtlFillMemory from ntdll.dll. But it is in C++!!! Why is it work in C#?? On official documentation for DllImport attribute here we can read the next:
As a minimum requirement, you must supply the name of the DLL
containing the entry point.
But in that case kernel32.dll CANNOT contating entry point for ZeroMemory. What is going on?? Help, please.
The OP is correct in that kernel32.dll has no export ZeroMemory export, yet the C# DllImport somehow succeeds to magically resolve the ZeroMemory reference to the correct RtlZeroMemory export in .NET apps targeted at the Framework (but not at Core).
Turns out that a handful of Win32 APIs documented as inlines/macros (MoveMemory, CopyMemory FillMemory, ZeroMemory) are specifically checked by the Framework code and internally rerouted to the correct exports. While not formally documented, this was acknowledged in a MS-sanctioned comment under a .NET Runtime issue.
As an FYI, there were a few special cased P/Invoke names in .NET Framework: MoveMemory, CopyMemory, FillMemory, and ZeroMemory. All of these will work when pointing at kernel32 on .NET Framework, but will fail on .NET Core. Please use the EntryPoint property in the DllImport attribute to get the desired behavior. The proper export names can be found using dumpbin /exports kernel32.dll from a Visual Studio command prompt.
The above suggests adding an explicit EntryPoint for the declaration to work in both Framework and Core, for example:
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
public static extern void ZeroMemory(IntPtr address, IntPtr count);
The magic name remapping happens outside the open-sourced .NET code, but can be clearly seen in the disassembly contributed by Simon Mourier in a comment.
I'm using a c++ dll in a Unity3D project. It`s important to use CharSet = CharSet.Ansi
[DllImport("Exchange3D", EntryPoint = "GetElementValue", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern double fnGetElementValue(int nType, int nNumber, string strPointName);
strPointName is sent to the dll. But the dll receives it in an incorrect format in the Unity Project!
But I created a C# Console Application with the same code and all is working!
Any ideas?
A string needs to be const char* on the c++ side, and not std::string. You might also consider defining the c++ functiona as a ansi-c function instead since that can be easier to map in c#.
Have a look at this piece of information to get an idea of how to map a simple function that works with a string.
https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention(v=vs.110).aspx
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).
I've import dll in my code like that:
[DllImport("dll.dll", CharSet = CharSet.Ansi)]
private static extern int function(String pars, StringBuilder err);
I was wondered that function works, but it is not inside project and not inside Debug or Release folders. I.e. "dll.dll" should not be availabe because it is not in the current project folder, however it IS available. Now I want to know the exact full path of the dll used at runtime, but don't know how to get it.
You're going to have to use the win32 API.
First use GetModuleHandle passing "dll.dll" to it. Then pass that handle to GetModuleFileName.
string GetDllPath()
{
const int MAX_PATH = 260;
StringBuilder builder = new StringBuilder(MAX_PATH);
IntPtr hModule = GetModuleHandle("dll.dll"); // might return IntPtr.Zero until
// you call a method in
// dll.dll causing it to be
// loaded by LoadLibrary
Debug.Assert(hModule != IntPtr.Zero);
uint size = GetModuleFileName(hModule, builder, builder.Capacity);
Debug.Assert(size > 0);
return builder.ToString(); // might need to truncate nulls
}
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll", SetLastError=true)]
[PreserveSig]
public static extern uint GetModuleFileName
(
[In] IntPtr hModule,
[Out] StringBuilder lpFilename,
[In][MarshalAs(UnmanagedType.U4)] int nSize
);
See Dynamic-Link Library Search Order -- this should hold true with P/Invoke, but the behavior is slightly alterable depending upon load method. However it doesn't say how to determine the filename of a loaded DLL ;-)
A completely un-tested and perhaps ill-advised solution to find the DLL path at run-time. This assumes that P/Invoke and LoadLibrary use the same resolution (e.g. P/Invoke doesn't use LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH) and that there are no awful conflicts with this approach.
Load the DLL with LoadLibrary or LoadLibaryEx.
Find the filename of the module using GetModuleFileName and the handle obtained in step #1.
Unload the module with FreeLibrary. P/Invoke should keep the module reference-count > 0, but again, this is untested ;-)
(There is no guarantee about the correctness or validity of the above solution. Suggestions, warnings and/or corrections welcome. YMMV.)
Happy coding.
If you really want to find out, use dependency-walker
It can even 'monitor' your application in runtime and detect dynamic loads so nothing remains hidden
For some reason, whenever my C# .NET 2.0 application makes a call to GetProcAddress it always returns zero.
public class MyClass
{
internal static class UnsafeNativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool SetDllDirectory(string lpPathName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
}
private void MyFunc()
{
IntPtr _dllHandle;
IntPtr _fptr;
string _fullPath = ".\\mydll.dll";
string _procName = "MyDllFunc";
_dllHandle = UnsafeNativeMethods.LoadLibrary(_fullPath);
_fptr = UnsafeNativeMethods.GetProcAddress(_dllHandle, _procName); // <-- Always returns zero.
}
}
I'm sure the function name is spelled correctly, and _fullPath is presumably correct because _dllHandle is always assigned a non-zero value. Any insight you may be able to provide is appreciated. Thanks.
GetProcAddress only comes in an ANSI flavor, hence we help the runtime by telling it to always use ANSI when marshalling the string parameter. We also prevent the runtime looking for a non-existent GetProcAddressA, because the default for C# is to set ExactSpelling to false.
http://www.pinvoke.net/default.aspx/kernel32.getprocaddress
You really need to add some error checking. At least verify if _dllHandle != IntPtr.Zero. Also, depending on the current working directory is dangerous, use Assembly.GetEntryAssembly().Location to get a full path name.
The function name is probably wrong. Exports tends to be decorated, like _MyDllFunc or _MyDllFunc#4. More wildly if it was compiled by a C++ compiler. Use Dumpbin.exe /exports on your DLL to see the real names.
Back to error handling, use SetLastWin32Error in the [DllImport] attribute. Throw Win32Exception if the function returns false or IntPtr.Zero.
Edit: I see the real problem. Using CharSet.Auto for GetProcAddress() is wrong. Very unlucky, it is just about the only Windows API function that only has an ANSI version. You have to use CharSet.Ansi. A good place to get proper [DllImport] declarations is pinvoke.net
You have not shown how you export the function from the DLL, but I suspect the problem is that the exported name is not what you thing it is. You can run dumpbin /exports mydll.dll to view the exports of the dll to verify the name.
If you show the a code snippet of the export, I could provide more direct advice. You can try decorate the exported function with extern "C" to eliminate the name mangling as a test.
Does your export in the .DEF file for the DLL match the input here? You can use dumpbin to find out what's exported, per other replies here.
What is the underlying Win32 error on GetProcAddress(), per GetLastError()?
You could try this in native code to work out the correct inputs first without the additional baggage of P/Invoke.