Pergunta

Eu tenho um caso de negócio pelo qual eu preciso ser capaz de especificar minha própria convenção de chamada ao usar P / Invoke. Especificamente, eu tenho uma dll legado que usa um ABI não-padrão, e eu preciso capaz de especificar a convenção de chamada para cada função.

Por exemplo, uma função neste dll aceita seus dois primeiros argumentos via EAX e EBX, com o restante através de pilha. Outra função aceita um argumento via ECX, com o restante na pilha. Eu tenho algumas centenas dessas funções, e gostaria de evitar escrever minha própria DLL ponte intermediária, a fim de acessar essas funções.

A minha outra opção seria a de mão-roll meu próprio costume P / Invoke, o que é indesejável por razões óbvias.

Qualquer ajuda é apreciada, obrigado,

Foi útil?

Solução

Eu não entendo o que você quer dizer com o costume P / Invoke, mas eu não posso ver como você poderia fugir sem não gerenciado C ++ com linha de montagem. No entanto, uma vez que quase tudo é passado como valores de 32 bits, você pode começar afastado com a escrita de apenas um proxy para cada assinatura função, como apposed a um por função. Ou você poderia escrever um gerador de código que gera proxies de XML. Eu não posso ver esta versão sendo embora muito indesejável, uma vez que todas as funções de proxy será muito 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
   }
}

Outras dicas

Estou bastante certo de que não há caminho embutido de realizar o que você quer sem uma dll separado. Eu não vi uma maneira de especificar uma convenção de chamada diferente do que o sistema oferece suporte de tempo de execução.

Eu estava aprendendo sobre convenções de chamada a um tempo atrás e escreveu algum código para converter convenções de chamada. O código é chamado a partir de um C # wrapper especial, o emitem usos de biblioteca do wrapper reflexão (não conseguia Marshal.getdelegateforfunctionpointer de trabalho) para emitir um novo p / invoke método para método especial esboço nu. Ela corrige os parâmetros e, em seguida, chama o método realmente.

Aqui está o código c. Eu não tenho o C # parte calhar :( eu estava aprendendo assembler também no momento que o código pode chupar:)

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top