2010-02-22 10 views
8

Tak, chciałbym móc wywoływać funkcje z biblioteki dll C++. Z pewnych powodów chciałbym wywołać je z bloku __asm ​​w moim kodzie C++. Moje pytanie jest takie: Wiem, że przed I wywołania funkcji, muszę wcisnąć swoje argumenty na stosie w kolejności określonej przez funkcję wzywającej convention.However, mogę po prostu zrobić coś takiego:Jak przekazywać argumenty do funkcji C++, gdy wywołuję je z wbudowanego zestawu

int a=5; 
double b = 5.0; 
__asm{ 
     push b 
     push a 
     call functionAddress 
} 

To, co mnie martwi, to fakt, że wydaje mi się, że standardowy rozmiar słowa w zestawie to 2 bajty, podczas gdy rozmiar int w C++ to zwykle 4 bajty, a 8 bajtów za podwójne. W powyższym przykładzie jestem naprawdę przesuwając pełną wartość każdej zmiennej lub tylko pierwszych kilku bajtów? Jeśli powyższy kod nie jest poprawny, jaki byłby właściwy sposób, aby to zrobić? Ponadto, jeśli funkcja, którą wywołujemy, zwraca wartość podwójną, gdzie jest przechowywana ta wartość? Zakładam, że nie może być w rejestrze, ponieważ może przechowywać tylko 32bity (4 bajty). Każda pomoc w tym bałaganie byłaby bardzo doceniana :)

+2

Co się stało, gdy próbowałeś? – Seth

+0

Mam problem z dublami: Zrobiłem sobie funkcję testową w bibliotece dll, która przyjmuje podwójną argumentację i zwraca 1, jeśli wejście jest większe niż 5.0. Chodzi o to, że próbowałem wywołać funkcję z zestawu kilka razy z argumentem 7.45454 i zwracana wartość nie zawsze jest taka sama. –

Odpowiedz

13

Aby przesunąć wartości 8-bajtowych, takich jak gry podwójnej, nie będzie mógł użyć zwykłej instrukcji PUSH. Ani nie przesuwaj parametrów zmiennoprzecinkowych (lub podwójnych) na stos zmiennoprzecinkowy. Musisz umieścić te parametry tłuszczu na stosie "ręcznie". Na przykład, aby przekazać π jako parametr do funkcji f:

__asm { 
    FLDPI     // load pi onto FP stack 
    SUB ESP,8    // make room for double on processor stack 
    FSTP QWORD PTR [ESP]  // store pi in proc stack slot (and pop from FP stack) 
    CALL f 
    ADD ESP,8    // clean up stack (assuming f is _cdecl) 
    } 
1

Ogólnie rzecz biorąc, naciskasz na pełny rozmiar komputera słowo. Różni się to w zależności od układu, ale w przypadku 32-bitowego Intel byłby to 4 bajty, a na 64-bitowym Intelie wynosiłby 8 (w zależności od kompilatora - Visual Studio nadal obsługuje tylko zestaw IA32 - czyli 4 bajty).

Najlepszą odpowiedzią jest przejrzenie dokumentacji konkretnego kompilatora.

4

32-bitowa architektura x86 automatycznie nakłada wartości, które są przekazywane na stos do 32 bitów.

Jest coś, o czym musisz pamiętać. Jeśli funkcja, do której dzwonisz, używa konwencji wywoływania __cdecl, musisz "pop", co później popchnąłeś. Jednak w przypadku funkcji __stdcall nie wolno tego robić.

extern "C" int __cdecl function1(int, double); 
extern "C" double __stdcall function2(char, char*); 

int a = 5; 
double b = 5.0; 
int retval1; 
char c = '5'; 
char *d = "Hello"; 
double retval2; 

__asm { 
    push b 
    push a 
    call function1 
    add esp, 4*2 // "pop" what we pushed 
    mov retval1, eax 
    push d 
    push c 
    call function2 
    mov retval2, eax 
} 
+2

Dla dodatkowego spokoju, po zorientowaniu się, co to * powinno * być, wykonaj równoważne wywołanie z C++, skompiluj z wyłączonymi optymalizacjami i spójrz na dane wyjściowe zespołu. Powinno to z grubsza pasować do tego, co robisz. –

+1

Ale w takim razie, jak byś naciskał podwójnie? W języku C++ ma być 8 bajtów, prawda? –