Calling Convention - Funktions Pointer



  • Hallo,

    Eine normale Funktion kann ich nicht als __thiscall deklarieren. Wenn ich jetzt jedoch einen funktionspointer erstelle, welche ich als __thiscall definiere was passiert dann beim Aufruf des pointers?

    Ich frage weil ich gerade eine klasse aus einer dll exportiere und nicht so recht weiß wie ich meinen konstruktor bzw memberfunktions pointer definieren soll, da ich den this pointer ja manuell via inline Assembler in den ecx gebe und dann die funktionspointer aufrufe! (Habe zuvor Platz für die klasse per malloc angelegt, die addresse geht an den ecx)

    Es funktioniert stdcall und thiscall, also was macht der Compiler da? Und wie soll ich meine funktionspointer jetzt definieren?

    die struct in der dll:

    struct CPP_DLL_API my_struct
    {
    	my_struct();
    	void set_i(BYTE j);
    	BYTE get_i();
    
    	private :
    	BYTE i;
    };
    

    Die beiden funktions pointer (thiscall oder stdcall?)

    typedef void (__thiscall *pmy_struct) (); //pointer zu my_struct constructor
    typedef BYTE (__thiscall *pget_i) (); //pointer zu get_i funktion aus my_struct
    

    so ungefähr sieht der funktions aufruf aus:

    HMODULE cpp_dll;
    cpp_dll = LoadLibraryA("cpp_dll.dll");
    my_struct *the_struct = (my_struct*)malloc(sizeof(my_struct));
    pmy_struct pCtor = (pmy_struct)GetProcAddress(cpp_dll, "??0my_struct@@QAE@XZ");
    __asm
    {
       MOV ECX, the_struct
    }
    
    pCtor();
    

    Artikel dazu : http://www.codeproject.com/Articles/9405/Using-classes-exported-from-a-DLL-using-LoadLibrar



  • Hast Du einen triftigen Grund Dir selbst das Leben schwer zu machen und nebenbei noch absolut unportabel zu sein?

    Wenn Du unbedingt soetwas die Klassen aus DLLs exportieren willst, würde ich mich entweder an COM halten oder die Klassenfunktionen nicht direkt, sondern über Wrapper Funktionen a la

    void myClassFunction(void *p, ... parameter)
    {
     (MyClass*)p)->classFunction(parameter);
    }
    

    exportieren.
    (Dies ist ein absolutes Minimalbespiel, eine richtige Implementierung würde vorm Cast den Pointer noch genauer prüfen).



  • Stimmt, ich mache es mir damit schwerer als es ist, aber es geht mir dabei um das Prinzip und das Verständnis von Klassen!



  • Ehrlich gesagt bin ich da auch überfragt, stelle aber gerne meine Meinung zur Diskussion:

    Meines Wissens nach unterscheiden sich __stdcall und __thiscall dadurch, dass bei __thiscall in ECX der this-pointer einer Klasse übergeben wird.

    Bei Dir kommen jetzt ein paar Dinge zusammen weswegen es in deinem Fall wohl keinen Unterschied macht, ob Du die Funktionen nun mit __stdcall oder __thiscall deklariert hast:

    Du verwendest C-Style Casts, ob die typedef Deklaration deiner Pointer wirklich zum Funktionstyp passt oder nicht, wird somit verdeckt. Meiner Ansicht nach müsste sie

    typedef void (__thiscall my_struct::*pmy_struct) (); //pointer zu my_struct constructor
    

    lauten. In diesem Fall könnte das __thiscall auch weggelassen werden (da dann implizit vorhanden). Einen solchen Pointer könntest Du gar nicht erst aufrufen, ohne beim Aufruf eine Instanz deiner Klasse mit angeben zu müssen. Dann könnte der Compiler auch seine Arbeit tun und beim Funktionsaufruf den this-Pointer selbst in ECX schreiben.

    Für Deine Pointerdeklaration konnte ich auf die Schnelle keinen Aufruf finden, der nicht zu einem Syntaxfehler geführt hätte. Vielleicht kann das jemand anderes beantworten.



  • Was der Compiler bei __thiscall und "normalen" Funktionszeigern macht weiss ich ehrlich gesagt nicht. Es wundert mich sogar dass er da keinen Fehler auswirft.
    Im Prinzip ist __thiscall identisch zu __stdcall , nur dass zusätzlich der implizite "this" Parameter in ECX übergeben wird.

    Probier' einfach mal den Funktionen einen my_struct* als ersten Parameter zu geben, und in diesem dann die Adresse des Objekts mitzugeben. Ohne die Adresse "manuell" mit __asm in das ECX Register zu laden.
    Wenn das funktioniert, wäre das mMn. die beste Lösung.

    Ansonsten kann man noch __fastcall "misbrauchen" - indem man nach dem "this" Parameter noch einen Dummy-Parameter definiert der dann in EDX übergeben wird (und von der __thiscall Funktion ignoriert, da __thiscall EDX nicht verwendet).

    BTW: Der von dir zitierte codeproject Artikel wurde wohl von jmd. geschrieben der auch nicht SO den Durchblick hat. Sonst hätte er z.B. nicht einfach (und noch dazu ohne Erklärung) das WINAPI Makro als Calling-Convention verwendet, und vor allem auch Funktionen mit Parametern in seinem Beispiel verwendet.



  • template <typename T, typename... Args>
    using fn_ExternMemberFunction = (__thiscall T::*)(Args...);
    
    int main()
    {
    	((CYourClass*)(pPointertoYourClass)->*)((fn_ExternMemberFunction<CYourClass, Arg1, Arg2...>)(pYourFunctionPointer))(Arguments);
    }
    

    Ich hoffe ich habe jetzt keinen Fehler reingebracht.



  • Der Code war Buggy, so müsste es passen:

    template <typename RetType, typename T, typename... Args>
    using fn_ExternMemberFunction = RetType(__thiscall T::*)(Args...);
    
    int main()
    {
    	(((CYourClass*)(pPointertoYourClass)->*)((fn_ExternMemberFunction<YourRetType, CYourClass, Arg1, Arg2...>)(pYourFunctionPointer)))(Arguments);
    }
    


  • Jetzt da ich daheim bin hier:

    template <typename T, typename... Args>
    using fn_ExternFunctionConstructorType = void(__thiscall T::*)(Args...);
    
    template <typename RetType, typename T, typename... Args>
    using fn_ExternFunctionType = RetType(__thiscall T::*)(Args...);
    
    class __declspec(dllexport) CTest
    {
    public:
        CTest()
        {
            std::cout << 6 << std::endl;
        }
        int bla(int val) { std::cout << val << std::endl; return -1; }
    };
    
    int main(int argc, char** argv)
    {
        FARPROC pFuncConstructor = GetProcAddress(GetModuleHandle(NULL), "??0CTest@@QAE@XZ");
        FARPROC pFuncBla = GetProcAddress(GetModuleHandle(NULL), "?bla@CTest@@QAEHH@Z");
        CTest* pTest = (CTest*)malloc(sizeof(CTest));
        (pTest->*reinterpret_cast<fn_ExternFunctionConstructorType<CTest>&>(pFuncConstructor))();
        (pTest->*reinterpret_cast<fn_ExternFunctionType<int, CTest, int>&>(pFuncBla))(99);
        return 0;
    }
    

Log in to reply