DllImport with non-exported functions. Why is it working? - c#

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.

Related

How to import Delphi function (that returns PAnsiChar) from DLL in C#

Function in Delphi is -
FuncName: PAnsiChar (stdcall)
I don't know the rest of the function code, cause i can't open the Dll.
And i tried to implement it to C# like this -
[DllImport("Lib2-2-1.dll", EntryPoint = "FuncName", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.AnsiBStr)]
public static extern string FuncName();
when i'm trying to call this C# function in code - everything just closes without an Error, and the code stops executing. Could someone help me please and explain how to write it properly.
And another thing. I tried to open this Dll in DotPeek but it won't open and it says - Not supported, could someone please explain how can i view the code inside it.
okey
so, here is the answer, i found it in this question thread
here is the importing from Dll
[DllImport("Lib2-2-1.dll", EntryPoint = "FuncName", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[MethodImplAttribute(MethodImplOptions.PreserveSig)]
public static extern IntPtr FuncName();
And this how to call it
IntPtr ptr = FuncName();
string result = Marshal.PtrToStringAnsi(ptr);

Importing FORTRAN DLL in C# does not operate on the data inside the DLL

I am having an issue when importing a DLL from FORTRAN into C#. Basically the FORTRAN DLL is a database of variables and accessor functions working on the data. I want to use these exported functions to set and get the data from the DLL.
This is what the FORTRAN DLL contains:
! Database
include "test.f90"
!DEC$ ATTRIBUTES DLLEXPORT :: /DBASE/
subroutine DBASE_GET_ADDRESS(iAddress, nBytes, Bytes)
!DEC$ ATTRIBUTES ALIAS:'DBASE_MODULE_mp_DBASE_GET_ADDRESS' :: DBASE_GET_ADDRESS
!DEC$ ATTRIBUTES DLLEXPORT :: DBASE_GET_ADDRESS
subroutine DBASE_SET_ADDRESS(iAddress, nBytes, Bytes)
!DEC$ ATTRIBUTES ALIAS:'DBASE_MODULE_mp_DBASE_SET_ADDRESS' :: DBASE_SET_ADDRESS
!DEC$ ATTRIBUTES DLLEXPORT :: DBASE_SET_ADDRESS
The database is included in "test.f90" and it is exported so other unmanaged code can access it directly. Generally it is aimed to work as shared memory.
The 2 subroutines are accessing the data inside "test.f90". I haven't pasted the whole body as it's a formatting nightmare on SO but they work as intended.
Now my initial thought was that I can import these functions in C# and operate on the database variables (which are also part of the DLL) from there. This is what I have done:
[DllImport("Test_Dbase.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void DBASE_MODULE_mp_DBASE_GET_ADDRESS(ref Int32 iAddress, ref Int16 nBytes, ref Byte Bytes);
[DllImport("Test_Dbase.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void DBASE_MODULE_mp_DBASE_SET_ADDRESS(ref Int32 iAddress, ref Int16 nBytes, ref Byte Bytes);
Then I wrote some wrapper functions so I can pass in a variable name with value for easy access. This seems to work fine with the DLL added in my local solution I can set and retrieve data successfully.
The problem appears when I bring my C# executable in with the other processes
the work on this shared DLL. My executable seems to operate on a local copy of it and no changes are seen in the other processes.
Top: C# process Bottom: Fortran process
I am pretty new to Interop between managed and unmanaged code so it could very well be that my approach is massively flawed. Any input would be appreciated. I can provide more code or info if needed.
Thanks,
Dragos
You have to load dll dynamically.
public delegate void FortnarInterface(ref Int32 iAddress, ref Int16 nBytes, ref Byte Bytes);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);
IntPtr dllHandle = LoadLibrary("Test_Dbase.dll");
IntPtr functionHandle = Kernel32.GetProcAddress(dllHandle, "DBASE_MODULE_mp_DBASE_GET_ADDRESS");
FortnarInterface function =(FortnarInterface)Marshal.GetDelegateForFunctionPointer(functionHandle, typeof(FortnarInterface));
//call your fortran function
function(your arguments here)
Kernel32.FreeLibrary(dllHandle);

Making C# library using native Windows API

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.

GetProcAddress returns 0 for the function name "RtlGetLastErrorString" in EasyHook32.dll?

Opening the source code of EasyHook project, I can see that there are some DllImports importing the function RtlGetLastErrorString like this:
//DllName here is a const string equaling to "EasyHook32.dll"
[DllImport(DllName, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public static extern String RtlGetLastErrorString();
So I assumed that function should exist. However the following code does not work expectedly (GetProcAddress returns 0):
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr GetModuleHandle(String path);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string path);
var mh = LoadLibrary("pathToEasyHook32.dll");
var procAddr = GetProcAddress(mh, "RtlGetLastErrorString");
mh is successfully returned. I've also tried different variant names: RtlGetLastErrorStringA, RtlGetLastErrorStringW but it's still the same.
GetLastError shows that the function name could not be found (error code: 127). So I believe it actual does not exist. But I wonder why the DllImport seems to find that function?
Is there any tool to view that function entry inside EasyHook32.dll? I've tried using OllyDbg but I'm not sure I used it well, I've tried viewing the Executable modules but they all look like the underlying modules loaded from Windows. I've tried finding all names in all modules and it seemed that there wasn't any entry named RtlGetLastErrorString. So looks like my understanding about DllImport has something wrong. At least the way DllImport obtains the function is different from the GetProcAddress does. It would be very nice if you could give me some explanation on this problem. Thank you very much!
You could use dependency walker to view the exported function name for dll.
The correct name for RtlGetLastErrorString is _RtlGetLastErrorString#0.

C# GetProcAddress Returns Zero

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.

Categories

Resources