ChrisKemmerer
2009-05-07 12:43:01 UTC
I have a .NET assembly that interacts with a third party driver that
communicates using custom Windows messages. This assembly is then used by
others in their application. The purpose of the assembly is to hide the ugly
details of communicating with the driver. In my assembly I have a class
derived from NativeWindow that creates a message only window (HWND_MESSAGE).
In the overridden WndProc procedure I check for the custom messages and
handle them. This works fine when using the assembly from a normal Windows
Forms application in C# or VB.NET. It doesn't work with remoting.
For testing I created a stripped down version of my assembly and checked to
make sure that the overridden WndProc was being called and found that it was
not. I could tell that the message window was created and can find it using
another test application that calls FindWindowEx and then posts a WM_NULL
message to it. I could also see that Windows messages were being processed
while the message window was being created (WM_NCCREATE, WM_CREATE, ...).
After that, nothing. Again, this assembly works fine with normal Windows
Forms apps but not when remoted. Below is my code.
Just to make sure this wasn't just an issue with Message Windows I tried
creating a normal window and still had the same problem.
I'm hoping it is just something stupid that I forgot. Thanks.
-------------------------
RemoteNET.h
-------------------------
using namespace System;
using namespace System::Windows::Forms;
namespace RemoteNET {
ref class CNETWnd;
public ref class CTestClass
{
public:
CTestClass();
~CTestClass();
private:
CNETWnd^ m_pWnd;
};
public ref class CNETWnd : public NativeWindow
{
public:
CNETWnd(CTestClass^ pClass);
~CNETWnd();
protected:
CTestClass^ m_pClass;
protected:
virtual void WndProc(Message % msg) override;
};
}
}
}
------------------
RemoteNET.cpp
------------------
namespace RemoteNET
{
CTestClass::CTestClass()
{
m_pWnd = gcnew CNETWnd(this);
}
CTestClass::~CTestClass()
{
}
CNETWnd::~CNETWnd()
{
}
CNETWnd::CNETWnd(CTestClass^ pClass) : m_pClass(pClass)
{
CreateParams^ cp = gcnew CreateParams;
// create the message window to receive Windows messages
cp->Caption = "NETWnd";
cp->ClassName = "Message";
cp->Parent = IntPtr(HWND_MESSAGE);
this->CreateHandle(cp);
}
void CNETWnd::WndProc(Message % msg)
{
System::Diagnostics::Debug::WriteLine(String::Format("WndProc msg:
0x{0:X}.", msg.Msg));
NativeWindow::WndProc(msg);
}
}
}
}
My remote class that wraps the .NET assembly.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RemoteNET;
namespace RemoteClass
{
public class RemoteClass : MarshalByRefObject
{
private CTestClass objTestClass;
public RemoteClass()
{
objTestClass = new CTestClass();
}
public void DoSomething()
{
return;
}
}
}
Remote Server.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace RemoteServer
{
class Program
{
static void Main(string[] args)
{
string configFile =
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
//string configFile = "App.config";
RemotingConfiguration.Configure(configFile, true);
Console.Write("Sever is Ready........");
Console.Read();
}
}
}
Remote server config file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown
type="RemoteClass.RemoteClass, RemoteClass"
objectUri="RemoteController"
mode="Singleton"
/>
</service>
<channels>
<channel ref="tcp" port="2000">
<clientProviders>
<formatter ref="binary"/>
</clientProviders>
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full"/>
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Client.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace RemoteClient
{
public partial class TestClass : Form
{
RemoteClass.RemoteClass test;
public TestClass()
{
InitializeComponent();
}
private void TestClass_Load(object sender, EventArgs e)
{
string configFile =
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
RemotingConfiguration.Configure(configFile, true);
string url = "tcp://localhost:2000/CameraController";
test =
(RemoteClass.RemoteClass)RemotingServices.Connect(typeof(RemoteClass.RemoteClass), url);
}
private void button1_Click(object sender, EventArgs e)
{
test.DoSomething();
}
}
}
Client config file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>
<client>
<wellknown
type="RemoteClass.RemoteClass, RemoteClass"
url="tcp://localhost:2000/RemoteController"
/>
</client>
<channels>
<channel ref="tcp" port="0">
<clientProviders>
<formatter ref="binary"/>
</clientProviders>
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full"/>
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
communicates using custom Windows messages. This assembly is then used by
others in their application. The purpose of the assembly is to hide the ugly
details of communicating with the driver. In my assembly I have a class
derived from NativeWindow that creates a message only window (HWND_MESSAGE).
In the overridden WndProc procedure I check for the custom messages and
handle them. This works fine when using the assembly from a normal Windows
Forms application in C# or VB.NET. It doesn't work with remoting.
For testing I created a stripped down version of my assembly and checked to
make sure that the overridden WndProc was being called and found that it was
not. I could tell that the message window was created and can find it using
another test application that calls FindWindowEx and then posts a WM_NULL
message to it. I could also see that Windows messages were being processed
while the message window was being created (WM_NCCREATE, WM_CREATE, ...).
After that, nothing. Again, this assembly works fine with normal Windows
Forms apps but not when remoted. Below is my code.
Just to make sure this wasn't just an issue with Message Windows I tried
creating a normal window and still had the same problem.
I'm hoping it is just something stupid that I forgot. Thanks.
-------------------------
RemoteNET.h
-------------------------
using namespace System;
using namespace System::Windows::Forms;
namespace RemoteNET {
ref class CNETWnd;
public ref class CTestClass
{
public:
CTestClass();
~CTestClass();
private:
CNETWnd^ m_pWnd;
};
public ref class CNETWnd : public NativeWindow
{
public:
CNETWnd(CTestClass^ pClass);
~CNETWnd();
protected:
CTestClass^ m_pClass;
protected:
virtual void WndProc(Message % msg) override;
};
}
}
}
------------------
RemoteNET.cpp
------------------
namespace RemoteNET
{
CTestClass::CTestClass()
{
m_pWnd = gcnew CNETWnd(this);
}
CTestClass::~CTestClass()
{
}
CNETWnd::~CNETWnd()
{
}
CNETWnd::CNETWnd(CTestClass^ pClass) : m_pClass(pClass)
{
CreateParams^ cp = gcnew CreateParams;
// create the message window to receive Windows messages
cp->Caption = "NETWnd";
cp->ClassName = "Message";
cp->Parent = IntPtr(HWND_MESSAGE);
this->CreateHandle(cp);
}
void CNETWnd::WndProc(Message % msg)
{
System::Diagnostics::Debug::WriteLine(String::Format("WndProc msg:
0x{0:X}.", msg.Msg));
NativeWindow::WndProc(msg);
}
}
}
}
My remote class that wraps the .NET assembly.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RemoteNET;
namespace RemoteClass
{
public class RemoteClass : MarshalByRefObject
{
private CTestClass objTestClass;
public RemoteClass()
{
objTestClass = new CTestClass();
}
public void DoSomething()
{
return;
}
}
}
Remote Server.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace RemoteServer
{
class Program
{
static void Main(string[] args)
{
string configFile =
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
//string configFile = "App.config";
RemotingConfiguration.Configure(configFile, true);
Console.Write("Sever is Ready........");
Console.Read();
}
}
}
Remote server config file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown
type="RemoteClass.RemoteClass, RemoteClass"
objectUri="RemoteController"
mode="Singleton"
/>
</service>
<channels>
<channel ref="tcp" port="2000">
<clientProviders>
<formatter ref="binary"/>
</clientProviders>
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full"/>
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Client.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace RemoteClient
{
public partial class TestClass : Form
{
RemoteClass.RemoteClass test;
public TestClass()
{
InitializeComponent();
}
private void TestClass_Load(object sender, EventArgs e)
{
string configFile =
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
RemotingConfiguration.Configure(configFile, true);
string url = "tcp://localhost:2000/CameraController";
test =
(RemoteClass.RemoteClass)RemotingServices.Connect(typeof(RemoteClass.RemoteClass), url);
}
private void button1_Click(object sender, EventArgs e)
{
test.DoSomething();
}
}
}
Client config file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>
<client>
<wellknown
type="RemoteClass.RemoteClass, RemoteClass"
url="tcp://localhost:2000/RemoteController"
/>
</client>
<channels>
<channel ref="tcp" port="0">
<clientProviders>
<formatter ref="binary"/>
</clientProviders>
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full"/>
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>