Domanda

Ho un business case per cui ho bisogno di essere in grado di specificare la mia convenzione di chiamata quando si usa P / Invoke. In particolare, ho una dll legacy che utilizza un non standard ABI, e ho bisogno di in grado di specificare la convenzione di chiamata per ogni funzione.

Per esempio, una funzione in questa DLL accetta i primi due argomenti tramite EAX e EBX, con il resto tramite stack. Un'altra funzione accetta un argomento via ECX, con il resto in pila. Ho qualche centinaio di queste funzioni, e vorrei evitare di scrivere il mio DLL ponte intermedio al fine di accedere a queste funzioni.

La mia altra opzione sarebbe a portata di mano-rotolare il mio personalizzato P / Invoke, che è indesiderabile per ovvi motivi.

Ogni aiuto è apprezzato, grazie,

È stato utile?

Soluzione

Non capisco cosa vuoi dire con l'abitudine P / Invoke, ma non riesco a capire come si potrebbe ottenere via senza non gestiti C ++ con assembly inline. Tuttavia, dal momento che quasi tutto ciò che è passato come valori a 32 bit, si potrebbe ottenere via con la scrittura di un solo proxy per ogni firma funzioni, come apposto ad uno per la funzione. Oppure si potrebbe scrivere un generatore di codice che genera i proxy da XML. Non riesco a vedere questa versione essendo troppo indesiderabile, però, dal momento che tutte le funzioni di proxy saranno molto semplice:

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
   }
}

Altri suggerimenti

Sono abbastanza certo non c'è modo builtin di realizzare ciò che si vuole senza un dll separato. Non ho mai visto un modo per specificare una convenzione di chiamata diverso da quello che sostiene il sistema di runtime.

stavo imparando di chiamare le convenzioni un po 'indietro e ho scritto un codice per convertire convenzioni di chiamata. Il codice viene chiamato da uno speciale involucro C #, la libreria wrapper utilizza la riflessione emettono (non poteva ottenere Marshal.getdelegateforfunctionpointer di lavoro) di emettere un nuovo p / metodo Invoke per speciale stub nudo. Fissa i parametri e poi invoca il metodo di realtà.

Ecco il codice c. Non ho la parte C # a portata di mano :( Ero apprendimento assembler anche al tempo in modo che il codice potrebbe aspirare:)

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;
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top