在具有 ui/非 ui 线程差异的 WPF 中使用 PInvoke

Posted

技术标签:

【中文标题】在具有 ui/非 ui 线程差异的 WPF 中使用 PInvoke【英文标题】:using PInvoke in WPF with ui/non-ui thread difference 【发布时间】:2013-02-05 16:44:26 【问题描述】:

我从 WPF 应用程序中的 c# 代码访问 c++ dll 库。从ui线程访问dll时我没有问题。但是我需要 UI 在 c++ dll 的代码运行时做出响应。所以我尝试从另一个线程访问它。但是当我尝试在非 ui 线程中调用相同的函数(我从 ui 线程调用)第一次时,该函数返回但返回值表明存在内部错误功能。但是,当我从非 ui 线程再运行一次或两次 时,它会成功返回一个非错误值。我没有 dll 的源代码,所以我不确定里面发生了什么。

c++函数

UINT32 myfunc1()

在 C# 中

[DllImport("mydll.dll")]
public static extern uint myfunc1();

我如何到达 ui 线程中的代码

uint errorCode = myfunc1(); // returns 0 means no error occured

我如何在非 ui 线程中获取代码

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, args) =>  args.Result = myfunc1(); ;
// 1st call args.Result is 10 means internal error ocurred
// 2nd call args.Result is sometimes 0 and sometimes 10
// 3rd or later calls args.Result is 0 means no error occured
worker.RunWorkerCompleted += doSth();
worker.RunWorkerAsync();

【问题讨论】:

你有什么问题? 我认为问题是为什么在非ui线程中第一次调用dll函数会出错。 【参考方案1】:

它“可能”与线程上使用的 Apartment 模型有关。

您的 UI 线程将使用 STA(单线程单元)模型...它依赖于消息循环进行同步/序列化访问。

您的BackgroundWorker 将使用 MTA(多线程单元)模型。

该 DLL 库中可能有一些代码依赖于在 UI/STA 线程中运行以进行正确同步,例如也许是一些 COM 调用。它有时有效,有时无效的原因可能是由于竞争条件......由于缺乏同步。

可以指定线程是 STA 线程....但不是在 BackgroundWorker 线程上,因为它们是由 ThreadPool 创建和管理的,并且始终设置为使用 MTA 单元模型,可以重用,等等......并且一个线程必须在它开始之前设置它的公寓状态......之后它就不能被改变。

因此,改为创建一个新的Thread,将其单元设置为 STA,并发送一个消息循环。

How can I make a background worker thread set to Single Thread Apartment?

How to use web browser object in background worker in C#

http://bytes.com/topic/c-sharp/answers/823166-can-backgroundworker-single-threaded-apartment

【讨论】:

这似乎很有可能。公寓模型对任何基于 COM 的东西都有巨大的影响,尤其是。

以上是关于在具有 ui/非 ui 线程差异的 WPF 中使用 PInvoke的主要内容,如果未能解决你的问题,请参考以下文章

WPF怎么跨线程访问UI控件

WPF_UI线程

WPF Dispatcher.BeginInvoke子线程更新UI

使用 WPF ui 进行多线程处理

富客户端 wpf, Winform 多线程更新UI控件

WPF 线程 Dispatcher