HELP: Problem using WndProc MessageWindow with native DLL

H

Hashka

Hello everyone.

I have a big problem in my C# code for my Smart device project
(IPAQ/pocketpc 2003)
I am trying to apply the MSDN "Asynchronous callbacks from native
Win32 code" sample to my own case and I do not managed to make it work
as I want.
I have a GUI C# application that use a native DLL function (ReplyMe),
called thanks to P/Invoke interop feature of .NET.
This native function only sends a window message to the handle of the
window that called it, and waits for 5 seconds before returning true.

My C# application has its own MessageWindow object, with the WndProc
method overiden.
The C# application pass the handle of the MessageWindow to the native
function
in order to enable it to use SendMessage function with the approprate
handle as parameter.

All is working pretty great except the fact that the WndProc method of
my own MessageWindow is never triggered, and never catch any message.
The native function is called correctly, is processed correctly (waits
the correct time), and retuns the correct value (true boolean).
However it seems that the message sent is not received on the managed
C# side.

I am really out of resources.
I copy my whole code in order to let you take a look to it if you have
the will.

I describe a bit what i am giving to you

*** Form1.cs: The C# basic form, it has an attribute of type
JoMessageWindow in order to collect the windows messages

*** JoMessageWindo.cs: My own MessageWindow class, with an attribute
of type Form1 in order to modify some GUI attributes of the Form.
I have overriden the WndProc method in order to
deal the message as i want.
The problem is that i never go through the
WndProc method code

*** LibWrap.cs: This is the helper classe that enable to call my
native function

*** JoDll.h and JoDll.cpp: the source code of my native DLL

The C# was compiled thanks to Visual Studio .NET 2003
the DLL was compiled thanks to EVC4.0 sp2

I would really appreciate any help
Thanks again for reading this long long post

/************
* Form1.cs *
************/

using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using Microsoft.WindowsCE.Forms;

namespace tescs
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button Identify;
public System.Windows.Forms.Label info_msg;

//
// I reference my own MessageWindow
//
private JoWindowMessage m__msgWnd;


public Form1()
{
InitializeComponent();

//
// I construct own MessageWindow with the handle of the current form
//
m__msgWnd = new JoWindowMessage(this);
}

protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}

private void InitializeComponent()
{
this.Identify = new System.Windows.Forms.Button();
this.info_msg = new System.Windows.Forms.Label();
this.Identify.Location = new System.Drawing.Point(80, 112);
this.Identify.Text = "Identify";
this.Identify.Click += new
System.EventHandler(this.Identify_Click);
this.info_msg.Location = new System.Drawing.Point(56, 40);
this.info_msg.Size = new System.Drawing.Size(128, 40);
this.info_msg.Text = "Click on the button";
this.info_msg.TextAlign =
System.Drawing.ContentAlignment.TopCenter;
this.Controls.Add(this.info_msg);
this.Controls.Add(this.Identify);
this.Text = "Form1";

}

static void Main()
{
Application.Run(new Form1());
}

private void Identify_Click(object sender, System.EventArgs e)
{
//
// I call here the helper fonction of my native DLL. I give the handle
of my own MessageWindow handle
//
bool ret = LibWrap.ReplyMe(m__msgWnd.Hwnd);
if(ret)
info_msg.Text = "Identified";

else
info_msg.Text = "Not identified";
}

public void DisplayInfoCB (string item)
{
info_msg.Text = item;
}
}
}


/**********************
* JoWindowMessage.cs *
**********************/

using System;
using Microsoft.WindowsCE.Forms;
using System.Windows.Forms;


namespace tescs
{
//
// This is my own MessageWindow. This is a classic MessageWindow with
a reference on a Form
//
public class JoWindowMessage:MessageWindow
{
private const int WM_USER = 0x400;
private const int WM_ASYNCDATA_RECEIVED = WM_USER + 1;
private Form1 destinationForm;

public JoWindowMessage(Form1 destinationForm)
{
this.destinationForm = destinationForm;
}
//
// This is the override of the WndProc. HERE IS THE PROBLEM, WE NEVER
COME IN THIS METHOD
//
protected override void WndProc(ref Message msg)
{
switch(msg.Msg)
{
case WM_ASYNCDATA_RECEIVED:
unsafe
{
string str = new string((char *)msg.LParam.ToPointer());
destinationForm.DisplayInfoCB(str);
break;
}
}

// call the base class WndProc for default message handling
base.WndProc(ref msg);
}
}
}


/**************
* LibWrap.cs *
**************/

using System;
using System.Runtime.InteropServices;

namespace tescs
{
public class LibWrap
{
[DllImport("JoDll.dll")]
public static extern bool ReplyMe(IntPtr hWnd);
}
}


/***********
* JoDll.h *
***********/

