Click here to Skip to main content
11,478,896 members (64,407 online)
Click here to Skip to main content

Late binding on native DLLs with C#

, 12 Nov 2001 BSD 498.6K 5K 94
Rate this:
Please Sign up or sign in to vote.
Delaying which DLL export to call until runtime is not possible with C#. This article shows you how to make it possible.
<!-- Download Links --> <!-- Article image -->

Sample Image - DynInvok.gif

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 DllImport attribute, but this must be declared at compile time or, at the very least, generated on the fly using Reflection.Emit. This article will show an alternative way which requires the use of a little x86 assembler to meet our goal.

LoadLibrary

Windows 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 LoadLibrary() Win32 API call can be used. These are called implicit linking and explicit linking respectively. Both these types can be seen in action using Dependency Walker available from http://www.dependencywalker.com/. The CLR, Visual BASIC 6 and the /delayload feature of MSVC6 all use explicit linking to call DLL functions.

We can quite happily call LoadLibrary() from C# to load a DLL into our address space. The problem comes when we try to call a function in the DLL. Win32 provides the GetProcAddress() function to return the memory address of a function exported from the given DLL and we can easily obtain this memory address, but we can do nothing with it. It is simply an integer. The CLR provides no way to jump to this location in memory, passing appropriate parameters too.

The CLR does allow us to do the reverse and pass a pointer to a managed function to a DLL using the delegate keyword, but there is no way to specify that a value returned from an unmanaged API call should be treated as a delegate. Perhaps we may see this in .Net version 2, but for now we need to find another way to call the function.

Going low-level

One 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 stdcall calling convention. This means that parameters are pushed onto the stack beginning at the right-most parameter. Thus, the first in the parameter list will be at the top of the stack. The return address is then placed on the stack and control is transferred to the callee. It is the callee's responsibility to pop all the parameters off the stack and not fiddle with more registers than absolutely necessary.

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.

Screenshot of Dependency Walker watching this taking place

In the screenshot above, notice how GetProcAddress() is being used to find the address of GetProcAddress()! This is because PInvoke uses GetProcAddress to find the address of any function specified by the DllImport attribute.

InvokeFunc Implementation

As we discussed earlier, the stdcall calling conventions places parameters onto the stack in reverse order. Thus, our function pointer will be at the top of the stack because it is first in the parameter list. If we can take this parameter off the stack, then jump to that location in memory, it would be the equivalent of calling that function without the intermediate proxy function.

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 pointer
Because 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.

Conclusion

Everything 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 msjava.dll, which of course is missing from Windows XP due to the Microsoft-Sun agreement on Java technology. msjava.dll provides an export with the name call() which duplicates this functionality.

License

This article, along with any associated source code and files, is licensed under The BSD License

Share

About the Author

Richard Birkby
Web Developer
United Kingdom United Kingdom
Richard Birkby is a software engineer from London, UK, specializing in .Net. Richard has coded for many different sized companies from small venture-capital funded start-ups, to multi-national corporations (ie Microsoft). When he's not programming, he enjoys driving his sports car or eating curry (although never at the same time!).

Richard helps run CurryPages.com and has several other covert ventures in development. Stay tuned!

