Dynamically creating variable length argument list
-
Maybe I'm missing something but if you know what the parameters are going to be, you should just be able to call the original function, and pass in the parameters that you've already extracted. Something like:
int _wsprintf( LPTSTR lpOut, LPCTSTR lpFmt, ... )
{
// you know how many arguments there are and their type
// based on the number of '%' in the format specifierreturn (wsprintf(lpOut, lpFmt, var1, var2, etc));
}
Five birds are sitting on a fence. Three of them decide to fly off. How many are left?
The problem is the var1, var2, etc. part :). If they pass in just 2 parameters, I need to call wsprintf one way. If they pass 3, another way, and so on. I could potentially create a huge if statement with a call to the original function for each number of parameters. In that case, I assume you'd treat all the arguments as void *, but you'd have to put a limit on how many actual parameters you wanted to support. You could cover up to 10 parameters for instance, but the instant they pass 11, your function won't call the original correctly. I'd like to find the correct way to do this by dynamically creating the argument list with the arguments they passed in. I've seen built in functions that look like they would work on gcc, but I don't know what the VC++ equivalent would really be.
-
The problem is the var1, var2, etc. part :). If they pass in just 2 parameters, I need to call wsprintf one way. If they pass 3, another way, and so on. I could potentially create a huge if statement with a call to the original function for each number of parameters. In that case, I assume you'd treat all the arguments as void *, but you'd have to put a limit on how many actual parameters you wanted to support. You could cover up to 10 parameters for instance, but the instant they pass 11, your function won't call the original correctly. I'd like to find the correct way to do this by dynamically creating the argument list with the arguments they passed in. I've seen built in functions that look like they would work on gcc, but I don't know what the VC++ equivalent would really be.
Using
wsprintf()
as an example is kind of awkward since the format specifier governs the count/type of the arguments passed to the function. In any case, you're going to need to parse the format specifier and extract each argument appropriately. Once you have all of them, a call towsprintf()
can be made. I used to have the code forprintf()
but that was ages ago, so the closest I can come to now is the code for MFC'sCString::Format()
function. It's a rather long example of how to handle format specifiers. For each '%' found, you'll extract the appropriate type argument from the variable list, and store it in a variable. When the variable list has been exhausted, you can then callwsprintf()
. So, if someone calledwsprintf()
like:wsprintf(szBuffer, "%c %d\n", 'c', 97);
you'd end up with achar
variable and anint
variable. At the end of your hook function, you'd have something like:return wsprintf(lpOut, lpFmt, somechar, someint);
Five birds are sitting on a fence. Three of them decide to fly off. How many are left?
-
The problem is the var1, var2, etc. part :). If they pass in just 2 parameters, I need to call wsprintf one way. If they pass 3, another way, and so on. I could potentially create a huge if statement with a call to the original function for each number of parameters. In that case, I assume you'd treat all the arguments as void *, but you'd have to put a limit on how many actual parameters you wanted to support. You could cover up to 10 parameters for instance, but the instant they pass 11, your function won't call the original correctly. I'd like to find the correct way to do this by dynamically creating the argument list with the arguments they passed in. I've seen built in functions that look like they would work on gcc, but I don't know what the VC++ equivalent would really be.
A simple approach might be to read the original parameters into a vector and then pass the vector to the function that will be responsible for reconstructing the parameter list. Doing something like this wouldn't affect the number of parameters present in the "wsprintf" function. If the original "wsprintf" function contains two parameters, then two parameters are what the vector will contain, but if it turns out that the "wsprintf" contains 3, or 4, or 5, (etc.) parameters, then that's the number of parameters the vector will contain. Afterwards, you simply pass the vector itself as a parameter to the function that will be responsible for reconstructing the new parameter list you will need. William Fortes in fide et opere!
-
Using
wsprintf()
as an example is kind of awkward since the format specifier governs the count/type of the arguments passed to the function. In any case, you're going to need to parse the format specifier and extract each argument appropriately. Once you have all of them, a call towsprintf()
can be made. I used to have the code forprintf()
but that was ages ago, so the closest I can come to now is the code for MFC'sCString::Format()
function. It's a rather long example of how to handle format specifiers. For each '%' found, you'll extract the appropriate type argument from the variable list, and store it in a variable. When the variable list has been exhausted, you can then callwsprintf()
. So, if someone calledwsprintf()
like:wsprintf(szBuffer, "%c %d\n", 'c', 97);
you'd end up with achar
variable and anint
variable. At the end of your hook function, you'd have something like:return wsprintf(lpOut, lpFmt, somechar, someint);
Five birds are sitting on a fence. Three of them decide to fly off. How many are left?
wsprintf vs. printf vs. CString::Format doesn't make too big of a difference in regard to this problem. Just to be clear, I could rewrite any of the above functions. The challenge lies in forwarding these function calls to their originals in a completely generic manner (no matter how many parameters are passed in). It's not a problem if I know the number of arguments they're going to pass every time. It's pretty trivial then to pull these arguments out (using the format specifiers in this case to pull them out based upon type) and make a call to wsprintf. It's also not a problem if I don't know how many arguments they're passing, but I don't need to forward the call and therefore I don't have to dynamically create a variable length argument list. The problem is they're passing me an unspecified number of arguments, and I have to forward that call regardless of the number. I'm trying to wrap a function (it's not actually wsprintf, it's just an example) with variable arguments, and I have no idea what the typical calling usage is. Think of the problem this way: Your mission is to write a replacement function for wsprintf, printf, or CString::Format (I don't really care which one we talk about). This function should write some debug info and forward the call to the original function no matter how the user calls the original function (in other words, no matter how many parameters they actually end up passing).
-
A simple approach might be to read the original parameters into a vector and then pass the vector to the function that will be responsible for reconstructing the parameter list. Doing something like this wouldn't affect the number of parameters present in the "wsprintf" function. If the original "wsprintf" function contains two parameters, then two parameters are what the vector will contain, but if it turns out that the "wsprintf" contains 3, or 4, or 5, (etc.) parameters, then that's the number of parameters the vector will contain. Afterwards, you simply pass the vector itself as a parameter to the function that will be responsible for reconstructing the new parameter list you will need. William Fortes in fide et opere!
This wouldn't work in this situation since I eventually need to pass the arguments to the original wsprintf function (in this example). Creating the vector would be easy. Taking that vector and reading out each argument would be easy. Calling wsprintf appropriately with these arguments would NOT be easy (perhaps impossible?). That's the problem in general. Create a variable length argument list on the fly. Since I'm doing a DLL Hook on a function, I get called exactly like the original function. I then want to do a bit of processing and then call the original function. There's no reason for me to put in an intermediate function that uses vectors to pass the variable length arguments since I still have to put a variable length argument list together (the whole problem to begin with).
-
This wouldn't work in this situation since I eventually need to pass the arguments to the original wsprintf function (in this example). Creating the vector would be easy. Taking that vector and reading out each argument would be easy. Calling wsprintf appropriately with these arguments would NOT be easy (perhaps impossible?). That's the problem in general. Create a variable length argument list on the fly. Since I'm doing a DLL Hook on a function, I get called exactly like the original function. I then want to do a bit of processing and then call the original function. There's no reason for me to put in an intermediate function that uses vectors to pass the variable length arguments since I still have to put a variable length argument list together (the whole problem to begin with).
While the vector approach (I previously mentioned) would work, I agree, it could be a pain to go that route. Perhaps a simpler approach might be to just pass the address of "wsprintf" itself as a parameter, and then activate its address (which would be activating wsprintf itself). IOW, handle wsprintf as a pointer to a function. The address of "wsprintf" is the name itself. Include the name as an extra parameter in the DLL Hook function that gets called, and at the appropriate time simply use it as you would at any other time. For example:
void DLLHookFunc(etc., wsprintf);
//then later do this
wsprintf(parameter1, ...);William Fortes in fide et opere!
-
While the vector approach (I previously mentioned) would work, I agree, it could be a pain to go that route. Perhaps a simpler approach might be to just pass the address of "wsprintf" itself as a parameter, and then activate its address (which would be activating wsprintf itself). IOW, handle wsprintf as a pointer to a function. The address of "wsprintf" is the name itself. Include the name as an extra parameter in the DLL Hook function that gets called, and at the appropriate time simply use it as you would at any other time. For example:
void DLLHookFunc(etc., wsprintf);
//then later do this
wsprintf(parameter1, ...);William Fortes in fide et opere!
Based upon a post on microsoft.public.vc.language I just made and got a response on, I think the best bet would be to make my function not affect the stack at all (declspec naked), possibly do some inline assembly to get the parameters passed in without affecting the stack (no pops), and then doing an assembly JMP to the original function. That would insure the parameters are on the stack just like they were passed in, and it would allow me not to worry about recreating the variable length argument list. It's going to be a pain to pull off with my assembly skills though :).
-
Based upon a post on microsoft.public.vc.language I just made and got a response on, I think the best bet would be to make my function not affect the stack at all (declspec naked), possibly do some inline assembly to get the parameters passed in without affecting the stack (no pops), and then doing an assembly JMP to the original function. That would insure the parameters are on the stack just like they were passed in, and it would allow me not to worry about recreating the variable length argument list. It's going to be a pain to pull off with my assembly skills though :).
-
I hate variable length argument functions in C++, but for what I'm doing, I have no choice using it :). I'm trying to insert a DLL Hook for a DLL that contains a function that has a variable length argument list (it has ... as a parameter). I want to do some processing with what they pass in, and then forward the call to the original function (the one in the DLL that was hooked). My question is how can I forward this function call? How do I dynamically create the parameters for this function call? To help with my explantaion, imagine I'm hooking wsprintf. This function takes two parameters and then a variable number of arguments after this. I know how to read the arguments they've passed in (obviously the first 2 are easy, and then you have to pull out the variable length remaining parameters). Once I have this information, how do I then create a new call with the first two parameters as normal, and then tag on the variable length arguments after them? If I want to hook wsprintf in this example, do some processing on that function, and then pass it thru to the original, I've got to be able to do this. Anyone have any ideas? Please let me know if you don't understand my problem too. Thanks, Kentamanos
I keep returning to the days when I only programed in 'C'. The following should help, it is out of my old dos windowing library and is still valid even in 'C++' (MS).
int w_printf(
int r, /* row */
int c, /* column */
int color,/* color */
unsigned flags, /* formating flags */
const char *format, /* format (see printf())
... /* variable arguments */
)
{
int n=-1;
char *buf=NULL;
size_t size=0;
va_list ap;
va_start( ap,format );
while( n == -1 ) {
size += 128; /* change size */
if( buf ) free(buf); /* free old buffer */
buf = (char *)malloc(size); /* allocate new buffer */
if( !buf ) n = 0; /* allocation failer */
else n = _vsnprintf(buf,size,format,ap); /* do basic formating */
}
va_end(ap);
if( buf ) {
n = w_pfmt( r,c,color,flags,buf );
free(buf);
}
return n;
}INTP
-
wsprintf vs. printf vs. CString::Format doesn't make too big of a difference in regard to this problem. Just to be clear, I could rewrite any of the above functions. The challenge lies in forwarding these function calls to their originals in a completely generic manner (no matter how many parameters are passed in). It's not a problem if I know the number of arguments they're going to pass every time. It's pretty trivial then to pull these arguments out (using the format specifiers in this case to pull them out based upon type) and make a call to wsprintf. It's also not a problem if I don't know how many arguments they're passing, but I don't need to forward the call and therefore I don't have to dynamically create a variable length argument list. The problem is they're passing me an unspecified number of arguments, and I have to forward that call regardless of the number. I'm trying to wrap a function (it's not actually wsprintf, it's just an example) with variable arguments, and I have no idea what the typical calling usage is. Think of the problem this way: Your mission is to write a replacement function for wsprintf, printf, or CString::Format (I don't really care which one we talk about). This function should write some debug info and forward the call to the original function no matter how the user calls the original function (in other words, no matter how many parameters they actually end up passing).
Ok, how about:
int _wsprintf( LPTSTR lpOut, LPCTSTR lpFmt, ... )
{
va_list list;
int nReturn;va\_start(list, lpFmt); nReturn = wsprintf(lpOut, lpFmt, list); va\_end(list); return nReturn;
}
Five birds are sitting on a fence. Three of them decide to fly off. How many are left?
-
Ok, how about:
int _wsprintf( LPTSTR lpOut, LPCTSTR lpFmt, ... )
{
va_list list;
int nReturn;va\_start(list, lpFmt); nReturn = wsprintf(lpOut, lpFmt, list); va\_end(list); return nReturn;
}
Five birds are sitting on a fence. Three of them decide to fly off. How many are left?
I created a Win32 Console App to test this approach, and it created garbage for me. Did you get this method to work? Here is the code I used:
#include <windows.h> #include <tchar.h> #include <stdio.h> int MyWsprintf( LPTSTR lpOut, LPCTSTR lpFmt, ...) { va_list list; int nReturn; va_start(list, lpFmt); nReturn = wsprintf(lpOut, lpFmt, list); va_end(list); return nReturn; } int _tmain(int argc, _TCHAR* argv[]) { char buf[1024]; MyWsprintf(buf, "%s %s %s", "test1", "test2", "test3"); printf(buf); }
If I change the call of MyWsprintf to the real wsprintf, it works great, indicating the problem is in the rewrite.
-
Good luck to you on whichever way you choose. I still think passing 'wsprintf' as a pointer parameter in the DLL Hook function and then later use it as you normally would, is the simplest way. ;) William Fortes in fide et opere!
I'm not quite sure what you mean by that. For simplicity sake, don't worry about the DLL Hook portion of this problem. Attempt to write a function that generically wraps printf for instance that you can call exactly like printf.
-
I created a Win32 Console App to test this approach, and it created garbage for me. Did you get this method to work? Here is the code I used:
#include <windows.h> #include <tchar.h> #include <stdio.h> int MyWsprintf( LPTSTR lpOut, LPCTSTR lpFmt, ...) { va_list list; int nReturn; va_start(list, lpFmt); nReturn = wsprintf(lpOut, lpFmt, list); va_end(list); return nReturn; } int _tmain(int argc, _TCHAR* argv[]) { char buf[1024]; MyWsprintf(buf, "%s %s %s", "test1", "test2", "test3"); printf(buf); }
If I change the call of MyWsprintf to the real wsprintf, it works great, indicating the problem is in the rewrite.
That was a bad suggestion on my part (at least I thought it would work based on this code snippet). I guess the address that
va_start()
setslist
to is not the same as the stack address thatwsprintf()
is popping arguments from.
Five birds are sitting on a fence. Three of them decide to fly off. How many are left?