专栏首页MasiMaro 的技术博文COM学习(四)——COM中的数据类型

COM学习(四)——COM中的数据类型

上一次说到,COM为了跨语言,有一套完整的规则,只要COM组件按照规则编写,而不同的语言也按照对应的规则调用,那么就可以实现不同语言间相互调用。但是根据那套规则,只能识别接口,并调用没有参数和返回类型的接口,毕竟不同语言里面的基本数据类型不同,可能在VC++中char * 就表示字符串,而在Java或者c#中string是一个对象,二者的内存结构不同,不能简单的进行内存数据类型的强制转化。为了实现数据的正常交互,COM中又定义了一组公共的数据类型。

HRESULT类型:

在COM中接口的返回值强制定义为该类型,用于表示当前执行的状态是完成或者是出错,这个类型一般在VC中使用,别的语言在调用时根据接口的这个值来确定接下来该如何进行。 HRESULT类型的定义如下:

typedef _Return_type_success_(return >= 0) long HRESULT;

其实它就是一个32位的整数,微软将这个整数分成几个部分,各个部分都有详细的含义,这个值的详细解释在对应的winerror.h中。

//
// Note: There is a slightly modified layout for HRESULT values below,
//        after the heading "COM Error Codes".
//
// Search for "**** Available SYSTEM error codes ****" to find where to
// insert new error codes
//
//  Values are 32 bit values laid out as follows:
//
//   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
//   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//  +-+-+-+-+-+---------------------+-------------------------------+
//  |S|R|C|N|r|    Facility         |               Code            |
//  +-+-+-+-+-+---------------------+-------------------------------+
//
//  where
//
//      S - Severity - indicates success/fail
//
//          0 - Success
//          1 - Fail (COERROR)
//
//      R - reserved portion of the facility code, corresponds to NT's
//              second severity bit.
//
//      C - reserved portion of the facility code, corresponds to NT's
//              C field.
//
//      N - reserved portion of the facility code. Used to indicate a
//              mapped NT status value.
//
//      r - reserved portion of the facility code. Reserved for internal
//              use. Used to indicate HRESULT values that are not status
//              values, but are instead message ids for display strings.
//
//      Facility - is the facility code
//
//      Code - is the facility's status code
//
//
// Define the facility codes
//

根据上面的注释可以看到,以及我自己查阅相关资料,它里面总共有7个部分,各个部分代表的含义如下: S - 严重性 - 表示成功或失败0 - 成功,1 - 失败 R - 设施代码的保留部分,对应于NT的第二严重性位。1 - 严重故障 C - 第三方。 此位指定值是第三方定义还是Microsoft定义的。0 - Microsoft-定义,1 - 第三方定义 N - 保留部分设施代码。 用于指示映射的NT状态值。 X - 保留部分设施代码。 保留供内部使用。 用于指示不是状态值的HRESULT值,而是用于显示字符串的消息标识。 Facility - 表示引发错误的系统服务. 示例Facility代码如下所示: 2 - 调度(COM调度) 3 - 存储 (OLE存储) 4 - ITF (COM/OLE 接口管理) 7 - (原始 Win32 错误代码) 8 - Windows 9 - SSPI 10 - 控制 11 - CERT (客户端或服务器认证) ... Code - 设施的状态代码 其实这些没有必要知道的很详细,只需要知道里面常用的几个即可: S_OK:成功 S_FALSE:失败 E_NOINTERFACE:没有接口,一般是由QueryInterface或者CoCreateInterface函数返回,当我们传入的ID不对它找不到对应的接口时返回该值 E_OUTOFMEMORY:当内存不足时返回该值。 一般在COM的调用者看来,有的时候只要最高位不为0就表示成功,这个时候可能会继续使用,所以在我们自己编写组件的时候要根据具体情况选择返回值,不要错误了就返回S_FALSE,其实我们看它的定义可以知道它是等于1的,最高位为0,仍然是成功的。如果返回S_FALSE可能会造成意想不到的错误,而且还难以调试。

BSTR

COM中规定了一种通用的字符串类型BSTR,查看BSTR的定义如下:

typedef /* [wire_marshal] */ OLECHAR *BSTR;
typedef WCHAR OLECHAR;

从上面的定义上不难看出BSTR其实就是一个WCHAR ,也就是一个指向宽字符的指针。COM中使用的是UNICODE字符串,在编写COM程序的时候经常涉及到CString、WCHAR、char等的相互转化,其实本质上就是多字节字符与宽字节字符之间的转化。我们平时在进行char 与WCHAR之间转化的函数像WideCharToMultiByte和MultiByteToWideChar,以及W2A和A2W等。 COM为了方便使用,另外也提供了一组转化函数_com_util::ConvertBSTRToString以及_com_util::ConvertStringToBSTR用在在char与BSTR之间进行转化。需要注意的是,这组函数返回的字符串是在堆上分配出来的,使用完后需要自己释放。 在BSTR类型中,定义了两个函数SysAllocString(),和SysFreeString()用来分配和释放一个BSTR的内存空间。 在这总结一下他们之间的相互转化: char*----->BSTR: _com_util::ConvertStringToBSTR WCHAR---->BSTR:可以直接用 = 进行赋值,也可以使用SysAllocString BSTR---->WCHAR:一般是直接使用等号即可,但是在WCHAR使用完之前不能释放,所以一般都是赋值给一个CString BSTR---->char:_com_util::ConvertBSTRToString Convert函数是定义在头文件atlutil.h中并且需要引用comsupp.lib文件 另外COM封装了一个_bstr_t的类,使用这个类就更加方便了,它封装了与char*之间的相互转化,可以直接使用赋值符号进行相互转化,同时也不用考虑回收内存的问题,它自己会进行内存回收。

