MessageWindow in a thread possible?

T

Tim Johnson

I developed a class that contains a MessageWindow which gets windows
messages from a legacy DLL:

using Microsoft.WindowsCE.Forms;
public class Mine
{
internal class MsgWin : MessageWindow
{
protected override void WndProc(ref Message msg)
{
//check for messages here
}
}

internal MsgWin myWin = new MsgWin();

public void Init()
{
//Send a legacy dll our window handle via P/Invoke
LegacyInit((myWin.Hwnd);
}
}

This all works fine in my test C# app.

But when I try to use this class from within a secondary thread, I don't get
messages. Since the legacy DLL is doing PostMessage to whatever hWnd I pass
it, shouldn't my WndProc see them? I'm wondering if somehow the main UI
thread is getting them instead so .

Is there any way to make the MessageWindow in my 2nd thread get these
messages?

--

Tim Johnson
High Point Software, Inc.
www.high-point.com
(503) 312-8625
 
G

Guest

It should still work. It's a Window and messages will still go to it. Post
a small example.

-Chris
 
T

Tim Johnson

I couldn't get back to this thread from 3 weeks ago till now, but the upshot
was people said "it should work; send a small sample". So here it is.

The recap is that using a MessageWindow from a main UI thread works, it gets
windows messages posted from a C++ dll. But the same class used from a
secondary thread doesn't. In the debugger it doesn't even get into the
MessageWindow proc.

I'm not sure if the newsgroup file-attach works, so here's the code recap in
case it doesn't show up in the next meessage:

1. Made an unmanaged evc4.0 C++ app that has 1 exported function "Init"
which takes an hWnd and posts a WM_USER message back to it:

void MsgWinInit(HWND hWnd)
{
::postMessage(hWnd, WM_USER, 0, 0);
}

2. Made a C# class "MWTest" containing a MessageWindow and which uses
P/Invoke to call the C++ Init, passing the hWnd of the MessageWindow class.

public class MWTest
{
public delegate void Event1Handler();
public static event Event1Handler Event1;

//MessageWindow class to handle Win msgs coming from C++ dll
internal class MsgWin : MessageWindow
{
public const int WM_USER = 1024;

protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_USER) //******** breakpoint here doesn't fire
when using 2nd thread
{
MessageBox.Show("Got MSG");
MWTest.Event1();
}
base.WndProc(ref msg);
}
}

[DllImport("MsgWinTest.dll")]
static extern void MsgWinInit(IntPtr hWnd);

internal MsgWin netWin = new MsgWin(); //Our MessageWindow handler

public void Init()
{
MsgWinInit(netWin.Hwnd);
}
}

3. A C# app using that class has 2 buttons:
btn1 - create an instance of MWTest, setup a callback event, and call
Init. It gets the message in the MessageWindow and in the event handler.

btn2 - create a 2nd thread, which creates an instance of MWTest, sets up
the event, and calls Init. It doesn't get it anything.

Here's that code:

//Global
bool mStop = false;

//--------------------- TEST MAIN
THREAD ----------------------------------------
private void button1_Click(object sender, System.EventArgs e)
{
MWTest mWin = new MWTest();

MWTest.Event1+= new MsgWin.MWTest.Event1Handler(OnEvent1A);

mWin.Init();
}

private void OnEvent1A()
{
MessageBox.Show("Got event in main thread"); //******* I get this
message

MWTest.Event1-= new MsgWin.MWTest.Event1Handler(OnEvent1A);
}

//--------------------- TEST 2ND
THREAD ----------------------------------------
private void button2_Click(object sender, System.EventArgs e)
{
//Start 2nd thread
Thread t2 = new Thread(new ThreadStart(T2Proc));
t2.Start();
}

private void T2Proc()
{
mStop = false;

MWTest mWin = new MWTest();

MWTest.Event1+= new MsgWin.MWTest.Event1Handler(OnEvent1B);

mWin.Init();

while(!mStop)
{
Thread.Sleep(250);
}

MessageBox.Show("Finished Thread2");
}

