【问题标题】:C++ how to change private members of managed object using a native one?C ++如何使用本机更改托管对象的私有成员?
【发布时间】:2015-04-17 16:12:36
【问题描述】:

我正在做一个学生项目。这是一款网卡游戏。该解决方案包含 3 个项目。客户端的 GUI 使用 Windows 窗体,因此它具有托管类。本机 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。从而获取并保持和使用指向它的指针。然后我认为这一切都会奏效。该怎么做?

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

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

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

【问题讨论】:

  • 可能使用委托 (C++/CLI) 是一种解决方案。 Here 写的是如何将委托^传递给需要函数指针的本机函数
  • This 更有用。它有效。

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


【解决方案1】:

终于解决了!!使用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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-15
    • 2011-04-30
    • 1970-01-01
    • 2013-12-30
    • 2019-07-11
    • 2016-01-10
    相关资源
    最近更新 更多