Is there a way to determine the number of arguments a procedure needs before it is called from invoke?

Say you don't know how many arguments are needed for a given procedure... Is it possible to determine this (with macro or otherwise) before calling the function?

Sliver
Posted on 2002-03-14 03:03:19 by Sliver
If you know it to be a stdcall function, here is what i would do: push an excessive number of arguments onto the stack, then call the function. When (or if) the function returns, examine the stack to see how many of the original 10 parameters are still there, that way you will know how many parameters the function used. Obviously this has some limitations: the function may crash or GPF due to incorrect parameter values, the function may not clean up the stack properly, the function you called may call another function etc which means you may be a couple of levels deep if (when) you crash.....

AFAIK, there is no official way to tell, you either have to reverse engineer the function (which is not illegal), or use pure guesswork. If you use this on some of the low-level undocumented windows APIs, be prepared for a possible mess if you get it wrong :)
Posted on 2002-03-14 04:53:27 by sluggy
Obviously invoke checks that the amount of param's is correct -- Is there a way to exploit tht fact and use the same technique?

Sliver
Posted on 2002-03-14 10:40:06 by Sliver
Yeah, invoke does check that you have the correct number of parameters, but it can only do that because you have declared the functions you are calling somewhere. But if you have already declared them somewhere, then you usually know what parameters are needed :)

Remember, invoke is actually a macro, and it forces the compiler to do a typecheck on the parameters as well as check the number, in the actual code you just get a bunch of pushes then a call.
Posted on 2002-03-14 14:29:48 by sluggy
If you don't know how many parameters a procedure requires, it's probably not the best programming practise to use it. Though I can't think of a situation (save some fantasic self programing ai implementation) where you'd ever have to call code that you don't have documented, though sluggy's aproach sounds pretty good.
Posted on 2002-03-14 15:23:45 by Canite
try checking the mcall macro, it has a variable called mcallNumArgs...Maybe you could get some info from it. I don't know, just a guess. Since I'm not a macro expert.


Check_mcallArgs MACRO a1,a2,a3,a4,a5,a6,a7,a8,a9,a10
LOCAL x,len
IFNB <a1>
mcallNumArgs=mcallNumArgs+1
Check_mcallArgs <a2>,<a3>,<a4>,<a5>,<a6>,<a7>,<a8>,<a9>,<a10>
len SIZESTR <a1>
IF (len GE 5)
x SUBSTR <a1>,1,5
IFIDNI x,<ADDR >
mcallDanger=mcallDanger+1
ENDIF
ENDIF
ENDIF
ENDM

mcall MACRO ObjPtr:REQ,MethodName:REQ,Args:VARARG
CurrMethodProto CATSTR <MethodName>,<Proto>
IFNB <Args>
mcallDanger=0
mcallNumArgs=0
IFIDNI <ObjPtr>,<[eax]>
Check_mcallArgs Args
ENDIF
IF mcallDanger
echo Warning: Dangerous mcall!
push [eax] ; save ObjPtr
invoke CurrMethodProto ptr [eax],Args
ORG $-2
mov eax,[esp+mcallNumArgs*4]
push eax ; this
mov eax,[eax] ; vtable*
call dword ptr [eax+MethodName]
add esp,4 ; clean up stack
ELSE
invoke CurrMethodProto ptr [eax],Args
ORG $-2
mov edx,edx ; filler!
mov eax,ObjPtr
push eax ; this
mov eax,[eax] ; vtable*
call dword ptr [eax+MethodName]
ENDIF
ELSE ;----- No args
mov eax,ObjPtr
push eax ; this
mov eax,[eax] ; vtable*
invoke CurrMethodProto ptr [eax+MethodName]
ENDIF
ENDM



I forgot, credit goes to bizarre creations for this awesome macro. :)
Posted on 2002-03-14 15:28:57 by stryker
I want bitRake's opinion on that macro, it lost me :)
Posted on 2002-03-14 17:57:46 by sluggy
Well, I do know how many params a specific procedure takes...

What I'm trying to do is add the functionality to Object.inc to define my own constructor and still have a default constructor...

In otherwords

In C++


Box() <--default constructor
Box(param1, param2, param3) <-- my own overloaded constructor

Now both of these are defined with PROTO's

When you create a new class using Object.inc
It calls the NEWOBJECT macro:



NEWOBJECT MACRO ObjType:REQ, args:VARARG
invoke GetProcessHeap
invoke HeapAlloc, eax, NULL, SIZEOF ObjType

push eax
IFNB <args>
invoke ObjType&_Init, eax, &args
ELSE
invoke ObjType&_Init, eax
ENDIF
pop eax
ENDM


This macro is used to create a new instance of a class definition. The instances are taken from the local heap. As well this macro will call the constructor of the class to do any needed initializations.

As you can see it invokes the initilization function for the class (ie. the default construtor) or an overloaded constructor, but it doesn't have the option of both

What I want to do is, depending on the amount of params passed, to pick the correct constructor...

So I can do:



NEWOBJECT Box, 10, 20, 30 ;length, width, height
mov hBox, eax

NEWOBJECT Box ;the length width height == 0 or some predefined value
mov hBox2, eax

Posted on 2002-03-14 20:14:13 by Sliver
I'm not sure this will be usefull, but here's how I handled arbitrary numbers of parameters in a single object constructor prototype when I wrote CoLib for COM objects:

What I did was pass an extra parameter, a simple dword. Then the constructor procedure (method? procedure?) was at liberty tro interpet this value in any way you choose.

One typical use is to pass a reference to a structure, and the constructor would then know how to interpet this struct. This allowed me the option to initialize objects as they are created, without having to create any special object creator super objects.

Should you wish to use default values, pass in a NULL, and have the constructor interpet this as 'use default.'

Also, the struct could be a list of variable pointers, so every parameter could be either 'turned on' (valid pointer in its position) or 'turned off' (with a NULL pointer).

Sorry, I'm not very familiar with Nan and Thomas's work to know how usefull this suggestion is.

It certainly lacks some elegance.
Posted on 2002-03-15 23:25:32 by Ernie
If your C++ compiler can compile to asm code, which I think most can, try overloading the constructor in C++ with some simple function. Then take a look at the asm code to see how the compiler manages the situation.
Posted on 2002-03-16 02:54:08 by Canite
I read Ernie's reply, then i came up with almost the same thought as Canite. Is there a description anywhere (or better still, a logic diagram), of what the C compiler produces for overloaded functions (not just constructors)?
Posted on 2002-03-16 03:49:12 by sluggy
A C++ compiler can just choose the right function at compile-time, the problem is not at code level.
You could create several protos for the same function using a number suffix:


Box_Init PROTO STDCALL :DWORD ;default
Box_Init4 PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD

Then get the number of arguments passed to the newobject macro, concatenate this number to {ClassName}_Init (example: Box_Init4). With IFDEF, check if this symbol exists. If it does, use that symbol in your invoke. If it doesn't, use the default constructor instead.

Thomas
Posted on 2002-03-16 03:59:27 by Thomas
This was exactly how I planned on doing it (just didn't know how to find the # of args.) Although I'm working on the functionality of my program before i deal with this...

Sliver
Posted on 2002-03-16 06:28:29 by Sliver