Dea*_*ler 15 c# com internet-explorer
我从System.Thread.Timer线程池得到了这个(上面标题中的错误)所以我有我的TimerWrapper包装System.Thread.Timer将实际执行移动到System.Thread.ThreadPool我仍然得到它我将它移动一个新的线程(回调).Start(),我仍然得到它.当我把它放在一个全新的线程上时,如何调度输入同步调用?
这是一个非常小的原型应用程序,其中我所做的就是触发一个正在执行此操作的计时器...
IEnumerable swc = SHDocVw.ShellWindows()
HashSet<WindowInfo> windows = new HashSet<WindowInfo>();
foreach (SHDocVw.InternetExplorer ie in swc)
{
if (!ie.FullName.ToLower().Contains("iexplore.exe"))
continue;
IntPtr hwnd;
IEPlugin.IOleWindow window = ie.Document as IEPlugin.IOleWindow;
window.GetWindow(out hwnd);
WindowInfo info = new WindowInfo();
info.handle = hwnd;
info.extraInfo = ie;
windows.Add(info);
}
Run Code Online (Sandbox Code Playgroud)
Bre*_*McK 36
恭喜; 你已经设法偶然发现了我最喜欢的COM怪癖之一,在这种情况下,使用IOleWindow的GetWindow方法有一个令人愉快的模糊限制 - 以及一条错误信息,可以让你知道发生了什么.这里的根本问题是该GetWindow()方法被标记为[input_sync]- 来自SDK中的include\oleidl.idl文件:
interface IOleWindow : IUnknown
{
...
[input_sync]
HRESULT GetWindow
(
[out] HWND *phwnd
);
Run Code Online (Sandbox Code Playgroud)
不幸的是,IOleWindow的文档没有提到这个属性,但是其他一些文档,比如IOleDocumentView :: SetRect()会做:
此方法使用[input_sync]属性定义,这意味着在执行此方法时,视图对象无法生成或进行另一个非input_sync RPC调用.
这个属性背后的想法是保证调用者(可能是像Word或其他OLE控件主机的应用程序),它可以安全地调用这些方法,而不必担心重入.
事情变得棘手的是COM决定强制执行:如果它认为可能违反这些约束,它将拒绝对[input_sync]方法的跨公寓调用.所以,IIRC,如果你在SendMessage()内,你就不能进行跨公寓[input_sync]调用 - 这就是错误信息有点暗示的情况.并且 - 这是让你在这里的那个 - 你不能从MTA线程调用跨公寓[input_sync]方法.也许COM在这里的实施过程中有点过于热心,但无论如何,这就是你必须要处理的事情.
(关于MTA与STA线程的简短评论:在COM中,线程和对象是STA或MTA.STA,Single-Threaded-Aparment,是Windows UI的工作方式;单个线程拥有UI以及与之关联的所有对象,以及那些对象期望被该线程单独调用.MTA或Multi-Threaded-Aparment更多是免费的;对象可以随时从任何线程调用,因此需要自己进行同步MTA线程通常用于工作和后台任务.所以你可以在一个STA线程上管理UI,但是在后台使用一个或多个MTA线程下载一堆文件.COM做了一堆让这两个人互相交流,并试图隐藏一些复杂性.这里的部分问题是你在混合这些隐喻:ThreadPools与后台工作相关联,所以是MTA,但IOleWindow是以UI为中心的,所以是STA - 而GetWindow恰好是一种真正严格执行的方法 这个.)
长话短说,你不能从ThreadPool thead调用这个方法因为它们是MTA线程.此外,默认情况下新线程是MTA,所以只创建一个新线程来完成工作是不够的.
相反,创建新线程,但tempThread.SetApartmentState(ApartmentState.STA);在启动之前使用,这将为您提供一个STA线程.您可能需要在该STA线程中实际放置处理shell COM对象的所有代码,而不仅仅是对GetWindow()的单次调用 - 我不记得确切的详细信息,但是如果你最终获得了原始的COM对象(这里似乎是ShellWindows)在MTA ThreadPool线程上,即使您尝试从STA调用它,它仍将与该MTA保持关联.
如果你可以从一个STA线程而不是一个来自ThreadPool的MTA中完成所有的工作,那就更好了,这样就可以避免这种情况.而不是使用专为后台/非UI代码设计的System.Threading.Timer,而是尝试使用以UI为中心的System.Windows.Forms.Timer.这需要一个消息循环 - 如果你已经在你的应用程序中有窗口和表单,你已经有了一个,但如果没有,在测试代码中执行此操作的最简单方法是在同一个中执行一个MessageBox()主线代码等待退出的位置(通常使用Sleep或Console.ReadLine或类似代码).
| 归档时间: |
|
| 查看次数: |
11479 次 |
| 最近记录: |