Muchas API de Win32 toman punteros a estructuras con diseños específicos. De estos, un subconjunto grande sigue un patrón común donde el primer DWORD tiene que inicializarse para tener el tamaño de la estructura antes de que se llame. A veces requieren pasar un bloque de memoria, en el que escribirán una estructura, y el bloque de memoria debe tener un tamaño que se determina llamando primero a la misma API con un puntero NULL y leyendo el valor de retorno para descubrir el correcto tamaño. Algunas API asignan una estructura y le devuelven un puntero, de modo que el puntero debe desasignarse con una segunda llamada.
No me sorprendería que el conjunto de API que se pueden llamar de una sola vez, con argumentos individuales convertibles desde una simple representación de cadena, sea bastante pequeño.
Para que esta idea de aplicación general, tendríamos que ir a toda una extrema:
typedef void DynamicFunction(size_t argumentCount, const wchar_t *arguments[],
size_t maxReturnValueSize, wchar_t *returnValue);
DynamicFunction *GenerateDynamicFunction(const wchar_t *code);
Usted pasaría un fragmento de código simple a GenerateDynamicFunction, y sería envolver ese código de alguna repetitivo estándar y a continuación, invoque un compilador/vinculador de C para crear una DLL (hay bastantes opciones gratuitas disponibles) que contiene la función. Sería LoadLibrary
esa DLL y usar GetProcAddress
para encontrar la función y luego devolverla. Esto sería costoso, pero lo haría una vez y almacenaría en caché el DynamicFunctionPtr resultante para un uso repetido. Puede hacer esto de forma dinámica manteniendo los punteros en una tabla hash, codificados por los fragmentos de código.
El repetitivo podría ser:
#include <windows.h>
// and anything else that might be handy
void DynamicFunctionWrapper(size_t argumentCount, const wchar_t *arguments[],
size_t maxReturnValueSize, wchar_t *returnValue)
{
// --- insert code snipped here
}
lo tanto, un ejemplo de uso de este sistema sería:
DynamicFunction *getUserName = GenerateDynamicFunction(
"GetUserNameW(returnValue, (LPDWORD)(&maxReturnValueSize))");
wchar_t userName[100];
getUserName(0, NULL, sizeof(userName)/sizeof(wchar_t), userName);
Se podría mejorar esto haciendo GenerateDynamicFunction
aceptar el número del argumento, por lo que podría generar una comprobar al inicio del envoltorio que se ha pasado la cantidad correcta de argumentos. Y si coloca una tabla hash allí para almacenar en caché las funciones de cada codenippet encontrado, podría acercarse a su ejemplo original. La función Llamar tomaría un fragmento de código en lugar de simplemente un nombre de API, pero de lo contrario sería el mismo. Buscaría el fragmento de código en la tabla hash, y si no estaba presente, llamaría a GenerateDynamicFunction y almacenaría el resultado en la tabla hash para la próxima vez. Entonces realizaría la llamada en la función. Ejemplo de uso:
wchar_t userName[100];
Call("GetUserNameW(returnValue, (LPDWORD)(&maxReturnValueSize))",
0, NULL, sizeof(userName)/sizeof(wchar_t), userName);
Por supuesto que no tendría mucho sentido hacer nada de esto a menos que la idea era abrir una especie de agujero de seguridad en general. p.ej. para exponer Call
como un servicio web. Las implicaciones de seguridad existen para su idea original, pero son menos evidentes simplemente porque el enfoque original que sugirió no sería tan efectivo. Cuanto más poderosos lo logremos, mayor será un problema de seguridad.
de actualización basado en los comentarios:
El marco .NET tiene una característica llamada p/invocar, que existe precisamente para resolver su problema. Entonces, si estás haciendo esto como un proyecto para aprender sobre cosas, podrías mirar p/invoke para tener una idea de cuán complejo es. Posiblemente pueda orientar el .NET Framework con su lenguaje de scripts: en lugar de interpretar scripts en tiempo real o compilarlos en su propio bytecode, puede compilarlos en IL. O puede alojar un lenguaje de scripts existente de los muchos ya disponibles en .NET.
Estoy usando char * aquí como una matriz de bytes en lugar de cadenas. La suposición es que pongo los metadatos a disposición de la función 'Llamada' que describe los tamaños de los tipos reales (es decir, el número de bytes) que se requieren. En ese sentido, los tipos * son * conocidos: son un número conocido de bytes. –
En general, necesita más que el número de bytes para hacer una llamada, ya que la convención de aprobación puede ser diferente para diferentes tipos de datos (particularmente para argumentos de coma flotante). Pero puede que tengas razón en que win32 __stdcall simplemente pone todo en la pila, no lo conozco en detalle. – puetzk