#ifdef JODLL_EXPORTS
#define JODLL_API __declspec(dllexport)
#else
#define JODLL_API __declspec(dllimport)
#endif

#define WM_ASYNCDATA_RECEIVED WM_USER+1

extern "C" JODLL_API bool ReplyMe(HWND hWnd);



/*************
* JoDll.cpp *
*************/

#include "stdafx.h"
#include "Joll.h"

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

//
// This is my native function called from the managed side.
//It just sends a message to the handle provided (that is the
MessageWindow),
//and wait for 5 seconds
//
JODLL_API bool ReplyMe(HWND hWnd)
{
SendMessage(hWnd), WM_ASYNCDATA_RECEIVED, 0, NULL);
Sleep(5000);
return true;
}
 
K

Kevin Hutchison

The code should work ... and (after trying it) does. Make sure that the
correct version of JoDll.dll is on the device. I link external DLLs to my
project when developing so that I always have the latest version.

Good Luck,

- K.


Hashka said:
Hello everyone.

I have a big problem in my C# code for my Smart device project
(IPAQ/pocketpc 2003)
I am trying to apply the MSDN "Asynchronous callbacks from native
Win32 code" sample to my own case and I do not managed to make it work
as I want.
I have a GUI C# application that use a native DLL function (ReplyMe),
called thanks to P/Invoke interop feature of .NET.
This native function only sends a window message to the handle of the
window that called it, and waits for 5 seconds before returning true.

My C# application has its own MessageWindow object, with the WndProc
method overiden.
The C# application pass the handle of the MessageWindow to the native
function
in order to enable it to use SendMessage function with the approprate
handle as parameter.

All is working pretty great except the fact that the WndProc method of
my own MessageWindow is never triggered, and never catch any message.
The native function is called correctly, is processed correctly (waits
the correct time), and retuns the correct value (true boolean).
However it seems that the message sent is not received on the managed
C# side.

I am really out of resources.
I copy my whole code in order to let you take a look to it if you have
the will.

I describe a bit what i am giving to you

*** Form1.cs: The C# basic form, it has an attribute of type
JoMessageWindow in order to collect the windows messages

*** JoMessageWindo.cs: My own MessageWindow class, with an attribute
of type Form1 in order to modify some GUI attributes of the Form.
I have overriden the WndProc method in order to
deal the message as i want.
The problem is that i never go through the
WndProc method code

*** LibWrap.cs: This is the helper classe that enable to call my
native function

*** JoDll.h and JoDll.cpp: the source code of my native DLL

The C# was compiled thanks to Visual Studio .NET 2003
the DLL was compiled thanks to EVC4.0 sp2

I would really appreciate any help
Thanks again for reading this long long post

/************
* Form1.cs *
************/

using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using Microsoft.WindowsCE.Forms;

namespace tescs
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button Identify;
public System.Windows.Forms.Label info_msg;

//
// I reference my own MessageWindow
//
private JoWindowMessage m__msgWnd;


public Form1()
{
InitializeComponent();

//
// I construct own MessageWindow with the handle of the current form
//
m__msgWnd = new JoWindowMessage(this);
}

protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}

private void InitializeComponent()
{
this.Identify = new System.Windows.Forms.Button();
this.info_msg = new System.Windows.Forms.Label();
this.Identify.Location = new System.Drawing.Point(80, 112);
this.Identify.Text = "Identify";
this.Identify.Click += new
System.EventHandler(this.Identify_Click);
this.info_msg.Location = new System.Drawing.Point(56, 40);
this.info_msg.Size = new System.Drawing.Size(128, 40);
this.info_msg.Text = "Click on the button";
this.info_msg.TextAlign =
System.Drawing.ContentAlignment.TopCenter;
this.Controls.Add(this.info_msg);
this.Controls.Add(this.Identify);
this.Text = "Form1";

}

static void Main()
{
Application.Run(new Form1());
}

private void Identify_Click(object sender, System.EventArgs e)
{
//
// I call here the helper fonction of my native DLL. I give the handle
of my own MessageWindow handle
//
bool ret = LibWrap.ReplyMe(m__msgWnd.Hwnd);
if(ret)
info_msg.Text = "Identified";

else
info_msg.Text = "Not identified";
}

public void DisplayInfoCB (string item)
{
info_msg.Text = item;
}
}
}


/**********************
* JoWindowMessage.cs *
**********************/

using System;
using Microsoft.WindowsCE.Forms;
using System.Windows.Forms;


