在一个新线程(_beginthreadex)中调用 COM 库 Excel.Application 很受伤
在 c 中,使用 _beginthreadex() 创建了一个线程,然后调用 COM 库 Excel.Application,一直在努力,可是从未成功。
#define E_NOINTERFACE ((HRESULT) 0x80004002L)
来来回回折腾了一天功夫,结果是暂时放弃。
下面是当时网上查询的相关资料,先记录下来,也许将来能用到。
---------------------------------------------------------------------------------------------------
一个COM对象明明支持一个接口,创建时却返回E_NOINTERFACE的问题:http://www.cppblog.com/xgzpan/archive/2008/04/29/48405.html
------------------------------------------------
Excel 开发中与线程相关的若干问题:https://www.yycoding.xyz/post/2014/1/5/some-thread-releated-problems-and-solutions-in-excel-development
Excel也是一种STA(Single Thread Apartment)线程的应用程序,Excel插件是寄宿在Excel中运行的,这也就意味着插件也是一种STA线程的应用程序。插件在操作Excel的时候,如果是在Excel的主线程中,可以直接获取Excel对象进行操作,比如写入单元格值,对单元格进行格式化等操作。但是通常,我们会在多线程或者后台工作线程中去处理一系列复杂的数据或者逻辑,待处理完成获得结果之后,再像WinForm那样,回到UI线程中,去更新界面信息,对于Excel插件来说,就是回到Excel的主线程上来,然后再更新界面。但是Excel又是一种不同于一般Winform 类型的STA,它是COM并且Excel插件是寄宿在其上的,所以还有一些需要注意的地方。
------------------------------------------------
COM组件的线程模型被称之为Apartment模型,COM对象初始化时其执行上下文(Execution Context),他要么和单个线程关联STA(Single Thread Apartment ) 要么和多个线程关联MTA(Multi Thread Apartment)。Excel是一种STA线程的应用程序,使用多线程直接怼会出问题,必须借助线程的同步上下文实现线程间的消息传递。
来自:https://www.cnblogs.com/yzhyingcool/p/10865079.html
------------------------------------------------
STA和MTA线程模式的区别:https://blog.csdn.net/dianben2652/article/details/102391264
我现在想在一个windows form的程序中实现从某个word文档复制图片并保存的方案。
具体是:打开word文档,将图片信息复制到粘贴板中,然后从粘贴板中取得图片信息,再保存到本地目录中。
说明:(本来是放在代码下面的,无奈POST之后就被代码挡住不显示了)
如果在某个按钮的事件中,直接调用该方法,那么界面将变得没有响应。所以我们需要考虑使用多线程来解决这个问题。Thread t = new Thread(new TheardStart(CopyImages); t.Start();
如果是这样,则程序会发生错误.。要么显示出现异常,要么没异常但是Clipboard为空,取不到任何数据!为什么呢?
因为Word.Application 是Automation并且STA-Based,不能在没有指定ThreadApartment的线程中被调用。所以导致了各种错误,所以需要在t.Start();前面加上t.Apartment = ApartmentState.STA;这样就完全正常了。
对于MTA的多线程我们就见的比较多了,不再举例了。
另外一点不明白,我监视任务管理器发现,我在执行Thread t = new Thread(new TheardStart(CopyImages);t.Apartment = ApartmentState.STA; t.Start();之后该程序的进程中线程数从3个增加到6个,如果创建的是MTA的线程则只增加1。我的理解是STA线程为需要维护内部隐藏的窗口类和消息队列而增加的。
------------------------------------------------
STA: Single-Thread Apartment, 中文叫单线程套间。就是在COM库初始化的时候创建一个内存结构,然后让它和调用CoInitialize的线程相关联。这个内存结构针对每个线程都会有一个。支持STA的COM对象只能在创建它的线程里被使用,其它线程如果再创建它就会失败。
MTA: Mutil-Thread Apartment,中文叫多线程套间。COM库在进程中创建一个内存结构,这个内存结构在整个进程中只能有一个,然后让它和调用CoInitializeEx的线程相关联。支持MTA的COM对象可以在任意线程里被使用。多有针对它的调用都会被封装成为消息。
其实STA和MTA是COM规定的一套线程模型,用于保障多线程情况下你的组件代码的同步。比如说有一个COM对象它内部有一个静态变量 gHello,那么这个对象无论生成多少实例对于gHello在内存中只能有一份,那么如果有两个不同的实例在两个线程里面同时去读写它,就有可能出错,所以就要就要有种机制进行同步保护,STA或者MTA就是这种机制。
进程相对于一个小城镇,线程相当于这个城镇里的居民,STA(单线程套间)相当于居民房,是私有的,MTA(多线程套间)相当于旅馆,是公用的,Com对象相当于居民房或旅馆里的物品.接下去就好理解了,一个小城镇(进程)里可以有很多很多的(居民)线程,这个城镇(进程)只有一间旅馆(MTA),但可以有很多很多的居民房(STA).只有居民(线程)进入了房间(居民房或旅馆,STA或MTA)以后才能使用该房间里的物品(COM对象),居民房(STA)里的物品(COM对象)只能供这间房子的主人(创建该STA的线程)使用,其它居民(线程)不能访问.同样,只有入住到旅馆(MTA)里的居民(线程,可以有多个)才可以访问到旅馆(MTA)里的物品(com对象),但因为是公用的,所以要合理的分配(同步)才能不会产生混乱.
来自:https://www.cnblogs.com/PeterPan-luo/p/4014262.html
------------------------------------------------
什么情况下需要通过COINIT_MULTITHREADED进入MTA?
对于STA组件,无论客户端是什么方式初始化,它总是创建在STA中;
对于Free组件,无论客户端是什么方式初始化,它总是创建在MTA中;
对于both组件,如果客户端以STA初始化,它创建在STA中,如果客户端以MTA初始化,它创建在MTA中。
只要创建在MTA中的组件,都能跨线程直接访问而无需列集。
那什么情况下需要通过COINIT_MULTITHREADED进入MTA?
只有当需要创建的组件既有APPARTMENT类的,也有FREE类的时候,
才需要COINIT_MULTITHREADED进入MTA。
这样既可以另其套间存放创建APPARTMENT类组件,也可以在本MTA中存放FREE套间。
这样,既保护了APARTMNET类组件的多线程访问,也实现了FREE类组件的效率问题。
https://blog.csdn.net/wishfly/article/details/2062912