如何包装作为成员函数的DLL函数?

时间:2013-02-25 16:47:32

标签: c++ function dll wrapper

我正在创建一个包装类,它包含特定第三方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。没有包装函数,要调用它我只需创建一个Camera指针向量,然后执行:

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 个答案:

答案 0 :(得分:1)

要调用成员函数,您必须拥有一个对象。要有一个对象,你必须从某个地方获取它。一个好的小行为功能可以从中获取东西的唯一地方就是它的参数列表。

因此,每个函数显然必须有一个接收The Object的参数。

如果希望可以从C调用包装函数,则不能将类类型作为函数参数。所以你要么声明它为void*并在里面做一个演员,要么只是欺骗并输入它(仅限C!)struct YourClassName*(没有定义结构)。对于C ++,它仍应使用class关键字。使用预处理器。

简而言之,

foo->bar(moo, roo) 

是花哨的语法C ++的说法

FooType_bar(foo, moo, roo)

你应该考虑在实际拼写前者的同时包装后者。

调用者如何获取对象呢?您的一个函数可以创建对象(使用new)并返回指向它们的指针。另一个人可以删除。或者,您可以返回指向预分配数组元素的指针。管他呢。基本上,您将使用的方式包装为获取原始未包装库的用户指针。

就是这样。

答案 1 :(得分:0)

如果要调用memberfunctions并确保使用正确的调用约定,则只需将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;
}

答案 2 :(得分:0)

您可以通过将成员函数的thiscall转换为fastcall

来完成此操作

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

答案 3 :(得分:0)

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

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

这不起作用,因为正如问题描述的那样,你无法包装成员函数。我发现我需要在成员函数调用之上的一层上进行所有对象管理,但仍然在包装函数之下。我最终得到的是一堆“桥”函数(这就是我所说的),它“弥合”包装函数和成员函数之间的差距。所以现在,我包装的函数看起来像这样:

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

然后我只是做了一些__declspec(dllexport)__declspec(dllimport)来将这些函数转换为.dll文件,就是这样!