1

我必须使用 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 是指向我的应用程序的窗口句柄的指针,我通过调用

[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 发送的测试应用程序。

提前感谢大家。

4

1 回答 1

2

所以,这里是交易:GetForegroundWindow() 没有检索正确的窗口句柄。碰巧我的应用程序有两个窗口。一种是我用于测试的主要形式,另一种显示我正在做的所有结果和日志记录。因此,该方法实际上是返回登录窗口的句柄,而不是表单窗口。

使用this.Handle并将该值传递给本机方法使一切正常。

感谢汉斯指出这一点。

于 2011-10-26T14:11:52.833 回答