C ++如何使用本机对象更改托管对象的私有成员?

时间:2015-02-17 01:58:43

标签: visual-c++ c++-cli mixed-mode

我正在从事学生项目。这是一个网卡游戏。该解决方案包含3个项目。客户端使用Windows窗体的GUI,因此它具有托管类。本机C ++中的静态客户端库。 GUI的项目参考了它,因此使用了混合规则'。服务器也是本机C ++。我使用RPC中间件进行通信。它仅适用于本机C ++。这就是为什么我需要静态库来隐藏客户端的所有通信细节。

由于服务器可以随时改变其状态并且应该在客户端的GUI中显示,因此我使用回调方法来更改Windows窗体'组件。在这里我发现了一个问题,因为我需要在本机对象的帮助下更改托管类的私有成员。

可能有不同的方法可以做到这一点。我的想法是将一个指向托管类实例的指针发送到本机类的实例并将其保存在那里。所以稍后我可以从该托管类的本机对象公共成员函数调用来更改组件。

这是来自我的混合规则' GUI项目:

//Native class for changing window 'Lobby'
class LobbyI : public ClientLib::Lobby {
public:
    LobbyI();
    ~LobbyI();

    //Should change window due to current Server's state
    void reDraw(const CommonLogic::ServerState&);  
};

// Managed class implements GUI for window 'Lobby'
// generated by Visual Studio designer
public ref class LobbyGUI : public System::Windows::Forms::Form {
    //My members
    ClientLib::Mediator* mediatorPtr; // Is it correct?
    LobbyI* lobbyPtr; // ?
public:
    LobbyGUI(void) {
        InitializeComponent();

        mediatorPtr = new ClientLib::Mediator(); // Is it correct?
        lobbyPtr = new LobbyI(); // ?
        mediatorPtr->setCallback(lobbyPtr);
    }
protected:
    ~LobbyGUI() {
        if (components) { delete components; }

        delete lobbyPtr; // Is it correct?
        lobbyPtr = nullptr; // ?
        delete mediatorPtr; // ?
        mediatorPtr = nullptr; // ?
    }
private: System::Windows::Forms::Button^  buttonLogIn;
//...

这是来自本机静态库 ClientLib

class Lobby {
public:
    virtual ~Lobby();
    virtual void reDraw(const CommonLogic::ServerState&) = 0;
};

class Mediator {
    CommonLogic::ServerState serverState;
    Lobby* lobbyPtr;
public:
    Mediator();
    ~Mediator();
    void setCallback(Lobby* ptr) { lobbyPtr = ptr; }
    void reDrawLobby() { lobbyPtr->reDraw(serverState); }
}; 

此代码构建正常。我现在唯一需要的是本机派生类 LobbyI 的成员函数 reDraw()能够更改托管类 LobbyGUI 实现的窗口>。因此获取并保持和使用指针。然后我认为一切都会奏效。怎么做?

也许它不是最好的实现。我很乐意阅读其他建议。

我也怀疑我在托管类中使用指向本机类的指针的方式。这是对的吗?在我在析构函数ptr=nullptr;之后插入delete ptr;之前,它无法正常工作。

更新:现在我在代码中看到了冗余。抽象类 Lobby 是没用的。我只需要在托管类中实现 reDraw()函数,该函数显然可以访问窗口的组件。然后将安全指针传递给本机类函数,该函数需要指向函数的指针作为参数。

1 个答案:

答案 0 :(得分:0)

最后我解决了!!使用this article。在以下代码中,本机对象存储指向托管对象的函数的指针。因此可以随时调用此回调函数。 委托用作类型安全函数指针的形式。 GCHandle 的实例用于防止代理被垃圾回收器重定位。

这是一个简单的CLR控制台应用程序,它使用从本机对象调用的回调函数递增并打印一些整数。因此,我们可以"使用本地成员更改托管对象的私有成员"。

using namespace System;
using namespace System::Runtime::InteropServices;

typedef void(__stdcall *ANSWERCB)(); // define type of callback function
#pragma unmanaged
class NativeClass {
    ANSWERCB cbFuncPtr = 0; // pointer to callback function
public:
    void setCallback(ANSWERCB fptr) {
        cbFuncPtr = fptr;
        incAndPrint();
    }
    void incAndPrint() { cbFuncPtr(); } // invokes callback which increments and prints
};
#pragma managed
ref class ManagedClass {
public: delegate void Del();
private:
    Int32 i;
    NativeClass* nativePtr;
    Del^ delHandle;
    GCHandle gch;
public:
    ManagedClass(Int32 ii) : i(ii)   {
        nativePtr = new NativeClass;
        delHandle = gcnew Del(this, &ManagedClass::changeAndPrintInt);
        gch = GCHandle::Alloc(delHandle);
        IntPtr ip = Marshal::GetFunctionPointerForDelegate(delHandle);
        ANSWERCB callbackPtr = static_cast<ANSWERCB>(ip.ToPointer());
        nativePtr->setCallback(callbackPtr);
    }
    ~ManagedClass()     {
        delete nativePtr;
        nativePtr = __nullptr;
        gch.Free();
    }
private:
    void changeAndPrintInt()  // callback function
    {
        Console::WriteLine(++i);  
    } 
};

int main(array<System::String ^> ^args)
{
    ManagedClass mc(1); 
    return 0;
}