private void OnEvent1B()
{
MessageBox.Show("Got event in 2nd thread"); //***** Don't get this
message

MWTest.Event1-= new MsgWin.MWTest.Event1Handler(OnEvent1B);
mStop = true;
}
}
--

Tim Johnson
High Point Software, Inc.
www.high-point.com
(503) 312-8625
 
T

Tim Johnson

Sorry, I inadvertently changed the Subject of this which probably messes
everything up. Here it is again on the right Subject...

=======================================================
I couldn't get back to this thread from 3 weeks ago till now, but the upshot
was people said "it should work; send a small sample". So here it is.

The recap is that using a MessageWindow from a main UI thread works, it gets
windows messages posted from a C++ dll. But the same class used from a
secondary thread doesn't. In the debugger it doesn't even get into the
MessageWindow proc.

I'm not sure if the newsgroup file-attach works, so here's the code recap in
case it doesn't show up in the next meessage:

1. Made an unmanaged evc4.0 C++ app that has 1 exported function "Init"
which takes an hWnd and posts a WM_USER message back to it:

void MsgWinInit(HWND hWnd)
{
::postMessage(hWnd, WM_USER, 0, 0);
}

2. Made a C# class "MWTest" containing a MessageWindow and which uses
P/Invoke to call the C++ Init, passing the hWnd of the MessageWindow class.

public class MWTest
{
public delegate void Event1Handler();
public static event Event1Handler Event1;

//MessageWindow class to handle Win msgs coming from C++ dll
internal class MsgWin : MessageWindow
{
public const int WM_USER = 1024;

protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_USER) //******** breakpoint here doesn't fire
when using 2nd thread
{
MessageBox.Show("Got MSG");
MWTest.Event1();
}
base.WndProc(ref msg);
}
}

[DllImport("MsgWinTest.dll")]
static extern void MsgWinInit(IntPtr hWnd);

internal MsgWin netWin = new MsgWin(); //Our MessageWindow handler

public void Init()
{
MsgWinInit(netWin.Hwnd);
}
}

3. A C# app using that class has 2 buttons:
btn1 - create an instance of MWTest, setup a callback event, and call
Init. It gets the message in the MessageWindow and in the event handler.

btn2 - create a 2nd thread, which creates an instance of MWTest, sets up
the event, and calls Init. It doesn't get it anything.

Here's that code:

//Global
bool mStop = false;

//--------------------- TEST MAIN
THREAD ----------------------------------------
private void button1_Click(object sender, System.EventArgs e)
{
MWTest mWin = new MWTest();

MWTest.Event1+= new MsgWin.MWTest.Event1Handler(OnEvent1A);

mWin.Init();
}

private void OnEvent1A()
{
MessageBox.Show("Got event in main thread"); //******* I get this
message

MWTest.Event1-= new MsgWin.MWTest.Event1Handler(OnEvent1A);
}

//--------------------- TEST 2ND
THREAD ----------------------------------------
private void button2_Click(object sender, System.EventArgs e)
{
//Start 2nd thread
Thread t2 = new Thread(new ThreadStart(T2Proc));
t2.Start();
}

private void T2Proc()
{
mStop = false;

MWTest mWin = new MWTest();

MWTest.Event1+= new MsgWin.MWTest.Event1Handler(OnEvent1B);

mWin.Init();

while(!mStop)
{
Thread.Sleep(250);
}

MessageBox.Show("Finished Thread2");
}

private void OnEvent1B()
{
MessageBox.Show("Got event in 2nd thread"); //***** Don't get this
message

MWTest.Event1-= new MsgWin.MWTest.Event1Handler(OnEvent1B);
mStop = true;
}
}
--

Tim Johnson
High Point Software, Inc.
www.high-point.com
(503) 312-8625


It should still work. It's a Window and messages will still go to it.
Post a small example.

-Chris



--

Tim Johnson
High Point Software, Inc.
www.high-point.com
(503) 312-8625
 
S

Sergey Bogdanov

AFAIK, you can not create a window from a separate thread only from UI
thread (though you can use Control.Invoke to execute code in UI thread
context).

Best regards,
Sergey Bogdanov
http://www.sergeybogdanov.com


