I´m writing a global keyboard hook in Delphi 2007 and Windows 10. I´ve found different signatures for a Delphi LowLevelKeyboardProc callback. Someones like this and windows documentation (https://learn.microsoft.com/es-es/previous-versions/windows/desktop/legacy/ms644985(v=vs.85))
LowLevelKeyboardProc() never is executed
prompt that lparam is a pointer to a TKBDLLHOOKSTRUCT record.
Other ones however (http://www.delphifaq.com/faq/delphi_windows_API/f512.shtml and https://www.swissdelphicenter.ch/en/showcode.php?id=1722), show
function KeyHookFunc(Code, VirtualKey, KeyStroke: Integer): LRESULT; stdcall;
completely ignoring the TKBDLLHOOKSTRUCT record pointer.
Which one is correct?
I'm guessing the first one is the correct one but I need to be sure. I´ve been using such signature but when I call another Function in the same unit (and DLL) passing lParam it causes Access Violation when accessing it. All functions and procedures within the DLL use stdcall
All of the callback declarations are correct, although the latter two links do not give examples of LowLevelKeyboardProc but KeyboardProc. IOW, they are not low level keyboard hooks.
Note the latter two wouldn't work in 64 bits due to Integer usage instead of WPARAM, LPARAM and LRESULT.
Related
Testing this code:
procedure fii(i:integer);
var
Hbar: Thandle;
Foo: procedure (X: Integer); //i removed the stdcall here!
begin
Hbar := LoadLibrary('BAR.DLL');
if Hbar >= 32 then { success }
begin
Foo := GetProcAddress(HBar, 'FOO');
...
Foo(i); // if we debug trace this to the call of the dll we get not desired value
...
FreeLibrary(HBar);
end
else
MessageDlg('Error: could not find BAR.DLL', mtError, [mbOk], 0)
end.
What will happen if we dropped the stdcall in 32 bit or 64 bit ?
The answer is , the value $18f2dc passed into x. If its a string, it will point to position of $18f2dc ,and try to extract the string garbage from there.
This happens because Delphi just pass Pointer(s) in the code.
It could changed from run to run and not be $18f2dc, but its a 3 byte that are passed. (if you pass int its $18f2dc in decimal 1635036).
Does this number has a unique meaning?
Why is stdcall needed?
What will happen if we dropped the stdcall in 32 bit?
When no calling convention is specified, then the default of register is used. Since this does not match the true calling convention, then garbage will be passed in the parameters.
In this particular case, the function that you call expects the parameters to be passed on the stack. The function will therefore read an integer from the stack. On the other hand, the calling code assumes register calling convention and puts the argument in the EAX register. Because the calling code did not push the argument onto the stack, the function gets what just happens to be on the stack when the function is called.
To be honest, there's not really much point in trying to reasons about ABI(Application Binary Interface - the interface between two program modules) mismatches like this. You would really try to reason about what would happen if you declared a different number of arguments on each side of the interop(Interoperability - property of a product or system, whose interfaces are completely understood, to work with other products or systems) boundary, and that's really little different from using the wrong calling convention. With binary interop you just have to make sure that everything is matching: argument types, return value types, function names, calling convention, the values passed to the function, etc.
What will happen if we dropped the stdcall in 64 bit?
There is a single calling convention for the Windows x64 platform. The calling convention directives are ignored by the compiler.
Although the 64 bit compiler ignores these directives it is still good practise to include them. That way your code will work when you compile it for 32 bit.
I'm attempting to call GetStringTypeW from a non-unicode delphi application and, no matter what I do, I get ERROR_INVALID_FLAGS back. I couldn't find any code sample of that function in use either.
I also had to redifine the function header because the ones provided in windows.pas incorrectly identifies the 3rd parameter as a boolean (it's an INT)
here is my definition for the function:
function GetStringTypeW(dwInfoType: DWORD; lpSrcStr: PWideChar; cchSrc: Integer; lpCharType: Pointer): BOOL;
(For some reason, that function isn't defined as stdcall. Trying it to define it as stdcall will result in an access violation.)
And my call:
var
aCharType: Array of WORD;
APassword: WideString
begin
{..}
SetLength(aCharType, Length(APassword));
if not GetStringTypeW(CT_CTYPE1, PWideChar(APassword[1]), Length(APassword), #aCharType[0]) then
RaiseLastOSError;
{..}
The error I get is
System Error. Code: 1004.
Invalid flags.
I've verified that CT_CTYPE1 is equal to 1.
Does anyone know what could be wrong or have a code sample for using this function ?
The declaration in Windows.pas is indeed wrong, but your correction is wrong, too. You've fixed the parameter types, but you need to fix the calling convention. It should be stdcall:
function GetStringTypeW(
dwInfoType: DWORD;
const lpSrcStr: PWideChar;
cchSrc: Integer;
lpCharType: PWordArray
): BOOL; stdacll;
Without the calling-convention fix, Delphi puts most of the parameters in registers, but the OS expects to find them on the stack. The stack doesn't contain the right values at the right places, to it fails. A bad calling convention can also lead to access violations because the stack is left in an inconsistent state, but since you immediately throw your own exception anyway, that might conceal the stack problems.
When you use stdcall, you get an access violation because you're passing a character and claiming it's a pointer. The OS attempts to dereference the "pointer," but since the character value doesn't represent a valid address, it fails. Type-cast the whole string, not just one character, when you call the function:
GetStringTypeW(CT_CTYPE1, PWideChar(APassword), Length(APassword), PWordArray(aCharType))
Your second parameter is wrong. You are type-casting a single WideChar value to a PWideChar, instead of obtaining the memory address of that WideChar. In other words, change this:
PWideChar(APassword[1])
To this:
PWideChar(APassword)
Or this (only if the length of APassword is never 0):
#APassword[1]
I am using Delphi 2010. Is it possible to tell Delphi to not generate a prologue for a function? I'm writing some pure assembly functions like this:
procedure SomeAssembly; stdcall;
begin
asm
...
end;
end;
and I would like to tell Delphi not to generate a prologue and epilogue for this function, like C++'s __declspec(naked) feature.
And so no one wastes their time, I don't need help getting these functions to work with the prologue; I can already do that. It's just a large inconvenience and will make maintenance an huge hassle. I'll have to manually inspect the prologues generated by the compiler to see their length, and if that changes, my program will crash.
I also know I can write the function as a series of bytes in a byte array, but that would be even worse than having to go find the length of Delphi's prologue.
Delphi doesn't generate prologues or epilogues for functions having no arguments and declared with the register calling convention. If you want functions without prologues, declare them as zero-argument, register-calling-convention functions. Also, skip the begin-end block and go straight into assembly.
procedure SomeAssembly; // register; (implied)
asm
// ...
end;
Since you're effectively lying about the nature of the functions, calling them may be tricky. If you've implemented a function as though it received parameters and used a different calling convention, then you'll have to make sure the compiler knows about that at the call site. To do that, declare a function pointer that reflects the "real" type of your function instead of the declared type. For example, if your function is really a two-argument stdcall function, declare something like this:
type
TSomeAssemblyFunc = function (Arg1: Integer; Arg2: PAnsiChar): Boolean; stdcall;
var
SomeAssemblyProc: TSomeAssemblyProc;
Now, assign that variable so it points at your function:
SomeAssemblyProc := TSomeAssemblyProc(#SomeAssembly);
if SomeAssembly(2, 'foo') then ...
In addition to skipping the prologue and epilogue, the compiler will generate the incorrect RET instruction for this function (because of the different calling convention), so you'll have to make sure you say ret 8 in your code instead of letting the compiler's default ret instruction occur.
Finding the length of Delphi's prologue is trivial, if you have a working debugger:
Set a breakpoint at the start of the function.
Call the function.
When the debugger stops at the breakpoint, switch to the CPU view.
Look at the instructions that make up the prologue.
Count the bytes displayed beside those instructions.
According to the this embarcadero docwiki you can skip the surrounding begin and end and the compiler will skip some of it's stuff. But if you really want pure assembler, why not put your function into a separate assembler file, assemble it with tasm (the exe is named tasm32) and link to it. You'll then use the assembler directive in the delphi code.
Doesn't
procedure SomeAssembly; stdcall;
asm
...
end;
do the trick?
In extension to this question, I guess I'll best show what I've got so far.
What I'm trying to do is create a Firefox extension with Delphi, that'll work with the Firefox versions of the future that will use an exported NSModule structure, and no longer an NSGetModule function.
Main questions I'm struggling with for the moment is:
Is the code below correct? I may be wrong with how the pointers and arrays of records work.
How to debug this? If I build it and it runs then I'm kind of sure it'll work, but in debugging my library I can only check if my init code does its job. (and for now, Firefox 3.6 doesn't seem to pick up my #mozilla.org/network/protocol;1?name=xxm contract)
The code I'm trying to port is here:
http://mxr.mozilla.org/mozilla-central/source/xpcom/components/Module.h
type
TConstructorProcPtr=function(aOuter:nsISupports;const aIID:TGUID;var aResult:pointer):nsresult;
TLoadFuncPrt=function:nsresult;
TUnloadFuncPrt=procedure;
TCIDEntry=record
cid:TGUID;
service:boolean;
getFactoryProc:pointer;//TGetFactoryProcPtr;
constructorProc:TConstructorProcPtr;
end;
TContractIDEntry=record
contractid:PChar;
cid:TGUID;//PGUID?
end;
TCategoryEntry=record
category,entry,value:PChar;
end;
TXPCOMModule=packed record
kVersion:integer;//=1;
mVersion:cardinal;//kModuleVersion
mCIDs:^TCIDEntry;//pointer to first in array, last should be nil
mContractIDs:^TContractIDEntry;//pointer to first in array, last should be nil
mCategoryEntries:^TCategoryEntry;//pointer to first in array, last should be nil
getFactoryProcPtr:pointer;//TGetFactoryProcPtr;
loadProc:TLoadFuncPrt;
unloadProd:TUnloadFuncPrt;
end;
You almost certainly need the cdecl calling convention on all your procedure- and function-pointer declarations:
TConstructorProcPtr = function(aOuter: nsISupports; const aIID: TGUID; var aResult: Pointer): nsresult; cdecl;
TLoadFuncPrt = function: nsresult; cdecl;
TUnloadFuncPrt = procedure; cdecl;
I assume you've declared nsISupports as a Delphi interface. Otherwise, you need to make sure the aOuter parameter above is a pointer as it is in the C++ code.
For TContractIDEntry, and all the other places where you use PChar, I advise you to use PAnsiChar instead. The size of Delphi's Char type changed a couple of years ago, but the C++ char is and always will be one byte, so use Delphi's one-byte character type explicitly. Also, your comment wondering whether to declare the cid field as a PGUID was correct; asterisk means pointer.
TContractIDEntry = record
contractid: PAnsiChar;
cid: PGUID;
end;
The kVersion field should not be a member of the record you declare. In C++, it's a static member, which means it occupies no space in the structure itself; it's shared by all instances of that type. It's equivalent to a class field in a Delphi class, but I don't think records offer that feature. Make it a unit-level variable instead of a field.
I have to use an existing dll, but got a problem with it.
Here's how I try to use it:
unit u_main;
...
implementation
procedure getUserData(var User, Pass: string); stdcall; external 'Common5.dll';
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
u, p: string;
begin
getUserData(u,p);
end;
...
end.
When I press the button the get the userData, I get an EInvalidPointer exception.
The dll is registerd and in some other projects it's in use and work. any ideas?
EDIT:
The DLL was created in Delphi7 and is now used in a Delphi 2009 project.
Maybe there's a problem with unicode strings or something like that?
You need to rebuild the Delphi 7 DLL, make it follow the WinApi standard of getting PChar and BufferLen parameters. You've got multiple problems with the current implementation:
string is platform-specific, it's implementation may change between delphi versions (and did change). You're not supposed to use string outside the platform!
You're passing the parameters as "var", suggesting the DLL might change the value of user and/or pass. String is an special, managed type, changing it requires allocating memory for the new string. This in turns requires you to share the memory manager between the DLL and the EXE (using sharemem.pas and BorlandMM.dll - or variants). The trouble is, sharing the memory manager between different versions of Delphi is an unsupported configuration! (gotton from embarcadero forums)
The Delphi 7 is hoping to receive an simple AnsiString (1 byte chars), while the Delphi 2009 exe is sending Unicode strings (2 bytes per char).
Along with using PChar, be sure to pre-allocate the space before you call GetUserData. i.e. if you assign 'foo' into a pchar that's empty, you'll blow up. So either use static length PChar/PAnsiChar arrays, or use this technique:
var
s : AnsiString;
begin
setlength(s,256);
MyDLLProc(PAnsiChar(s));
end;