COM套间对.NET程序使用COM对象的影响(上)

8 篇文章 0 订阅
8 篇文章 0 订阅

COM时代里,套间是用来简化多线程环境下使用COM对象的,然而在.NET里面,微软又放弃了套间的概念,这样给我们在.NET里面使用COM对象的时候造成了很多的麻烦。例如有的时候你会发现在有的线程里面创建了COM对象并将它的引用保存在全局变量里面,在其他的线程里面使用的时候,却发现.NET扔出一个InvalidCastException的异常,发生这种情况大多数都是因为两个.NET线程运行在不同的套间引起的。比如下面的COM服务器和C#客户端:

C#客户端的源代码

1. using System;

2. using System.Collections.Generic;

3. using System.Linq;

4. using System.Runtime.InteropServices;

5. using System.Text;

6. using System.Diagnostics;

7. using System.Security.Cryptography;

8. using System.Security.Principal;

9. using Microsoft.Win32.SafeHandles;

10. using System.ComponentModel;

11. using System.Reflection;

12. using System.Security;

13. using System.IO;

14. using System.Threading;

15. using System.Security.Permissions;

16.

17. using ApartmentComponentLib;

18.

19. namespace CSharpQuestions

20. {

21.     public class Watcher

22.     {

23.         private object m_IStaObject = null;

24.

25.         [STAThread]

26.         public static void Main()

27.         {

28.             Console.WriteLine(Thread.CurrentThread.GetApartmentState());

29.             Watcher watcher = new Watcher();

30.             watcher.Initialize();

31.             watcher.CreateThreads().Join();

32.

33.             Console.WriteLine("Press any key");

34.             Console.ReadLine();

35.         }

36.

37.         private Thread CreateThreads()

38.         {

39.             Thread thread = new Thread(ThreadFunc);

40.             thread.Start();

41.

42.             return thread;

43.         }

44.

45.         private void ThreadFunc()

46.         {

47.             Console.WriteLine(Thread.CurrentThread.GetApartmentState());

48.             IStaObject2 obj = (IStaObject2)m_IStaObject;

49.             obj.TestMethod();

50.         }

51.

52.         private void Initialize()

53.         {

54.             m_IStaObject = new StaObject2Class();

55.         }

56.     }

57. }

 

COM服务器端

IDL文件

1. import "oaidl.idl";

2. import "ocidl.idl";

3.

4. [

5.     object,

6.     uuid(34CF395D-F7F8-41FC-9074-E966304DA425),

7.     dual,

8.     nonextensible,

9.     helpstring("IStaObject Interface"),

10.    pointer_default(unique)

11. ]

12. interface IStaObject : IDispatch{

13.    [id(1), helpstring("method TestMethod")] HRESULT TestMethod(void);

14. };

15. [

16.    object,

17.    uuid(2451960E-F141-4F09-AB71-124E62B6A25E),

18.    helpstring("IStaObject2 Interface"),

19.    pointer_default(unique)

20. ]

21. interface IStaObject2 : IUnknown{

22.    HRESULT TestMethod(void);

23. };

24. [

25.    uuid(E410D347-D200-4362-82B8-F3361FA54446),

26.    helpstring("ApartmentComponentLib Type Library")

27. ]

28. library ApartmentComponentLib

29. {

30.    importlib("stdole2.tlb");

31.    [

32.           uuid(2C0624C9-4C88-4114-A165-9E4AA59A241F),

33.           helpstring("StaObject Class")

34.    ]

35.    coclass StaObject

36.    {

37.           [default] interface IStaObject;

38.    };

39.    [

40.           uuid(274BDE35-680D-48DC-A2F3-3AE26E7700DA),

41.           helpstring("StaObject2 Class")

42.    ]

43.    coclass StaObject2

44.    {

45.           [default] interface IStaObject2;

46.    };

47. };

 

头文件

1. // StaObject2.h : Declaration of the CStaObject2

2.

3. #pragma once

4. #include "resource.h"       // main symbols

5.

6. #include "ApartmentComponent_i.h"

7.

8. #if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)

9. #error "Single-threaded COM objects are not properly supported on Windows CE platform"

10. #endif

11.

12. // CStaObject2

13. class ATL_NO_VTABLE CStaObject2 :

14.    public CComObjectRootEx<CComSingleThreadModel>,

15.    public CComCoClass<CStaObject2, &CLSID_StaObject2>,

16.    public IStaObject2

17. {

18. public:

19.    CStaObject2()

20.    {

21.    }

22.

23. DECLARE_REGISTRY_RESOURCEID(IDR_STAOBJECT2)

24.

25.

26. BEGIN_COM_MAP(CStaObject2)

27.    COM_INTERFACE_ENTRY(IStaObject2)

28. END_COM_MAP()

29.

30.

31.

32.    DECLARE_PROTECT_FINAL_CONSTRUCT()

33.

34.    HRESULT FinalConstruct()

35.    {

36.           return S_OK;

37.    }

38.

39.    void FinalRelease()

40.    {

41.    }

42.

43. public:

44.    STDMETHOD(TestMethod)(void);

45.

46. };

47.

48. OBJECT_ENTRY_AUTO(__uuidof(StaObject2), CStaObject2)

 

CPP文件

1. #include "stdafx.h"

2. #include "StaObject2.h"

3. #include <iostream>

4.

5. using namespace std;

6.

7. // CStaObject

8. STDMETHODIMP CStaObject2::TestMethod(void)

9. {

10.    cout << "CStaObject2::TestMethod" << endl;

11.

12.    return S_OK;

13. }

Rgs文件

1. HKCR

2. {

3.     ApartmentComponent.StaObject2.1 = s 'StaObject2 Class'

4.     {

5.            CLSID = s '{274BDE35-680D-48DC-A2F3-3AE26E7700DA}'

6.     }

7.     ApartmentComponent.StaObject2 = s 'StaObject2 Class'

8.     {

9.            CLSID = s '{274BDE35-680D-48DC-A2F3-3AE26E7700DA}'

10.           CurVer = s 'ApartmentComponent.StaObject2.1'

11.    }

12.    NoRemove CLSID

13.    {

14.           ForceRemove {274BDE35-680D-48DC-A2F3-3AE26E7700DA} = s 'StaObject2 Class'

15.           {

16.                  ProgID = s 'ApartmentComponent.StaObject2.1'

17.                  VersionIndependentProgID = s 'ApartmentComponent.StaObject2'

18.                  InprocServer32 = s '%MODULE%'

19.                  {

20.                        val ThreadingModel = s 'Free'

21.                  }

22.                  'TypeLib' = s '{E410D347-D200-4362-82B8-F3361FA54446}'

23.           }

24.    }

25. }

 

COM 服务器注册,然后使用tlbimp.exe生成一个可以被C#客户端程序引用的IAInterop Assembly), 并且编译运行上面的C#客户端程序,你会发现.NET会抛出一个System.InvalidCastException异常:

System.InvalidCastException occurred

  Message="Unable to cast COM object of type 'ApartmentComponentLib.StaObject2Class' to interface type 'ApartmentComponentLib.IStaObject2'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{2451960E-F141-4F09-AB71-124E62B6A25E}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."

  Source="ApartmentComponentLib"

  StackTrace:

       at ApartmentComponentLib.StaObject2Class.TestMethod()

  InnerException:

从高亮显示的消息里面可以看出,当我们试图在另外一个线程使用另一个线程创建的对象的时候,查询所需要的COM接口失败也就是为什么.NET扔出来一个InvalidCastException

这就是一个典型的跨套间使用COM对象失败的例子,因为跨套间使用COM对象时,COM要求所使用的COM接口是可列集(Marshal)的。如果所要求(QueryInterface)的接口不能被列集(Marshal),在调用端那里QueryInterface返回E_NOINTERFACE,虽然看起来好像是不支持所查询的接口,实际上是因为COM没有办法将接口从一个套间列集到另外一个套间里面去。

具体的原因在后续文章里面解释。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值