Quick Search for:  in language:    
DLL,seen,methods,calling,DLLs,runtime,require
   Code/Articles » |  Newest/Best » |  Community » |  Jobs » |  Other » |  Goto » | 
CategoriesSearch Newest CodeCoding ContestCode of the DayAsk A ProJobsUpload
Delphi Stats

 Code: 209,911. lines
 Jobs: 14. postings

 How to support the site

 
Sponsored by:

 
You are in:
 

Does your code think in ink?
Login





Latest Code Ticker for Delphi.
Record From Microphone to Wave File
By Una-Rat on 11/25


List Manager
By james hill on 11/17


Click here to see a screenshot of this code!Skin app
By Jonathan Curry on 11/15

(Screen Shot)

Click here to put this ticker on your site!


Add this ticker to your desktop!


Daily Code Email
To join the 'Code of the Day' Mailing List click here!

Affiliate Sites



 
 
   

Loading and calling DLLs at runtime

Print
Email
 

Submitted on: 8/23/2002 5:51:43 PM
By: James Mistry  
Level: Advanced
User Rating: By 1 Users
Compatibility:Delphi 5

Users have accessed this article 5821 times.
 
(About the author)
 
     I have seen a few methods of calling DLLs at runtime that have required the programmer to know the name of the DLL and function signature (arguments) before compilation, but this is no good if you want to call a function you know nothing about. For some reason the topic is very poorly documented by Microsoft so, armed with some old C++ code and a couple of beers I set out to make a function that would call an export from a DLL with absolutely no information required at design time. The DLL's file name, the name of the function and the arguments are all supplied at runtime. Supports most integer data types as well as Single and PChar (see article for details).

 
 
Terms of Agreement:   
By using this article, you agree to the following terms...   
1) You may use this article in your own programs (and may compile it into a program and distribute it in compiled format for languages that allow it) freely and with no charge.   
2) You MAY NOT redistribute this article (for example to a web site) without written permission from the original author. Failure to do so is a violation of copyright laws.   
3) You may link to this article from another website, but ONLY if it is not wrapped in a frame. 
4) You will abide by any additional copyright restrictions which the author may have placed in the article or article's description.
DLL By Name

{
DLL By Name for Delphi
by James Mistry
Copyright © 2002, James Mistry. All rights reserved.
DLL By Name allows you to access a DLL's function by specifying the DLL
name, the function name and the arguments. DLL By Name handles most data
types including Single and is ideal for creating a DLL-based plugin system
or implementing DLL support in a scripting language.
This was inspired by a C++ version by Adam Straughan.
Unlike C++, however, this function could easily be implemented into a DLL
and accessed from other languages.
You're free to do what you like with the code as long as the original
copyright stays on if you distribute it.
Credit to me is not compulsory in compiled code, but it would be nice - I
don't get much motivation.
You should have seen my victory dance when I got this to work: never has a
fat man moved so fast...
Well, if you think this is complicated I'd advise you stay away from the
VB version unless you enjoy programming in hexadecimal ASM (ughh).
Known Bugs/limitations:
1. In C++, Char and Char* (pointer) can be used. However, only PChar would work with Char* in Delphi, not
^PChar so I'm left with an equivalent for Char*, but not Char. I'm not a C++ programmer by birth so if anyone else knows
a data type I can use as an equivalent to Char, I'd appreciate an e-mail (jm@sandown-software.com).
2. At the moment the function isn't configured for use with custom types but a few adaptations (maybe in the next
version) would fix that.
3. The only Real data type presently supported is Single. Because no other Real types only occupy 4 bytes in memory,
they wouldn't fit into the temporary buffer I assign using a Longword. However, a couple of small changes should
enable larger types. I'll implement this into version 2.
}

