我正在编写一个小型测试应用程序来测试通信接口(interface)。通信接口(interface)用 C++(一个 DLL)编写,测试应用程序使用 C#。通信接口(interface)反过来与使用 Windows 消息传输和接收数据的低级硬件堆栈对话。为了实现这一点,通信接口(interface) DLL 创建了一个不可见的子窗口,其父窗口是 C# 测试应用程序窗口。与硬件对话的顺序如下:
初始化通信库。此步骤需要将主窗口句柄和应用程序实例传递到低级堆栈以进行 Windows 消息传递。
使用设备地址连接
读/写
关闭
取消初始化通信库。
现在在第 2 步中,DLL 创建一个不可见的窗口来与低级硬件堆栈进行通信。由于第 2 步是阻塞调用,我希望我的 UI 在此期间能够响应,以防连接时间过长。因此,我尝试使用线程或 BeginInvoke 调用异步连接。但是我观察到建立连接后,只要子窗口存在,应用程序窗口就会挂起。子窗口似乎阻止了所有传入主窗口的消息。这似乎是因为子窗口是在另一个线程中创建的。
但我不希望连接在主线程中,因为它会挂起 UI。
我欢迎任何关于如何避免这个问题的想法?提前致谢。
-哈里什
最佳答案
所有与窗口句柄的通信都必须在创建该句柄的线程上完成。这可能意味着对 DLL 的所有调用都应在辅助线程上完成。
您可以尝试以下方法:
- 在初始化DLL之前,启动一个后台线程;
- 在该线程上,创建一个您不显示的 WinForms 窗口。您可以这样做:
-
public static Form BackgroundForm;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
new Thread(new ThreadStart(Secondary)).Start();
Application.Run(new MainForm());
}
static void Secondary()
{
BackgroundForm = new Form();
// Calling Handle creates the system HWND. You do not have to call Show
// or something similar on this Form to make the handle available or use
// Invoke or BeginInvoke.
var handle = BackgroundForm.Handle;
// Initialize the DLL here with the handle.
Application.Run();
// Unintialize the DLL.
}
- 然后,使用从后台窗体中获取的句柄初始化 DLL;
- 当您需要调用 DLL 时,使用
Invoke
和BeginInvoke
对此后台表单执行此操作; - 一旦到了关闭应用程序的时间,通过
Invoke
或BeginInvoke
执行Application.ExitThread()
。
您看到的主窗体被阻止的问题可能是因为在 DLL 中创建的子窗口具有主窗体的句柄,因为它是父窗口,但这只是一个猜测。这应该也可以使用这个系统来解决。
关于c# - 阻塞函数调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4014613/