namespace tescs
{
//
// This is my own MessageWindow. This is a classic MessageWindow with
a reference on a Form
//
public class JoWindowMessage:MessageWindow
{
private const int WM_USER = 0x400;
private const int WM_ASYNCDATA_RECEIVED = WM_USER + 1;
private Form1 destinationForm;

public JoWindowMessage(Form1 destinationForm)
{
this.destinationForm = destinationForm;
}
//
// This is the override of the WndProc. HERE IS THE PROBLEM, WE NEVER
COME IN THIS METHOD
//
protected override void WndProc(ref Message msg)
{
switch(msg.Msg)
{
case WM_ASYNCDATA_RECEIVED:
unsafe
{
string str = new string((char *)msg.LParam.ToPointer());
destinationForm.DisplayInfoCB(str);
break;
}
}

// call the base class WndProc for default message handling
base.WndProc(ref msg);
}
}
}


/**************
* LibWrap.cs *
**************/

using System;
using System.Runtime.InteropServices;

namespace tescs
{
public class LibWrap
{
[DllImport("JoDll.dll")]
public static extern bool ReplyMe(IntPtr hWnd);
}
}


/***********
* JoDll.h *
***********/

#ifdef JODLL_EXPORTS
#define JODLL_API __declspec(dllexport)
#else
#define JODLL_API __declspec(dllimport)
#endif

#define WM_ASYNCDATA_RECEIVED WM_USER+1

extern "C" JODLL_API bool ReplyMe(HWND hWnd);



/*************
* JoDll.cpp *
*************/

#include "stdafx.h"
#include "Joll.h"

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

//
// This is my native function called from the managed side.
//It just sends a message to the handle provided (that is the
MessageWindow),
//and wait for 5 seconds
//
JODLL_API bool ReplyMe(HWND hWnd)
{
SendMessage(hWnd), WM_ASYNCDATA_RECEIVED, 0, NULL);
Sleep(5000);
return true;
}
 
H

Hashka

I have actually one more information to precise.
I think that the WndProc eventually works.
In fact, I Think that the WndProc is triggered once the ReplyMe
function has finished and retuned the true boolean.
I made one extra test by removing the part

if(ret)
info_msg.Text = "Identified";
else
info_msg.Text = "Not identified";

located after the ReplyMe function call.

Normally with this code, the info_msg text label always displays
"Identified" after the return of the ReplyMe function (since this one
always returns true).
But I added the line

destinationForm.info_msg.Text = "I passed in the WndProc method";

at the very begining of the WndProc method, and i notice that the
info_msg text label finally displays the "I passed in the WndProc
method" string ONCE THE REPLYME FUNCTION HAS FINISHED (that is 5
seconds after).
This is like the C# application WndProc method was not multithreaded.
Is that right?
Should I create a new thread to start my ReplyMe function (that is
actually 5 seconds blocking)?
thanks again for your help
++
HK
 
K

Kevin Hutchison

ONCE THE REPLYME FUNCTION HAS FINISHED (that is 5 seconds after).
That should be the expected behavior.

It is not entirely clear to me what behavior you are expecting. The example
you are citing uses a thread in the unmanaged code to produce the
asynchronous results. If you want similar behavior, you will need to use
similar code.

Good Luck!

- K.
 
A

Alex Feinman [MVP]

If I understand correctly, what you are doing is calling a DLL function from
a main thread (aka GUI thread) and then block execution inside that function
for 5 seconds. Here's what happens next: you dll sends message back to
message window, message window wnd proc gets invoked and changes the text of
a label. Label now needs to be repainted, so when the main window message
loop runs again, it will receive and process WM_PAINT for the label. Problem
is that the DLL is already inside Sleep call so main message loop won't run
until that is finished. A correct solution is not to block interface thread
and spawn another thread for lengthy operations. A simple solution (and not
very good one) is to add Label.Update() and Application.DoEvents() to the
DisplayInfoCB fnction

--
Alex Feinman
---
Coming to MDC? make sure to stop by the session CLI345
on Thursday for OpenNETCF talk

Hashka said:
Hello everyone.

I have a big problem in my C# code for my Smart device project
(IPAQ/pocketpc 2003)
I am trying to apply the MSDN "Asynchronous callbacks from native
Win32 code" sample to my own case and I do not managed to make it work
as I want.
I have a GUI C# application that use a native DLL function (ReplyMe),
called thanks to P/Invoke interop feature of .NET.
This native function only sends a window message to the handle of the
window that called it, and waits for 5 seconds before returning true.

My C# application has its own MessageWindow object, with the WndProc
method overiden.
The C# application pass the handle of the MessageWindow to the native
function
in order to enable it to use SendMessage function with the approprate
handle as parameter.

All is working pretty great except the fact that the WndProc method of
my own MessageWindow is never triggered, and never catch any message.
The native function is called correctly, is processed correctly (waits
the correct time), and retuns the correct value (true boolean).
However it seems that the message sent is not received on the managed
C# side.

I am really out of resources.
I copy my whole code in order to let you take a look to it if you have
the will.

I describe a bit what i am giving to you

*** Form1.cs: The C# basic form, it has an attribute of type
JoMessageWindow in order to collect the windows messages