uses
Windows, Dialogs;
type //This is an enumeration type
ARGTYPE = (ARG_NONE = -1, // Terminator or no arguments
ARG_UI1 = 0, // unsigned char
ARG_I1, // signed char
ARG_UI2, // unsigned short
ARG_I2, // signed short
ARG_UI4, // unsigned long
ARG_I4, // signed long
ARG_R4, // float
ARG_PUI1, // unsigned char* (pointer)
ARG_PI1, // signed char* (pointer)
ARG_PUI2, // unsigned short* (pointer)
ARG_PI2, // signed short* (pointer)
ARG_PUI4, // unsigned long* (pointer)
ARG_PI4, // signed long* (pointer)
ARG_PR4); // float* (pointer)
type
DLL_ARG = record //Implements data types defined in ARGTYPE
case eType: ARGTYPE of //Here we have the equivalent of absolute addressing in types (union structs in C++)
//eType is treated as a member of DLL_ARG and implicitly declared in the case statement
ARG_UI1: (ucVal: PChar); //I don't know what data type to use for this: it's supposed to be the same as a C++ char*
ARG_PUI1: (pucVal: PChar);
ARG_I1: (cVal: ShortInt);
ARG_PI1: (pcVal: ^ShortInt);
ARG_UI2: (usVal: Word);
ARG_PUI2: (pusVal: ^Word);
ARG_I2: (sVal: SmallInt);
ARG_PI2: (psVal: ^SmallInt);
ARG_UI4: (ulVal: Longword);
ARG_PUI4: (pulVal: ^Longword);
ARG_I4: (lVal: Integer);
ARG_PI4: (plVal: ^Integer);
ARG_R4: (fltVal: Single);
ARG_PR4: (pfltVal: ^Single);
end;

implementation
//************************ Notes ************************
//Reals CAN be returned using the Single type
//which occupies 4 bytes - the same as Longword (unsigned int in C++), the
//data type used for temporary storage in the function call.
//If you want to be able to pass/return anything bigger than Longword
//(e.g. Currency) then you're going to have to re-design the data type
//system I've used.
//It is possible to pass/return custom types but you'd have to
//look into some kind of type-checking system. The memory copying shouldn't
//cause a problem as long as you find the size of any types used. The
//safest way to do this is with SizeOf.
function CallDLLByName(szLibrary, szFunction: PChar; Arguments: array of DLL_ARG; nArgCount: Integer; var pRetVal: DLL_ARG): Integer; cdecl; //Returns error code
var
pFun: Integer; //The address of the procedure
dwTemp: Longword; //Temporary storage - big enough for all types
dwRet: Longword; //What to return
i: Integer; //Iterator for arguments
m_hDLL: Integer; //The handle to the DLL
Begin
pFun := 0;
dwRet := 0;
m_hDLL := 0;
m_hDLL := LoadLibrary(szLibrary); //Load the DLL
if m_hDLL = 0 then //Load error: DLL either wasn't found or couldn't be loaded
Begin
MessageBox(Form1.Handle, 'DLL load error.', 'Error', 48);
Result := 1; //LoadError
exit;
end;
pFun := Integer(GetProcAddress(m_hDLL, szFunction)); //Cast the return value of GetProcAddress to Integer and assign it to pFun
if pFun <> 0 then //Procedure address was obtained successfully
Begin
//Loop through the arguments in reverse
for i := High(Arguments) downto Low(Arguments) do
Begin
//Copy data to the temporary buffer
CopyMemory(@dwTemp, @Arguments[i].lVal, SizeOf(Longword));
//Note that SizeOf can return the amount of memory a specified data type
//occupies, hence SizeOf(Longword)
//Now put it on the stack (I wish I was a pixie)

asm push dwTemp end;
end;
if pRetVal.eType = ARG_R4 then
Begin
asm
call dword ptr [pFun] //Call the function
fstp dword ptr [dwRet] //Perform a store-and-pop
end;
end
else
Begin

asm //In C++ we could have just used dwRet = (pFun)() but not in Delphi
call dword ptr [pFun] //Call the function
mov dword ptr [dwRet], eax
end;
end;
//Unload the stack by looping through Arguments
for i := Low(Arguments) to High(Arguments) do
Begin
asm pop dwTemp end;
//We have to copy the data back because values might have been
//changed

CopyMemory(@Arguments[i].cVal, @dwTemp, SizeOf(Longword));
end;
if pRetVal.eType <> ARG_NONE then //Return the value if requested
Begin
pRetVal.eType := ARG_I4; //Use the entire buffer
pRetVal.lVal := dwRet;
end;
end
else
Begin

ShowMessage(szLibrary + ' does not export a function called "' + szFunction + '".');
Result := 2; //FunctionError
exit;
end;
Result := 0; //Success
end;
procedure Test;
var
GetRet: DLL_ARG;
buf: PChar;
Args: array[0..2] of DLL_ARG;
begin
Args[0].eType := ARG_UI4;
Args[1].eType := ARG_PUI1;
Args[2].eType := ARG_UI4;
Args[0].ulVal := 3912; //Replace this with the hWnd of a window in decimal
Args[2].ulVal := 20; //The buffer size for received text
Args[1].pucVal := '12345678901234567890'; //Populate the buffer
GetRet.eType := ARG_UI4; //We want a return value of type Longword
CallDLLByName('C:\WINDOWS\SYSTEM\user32.dll', 'GetWindowTextA', Args, 3, GetRet); //Call GetWindowTextA
ShowMessage(String(Args[1].pucVal)); //Show the result
end;
Begin