Tim said:
Sorry, I inadvertently changed the Subject of this which probably messes
everything up. Here it is again on the right Subject...

=======================================================
I couldn't get back to this thread from 3 weeks ago till now, but the upshot
was people said "it should work; send a small sample". So here it is.

The recap is that using a MessageWindow from a main UI thread works, it gets
windows messages posted from a C++ dll. But the same class used from a
secondary thread doesn't. In the debugger it doesn't even get into the
MessageWindow proc.

I'm not sure if the newsgroup file-attach works, so here's the code recap in
case it doesn't show up in the next meessage:

1. Made an unmanaged evc4.0 C++ app that has 1 exported function "Init"
which takes an hWnd and posts a WM_USER message back to it:

void MsgWinInit(HWND hWnd)
{
::postMessage(hWnd, WM_USER, 0, 0);
}

2. Made a C# class "MWTest" containing a MessageWindow and which uses
P/Invoke to call the C++ Init, passing the hWnd of the MessageWindow class.

public class MWTest
{
public delegate void Event1Handler();
public static event Event1Handler Event1;

//MessageWindow class to handle Win msgs coming from C++ dll
internal class MsgWin : MessageWindow
{
public const int WM_USER = 1024;

protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_USER) //******** breakpoint here doesn't fire
when using 2nd thread
{
MessageBox.Show("Got MSG");
MWTest.Event1();
}
base.WndProc(ref msg);
}
}

[DllImport("MsgWinTest.dll")]
static extern void MsgWinInit(IntPtr hWnd);

internal MsgWin netWin = new MsgWin(); //Our MessageWindow handler

public void Init()
{
MsgWinInit(netWin.Hwnd);
}
}

3. A C# app using that class has 2 buttons:
btn1 - create an instance of MWTest, setup a callback event, and call
Init. It gets the message in the MessageWindow and in the event handler.

btn2 - create a 2nd thread, which creates an instance of MWTest, sets up
the event, and calls Init. It doesn't get it anything.

Here's that code:

//Global
bool mStop = false;

//--------------------- TEST MAIN
THREAD ----------------------------------------
private void button1_Click(object sender, System.EventArgs e)
{
MWTest mWin = new MWTest();

MWTest.Event1+= new MsgWin.MWTest.Event1Handler(OnEvent1A);

mWin.Init();
}

private void OnEvent1A()
{
MessageBox.Show("Got event in main thread"); //******* I get this
message

MWTest.Event1-= new MsgWin.MWTest.Event1Handler(OnEvent1A);
}

//--------------------- TEST 2ND
THREAD ----------------------------------------
private void button2_Click(object sender, System.EventArgs e)
{
//Start 2nd thread
Thread t2 = new Thread(new ThreadStart(T2Proc));
t2.Start();
}

private void T2Proc()
{
mStop = false;

MWTest mWin = new MWTest();

MWTest.Event1+= new MsgWin.MWTest.Event1Handler(OnEvent1B);

mWin.Init();

while(!mStop)
{
Thread.Sleep(250);
}

MessageBox.Show("Finished Thread2");
}

private void OnEvent1B()
{
MessageBox.Show("Got event in 2nd thread"); //***** Don't get this
message

MWTest.Event1-= new MsgWin.MWTest.Event1Handler(OnEvent1B);
mStop = true;
}
}
 
T

Tim Johnson

I think you are right. In my sample I posted earlier I get this behavior:

1 - if I create an instance of my class that contains the MessageWindow in
the UI thread then use it, it works and I get the posted messages.

2 - if I create the instance in a 2nd thread then use it, it doesn't work.