Comments and Discussions

 
QuestionHow to work on X64? Pin
4398850681-Oct-13 23:46
member4398850681-Oct-13 23:46 
AnswerRe: How to work on X64? Pin
ahmed zahmed11-Oct-13 1:14
memberahmed zahmed11-Oct-13 1:14 
QuestionLoading unmanaged DLL from memory Pin
FlyersWeb5-Nov-12 11:19
memberFlyersWeb5-Nov-12 11:19 
AnswerRe: Loading unmanaged DLL from memory Pin
Shawn-USA5-Aug-13 16:11
memberShawn-USA5-Aug-13 16:11 
SuggestionDynamically calling an unmanaged dll from .NET (C#) Pin
Damavand2-Mar-12 5:08
memberDamavand2-Mar-12 5:08 
GeneralRe: Dynamically calling an unmanaged dll from .NET (C#) Pin
Richard Birkby2-Mar-12 7:09
memberRichard Birkby2-Mar-12 7:09 
QuestionI had to change the makefile a bit to path to Csc.exe Pin
dcarl66117-Aug-11 7:19
memberdcarl66117-Aug-11 7:19 
GeneralLoading User dlls Pin
Debojyoti Majumder14-Jan-11 2:46
memberDebojyoti Majumder14-Jan-11 2:46 
QuestionCreate invoke.dll on 64-bit machines Pin
Alexander Pikus3-Oct-10 4:58
memberAlexander Pikus3-Oct-10 4:58 
AnswerRe: Create invoke.dll on 64-bit machines Pin
4398850681-Oct-13 14:47
member4398850681-Oct-13 14:47 
GeneralDon't do this any more Pin
Moxxis7-Jun-10 9:42
memberMoxxis7-Jun-10 9:42 
Questionwithout asm? Pin
Unruled Boy18-Dec-09 2:59
memberUnruled Boy18-Dec-09 2:59 
GeneralEasy way (.Net Marshaling) Pin
JonyRocketCZ25-Nov-09 4:42
memberJonyRocketCZ25-Nov-09 4:42 
GeneralRe: Easy way (.Net Marshaling) Pin
Richard Birkby25-Nov-09 4:49
memberRichard Birkby25-Nov-09 4:49 
GeneralRe: Easy way (.Net Marshaling) Pin
Unruled Boy18-Dec-09 2:58
memberUnruled Boy18-Dec-09 2:58 
Questioncall fun when not add references Pin
huuchau8419-Mar-09 20:07
memberhuuchau8419-Mar-09 20:07 
I have a lybrary:
namespace LBR
{
public class class1
{
public string getstr(string _str)
{
return _str + "test ok";
}
}
}
Buil:--> "LBR.DLL"
Creat a new project
not add reference file LBR.DLL to Bin, I copy LBR.DLL to "C:\\LBR.DLL". can i call fun getstr() in class "class1"???? Confused | :confused:
thanks!!!!!!
AnswerExcellent article Pin
defconhaya7-Mar-09 5:03
memberdefconhaya7-Mar-09 5:03 
GeneralRe: Excellent article Pin
4398850681-Oct-13 14:49
member4398850681-Oct-13 14:49 
GeneralDynamicly load a DLL functions Pin
Member 453941111-Feb-09 7:41
memberMember 453941111-Feb-09 7:41 
QuestionLINK: error LNK2001: unresolved external symbol _DLLMain Pin
nate alwine12-Nov-08 12:33
membernate alwine12-Nov-08 12:33 
QuestionDLLImport Pin
gclopes18-Sep-08 8:24
membergclopes18-Sep-08 8:24 
QuestionHow to compile code written in assembler language? Pin
Member 6628769-Jul-08 23:19
memberMember 6628769-Jul-08 23:19 
GeneralIs it possible to elevate this process for Vista Pin
yincekara11-Jun-08 7:12
memberyincekara11-Jun-08 7:12 
GeneralCompile time linking of dll Pin
harsh290414-Apr-08 23:31
memberharsh290414-Apr-08 23:31 
GeneralRe: Compile time linking of dll Pin
Mike_Silver_A2-Oct-08 1:53
memberMike_Silver_A2-Oct-08 1:53 
QuestionWhere i can find "Invoke.dll"? Pin
tropot28-Jun-07 23:20
membertropot28-Jun-07 23:20 
AnswerRe: Where i can find "Invoke.dll"? Pin
Richard Birkby28-Jun-07 23:37
memberRichard Birkby28-Jun-07 23:37 
GeneralRe: Where i can find "Invoke.dll"? Pin
tropot29-Jun-07 1:52
membertropot29-Jun-07 1:52 
GeneralRe: Where i can find "Invoke.dll"? Pin
dark_hunter6-Aug-07 14:09
memberdark_hunter6-Aug-07 14:09 
GeneralRe: Where i can find "Invoke.dll"? Pin
kandy_soliton22-Aug-07 19:44
memberkandy_soliton22-Aug-07 19:44 
GeneralRe: Where i can find "Invoke.dll"? Pin
Mike_Silver_A2-Oct-08 1:53
memberMike_Silver_A2-Oct-08 1:53 
GeneralRe: Where i can find "Invoke.dll"? Pin
giger12-Apr-10 12:35
membergiger12-Apr-10 12:35 
GeneralRe: Where i can find "Invoke.dll"? Pin
LastMandg419-Jan-11 1:58
memberLastMandg419-Jan-11 1:58 
GeneralReturning Values Pin
Diogo Alves25-Jun-07 7:37
memberDiogo Alves25-Jun-07 7:37 
Generalinvoking dll in c#.net dynamically Pin
pksave10-May-07 0:32
memberpksave10-May-07 0:32 
GeneralRe: invoking dll in c#.net dynamically Pin
SDX200025-May-07 3:34
memberSDX200025-May-07 3:34 
GeneralRe: invoking dll in c#.net dynamically Pin
Member 473280619-Mar-08 3:18
memberMember 473280619-Mar-08 3:18 
GeneralIs it possible Pin
just2click21-Aug-06 4:35
memberjust2click21-Aug-06 4:35 
QuestionCan I use .Net Dll in Borland C++ Pin
jags_vc5-Aug-05 2:29
memberjags_vc5-Aug-05 2:29 
GeneralThere is a Way inside of C# (Well Sorta) Pin
Scott Carr15-Apr-05 6:24
memberScott Carr15-Apr-05 6:24 
GeneralRe: There is a Way inside of C# (Well Sorta) Pin
Scott Carr15-Apr-05 6:28
memberScott Carr15-Apr-05 6:28 
GeneralRe: There is a Way inside of C# (Well Sorta) Pin
Tommy Svensson17-May-05 0:34
memberTommy Svensson17-May-05 0:34 
GeneralRe: There is a Way inside of C# (Well Sorta) Pin
Scott Carr17-May-05 4:58
memberScott Carr17-May-05 4:58 
GeneralRe: There is a Way inside of C# (Well Sorta) Pin
nyf1220@126.com15-Nov-06 20:15
membernyf1220@126.com15-Nov-06 20:15 
GeneralRe: There is a Way inside of C# (Well Sorta) Pin
Unruled Boy5-May-09 4:44
memberUnruled Boy5-May-09 4:44 
Generalneed the makefile as post build event Pin
daniele_massaro7-Mar-05 7:31
memberdaniele_massaro7-Mar-05 7:31 
Questiongetting an object pointer? activating it? Pin
looknir23-Jan-05 7:36
memberlooknir23-Jan-05 7:36 
QuestionHave you seen this article on MSDN? Pin
Tboner24-Nov-04 12:17
memberTboner24-Nov-04 12:17 
AnswerRe: Have you seen this article on MSDN? Pin
Tboner25-Nov-04 5:06
memberTboner25-Nov-04 5:06 
GeneralUsing Invoke.dll in WebServices Pin
sedel22-Nov-04 5:00
membersedel22-Nov-04 5:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150520.1 | Last Updated 13 Nov 2001
Article Copyright 2001 by Richard Birkby
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid