__stdcall calling conventions on global functions.

Started by
10 comments, last by Fruny 19 years, 9 months ago
Hi Guys, Forgive me if I'm repeating something which has already been covered (I've looked everywhere I can think of regarding this topic but without much luck so far). Basically, I'm trying to register some global functions which use the __stdcall calling convention (some Win32 API calls). I haven't yet managed however to find a combination of calling convention flags which prevent the occurance of an exception when making those calls. Below is the code I am using in my 'MessageBoxA' test call. As it is, the message box comes up fine, but crashes on return which is what leads me to believe it's the calling convention difference between __stdcall and __cdecl.

// C++

// Register win32 API calls.
m_pEngine->RegisterGlobalFunction( "int MessageBox( uint, string, string, bits )", asFUNCTION(MessageBoxA), asCALL_CDECL );

// Define all constants 
strConstants[0] = '\0';
sprintf( &strConstants[strlen(strConstants)], "const bits MB_OK = 0x%x;\n", MB_OK );
sprintf( &strConstants[strlen(strConstants)], "const bits MB_ICONINFORMATION = 0x%x;\n", MB_ICONINFORMATION );

// Add constants to the engine
m_pEngine->AddScriptSection( "core", "Constants", strConstants, strlen(strConstants)  );
(Note: 'string' is analogous to 'bstr' in my test engine).

//Script
bool Main()
{
    MessageBox( 0, "The script is starting up, just so you know! :)", "Initializing", MB_OK | MB_ICONINFORMATION );
    return true;
}
Can anyone help me out with this? If I have to write wrapper functions for all of the Win32 API calls which I would like to expose then that's not a terrible deal, but I would just like to make sure I'm not doing anything stupid before I go down that route. Thanks in advance! Adam [Edited by - daedalusd on June 30, 2004 9:26:17 PM]
Advertisement
Any callback system will require you to pass functions that use the calling convention it expects. After all, the code that does the call, with the argument passing and all, is already written and compiled into the library. If the function you are handing it doesn't handles its parameters in the same way... you're in trouble. Some systems may allow you to use different calling conventions, but you likely will have to specify which one you need when you do register the callback. See if the function you are using offers such a parameter.

Conclusion - you'll indeed have to write wrappers. Don't bother making them inline, since taking the address of a function prevents inlining anyway.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Ok thanks.

I thought I'd ask because it appears that AS handles all of the calling convention differences manually, using conditional hand written assembler for stack setup / cleanup and the handling of argument data / return values.

I'm way out of the loop these days on development using the Assembler language, so I wouldn't know where to start implementing a stable 'CallCDeclFunction' clone for __stdcall, or even what the major differences are between the two conventions behind the scenes :).

I really ought to get back into the low level stuff again someday heh.

Thanks anyway.

Kind Regards,

Adam
The major difference between __cdecl and __stdcall is that in __cdecl the calling function cleans up the stack and in __stdcall the called funciton cleans up the stack.
Currently __stdcall is not supported by AngelScript. The difference between __cdecl and __stdcall is that __stdcall removes its own parameters from the stack before returning, while with __cdecl the caller has to remove the parameters it sent after the function callee returns.

If you register a __stdcall function as with asCALL_CDECL you will get stack pointer errors.

Until I've added support for __stdcall. The easiest way out is to write a wrapper function using the __cdecl calling convention.

I might add support for __stdcall now that someone asked for it. It shouldn't be too much work. :)

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Hehe, I didn't refresh the page before replying so I didn't see SiCrane's response.

Actually, the __stdcall convention is exactly the same as __thiscall except that you don't have to put the object pointer in ECX.

I'll see if I can add support for __stdcall already for the next WIP.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Thanks for your quick response. This makes perfect sense.

I bit the bullet and added a new function to callfunc_x86.cpp 'CallSTDCallFunction' as well as the appropriate flags (#define asCALL_STDCALL 4) and updated the declaration validation in RegisterGlobalFunction.

The function I added was basically a clone of the CDecl function with the only modification really being the removal of the argument pop -the modification of the ESP register-.

This now appears to work, and there are no apparent crashes when calling __stdcall functions, but is it really this easy?

Really, I'm just looking for clarification that the only difference between calling __cdecl and __stdcall functions is that we don't pop the arguments from the stack at the end using:

add  esp, paramSize


I'm probably being over optimistic and I'm actually probably going to kill the OS in the process doing this hehe :).

(Update: Doh, didn't refresh either before posting. If that is the case and the implicit this pointer is the only difference between stdcall and thiscall, then hopefully simply removing that line should do the trick. If it is, would you like me to send you the changes to save you having to re-implement it, or are you good to go?)

Thanks in advance,

Adam
Just another quick question (sorry to be a pain). Is the __stdcall convention only applicable to the 'INTEL' platform?

I notice that the THISCALL convention, when 'ASM_AT_N_T' is defined still modifies ESP, which got me thinking as to whether I should just make the STDCall convention an analogue of CDecl within the AT&T define?

Thanks in advance for your help, and for all your help so far!

Adam
MessageBoxA uses ANSI strings.
MessageBoxW uses wide strings, which are compatible with bstr's.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Yep, I'm aware of this. However surely the asBSTR type does not use wide chararacters (wchar_t) which MessageBoxW expects? It would appear that it is merely prepended with a DWORD containing the string length (which seems to be the only thing it has in common with the OLE BSTR). It looks like it's still made up of a null terminated ANSI compatible string of single byte characters though.

typedef unsigned char * asBSTR;asBSTR asBstrAlloc(asUINT length){    unsigned char *str = new unsigned char[length+4+1];    *((asUINT*)str)  = length; // Length of string    str[length+4]    = 0;      // Null terminated    return str + 4;}
Please correct me if I'm wrong.

(Note: Attempting to use MessageBoxW, results in invalid characters being displayed.)

Kind Regards,

Adam

This topic is closed to new replies.

Advertisement