end
.

 
Report Bad Submission
Use this form to notify us if this entry should be deleted (i.e contains no code, is a virus, etc.).
Reason:
 
Your Vote!

What do you think of this article(in the Advanced category)?
(The article with your highest vote will win this month's coding contest!)
Excellent  Good  Average  Below Average  Poor See Voting Log
 
Other User Comments
8/23/2002 6:47:22 PM:Mark Deacon
Good code. This has added an invaluable feature to my application.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
8/23/2002 7:04:30 PM:James Mistry
Thankyou, Mark, for the comment, you beat me to it.<br> I'd just like to say that if you find any bugs or have any suggestions, don't hesitate to e-mail me at jm@sandown-software.com.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
8/23/2002 10:59:36 PM:SANDOWN Software
If you want me to update this code with the new proposed features then please vote and leave comments to tell me what you think of it.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
8/26/2002 8:02:05 AM:gridrun
Is there actually a VB version of this code??
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
8/26/2002 11:06:00 AM:James Mistry
Hi gridrun and thanks for your interest in my code. In answer to your question, yes, there is a VB code version that is similar to DLL By Name, although as I said in the article, it uses Hex ASM and isn't exactly the most stable of code. Despite this, it does the job as well as possible in VB so unless you want to use a C++/Delphi DLL, it's your only option. I haven't got an exact address where you can get the code, but I do know it's on PSC, was written by MaRiØ Glez Serrano and is called API without Declare. If you search for it in the VB section you should find it. James.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
8/26/2002 11:29:58 AM:James Mistry
There is a small(?) bug in the code. Just add the following line to the end of the CallDLLByName function: FreeLibrary(m_hDLL);
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
12/9/2002 9:10:30 AM:
wow, amazing what you stumble across in this big internet thingy. An artical inspired by something I wrote so long ago I'd forgotten :-) adam straughan
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
12/9/2002 9:29:26 AM:James Mistry
Thanks Adam, both for the comment and the original C++ code which inspired my article. This big Internet thingy is kinda useful, isn't it?
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
3/4/2003 3:03:01 AM:
Perhaps I'm missing something. Using the handle obtained from form.handle does not work. I've tested it in NT and XP running Delphi 6. Sad, since I could use a great function like this one.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
3/8/2003 7:36:14 AM:James Mistry
Using the handle obtained from form.handle for what? Please elaborate.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
3/20/2003 6:11:10 AM:
I've created an example program who uses a form. Calling your routine, it should return the capion of your form, but it doesnt. The handle passed to your routine is form1.handle (using the windows api directly does give me the correct answer). I've mailed you the source.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
3/23/2003 11:43:47 AM:James Mistry
There are problems with the code on NT-class operating systems. I'll be posting an update with a solution to the problem shortly. Please watch this space.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
4/28/2003 4:40:45 AM:
Just curious, have you found the reason of the trouble yet? Albert
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
4/30/2003 2:27:09 PM:James Mistry
I'm really sorry but I haven't had a chance to go back to the code. But don't worry, I will get round to it soon! James.
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
8/12/2003 3:26:43 AM:
Hi, Still investigating. Found some time to look into the problem yet? If not, can you give some clues I might get into it? Albert
Keep the Planet clean! If this comment was disrespectful, please report it:
Reason:

 
Add Your Feedback!
Note:Not only will your feedback be posted, but an email will be sent to the code's author in your name.

NOTICE: The author of this article has been kind enough to share it with you.  If you have a criticism, please state it politely or it will be deleted.

For feedback not related to this particular article, please click here.
 
Name:
Comment:

 

Categories | Articles and Tutorials | Advanced Search | Recommended Reading | Upload | Newest Code | Code of the Month | Code of the Day | All Time Hall of Fame | Coding Contest | Search for a job | Post a Job | Ask a Pro Discussion Forum | Live Chat | Feedback | Customize | Delphi Home | Site Home | Other Sites | About the Site | Feedback | Link to the Site | Awards | Advertising | Privacy

Copyright© 1997 by Exhedra Solutions, Inc. All Rights Reserved.  By using this site you agree to its Terms and Conditions.  Planet Source Code (tm) and the phrase "Dream It. Code It" (tm) are trademarks of Exhedra Solutions, Inc.