Pregunta

Tengo un caso de negocio en el que necesito poder especificar mi propia convención de llamadas cuando uso P/Invoke.Específicamente, tengo una DLL heredada que usa una ABI no estándar y necesito poder especificar la convención de llamada para cada función.

Por ejemplo, una función en este dll acepta sus dos primeros argumentos a través de EAX y EBX, y el resto a través de la pila.Otra función acepta un argumento a través de ECX y el resto en la pila.Tengo unos cientos de estas funciones y me gustaría evitar escribir mi propia DLL de puente intermedio para poder acceder a estas funciones.

Mi otra opción sería crear manualmente mi propio P/Invoke personalizado, lo cual no es deseable por razones obvias.

Cualquier ayuda se agradece, gracias.

¿Fue útil?

Solución

No entiendo a qué te refieres con P/Invoke personalizado, pero no veo cómo podrías salirte con la tuya sin C++ no administrado con ensamblado en línea.Sin embargo, dado que casi todo se pasa como valores de 32 bits, es posible que pueda escribir solo un proxy para cada firma de función, en lugar de uno por función.O podría escribir un generador de código que genere servidores proxy a partir de XML.Sin embargo, no veo que esta versión sea demasiado indeseable, ya que todas las funciones de proxy serán realmente simples:

int RealFunction(int param1, const char * param2, char param 3);

int MyFunction(int param1, int param2, int param3) { // argument types do not matter as long as they are not doubles or structures
   __asm {
      mov eax, param1
      mov ebx, param2
      push param3
      call RealFunction
      ; depending on calling convention, you might need to do add esp, 12 here
      ; if RealFunction does not return its result in eax, you will need to do mov eax, <wherever the return value is> here
   }
}

Otros consejos

Estoy bastante seguro de que no hay manera de lograr incorporado lo que quiere sin una DLL separada. No he visto una forma de especificar una convención de llamada distinto de lo que es compatible con el sistema de ejecución.

yo estaba aprendiendo sobre convenciones de llamada hace un tiempo y escribí algo de código para convertir convenciones de llamada. El código se llama desde un C # envoltorio especial, la biblioteca del reiniciador utiliza la reflexión emiten (no podía conseguir Marshal.getdelegateforfunctionpointer de trabajo) para emitir una nueva p / invocar el método para el método talón desnudo especial. Que fija los parámetros y luego invoca el método realidad.

Este es el código c. No tengo la parte C # mano :( yo estaba aprendiendo ensamblador también en el momento por lo que el código podría aspirar:)

typedef struct
{
    USHORT ParameterOneOffset;  // The offset of the first parameter in dwords starting at one
    USHORT ParameterTwoOffset;  // The offset of the second parmaeter in dwords starting at one
} FastCallParameterInfo;



    __declspec( naked,dllexport ) void __stdcall InvokeFast()
{
    FastCallParameterInfo paramInfo;
    int functionAddress;
    int retAddress;
    int paramOne, paramTwo;
    __asm
    {
        // Pop the return address and parameter info.  Store in memory.
        pop retAddress;
        pop paramInfo;
        pop functionAddress;

        // Check if any parameters should be stored in edx                          
        movzx ecx, paramInfo.ParameterOneOffset;     
        cmp ecx,0;
        je NoRegister;  

        // Calculate the offset for parameter one.
        movzx ecx, paramInfo.ParameterOneOffset;    // Move the parameter one offset to ecx
        dec ecx;                                    // Decrement by 1
        mov eax, 4;                                 // Put 4 in eax
        mul ecx;                                    // Multiple offset by 4

        // Copy the value from the stack on to the register.
        mov ecx, esp;                               // Move the stack pointer to ecx
        add ecx, eax;                               // Subtract the offset.
        mov eax, ecx;                               // Store in eax for later.
        mov ecx, [ecx];                             // Derefernce the value
        mov paramOne, ecx;                          // Store the value in memory.

        // Fix up stack
        add esp,4;                                  // Decrement the stack pointer
        movzx edx, paramInfo.ParameterOneOffset;    // Move the parameter one offset to edx
        dec edx;                                    // Decrement by 1
        cmp edx,0;                                  // Compare offset with zero
        je ParamOneNoShift;                         // If first parameter then no shift.

    ParamOneShiftLoop:
        mov ecx, eax;
        sub ecx, 4;
        mov ecx, [ecx]
        mov [eax], ecx;                             // Copy value over
        sub eax, 4;                                 // Go to next 
        dec edx;                                    // decrement edx
        jnz ParamOneShiftLoop;                      // Loop
    ParamOneNoShift:
        // Check if any parameters should be stored in edx                          
        movzx ecx, paramInfo.ParameterTwoOffset;     
        cmp ecx,0;
        je NoRegister;  

        movzx ecx, paramInfo.ParameterTwoOffset;    // Move the parameter two offset to ecx
        sub ecx, 2;                                 // Increment the offset by two.  One extra for since we already shifted for ecx
        mov eax, 4;                                 // Put 4 in eax
        mul ecx;                                    // Multiple by 4

        // Copy the value from the stack on to the register.
        mov ecx, esp;                               // Move the stack pointer to ecx
        add ecx, eax;                               // Subtract the offset.
        mov eax, ecx;                               // Store in eax for later.
        mov ecx, [ecx];                             // Derefernce the value
        mov paramTwo, ecx;                          // Store the value in memory.           

        // Fix up stack
        add esp,4;                                  // Decrement the stack pointer
        movzx edx, paramInfo.ParameterTwoOffset;    // Move the parameter two offset to ecx
        dec edx;                                    // Decrement by 1
        cmp edx,0;                                  // Compare offset with zero
        je NoRegister;                              // If first parameter then no shift.
    ParamTwoShiftLoop:
        mov ecx, eax;
        sub ecx, 4;
        mov ecx, [ecx]
        mov [eax], ecx;                             // Copy value over
        sub eax, 4;                                 // Go to next 
        dec edx;                                    // decrement edx
        jnz ParamTwoShiftLoop;                      // Loop


    NoRegister:
        mov ecx, paramOne;                          // Copy value from memory to ecx register
        mov edx, paramTwo;                          // 
        push retAddress;
        jmp functionAddress;
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top