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