使用 p/invoke 从托管 Dll 接收 C# 上的 Windows 消息
我必须使用 p/invoke 从 C# 调用一些本机 C 函数。到目前为止,我在将不同的方法和结构编组到 C# 时没有遇到任何问题。我的问题在于,我必须调用的许多方法都是异步的,并通过 Windows 消息将它们的最终结果返回到我的 WinForms 应用程序。例如,我调用了一个在 C 语言中具有以下签名的方法:
HRESULT AsyncOpenSession( LPSTR lpszLogicalName,
HANDLE hApp,
LPSTR lpszAppID,
DWORD dwTraceLevel,
DWORD dwTimeOut,
USHORT lphService,
HWND hWnd,
DWORD dwSrvcVersionsRequired,
LPWFSVERSION lpSrvcVersion,
LPWFSVERSION lpSPIVersion,
ULONG lpRequestID );
其中 lpszAppID 期望接收我的应用程序的名称 (MyApp.exe),而 hWnd 是指向我的应用程序的 WINDOW HANDLE 的指针,我可以使用调用
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static IntPtr GetCurrentWindowHandle()
{
IntPtr handle = GetForegroundWindow();
return handle;
}
导入的签名是:
[DllImport("MSXFS.DLL", BestFitMapping = true, CharSet = CharSet.Auto, SetLastError = true)]
private static extern int AsyncOpenSession([MarshalAs(UnmanagedType.LPStr)] string lpszLogicalName,
IntPtr hApp,
[MarshalAs(UnmanagedType.LPStr)] string lpszAppID,
UInt32 dwTraceLevel,
UInt32 dwTimeOut,
out Int32 lphService,
IntPtr hWnd,
UInt32 dwSrvcVersionsRequired,
out WFSVersion lpSrvcVersion,
out WFSVersion lpSPIVersion,
out Int32 lpRequestID);
我按如下方式调用该方法:
Int32 r = AsyncOpenSession(lpszLogicalName,
appHanlder,
"MyApp.exe",
dwTraceLevel,
dwTimeOut,
out hServ,
GetCurrentWindowHandle(),
dwSrvcVersionsRequired,
out srvcVersion,
out spiVersion,
out requestID);
最初,该方法调用返回一个结果代码,表明请求的调用已放入执行队列。在原始调用之后的任何时间,本机 Dll 都会完成请求的执行,并通过其窗口句柄和名称向应用程序发送 Windows 消息。此方法的 Windows 消息代码定义如下:
#define WM_USER 0x0400
#define OPEN_SESSION_COMPLETE (WM_USER + 1)
有关 Windows 消息的更多信息,请参见此处:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644931(v=vs.85).aspx
在我的应用程序中,我已将方法 WndProc 重写为能够按如下方式控制 Windows 消息:
protected override void WndProc(ref Message m)
{
const int wm_user = 0x0400;
const int OPEN_SESSION_COMPLETE = wm_user + 1;
switch (m.Msg)
{
case OPEN_SESSION_COMPLETE:
txtEventsState.Text = m.ToString();
break;
}
base.WndProc(ref m);
}
但我从未收到带有此代码的 Windows 消息。日志或事件查看器中没有错误。我知道我对该方法的调用成功了,因为我没有收到任何错误代码,并且因为在调用其他方法之前必须打开会话,并且我可以成功调用需要会话处理程序的其他方法(也没有错误代码)。
我导入的签名和对该方法的调用位于我的应用程序引用的类库项目中。我在这里做错了什么吗?任何人都可以阐明可能发生的事情吗?我无权访问本机代码,只能访问一个测试应用程序,该应用程序显示 Windows 消息是由本机 C Dll 发送的。
预先感谢大家。
I have to call some native C functions from C# using p/invoke. So far, I had no problems marshaling the different methods and structures to C#. Where my problem resides is in the fact that many of the methods I have to call are asynchronous, and return their final results to my WinForms application through Windows Messages. For instance, I have a call to a method that has the following signature in C:
HRESULT AsyncOpenSession( LPSTR lpszLogicalName,
HANDLE hApp,
LPSTR lpszAppID,
DWORD dwTraceLevel,
DWORD dwTimeOut,
USHORT lphService,
HWND hWnd,
DWORD dwSrvcVersionsRequired,
LPWFSVERSION lpSrvcVersion,
LPWFSVERSION lpSPIVersion,
ULONG lpRequestID );
Where lpszAppID expects to receive the NAME of my application (MyApp.exe) and hWnd is a pointer to the WINDOW HANDLE of my application, which I obtain with a call to
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static IntPtr GetCurrentWindowHandle()
{
IntPtr handle = GetForegroundWindow();
return handle;
}
The imported signature is:
[DllImport("MSXFS.DLL", BestFitMapping = true, CharSet = CharSet.Auto, SetLastError = true)]
private static extern int AsyncOpenSession([MarshalAs(UnmanagedType.LPStr)] string lpszLogicalName,
IntPtr hApp,
[MarshalAs(UnmanagedType.LPStr)] string lpszAppID,
UInt32 dwTraceLevel,
UInt32 dwTimeOut,
out Int32 lphService,
IntPtr hWnd,
UInt32 dwSrvcVersionsRequired,
out WFSVersion lpSrvcVersion,
out WFSVersion lpSPIVersion,
out Int32 lpRequestID);
I call the method as follows:
Int32 r = AsyncOpenSession(lpszLogicalName,
appHanlder,
"MyApp.exe",
dwTraceLevel,
dwTimeOut,
out hServ,
GetCurrentWindowHandle(),
dwSrvcVersionsRequired,
out srvcVersion,
out spiVersion,
out requestID);
Initially, the method call returns a result code that indicates the requested call has been put to the execution queue. Any time after the original call the native Dll completes the execution of the request and send a Windows Message to the application by means of its windows handle and name. The Windows Message code for this method is defined as follows:
#define WM_USER 0x0400
#define OPEN_SESSION_COMPLETE (WM_USER + 1)
More info about Windows Messages here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644931(v=vs.85).aspx
In my application I have overridden the method WndProc to be able to control the Windows Messages as follows:
protected override void WndProc(ref Message m)
{
const int wm_user = 0x0400;
const int OPEN_SESSION_COMPLETE = wm_user + 1;
switch (m.Msg)
{
case OPEN_SESSION_COMPLETE:
txtEventsState.Text = m.ToString();
break;
}
base.WndProc(ref m);
}
But I never receive a Windows Message with this code. There are no error in logs or event viewer. I know my call to the method succeeded because I do not get any error code and because it is mandatory to have an opened session before calling other methods, and I can call other methods that need a session handler successfully (no error code either).
My imported signature and call to the method is in a class library project that my application is referencing. Am I doing something wrong here? Can anyone give a light on what could be happening? I do not have access to the native code, just to a test application the shows that the Windows Messages are being sent by the native C Dll.
Thank to everybody in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
所以,问题就在这里:GetForegroundWindow() 没有检索正确的窗口句柄。碰巧我的应用程序有两个窗口。一个是我用于测试的主窗体,另一个显示我正在执行的所有结果和日志记录。因此,该方法实际上返回的是登录窗口的句柄,而不是表单窗口的句柄。
使用 this.Handle 并将该值传递给本机方法使一切正常工作。
感谢汉斯指出了这一点。
So, here it the deal: GetForegroundWindow() is not retriving the correct Window Handle. It happens that my application has two windows. One is the main form that I use for my test and the other displays all results and logging I am doing. So, the method was actually returning the Handle of the loggin window and not the form windows.
Using this.Handle and passing that value to the native methods made everything work correctly.
Thanks to Hans for pointing this out.