3 - if I create the instance back in the UI thread, but then use it from the
2nd thread (ie call the Init method), I get the messages. But in this case
I believe the WndProc is actually running on the UI thread when it gets the
message. In fact if in the WndProc I trigger the event that I thought was
"in" my 2nd thread, I think it's really being handled on the UI thread too.
In other words, the 2nd thread sets up the event handler ("...+=
handler(Event1B)"), but Event1B is really being run on the UI thread. But
if WndProc is triggering it, and WndProc is really on the UI thread, then
I'd guess Event1B is on the UI thread too.

Bottom line - the MessageWindow class has a "hidden window" but it somehow
requires you to instantiate it from within the UI thread to be able to
receive messages. If you instantiate it from a nonUI thread, that hidden
window never gets messages. If you have a legacy communications dll that
posts messages, like I have, this is really bad - because I need to process
those "background" messages on my main UI thread, then shunt them over to a
2ndary thread so the UI performance doesn't suffer.
--

Tim Johnson
High Point Software, Inc.
www.high-point.com
(503) 312-8625


Sergey Bogdanov said:
AFAIK, you can not create a window from a separate thread only from UI
thread (though you can use Control.Invoke to execute code in UI thread
context).

Best regards,
Sergey Bogdanov
http://www.sergeybogdanov.com


Tim said:
Sorry, I inadvertently changed the Subject of this which probably messes
everything up. Here it is again on the right Subject...

=======================================================
I couldn't get back to this thread from 3 weeks ago till now, but the
upshot
was people said "it should work; send a small sample". So here it is.

The recap is that using a MessageWindow from a main UI thread works, it
gets
windows messages posted from a C++ dll. But the same class used from a
secondary thread doesn't. In the debugger it doesn't even get into the
MessageWindow proc.

I'm not sure if the newsgroup file-attach works, so here's the code recap
in
case it doesn't show up in the next meessage:

1. Made an unmanaged evc4.0 C++ app that has 1 exported function "Init"
which takes an hWnd and posts a WM_USER message back to it:

void MsgWinInit(HWND hWnd)
{
::postMessage(hWnd, WM_USER, 0, 0);
}

2. Made a C# class "MWTest" containing a MessageWindow and which uses
P/Invoke to call the C++ Init, passing the hWnd of the MessageWindow
class.

public class MWTest
{
public delegate void Event1Handler();
public static event Event1Handler Event1;

//MessageWindow class to handle Win msgs coming from C++ dll
internal class MsgWin : MessageWindow
{
public const int WM_USER = 1024;

protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_USER) //******** breakpoint here doesn't fire
when using 2nd thread
{
MessageBox.Show("Got MSG");
MWTest.Event1();
}
base.WndProc(ref msg);
}
}

[DllImport("MsgWinTest.dll")]
static extern void MsgWinInit(IntPtr hWnd);

internal MsgWin netWin = new MsgWin(); //Our MessageWindow handler

public void Init()
{
MsgWinInit(netWin.Hwnd);
}
}

3. A C# app using that class has 2 buttons:
btn1 - create an instance of MWTest, setup a callback event, and call
Init. It gets the message in the MessageWindow and in the event handler.

btn2 - create a 2nd thread, which creates an instance of MWTest, sets
up
the event, and calls Init. It doesn't get it anything.

Here's that code:

//Global
bool mStop = false;

//--------------------- TEST MAIN
THREAD ----------------------------------------
private void button1_Click(object sender, System.EventArgs e)
{
MWTest mWin = new MWTest();

MWTest.Event1+= new MsgWin.MWTest.Event1Handler(OnEvent1A);

mWin.Init();
}

private void OnEvent1A()
{
MessageBox.Show("Got event in main thread"); //******* I get this
message

MWTest.Event1-= new MsgWin.MWTest.Event1Handler(OnEvent1A);
}

//--------------------- TEST 2ND
THREAD ----------------------------------------
private void button2_Click(object sender, System.EventArgs e)
{
//Start 2nd thread
Thread t2 = new Thread(new ThreadStart(T2Proc));
t2.Start();
}

private void T2Proc()
{
mStop = false;

MWTest mWin = new MWTest();

MWTest.Event1+= new MsgWin.MWTest.Event1Handler(OnEvent1B);

mWin.Init();

while(!mStop)
{
Thread.Sleep(250);
}

MessageBox.Show("Finished Thread2");
}

private void OnEvent1B()
{
MessageBox.Show("Got event in 2nd thread"); //***** Don't get this
message

MWTest.Event1-= new MsgWin.MWTest.Event1Handler(OnEvent1B);
mStop = true;
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top