0
votes

I'm working on a student project. It's a network card game. The solution contains 3 projects. Client's GUI using Windows Forms so it has managed classes. Static client's library in native C++. GUI's project has reference to it thus uses 'Mixed Rules'. Server is in native C++ as well. I use RPC middleware for communication. It works only with native C++. That is why I need the static library to hide there all the details of communication on client's side.

Since the server can at any moment change its state and that should be shown in client's GUI, I use callback approach to change Windows Forms' components. And here I found a problem because I need to change private members of managed class with the help of a native object.

There are probably different ways to do that. My idea is sending a pointer to instance of managed class into instance of native class and saving it there. So later I can call from that native object public member functions of that managed class to change components.

This is from my 'Mixed Rules' GUI project:

//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;
//...

This is from native static library 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); }
}; 

This code builds ok. The only thing I need now is that the member function reDraw() of native derived class LobbyI is able to change the window implemented by managed class LobbyGUI. Thus getting and keeping and using pointer to it. And then I think it all will work. How to do that?

Maybe it's not the nicest implementation in general. I would be happy to read other suggestion.

I'm also doubtful about the way I used pointers to native classes inside managed class. Is it correct? It didn't work correct until I inserted ptr=nullptr; after delete ptr; in destructor.

UPDATE: Now I see redundancy in my code. Abstract class Lobby is useless. I need only to implement reDraw() function in managed class which will have obviously access to components of the window. And then pass safe pointer to native class function which expects pointer to a function as a parameter.

1
Probably the use of delegates (C++/CLI) is a solution. Here is written how to Pass a delegate^ to a native function that expects a function pointerSyb3rian
This is more usefull. It works.Syb3rian

1 Answers

0
votes

Finally I've solved it!! Using this article. In the following code a native object stores provided pointer to a function of managed object. So this callback function can be invoked at any time. A delegate is used as a form of type-safe function pointer. Instance of GCHandle is used to prevent the delegate from being relocated by garbage collector.

Here is simple CLR Console Application which increments and prints some integer using callback function invoked from native object. Thus we can "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;
}