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

C++ how to change private members of managed object using a native one?

我正在做一个学生项目。这是一个网络卡牌游戏。该解决方案包含 3 个项目。客户端的 GUI 使用 Windows 表单,因此它管理了 classes。本机 C++ 中的静态客户端库。 GUI 的项目引用了它,因此使用 'Mixed Rules'。服务器也是原生 C++。我使用 RPC 中间件进行通信。它仅适用于本机 C++。这就是为什么我需要静态库来隐藏客户端通信的所有细节。

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

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

这是我的 'Mixed Rules' 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); }
}; 

此代码构建正常。我现在唯一需要的是原生派生classLobbyI的成员函数reDraw()能够改变window 由托管 class LobbyGUI 实施。从而获取并保留和使用指向它的指针。然后我认为这一切都会奏效。怎么做?

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

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

更新: 现在我发现代码中存在冗余。摘要class大堂没用。我只需要在托管 class 中实现 reDraw() 函数,这显然可以访问 window 的组件。然后将安全指针传递给本机 class 函数,该函数需要指向函数的指针作为参数。

终于解决了!!使用 this article。在下面的代码中,本机对象存储提供的指向托管对象函数的指针。所以这个回调函数可以随时调用。 delegate 用作类型安全函数指针的一种形式。 GCHandle 的实例用于防止委托被垃圾收集器重新定位。

这是一个简单的 CLR 控制台应用程序,它使用从本机对象调用的回调函数递增并打印一些整数。因此我们可以 "change private members of managed object using a native one".

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;
}