Login
Latest Code Ticker for Delphi.
Daily Code Email
|
|
|
| | 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 | | | Your Vote! |
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.
| 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.
| 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.
| 8/26/2002 8:02:05 AM:gridrun Is there actually a VB version of this
code??
| 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.
| 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);
| 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
| 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?
| 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.
| 3/8/2003 7:36:14 AM:James Mistry Using the handle obtained from
form.handle for what? Please elaborate.
| 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.
| 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.
| 4/28/2003 4:40:45 AM: Just curious, have you found the reason
of the trouble yet?
Albert
| 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.
| 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
| | 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. | | |
|