3

特定のサードパーティAPIで概説されている一連の関数をラップするラッパークラスを作成しています。このような非メンバー関数をラップしようとすると、次のようになります。

(AVTcamDllWrapper.h)

typedef VmbErrorType (WINAPI * AVTGETCAMERAS) (CameraPtrVector cameras);

class CAVTcamDllWrapper
{
    HMODULE mAVTCamLibrary; //I later have this point to the DLL

public:
    void AVTGetCameras (CameraPtrVector cameras);
};

(AVTcamDllWrapper.cpp)

void CAVTcamDllWrapper::AVTGetCameras(AVTNS CameraPtrVector cameras)
{

    AVTGETCAMERAS   pFunc = NULL;

    pFunc = (AVTGETCAMERAS) GetProcAddress(mAVTCamLibrary, "?GetCameras@VimbaSystem@VmbAPI@AVT@@AEAA?AW4VmbErrorType@@PEAV?$shared_ptr@VCamera@VmbAPI@AVT@@@23@AEAI@Z");
    DWORD dw = GetLastError();
    if(pFunc == NULL)
    {
        Exlog(L"CAVTcamDllWrapper::AVTGetCameras: Failed to locate AVTGetCameras method in AVTCamera DLL.");
        NIERR_SET_AND_THROW_ERROR(NIERR_CAMERA_ERROR, L"Failed to locate AVTGetCameras method in AVTCamera DLL.");
    }
    VmbErrorType vErr = pFunc(cameras);

    if(vErr != VmbErrorSuccess)
    {
        wstring exLogMsg = Format(exLogMsg, L"CAVTcamDllWrapper::AVTGetCameras(): Failed to get any cameras.  VmbErrorType = %d", vErr);
        Exlog(exLogMsg.c_str());

        NIERR_SET_AND_THROW_ERROR(NIERR_CAMERA_ERROR, L"Failed to get any cameras.");
    }
}

上記のコードは、非メンバー関数に最適です。たとえば、次のように言うだけで呼び出される関数をラップしようとしている場合です。

CallFunction(blah, blaaaaah);

その後、ラッパークラスは正常に機能し、pFuncは適切に設定され、VmbErrorType vErr = pFunc();行でエラーは発生しません。

ただし、私の関数の多くはメンバー関数であり、次のように呼び出されます。

SomeObject.CallMemberFunction(blah, bleh);

// or

SomeObjectPointer->CallMemberFunction(what, ever);

これらは私がラップできないように見える関数です。次の行でエラーが発生します。

VmbErrorType vErr = pFunc();

関数は、それを呼び出す特定のオブジェクトなしでは呼び出すことができないためです。私の例では、Camera内に存在する関数GetCamerasをラップしています。関数をラップせずに呼び出すには、カメラポインターのベクトルを作成し、次のようにします。

cameras[0]->GetCameras(VmbAccessModeFull);

動作します。cameras[0]しかし、GetCamerasの呼び出しは依存しており、呼び出し元のカメラがないと完全に役に立たないため、この関数をどのようにラップするのかわかりません。

では、上記のようなメンバー関数をラップするにはどうすればよいですか?


編集1:

特定のオブジェクトへの参照を渡そうとしましたが、

VmbErrorType vErr = theObject->pFunc();

しかし、明らかにこれは機能しません。これは、オブジェクト内に存在しないpFuncという名前の関数を探すためです。


編集2:

ラッパー関数を変更して、参照オブジェクトをパラメーターなどとして渡すようにしたいと思います。したがって、通常の代わりに:

cameras[0]->GetCameras(VmbAccessModeFull);

いくつか変更して、ラッパー関数を次のようにする必要があります。

mWrapperObject->WrappedGetCameras(VmbAccessModeFull, cameras[0]);

ラップされた関数がメンバー関数として機能するために必要なコンテキストを持つようにします。

4

4 に答える 4

1

メンバー関数を呼び出すには、オブジェクトが手元にある必要があります。オブジェクトを取得するには、どこかから取得する必要があります。良い小さな行儀の良い関数が何かを得ることができる唯一の場所は、そのパラメータリストです。

したがって、各関数には明らかにオブジェクトを受け取るパラメーターが必要です。

ラッパー関数をCから呼び出し可能にする場合は、クラス型を関数パラメーターとして使用することはできません。したがって、それをaとして宣言して内部でキャストするか、(構造体を定義せずにvoid*)チートして入力するだけです(Cの場合のみ! )。struct YourClassName*C ++の場合でも、classキーワードを使用する必要があります。プリプロセッサを使用します。

一言で言えば、

foo->bar(moo, roo) 

派手なshmancyC++の言い方です

FooType_bar(foo, moo, roo)

前者を実際につづりながら、後者をラップすることを検討する必要があります。

では、呼び出し元はどのようにしてオブジェクトを取得しますか?関数の1つが(を使用して)オブジェクトを作成newし、それらへのポインターを返すことができます。別の人は削除を行うことができます。または、事前に割り当てられた配列の要素へのポインタを返すこともできます。または何でも。基本的に、元のラップされていないライブラリのユーザーとしてオブジェクトへのポインターを取得するために使用する方法をラップします。

それについてです。

于 2013-02-27T17:07:51.043 に答える
0

memberfunctionsを呼び出す場合は、このポインターを最初の引数として渡す必要があり、正しい呼び出し規約を使用していることを確認してください。静的メンバー関数の場合、このポインターを渡す必要はありません。

x64では、すべてが__fastcallとしてコンパイルされるため、呼び出し規約について心配する必要はありません。指定する呼び出し規約に関係なく。

#include <iostream>
#include <stdint.h>


class Camera
{
    int i;
public:
    Camera()
    {
        i = 123;
    }


    void __stdcall print_1(int j, int k)
    {
        std::cout << i << j << k << std::endl;
    }

    void __cdecl print_2(int j, int k)
    {
        std::cout << i << j << k << std::endl;
    }

    void print_3(int j, int k)
    {
        std::cout << i << j << k << std::endl;
    }

    static void __cdecl print_s1(int j, int k)
    {
        std::cout << j << k << std::endl;
    }

    static void __stdcall print_s2(int j, int k)
    {
        std::cout << j << k << std::endl;
    }
};

int main() {
    Camera cam;
    Camera* pCam = &cam;

    // call __stdcall memberfunction
    typedef void (__stdcall* tprint_1)(Camera*,int,int);
    tprint_1 print_1 = (tprint_1)&Camera::print_1;
    print_1(pCam,1,2);

    // call __cdecl memberfunction
    typedef void (__cdecl* tprint_2)(Camera*,int,int);
    tprint_2 print_2 = (tprint_2)&Camera::print_2;
    print_2(pCam,3,4);

    // call __thiscall  memberfunction
    typedef void (__thiscall* tprint_3)(Camera*,int,int);
    tprint_3 print_3 = (tprint_3)&Camera::print_3;
    print_3(pCam,5,6);

    // call __thiscall  memberfunction different syntax
    typedef void (Camera::* tprint_4)(int,int);
    tprint_4 print_4 = (tprint_4)&Camera::print_3;
    (pCam->*print_4)(7,8);


    // static member functions don´t take a this pointer
    typedef void(__cdecl* tprint_s1)(int,int);
    tprint_s1 print_s1 = (tprint_s1)&Camera::print_s1;
    print_s1(9,10);

    // static member functions don´t take a this pointer
    typedef void(__stdcall* tprint_s2)(int,int);
    tprint_s2 print_s2 = (tprint_s2)&Camera::print_s2;
    print_s2(11,12);


    return 0;
}
于 2013-02-28T11:50:18.880 に答える
0

これを行うには、memberfunctionsのthiscallをfastcallに変換します

http://www.unknowncheats.me/forum/c-and-c/73849-thiscall-hooking.html

于 2013-03-06T09:11:29.880 に答える
0

これがその方法です。メンバー関数レイヤーとラッパー関数レイヤーの2つのレイヤーがあるとします。必要なのは、これら2つのレイヤーの間にある3番目のレイヤーを作成し、このレイヤーを.dllファイルにエクスポートすることです。最初は(質問したとき)、次のような関数をラップしようとしていました。

void SomeClass::SomeFunction(CString someParam)
{
    //blah blah
}

質問で説明されているように、メンバー関数をラップできないため、これは機能しませんでした。私が発見したのは、メンバー関数呼び出しの上のレイヤーで、ラッパー関数の下のレイヤーですべてのオブジェクト管理を行う必要があるということでした。私が最終的に得たのは、ラッパー関数とメンバー関数の間のギャップを「埋める」一連の「ブリッジ」関数(私がそれらと呼んだもの)でした。だから今、私はこのように見える関数をラップしました:

void BridgedSomeFunction(CString someParam)
{
    classObject.SomeFunction(someParam);
}

次に、これらの関数を.dllファイルに変換するためにいくつか__declspec(dllexport)の'と'を実行しました。これで完了です。__declspec(dllimport)

于 2013-03-14T20:28:48.137 に答える