|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Introduction
Consider the situation where the native, Win32 DLL is unknown at compile time.
Perhaps its name or location is stored in the Windows Registry, or is selected by
the user from a FileOpen dialog. How can we call a function exported from this library, but
only resolved at runtime? The prescribed way to call native functions from the CLR is through
PInvoke, using the LoadLibraryWindows provides two ways to load DLLs into the process of an executable. Either, the DLL
can be specified in the imports table and the Windows Loader will map the DLL automatically or the
We can quite happily call
The CLR does allow us to do the reverse and pass a pointer to a managed function to a DLL using the Going low-levelOne solution would be to write a small C++ DLL which merely forwards the call on. In other words, the C++ DLL is acting as a proxy for our intended function. The downside is that a new C++ DLL would have to be created every time a different DLL function needs to be called. The proxy function needs the exact number of parameters that the real function takes. Every C# programmer needs to know C++ to be able to do this.
A much better solution is to write a small, reusable DLL in x86 assembly language which can forward function calls
to any location. This is trivial to write if we know a bit about how Win32 DLLs are called. All DLLs are called
using the Consider the following function declaration: [DllImport("Invoke", CharSet=CharSet.Unicode)] public extern static int InvokeFunc(int funcptr, int hwnd, string message, string title, int flags);It is implemented in a DLL called Invoke.dll and has the export name InvokeFunc.
It also takes five parameters, of which the last four are the exact parameters taken by the MessageBox() function.
The first parameter is an address of a function. We will leave the implementation of
InvokeFunc for now and look
at code which can call this.
int hmod=LoadLibrary("User32"); int funcaddr=GetProcAddress(hmod, "MessageBoxW"); int result=InvokeFunc(funcaddr, 0, "Hello World", ".Net dynamic export invocation", 1 /*MB_OKCANCEL*/); Console.WriteLine("Result of invocation is " + result); FreeLibrary(hmod);This code loads the DLL into our process space, finds the address of a function we wish to call, then uses our special
InvokeFunc() function to call a function through a function pointer.
In the screenshot above, notice how InvokeFunc Implementation
As we discussed earlier, the The following fragment of x86 assembler achieves this pop ecx ; save return address pop edx ; Get function pointer push ecx ; Restore return address jmp edx ; Transfer control to the function pointerBecause we've used a jmp instruction rather than a call instruction, control will be transferred directly from the called function back to the CLR, passing any return value directly back. ConclusionEverything needed to compile and run this code is included with Visual Studio .Net. The x86 DLL built can be reused for calling any function pointer, not just a function with a specific signature and is only 2,560 bytes.
It turns out that if
you are not running .Net on Windows XP, there is a DLL with an equivalent proxy function to the one we built.
That DLL is
|
||||||||||||||||||||||