Frage

Ich habe einen Business Case, wodurch ich meine eigene Aufrufkonvention zu können, müssen angeben, wenn P / Invoke verwenden. Insbesondere habe ich einen Legacy-DLL, die ein Nicht-Standard-ABI verwendet, und ich brauche, um die Lage, die Aufrufkonvention für jede Funktion zu spezifizieren.

Zum Beispiel kann eine Funktion in dieser DLL übernimmt seine ersten beiden Argumente über EAX und EBX, mit dem Rest über Stapel. Eine weitere Funktion übernimmt ein Argument über ECX, mit dem Rest auf dem Stapel. Ich habe ein paar hundert dieser Funktionen, und möchte meine eigene Zwischenbrücke DLL, um zu vermeiden, schreiben diese Funktionen zugreifen zu können.

Meine andere Option wäre, von Hand rollen meine eigene benutzerdefinierte P / Invoke, die aus offensichtlichen Gründen nicht wünschenswert ist.

Jede Hilfe ist willkommen, danke,

War es hilfreich?

Lösung

Ich verstehe nicht, was Sie mit benutzerdefinierten P / Invoke bedeuten, aber ich kann nicht sehen, wie man ohne nicht verwaltete C wegkommen konnte ++ mit Inline-Montage. da fast alles als 32-Bit-Werte jedoch übergeben wird, könnten erhalten Sie mit dem Schreiben nur ein Proxy für jede Funktion Unterschrift weg, wie auf einem pro Funktion apposed. Oder Sie könnten einen Code-Generator schreiben, die Proxies von XML erzeugt. Ich kann nicht diese Version sehen allerdings auch unerwünscht sein, da alle Proxy-Funktionen ganz einfach sein wird:

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

Andere Tipps

Ich bin ziemlich sicher, dass es kein eingebauter Weg zu bewerkstelligen, was man ohne eine separate DLL möge. Ich habe keine Möglichkeit gesehen, eine Aufrufkonvention andere als zu spezifizieren, was das Laufzeitsystem unterstützt wird.

Ich war das Lernen über Konventionen eine Weile zurückzurufen und schrieb einige Code Aufrufkonventionen zu konvertieren. Der Code aus einem speziellen C # Wrapper aufgerufen wird, die Wrapper-Bibliothek verwendet Reflektion emittieren (konnte nicht Marshal.getdelegateforfunctionpointer arbeiten) eine neue p / aufrufen Verfahren für spezielle nackt Stub-Methode zu emittieren. Es behebt Parameter und ruft dann die tatsächlich Methode.

Hier ist der C-Code. Ich habe nicht den C # Teil zur Hand habe :( Ich war Assembler auch zu der Zeit lernen, so könnte der Code saugen:)

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;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top