Jump to content

  • Log In with Google      Sign In   
  • Create Account


- - - - -

__stdcall calling conventions on global functions.


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
11 replies to this topic

#1 daedalusd   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 June 2004 - 01:08 PM

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]

Sponsor:

#2 Fruny   Moderators   -  Reputation: 1653

Like
0Likes
Like

Posted 30 June 2004 - 01:33 PM

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.

#3 daedalusd   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 June 2004 - 02:37 PM

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

#4 SiCrane   Moderators   -  Reputation: 9566

Like
0Likes
Like

Posted 30 June 2004 - 02:43 PM

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.

#5 Andreas Jonsson   Moderators   -  Reputation: 3293

Like
0Likes
Like

Posted 30 June 2004 - 03:03 PM

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

#6 Andreas Jonsson   Moderators   -  Reputation: 3293

Like
0Likes
Like

Posted 30 June 2004 - 03:07 PM

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

#7 daedalusd   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 June 2004 - 03:18 PM

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

#8 daedalusd   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 June 2004 - 03:30 PM

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

#9 Shannon Barber   Moderators   -  Reputation: 1362

Like
0Likes
Like

Posted 30 June 2004 - 03:31 PM

MessageBoxA uses ANSI strings.
MessageBoxW uses wide strings, which are compatible with bstr's.

#10 daedalusd   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 June 2004 - 03:41 PM

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

#11 Andreas Jonsson   Moderators   -  Reputation: 3293

Like
0Likes
Like

Posted 30 June 2004 - 03:45 PM

daedalusd:

Yes and no. For the windows platform this is the only difference in the calling convention. It also changes the way the function name is scrambled for exportation, but AngelScript doesn't bother about that.

Perhaps I jumped the gun a bit when I said __stdcall and __thiscall are the same except for the object pointer. This is true for the MSVC compiler, but for g++ based compilers this is not true. I don't know how it is for other compilers. The various compilers are not in agreement on how to implement object method calls. And method pointers are even more complicated. But you can be pretty sure that __stdcall will work as you implemented it, as most compilers that work on the windows system can call Microsoft library functions. For other platforms like Linux I'm not sure that __stdcall even exists.

Please, do send me the code changes. It will be much faster for me to include it in the library that way (and I'll add your name to the contributions list :) You can send me the changed files, and I'll use WinMerge to find the differences.

Magmai Kai Holmlor:

Actually, the bstr type we are talking about is not the COM bstr, it is a simple byte string that I designed for AngelScript. Though it takes heavy influence from the COM bstr in that it stores the length just before the actual data, and also guarantees a null termination after the string data. (perhaps it was an unfortunate choice of name)

The correct choice in this case would be MessageBoxA().
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#12 Fruny   Moderators   -  Reputation: 1653

Like
0Likes
Like

Posted 30 June 2004 - 06:26 PM

MSDN says:
Quote:

Keyword Stack Parameter passing
cleanup

__cdecl Caller Pushes parameters on the stack,
in reverse order (right to left)

__stdcall Callee Pushes parameters on the stack,
in reverse order (right to left)

__fastcall Callee Stored in registers, then pushed on stack

thiscall Callee Pushed on stack; this pointer stored in ECX
(not a keyword)





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS