Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. Dynamically creating variable length argument list

Dynamically creating variable length argument list

Scheduled Pinned Locked Moved C / C++ / MFC
questionhelptutorialc++
15 Posts 5 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D David Crow

    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 to wsprintf() can be made. I used to have the code for printf() but that was ages ago, so the closest I can come to now is the code for MFC's CString::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 call wsprintf(). So, if someone called wsprintf() like: wsprintf(szBuffer, "%c %d\n", 'c', 97); you'd end up with a char variable and an int 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?

    K Offline
    K Offline
    Kentamanos
    wrote on last edited by
    #6

    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).

    D 1 Reply Last reply
    0
    • W WREY

      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!

      K Offline
      K Offline
      Kentamanos
      wrote on last edited by
      #7

      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).

      W 1 Reply Last reply
      0
      • K Kentamanos

        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).

        W Offline
        W Offline
        WREY
        wrote on last edited by
        #8

        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!

        K 1 Reply Last reply
        0
        • W WREY

          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!

          K Offline
          K Offline
          Kentamanos
          wrote on last edited by
          #9

          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 :).

          W 1 Reply Last reply
          0
          • K Kentamanos

            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 :).

            W Offline
            W Offline
            WREY
            wrote on last edited by
            #10

            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!

            K 1 Reply Last reply
            0
            • K Kentamanos

              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

              J Offline
              J Offline
              John R Shaw
              wrote on last edited by
              #11

              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

              1 Reply Last reply
              0
              • K Kentamanos

                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).

                D Offline
                D Offline
                David Crow
                wrote on last edited by
                #12

                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?

                K 1 Reply Last reply
                0
                • D David Crow

                  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?

                  K Offline
                  K Offline
                  Kentamanos
                  wrote on last edited by
                  #13

                  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.

                  D 1 Reply Last reply
                  0
                  • W WREY

                    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!

                    K Offline
                    K Offline
                    Kentamanos
                    wrote on last edited by
                    #14

                    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.

                    1 Reply Last reply
                    0
                    • K Kentamanos

                      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.

                      D Offline
                      D Offline
                      David Crow
                      wrote on last edited by
                      #15

                      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() sets list to is not the same as the stack address that wsprintf() is popping arguments from.


                      Five birds are sitting on a fence. Three of them decide to fly off. How many are left?

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups