3

我正在创建一个包装器类,它包装了特定 3rd 方 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);

哪个有效。但我不知道如何包装这个函数,因为对 GetCameras 的调用既依赖于cameras[0]又完全无用,如果没有相机来调用它。

那么如何包装上面显示的成员函数呢?


编辑1:

我试图传递对特定对象的引用并执行

VmbErrorType vErr = theObject->pFunc();

但显然这不起作用,因为它会考虑在 theObject 内部寻找一个名为 pFunc 的函数,该函数不存在。


编辑2:

我觉得我几乎要修改包装函数以将引用对象作为参数或其他东西传递。所以喜欢而不是常规:

cameras[0]->GetCameras(VmbAccessModeFull);

我将不得不修改一些东西并使我的包装函数看起来像这样:

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

这样被包装的函数将具有充当成员函数所需的上下文。

4

4 回答 4

1

To call a member function you must have an object at hand. To have an object you must get it from somewhere. The only place a good little well-behaving function can get stuff from is its parameter list.

So each of your functions obviously must have a parameter that receives The Object.

If you want your wrapper functions to be callable from C, you cannot have class types as function parameters. So you either declare it as a void* and do a cast inside, or just cheat and type it (for C only!) struct YourClassName* (without ever defining the struct). For C++, it should still use the class keyword. Use the preprocessor.

In a nutshell,

foo->bar(moo, roo) 

is the fancy shmancy C++ way of saying

FooType_bar(foo, moo, roo)

and you should think about wrapping the latter while actually spelling the former.

How the caller obtains The Object then? One of your functions could create objects (with new) and return pointers to them. Another one could do a delete. Or you can return pointers to elements of a pre-allocated array. Or whatever. Basically you wrap the ways you use to obtain pointers to objects as a user of the original, unwrapped library.

That's about it.

于 2013-02-27T17:07:51.043 回答
0

您可以通过将成员函数的 thiscall 转换为 fastcall 来做到这一点

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

于 2013-03-06T09:11:29.880 回答
0

如果您正在调用成员函数,则只需将 this 指针作为第一个参数传递,并确保您使用的是正确的调用约定。对于静态成员函数,您不必传递 this 指针。

在 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

这是它是如何完成的。假设你有两层:成员函数层和包装函数层。您需要做的是创建位于这两层之间的第三层,并将该层导出为 .dll 文件。起初(当我问这个问题时)我试图包装看起来像这样的函数:

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

这不起作用,因为正如问题所述,您不能包装成员函数。我发现我需要在成员函数调用之上的层上进行所有对象管理,但仍位于包装函数之下。我最终得到的是一堆“桥接”函数(这就是我所说的),它们“弥合”了包装函数和成员函数之间的差距。所以现在,我包装了如下所示的函数:

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

然后我简单地做了一些__declspec(dllexport)'s 和__declspec(dllimport)'s 把这些函数变成一个 .dll 文件,就是这样!

于 2013-03-14T20:28:48.137 回答