Search code examples
c#.netwinformsc++-cliwinforms-interop

Managed DLL referenced by 2 Winform apps. Winform 1 launches Winform 2, but context is shared unless both are executed separately


I have two WinForm client applications that reference the same managed DLL (written in C++/CLI) with the purpose of connecting to a native socket server.

Both Winform applications run fine when launched separately, but not when one launches the other.

Let's say that client Winform 1 is launched. It creates its own socket and context as intended and then proceeds to launch Winform 2 as a separate thread.

Winform 2 will also open its own socket as a client of the native server, but when Winform 2 closes its socket and exits, Winform 1 stops working because it seems to think it's Winform 2. So any server requests by WinForm 1 fail because its socket becomes the one previously closed by socket 2.

This behavior is new to me, but it must obviously extend beyond variable "SOCKET socket_id".

Is Winform 2 supposed to be launched as a separate process instead of the typical thread that executes Application.Run(Winform2)?

Thanks.

private void LaunchWinForm2Button_Click(object sender, EventArgs e)
{
     System.Threading.Thread myThread = 
         new System.Threading.Thread(new System.Threading.ThreadStart(StartWinForm2));

     myThread.Start(); 
}

private void StartWinForm2()
{
        CSharpFormApp.WinForm2 theWinForm2 = new CSharpFormApp.WinForm2();
        Application.Run(theWinForm2);

}

Solution

  • The problem was with a global pointer to native data in the C++/CLI DLL. Since the WinForms get their data through several C++/CLI interfaces, it was initially easier to just have them access native data through a global pointer.

    NativeLayer::NativeLayerData* native_data;
    
    
    public ref class Test1Implementer : ITest1
    {
         virtual bool TestConnect()
         {
            bool success = native_data->Connect();
            return success;
         }
    };
    
    public ref class Test2Implementer : ITest2
    {
         virtual bool TestDisconnect()
         {
            bool success = native_data->Disconnect();
            return success;
         }
    };
    

    Eventually, such an implementation would come back to haunt me, but those are the perils of attempting to use globals in industrial applications.

    Once I got rid of the managed global pointer to native data, everything works as intended. Here's a possible solution that allows for multithreading using nested interfaces:

    public ref class TestsImplementer : ITests 
    {
    private:
        NativeLayer::NativeLayerData* native_data;
    
    public:
          TestsImplementer()
          {
             native_data = new NativeLayer::NativeLayerData();
          }
    
          ref class Test1Implementer : ITest1
          {
             virtual bool TestConnect(TestsImplementer^ tests)
             {
                bool success = tests->native_data->Connect();
                return success;
             }
          };
    
          ref class Test2Implementer : ITest2
          {
             virtual bool TestDisconnect(TestsImplementer^ tests)
             {
                bool success = tests->native_data->Disconnect();
                return success;
             }
          };
    };