VARIANT 万能类型

现代编程语言一般有强类型的语言和弱类型的语言,强类型的像C/C++、Java这样的,必须在使用前定义变量类型,而弱类型像Python这样的可以直接定义变量而不用管它的类型,甚至可以写出像:

i = 0
i = "hello world"

这样的代码,而且不同语言中可能同一种类型的变量在内存布局上也可能不一样。解决不同语言之间变量类型的冲突,COM定义了一种万能类型——VARIANT。

typedef struct tagVARIANT VARIANT;
typedef struct tagVARIANT VARIANTARG;
struct tagVARIANT
{
    union 
    {
        struct __tagVARIANT
         {
            VARTYPE vt;
            WORD wReserved1;
            WORD wReserved2;
            WORD wReserved3;
            union 
            {
                LONGLONG llVal;
                LONG lVal;
                BYTE bVal;
                SHORT iVal;
                FLOAT fltVal;
                DOUBLE dblVal;
                VARIANT_BOOL boolVal;
                _VARIANT_BOOL bool;
                SCODE scode;
                CY cyVal;
                DATE date;
                BSTR bstrVal;
                IUnknown *punkVal;
                IDispatch *pdispVal;
                SAFEARRAY *parray;
                BYTE *pbVal;
                SHORT *piVal;
                LONG *plVal;
                LONGLONG *pllVal;
                FLOAT *pfltVal;
                DOUBLE *pdblVal;
                VARIANT_BOOL *pboolVal;
                _VARIANT_BOOL *pbool;
                SCODE *pscode;
                CY *pcyVal;
                DATE *pdate;
                BSTR *pbstrVal;
                IUnknown **ppunkVal;
                IDispatch **ppdispVal;
                SAFEARRAY **pparray;
                VARIANT *pvarVal;
                PVOID byref;
                CHAR cVal;
                USHORT uiVal;
                ULONG ulVal;
                ULONGLONG ullVal;
                INT intVal;
                UINT uintVal;
                DECIMAL *pdecVal;
                CHAR *pcVal;
                USHORT *puiVal;
                ULONG *pulVal;
                ULONGLONG *pullVal;
                INT *pintVal;
                UINT *puintVal;
                struct __tagBRECORD
                {
                    PVOID pvRecord;
                    IRecordInfo *pRecInfo;
                }    __VARIANT_NAME_4;
            }    __VARIANT_NAME_3;
        }    __VARIANT_NAME_2;
        DECIMAL decVal;
    }    __VARIANT_NAME_1;
};

从定义上看出,它其实是一个巨大的联合体,将所有C/C++的基本类型都包含进来,甚至包含了像BSTR, 这样的COM中使用的类型。它通过成员vt来表示它当前使用的是哪种类型的变量。vt的类型是一个枚举类型,详细的定义请参见MSDN。为了简化操作,COM中也对它进行了一个封装——_variant_t,该类型可以直接使用任何类型的数据对其进行初始化操作。但是在使用里面的值时还是得判断它的vt成员的值

COM中的其他操作

最后附上一张COM常用函数表以供参考:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 双亲委派模型

    类加载流程,先加载Bootstrap ClassLoader 启动类加载即最顶层的加载类。这部分由C++ 编写。

    mySoul
  • Android的JNI【实战教程】5⃣️---Android Studio 2.2 以上 NDK开发

    今天介绍一下Android Studio 2.2 下 NDK开发 ,那叫一个顺溜—-纵享丝滑! 虽然现在AS 2.2 之后,jni开发配置相当方便,但是还是...

    先知先觉
  • Python调用C++代码

    今天在研究PyTorch中Tensor的一些操作的时候,发现其底层Tensor的操作都是用C++写的,并使用pybind11进行C++和Python的桥接。所以...

    TheOneGIS
  • 文件库函数解析(1)

    algorithm为算法库,里面部分的函数需要编译的环境为c++11 对于我使用Devc++设置环境的方法如下

    用户4492257
  • C++实现一个简单的String类

    使用基本的C++知识实现一个简单的String类,这个类中包含了C++常用的知识点。感觉是很有意思的一个小代码片段。

    TheOneGIS
  • Android的JNI【实战教程】4⃣️--C调用Java代码

    下一篇我们介绍一下AS 2.2 以上配置NDK —-纵享丝滑 http://blog.csdn.net/github_33304260/article/de...

    先知先觉
  • Android的JNI【实战教程】1⃣️--java和c/c++的那些事

    JNI在Android和c/c++中起着重要的作用,就相当于桥梁。你知道抗日时候为啥要先炸桥梁就知道JNI在其中的重要性了。

    先知先觉
  • Android的JNI【实战教程】6⃣️--温控计

    demo下载地址:http://download.csdn.net/detail/github_33304260/9860547

    先知先觉
  • Android的JNI【实战教程】2⃣️--AS下NDK环境配置及第一个工程

    通过上一篇相信大家已经对java和c/c++之间的桥梁JNI有了初步认识,那么接下来就让我们写个小demo来实现。 http://blog.csdn.net...

    先知先觉
  • Android的JNI【实战教程】3⃣️--Java调用C代码

    看了上一篇: http://blog.csdn.net/github_33304260/article/details/62891083 我们应该已经可...

    先知先觉

扫码关注云+社区

领取腾讯云代金券