c ++ - en 'generel funktions signatur' peger, der peger på en vilkårlig funktion

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg vil forsøge at forklare bedre, hvad jeg vil gøre.
Jeg læser en fil med funktions signaturer, og jeg vil gerne skabe en peger til hver funktion.


For eksempel, en fil der ser sådan ud:


something.dll;int  f(char* x, int y, SOMESTRUCT z)
something.dll;void g(void)
something.dll;SOMESTRUCT l(longlong w)


Nu vil jeg i løbet af runtime kunne lave pointer til disse funktioner (ved at indlæse something.dll og bruge GetProcAddress til disse funktioner).


Nu returnerer GetProcAddress FARPROC, der peger på en vilkårlig funktion, men hvordan kan jeg bruge FARPROC til at kalde disse funktioner under runtime?
Fra hvad jeg ved, skal jeg kaste FARPROC til den korrekte underskrift, men jeg kan ikke gøre det under runtime (eller i det mindste ved jeg ikke hvordan).


Har nogen nogen ide om, hvordan man designer det?


Tak! :-)

Bedste reference



  1. Funktionstyper er kompileringstid i C ++, så det har ikke fungeret, medmindre du kan definere alle de typer, du vil bruge på forhånd.

  2. Det er et spørgsmål om at skubbe argumenterne til stakken (og lokale vars er sådan) og kalder funktionen som ugyldig (\_\_cdecl *) (void).

  3. Med nogle andre funktioner (f.eks. fastcall eller thiscall) kan det være mere problematisk.



Opdatering: Jeg har faktisk lavet et eksempel, og det virker på codepad:
(Fungerer også med stdcall-funktioner, på grund af stack-gendannelse efter justeret stakfordeling)


http://codepad.org/0cf0YFRH[10]


#include <stdio.h>

#ifdef \_\_GNUC\_\_
 #define NOINLINE \_\_attribute\_\_((noinline))
 #define ALIGN(n) \_\_attribute\_\_((aligned(n)))
#else
 #define NOINLINE \_\_declspec(noinline)
 #define ALIGN(n) \_\_declspec(align(n))
#endif

//#define \_\_cdecl

// Have to be declared \_\_cdecl when its available, 
// because some args may be passed in registers otherwise (optimization!)
void \_\_cdecl test( int a, void* b ) {
  printf( "a=\%08X b=\%08X
", a, unsigned(b) );
}

// actual pointer type to use for function calls
typedef int (\_\_cdecl *pfunc)( void );

// wrapper type to get around codepad's "ISO C++" ideas and gcc being too smart
union funcwrap { 
  volatile void* y; 
  volatile pfunc f; 
  void (\_\_cdecl *z)(int, void*);
};

// gcc optimization workaround - can't allow it to know the value at compile time
volatile void* tmp = (void*)printf("
");
volatile funcwrap x; 
int r;

// noinline function to force the compiler to allocate stuff 
// on stack just before the function call
NOINLINE
void call(void) {
  // force the runtime stack pointer calculation
  // (compiler can't align a function stack in compile time)
  // otherwise, again, it gets optimized too hard

  // the number of arguments; can be probably done with alloca()
  ALIGN(32) volatile int a[2]; 
  a[0] = 1; a[1] = 2; // set the argument values
  tmp = a; // tell compiler to not optimize away the array
  r = x.f(); // call the function; returned value is passed in a register

  // this function can't use any other local vars, because
  // compiler might mess up the order
}

int main( void ) {
  // again, weird stuff to confuse compiler, so that it won't discard stuff
  x.z = test; tmp=x.y; x.y=tmp;
  // call the function via "test" pointer
  call();
  // print the return value (although it didn't have one)
  printf( "r=\%i
", r );
}

Andre referencer 1


Når du har en FARPROC, kan du kaste FARPROC til en peger til den relevante funktionstype. For eksempel kan du sige


int (*fPtr)(char*, int, SOMESTRUCT) = (int (*)(char*, int, SOMESTRUCT))GetProcAddress("f");


Eller hvis du vil bruge typedef s for at gøre det lettere:


typedef int (*FType)(char *, int, SOMESTRUCT);
FType fPtr = (FType)GetProcAddress("f");


Nu hvor du har funktionspegeren gemt i en funktionspeger af den relevante type, kan du ringe f ved at skrive


fPtr("My string!", 137, someStructInstance);


Håber dette hjælper!

Andre referencer 2


Kompilatoren har brug for at kende den nøjagtige funktions signatur for at skabe den korrekte opsætning og nedrivning af opkaldet. Der er ingen nem måde at falske på. Hver signatur du læser fra filen vil have brug for en tilsvarende compile-time signatur for at matche imod.


Du kan muligvis gøre hvad du vil med intim viden om din compiler og nogle assembler, men jeg anbefaler det imod det.