Question

J'ai une étude de cas où je dois être en mesure de préciser ma propre convention d'appel lors de l'utilisation P / Invoke. Plus précisément, j'ai un dll héritage qui utilise un ABI non standard, et je dois en mesure de préciser la convention d'appel pour chaque fonction.

Par exemple, une fonction dans ce dll accepte ses deux premiers arguments via EAX et EBX, le reste par pile. Une autre fonction accepte un argument via ECX, avec le reste sur la pile. J'ai quelques centaines de ces fonctions, et je voudrais éviter d'écrire ma propre DLL pont intermédiaire pour accéder à ces fonctions.

Mon autre option serait de la main rouler ma propre P / Invoke, ce qui est indésirable sur mesure pour des raisons évidentes.

Toute aide est appréciée, merci,

Était-ce utile?

La solution

Je ne comprends pas ce que vous entendez avec P / Invoke, mais je ne vois pas coutume comment vous pourriez partir sans C ++ non gérés avec l'assembleur en ligne. Cependant, étant donné que presque tout est passé en tant que valeurs 32 bits, vous pouvez vous en sortir avec l'écriture une seule procuration pour chaque signature de la fonction, par opposition à une par fonction. Ou vous pourriez écrire un générateur de code qui génère des proxys de XML. Je ne vois pas cette version de trop indésirable cependant, puisque toutes les fonctions de proxy seront très simple:

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

Autres conseils

Je suis assez certain qu'il n'y a aucun moyen d'accomplir ce que builtin vous voulez sans dll séparée. Je ne l'ai pas vu un moyen de spécifier une convention d'appel autre que ce qui prend en charge le système d'exécution.

J'ai appris à appeler conventions un certain temps et écrit un code pour convertir les conventions d'appel. Le code est appelé à partir d'une enveloppe spéciale C #, la bibliothèque wrapper utilise l'émission de réflexion (ne pouvait pas faire fonctionner Marshal.getdelegateforfunctionpointer) pour émettre une nouvelle méthode p / Invoke pour la méthode stub nu spécial. Il fixe les paramètres et appelle ensuite la méthode réellement.

Voici le code c. Je n'ai pas le C # partie pratique :( Je suis assembleur apprendre aussi à l'époque, le code peut aspirer:)

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;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top