*** JoMessageWindo.cs: My own MessageWindow class, with an attribute
of type Form1 in order to modify some GUI attributes of the Form.
I have overriden the WndProc method in order to
deal the message as i want.
The problem is that i never go through the
WndProc method code

*** LibWrap.cs: This is the helper classe that enable to call my
native function

*** JoDll.h and JoDll.cpp: the source code of my native DLL

The C# was compiled thanks to Visual Studio .NET 2003
the DLL was compiled thanks to EVC4.0 sp2

I would really appreciate any help
Thanks again for reading this long long post

/************
* Form1.cs *
************/

using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using Microsoft.WindowsCE.Forms;

namespace tescs
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button Identify;
public System.Windows.Forms.Label info_msg;

//
// I reference my own MessageWindow
//
private JoWindowMessage m__msgWnd;


public Form1()
{
InitializeComponent();

//
// I construct own MessageWindow with the handle of the current form
//
m__msgWnd = new JoWindowMessage(this);
}

protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}

private void InitializeComponent()
{
this.Identify = new System.Windows.Forms.Button();
this.info_msg = new System.Windows.Forms.Label();
this.Identify.Location = new System.Drawing.Point(80, 112);
this.Identify.Text = "Identify";
this.Identify.Click += new
System.EventHandler(this.Identify_Click);
this.info_msg.Location = new System.Drawing.Point(56, 40);
this.info_msg.Size = new System.Drawing.Size(128, 40);
this.info_msg.Text = "Click on the button";
this.info_msg.TextAlign =
System.Drawing.ContentAlignment.TopCenter;
this.Controls.Add(this.info_msg);
this.Controls.Add(this.Identify);
this.Text = "Form1";

}

static void Main()
{
Application.Run(new Form1());
}

private void Identify_Click(object sender, System.EventArgs e)
{
//
// I call here the helper fonction of my native DLL. I give the handle
of my own MessageWindow handle
//
bool ret = LibWrap.ReplyMe(m__msgWnd.Hwnd);
if(ret)
info_msg.Text = "Identified";

else
info_msg.Text = "Not identified";
}

public void DisplayInfoCB (string item)
{
info_msg.Text = item;
}
}
}


/**********************
* JoWindowMessage.cs *
**********************/

using System;
using Microsoft.WindowsCE.Forms;
using System.Windows.Forms;


namespace tescs
{
//
// This is my own MessageWindow. This is a classic MessageWindow with
a reference on a Form
//
public class JoWindowMessage:MessageWindow
{
private const int WM_USER = 0x400;
private const int WM_ASYNCDATA_RECEIVED = WM_USER + 1;
private Form1 destinationForm;

public JoWindowMessage(Form1 destinationForm)
{
this.destinationForm = destinationForm;
}
//
// This is the override of the WndProc. HERE IS THE PROBLEM, WE NEVER
COME IN THIS METHOD
//
protected override void WndProc(ref Message msg)
{
switch(msg.Msg)
{
case WM_ASYNCDATA_RECEIVED:
unsafe
{
string str = new string((char *)msg.LParam.ToPointer());
destinationForm.DisplayInfoCB(str);
break;
}
}

// call the base class WndProc for default message handling
base.WndProc(ref msg);
}
}
}


/**************
* LibWrap.cs *
**************/

using System;
using System.Runtime.InteropServices;

namespace tescs
{
public class LibWrap
{
[DllImport("JoDll.dll")]
public static extern bool ReplyMe(IntPtr hWnd);
}
}


/***********
* JoDll.h *
***********/

#ifdef JODLL_EXPORTS
#define JODLL_API __declspec(dllexport)
#else
#define JODLL_API __declspec(dllimport)
#endif

#define WM_ASYNCDATA_RECEIVED WM_USER+1

extern "C" JODLL_API bool ReplyMe(HWND hWnd);



/*************
* JoDll.cpp *
*************/

#include "stdafx.h"
#include "Joll.h"

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

//
// This is my native function called from the managed side.
//It just sends a message to the handle provided (that is the
MessageWindow),
//and wait for 5 seconds
//
JODLL_API bool ReplyMe(HWND hWnd)
{
SendMessage(hWnd), WM_ASYNCDATA_RECEIVED, 0, NULL);
Sleep(5000);
return true;
}
 
H

Hashka

No actually what i want was:
*** Start an unmanaged function, that is blocking (5 seconds).
*** This function returns me some information in real time
*** I want my C# GUI to display the information returned as Window
messages in real time also.

My code was good except the fact that i needed to threaded the call of
my unmanaged function.
I thought that the WndProc was threaded by nature, I was wrong.

So All works correctly if you copy the call of the ReplyMe function
into another function, and if you create a thread that launch this
latter function in parallel of the GUI.

Thanks again for your support
++
HK
 

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