Hello using code below to call a function with asm and after call read the result. That works fine. Now,- how to read the argument changes afterwards, best regards
//push arguments:
for i:=lst.cnt downto 0 do begin //reverse order
ia:=longword(lst.fList^[i]);
asm push ia end; //push 32-Bit argument
end;
asm
call proc //call the routine
mov ia, eax //get result right after the call
end;
//how to read variable arguments here ?
edit:
The initial code has run for some time without any issues, even when using the loop. The reason for this question came, when i had to call a winapi routine like this:
function GetWindowRect(hWnd: HWND; var lpRect: TRect): BOOL; stdcall;
external user32 name 'GetWindowRect';
It has declared a variable record argument "lpRect" (size: 16 bytes). I was trying to (push) these 4 integers before call, and then read them after call. Turns out just passing a pointer to the data actually worked in this case.
Conclusion: So there is no such thing variable arguments, only pointers to value or record. Thanks for any inputs helped getting to this conclusion
If you pass the parameters by value, then list.fList^[i] contains the pointer to the value.
Just unreference and use it, for instance PInteger(list.fList^[i])^ for an integer parameter passed by reference (as var).
But note that your code is pretty awful, and using push within the loop is very risky. It would also need to follow the calling convention - a proc function defined with default Delphi register passes its first parameters into eax/ecx/edx. Check this reference material - your question seems to indicate that you are a bit confused with how it works.
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]
Sorry, probably this one is also easy for Delphi programmers, but it is not for me. I have a library function I am calling, and basically it eats my stack. It does so by pushing the variables for the function into the stack, but somehow Delphi does not pop them from the stack. So after the function ends I land in nowhere. Funny enough i can just do "pop eax" as many as I have parameters, and it works. Can anyone shed light on whats happening?
The working code goes like this:
function LoadIntoMemory(sdiPath: String): Integer;
var
retValue: Integer;
begin
retValue := file_open(PAnsichar(AnsiString(sdiPath)), #filedata, #filedatasize);
asm
pop eax
pop eax
pop eax
end;
end;
As said, without popping from the stack it crashes.
The function itself is from a C DLL, linked statically like this:
function file_open (filename: PAnsichar; filedata: PPAnsichar; filedatasize: PLongInt): Integer; stdcall; external 'libLib';
linking dynamically does not alter the behaviour.
OK, I have it now. Sorry for probably easy RTFM questions. I should have used cdecl; instead of stdcall. Only wished all these Delphi DLL linking tutorials would have mentioned it somewhere.
http://docwiki.embarcadero.com/RADStudio/XE4/en/Procedures_and_Functions
This is a calling convention mis-match. The callee is cdecl and the caller uses stdcall. The key difference is who cleans the stack. For stdcall it is the callee and for cdecl it is the caller.
This explains what you have observed. The actual function is cdecl. And so the does not clean the stack. But your calling code believes the function is stdcall and so expects the callee to clean the stack. Thus, neither side cleans it.
More details here: http://en.m.wikipedia.org/wiki/X86_calling_conventions
I'm using AllocateHWnd in a class I'm writing to receive system messages with a TWndMethod and the messages I'm receiving need to handle a 4-byte WPARAM, which specifically references a pointer. But I'm only getting 2 bytes in return. How do I set up things so I can correctly receive these messages within the class?
Edit: Specific code. I'm setting a message event up using SHChangeNotifyRegister, based on a Microsoft sample I downloaded. The proc works enough to pull back events (in lEvent) that I can buy off on, but the code Microsoft used defines WParam to be Thandle and LParam to be DWord. The specific problem I have is that when the function IsItemNotificationEvent is true, SHGetPathFromIDList is AVing or pulling back garbage. I kept looking this over and am not really seeing a problem other than what the docs I have indicate in that WParam is a Word (probably old) and that GetLastError at the point I put in the code returns "The handle is invalid".
function IsItemNotificationevent(lEvent: Longint): boolean;
var
flagval: Longint;
begin
flagval := (lEvent and (SCHNE_UPDATEIMAGE or SHCNE_ASSOCCHANGED
or SHCNE_EXTENDED_EVENT or SHCNE_FREESPACE
or SHCNE_DRIVEADDGUI or SHCNE_SERVERDISCONNECT));
Result := (flagval > 0);
end;
procedure TShellNotifyHandler.WindowProc(Var msg: TMessage);
var
hNotifyLock: THandle;
lEvent: Longint;
pgpidl: PitemIDList;
psi1: array[1..MAX_PATH] of Char;
begin
if Msg.Msg = FShellMsg then
begin
hNotifyLock := SHChangeNotification_Lock(THandle(Msg.WParam),DWord(Msg.LParam),
pgpidl, lEvent);
writeln(SysErrorMessage(GetLastError));
if (hNotifyLock > 0) then
begin
if IsItemNotificationEvent(lEvent) then
// this limits events for this to what Microsoft defined in their example
begin
if (pgpidl <> nil) then
SHGetPathFromIDList(pgpidl, #psi1);
Writeln('Path #1: ', String(psi1));
end;
SHChangeNotification_Unlock(hNotifyLock);
end;
if Assigned(FOnShellNotify) then
FOnShellNotify(Self, LEvent);
end
else
FWndProc(Msg);
end;
The main thing that I see wrong with this code, and I've only really studied the call to SHChangeNotification_Lock, is that you are unconditionally calling GetLastError.
The documentation for that API function is inadequate because it does not specify how errors are signalled. However, I would strongly expect that errors to be signalled by the function returning NULL. Since the documentation does not say anything about calling GetLastError it is entirely possible that the API function does not set the last error value. No matter, even if you can be sure that GetLastError can be called, you should only do so after a failure, ie. if the call to SHChangeNotification_Lock returns NULL. If you call GetLastError after a successful API call you will get the error code for the most recent failed API call, which is unrelated to the current call.
The bottom line is that I'm sure WParam is carrying all 4 bytes and that your problem is not with that part of the process.
The upshot of all this is the I strongly believe that SHChangeNotification_Lock is succeeding, but the call to SHGetPathFromIDList is failing. You don't check the return value for that. I bet it returns FALSE.
Take a look at the C++ declarations for the two functions.
SHChangeNotification_Lock returns the ID list in a parameter typed liked this:
PIDLIST_ABSOLUTE **pppidl
SHGetPathFromIDList receives the ID list in a parameter typed liked this:
PCIDLIST_ABSOLUTE pidl
I don't know what your declaration of SHChangeNotification_Lock looks like, but the one supplied in my version of Delphi (XE2) looks plain wrong. It has this parameter declared like this:
out pppidl: array of PItemIDList
I honestly can't see how a Windows API function can return a Delphi open array as an out parameter. I think it should be declared so:
out pppidl: PPItemIDList
and you may need to declare PPItemIDList to be ^PItemIDList.
Now, pppidl is an array. It points to the first element of an array of PItemIDList. So you would obtain the path of the first element by calling:
SHGetPathFromIDList(pppidl^, #psi1);
This, I believe, is the real problem you have.
Finally I can't understand why you would test for success with hNotifyLock > 0. The correct test is hNotifyLock <> 0. Now, I know that some of the Delphi types have changed in recent versions, but if THandle was a signed value in your version of Delphi then you code would be wrong. No matter what, the correct logical test is <>0.
Okay, I got this answered. A number of problems all over the board, actually:
1) I had things wrong when it comes to IsItemNotificationEvent. To have valid PIDLs, I needed to make sure that the event WASN'T one of those, because no PIDL is valid to process against those.
if IsItemNotificationEvent(lEvent) then
2) "out" was necessary in the definition to SHChangeNotification_Lock and not "var" or a simple pointer reference. I don't have anything that indicates what "out" does specifically, so if anyone can help, please do. The fixed definition is below.
function SHChangeNotification_Lock(hChangeNotification: THandle; dwProcessID: DWord;
out pppidl: PSHNotifyStruct; out plEvent: Longint): THandle; stdcall;
3) In my documentation (including the source samples), it indicates that multiple pidls are possible for some event types. Which makes the suggested correction invalid in the QC report. The problem with using the original definition is probably as suggested. It's not quite right. Reference the definition above, and you'll see a different type. That definition is below. No events have more than two parms, so it would suffice.
TSHNotifyStruct = packed record
dw1: PItemIDList;
dw2: PItemIDList;
end;
PSHNotifyStruct = ^TSHNotifyStruct;
Got it working as I expect it to now. I just need to find a valid list of two parm events and code in to make it a little cleaner (i.e. not reference the second pitemid if known to be invalid). Some samples of output from my test program are below to illustrate:
Event received: $00001000 Parm 1: (C:) Local Disk // update directory
Event received: $00000008 Parm 1: ChangeNotifyWatcher // make directory
Event received: $00000002 Parm 1: ChangeNotifyWatcher // create file
Event received: $00000010 Parm 1: ChangeNotifyWatcher Parm 2: RECYCLER // remove directory
Thanks all for your help!
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?