IDispatch - Windows中COM对象暴露出来的接口,支持不同语言与C++互调,附C++代码示例。

29 篇文章 3 订阅

        朋友们周末好,在这总结一下这周的比较难的问题,到现在仍然是懵的,因为技术太老了,几乎没人用的那种程度。如JS调用C++的代码,JS是脚本语言,而C++是过程语言,IDispatch提供了协议让他们之间能够沟通,相互调用,其实换成其他语言也行,只要是IDispatch支持的都可。那么就简单介绍吧。

目录

一、转载而来的IDispatch详细介绍

二、IDispatch五个接口

2.1 GetTypeInfoCount

2.2 GetTypeInfoOfGuid

2.3 GetIDsOfNames

2.4 Invoke        

2.5 RemoteInvoke

三、总结


一、转载而来的IDispatch详细介绍

贴一个从其他人博客转过来的IDispatch信息

1. C程序调用时,调用者必须预先知道接口规范(如,参数类型、参数字节长度、参数顺序等)。由于不同语言这些规范有所不同,COM未解决不同语言之间调用,提供了IDispatch接口。

2.  IDispatch要求其实例必须自我描述,即拿到一个对象后,可从对象中直接获取调用方式,而无须预先明确。

3.  IDispatch中通过VT_TYPE来指定相关类型,如 VT_I4为4字节整形、VT_BSTR为unicode字符串,VT_DISPATCH表示是一个IDispatch对象。

4. 给对象中每一属性或函数(Method)分配一个整形Id和一个字符串name,调用者可以通过name字符串确定如何调用。如,若name为"length"的属性,调用者就理解为长度。由于这里通常是根据name来理解相应属性,因此name描述应足够准确。如,以"length()"为名称的函数实现整数相加功能就是不恰当的。

5. 使用IDispatch对象时,首相调用 IDispatch::GetIDsOfNames()将属性、函数名称作为参数,获取对应的属性、函数id。

6. 再调用IDispatch::Invoke() 将id作为参数,实际调用功能。

7. 若为获取属性值,则 Invoke()调用时,传入 Dispatch_PropertyGet标志。

8. 若为设置属性值,则Invoke()调用时,传入 Dispatch_PropertyPut标志。并在 DispParams参数中指定修该属性改为何值。DispParams结构说明见后。

9. 若为调用函数,则 Invoke()调用时,传入 Dispatch_Method标志。若该Method需要参数,则通过IDispatch::Invoke()的DispParams参数指定。

10. 举例:具体看下方链接

        IDispatch接口介绍 - 走看看

以下是我搜到的信息:

        Dispatch是COM(Component Object Model,组件对象模型)中的一个接口,它主要用于动态调用类型库中定义的方法和属性。在COM中,IDispatch接口定义了五个方法:GetTypeInfoCount、GetTypeInfo、GetIDsOfNames、Invoke和RemoteInvoke。

        GetTypeInfoCount方法返回类型信息的数量。

        GetTypeInfo方法返回指定类型信息的ITypeInfo接口。

        GetIDsOfNames方法返回指定名称的对象的ID。

        Invoke方法用于调用指定名称的方法或属性。

        RemoteInvoke方法用于在远程机器上调用指定名称的方法或属性。

        IDispatch接口的主要用途是支持动态调用类型库中的方法和属性,这对于开发动态语言来说是非常重要的。通常,我们可以使用类型库中的IDispatch接口来调用类型库中定义的方法和属性。IDispatch接口还有一个重要的用途,就是支持OLE自动化(OLE Automation)。

        在OLE自动化中,我们可以使用IDispatch接口来调用控制应用程序的方法和属性,这样就可以使用脚本语言来控制应用程序的行为。总之,IDispatch是COM中一个非常重要的接口,它主要用于动态调用类型库中的方法和属性,同时也支持OLE自动化。

        在COM中,IDispatch接口定义了五个方法,包括GetTypeInfoCount、GetTypeInfo、GetIDsOfNames、Invoke和RemoteInvoke。这些方法提供了调用类型库中定义的方法和属性所需的所有信息。IDispatch接口通常用于开发动态语言和OLE自动化。在动态语言中,我们可以使用IDispatch接口来调用类型库中定义的方法和属性。在OLE自动化中,我们可以使用IDispatch接口来控制应用程序的行为。

        总之,IDispatch是COM中一个非常重要的接口,它为我们提供了调用类型库中定义的方法和属性的动态机制,同时还支持OLE自动化。

二、IDispatch五个接口

2.1 GetTypeInfoCount

下面是一个使用 GetTypeInfoCount 函数的例子:

#include <Windows.h>
#include <tchar.h>
#include <ole2.h>
#include <olectl.h>

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;
    ITypeInfo *pTypeInfo;
    UINT count;

    // 获取 ITypeInfo 对象
    hr = GetTypeInfoOfGuid(CLSID_MyObject, &pTypeInfo);
    if (FAILED(hr))
    {
        _tprintf(_T("GetTypeInfoOfGuid failed. Error code = 0x%x\n"), hr);
        return hr;
    }

    // 获取 ITypeInfo 对象中的类型信息的数量
    hr = GetTypeInfoCount(pTypeInfo, &count);
    if (FAILED(hr))
    {
        _tprintf(_T("GetTypeInfoCount failed. Error code = 0x%x\n"), hr);
        return hr;
    }

    _tprintf(_T("Number of type info = %d\n"), count);

    // 释放 ITypeInfo 对象
    pTypeInfo->Release();

    return 0;
}

        在上面的例子中,我们首先使用 GetTypeInfoOfGuid 函数获取了一个 ITypeInfo 对象,然后调用 GetTypeInfoCount 函数来获取该对象中的类型信息的数量。最后输出该数量并释放 ITypeInfo 对象。

        注意:上面的代码片段使用了一些 Windows API 函数,包括 GetTypeInfoOfGuid 和 ITypeInfo 接口。如果您不熟悉这些函数和接口的使用方法,可以先学习一下相关的知识。

2.2 GetTypeInfoOfGuid

        下面是一个使用 GetTypeInfoOfGuid 函数的例子:

#include <Windows.h>
#include <tchar.h>
#include <ole2.h>
#include <olectl.h>

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;
    ITypeInfo *pTypeInfo;
    UINT count;

    // 获取 ITypeInfo 对象
    hr = GetTypeInfoOfGuid(CLSID_MyObject, &pTypeInfo);
    if (FAILED(hr))
    {
        _tprintf(_T("GetTypeInfoOfGuid failed. Error code = 0x%x\n"), hr);
        return hr;
    }

    // 获取 ITypeInfo 对象中的类型信息的数量
    hr = GetTypeInfoCount(pTypeInfo, &count);
    if (FAILED(hr))
    {
        _tprintf(_T("GetTypeInfoCount failed. Error code = 0x%x\n"), hr);
        return hr;
    }

    _tprintf(_T("Number of type info = %d\n"), count);

    // 释放 ITypeInfo 对象
    pTypeInfo->Release();

    return 0;
}

        在上面的例子中,我们首先调用 GetTypeInfoOfGuid 函数获取了一个 ITypeInfo 对象,然后调用 GetTypeInfoCount 函数来获取该对象中的类型信息的数量。

        接下来,你可以使用获取到的 ITypeInfo 对象来进一步操作类型信息。例如,你可以使用 GetTypeAttr 函数获取类型属性信息,或者使用 GetFuncDesc 函数获取函数描述信息。

        注意:上面的代码片段使用了一些 Windows API 函数,包括 GetTypeInfoOfGuid 和 ITypeInfo 接口。如果您不熟悉这些函数和接口的使用方法,可以先学习一下相关的知识。

2.3 GetIDsOfNames

        GetIDsOfNames 是一个 Windows API 函数,它的作用是通过名字获取一个或多个命令或控件的标识符。下面是一个使用 GetIDsOfNames 函数的示例代码,假设当前程序有一个按钮,名字为 "MyButton",我们想要获取它的标识符:

#include <Windows.h>

int main() {
  // 首先需要获取当前进程的句柄
  HINSTANCE hInst = GetModuleHandle(NULL);
  
  // 定义一个数组存储按钮的名字
  LPOLESTR names[] = { L"MyButton" };
  
  // 定义一个数组存储按钮的标识符
  DISPID dispIds[1];
  
  // 获取按钮的标识符
  HRESULT hr = GetIDsOfNames(hInst, names, 1, dispIds);
  if (SUCCEEDED(hr)) {
    // 标识符已经获取成功
    // 可以使用 dispIds[0] 来访问按钮
  } else {
    // 获取标识符失败,可能是按钮名字不存在
  }
  
  return 0;
}

2.4 Invoke        

         Invoke 是一个 Windows API 函数,IDispatch::Invoke 方法是 COM 中的一个接口,它允许调用者调用接口中的方法和属性。

HRESULT Invoke(
  IDispatch *pDisp,
  DISPID    dispIdMember,
  REFIID    riid,
  LCID      lcid,
  WORD      wFlags,
  DISPPARAMS *pDispParams,
  VARIANT   *pVarResult,
  EXCEPINFO *pExcepInfo,
  UINT      *puArgErr
);

        下面是一个示例代码,它展示了如何使用 IDispatch::Invoke 调用接口中的方法:

#include <Windows.h>

int main() {
  // 首先需要获取 OLE 自动化对象的 IDispatch 接口
  IDispatch* pDisp = NULL;
  // 这里假设已经获取到了 pDisp 指针

  // 定义一个变量存储方法的标识符
  DISPID dispid;
  // 获取方法的标识符
  HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, &LPOLESTR("Add"), 1, LOCALE_USER_DEFAULT, &dispid);
  if (FAILED(hr)) {
    // 获取标识符失败,可能是方法名不存在
    return hr;
  }

  // 定义一个 DISPPARAMS 结构体存储参数信息
  DISPPARAMS params;
  // 参数数量为 2
  params.cArgs = 2;
  // 参数是从右到左传入的
  params.rgvarg = new VARIANT[2];
  // 参数是从右到左的,所以需要倒序赋值
  params.rgvarg[1] = 2;
  params.rgvarg[0] = 3;
  // 没有默认参数
  params.cNamedArgs = 0;
  params.rgdispidNamedArgs = NULL;

  // 定义一个 VARIANT 变量存储方法的返回值
  VARIANT result;
  // 调用方法
  hr = pDisp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &result, NULL, NULL);
if (SUCCEEDED(hr)) {
  // 方法调用成功
  // 可以使用 result 变量访问方法的返回值
} else {
  // 方法调用失败
}

// 使用完参数数组后记得释放内存
delete[] params.rgvarg;

return 0;

        首先,这个代码用于调用 OLE 自动化对象上的方法。为了调用方法,我们需要获取方法的标识符,然后使用 IDispatch::Invoke 方法传入标识符和参数信息,就可以调用方法了。

        代码中的第一部分,使用 IDispatch::GetIDsOfNames 方法获取方法的标识符。这个方法需要传入方法的名字,然后返回标识符。

        代码中的第二部分,使用 DISPPARAMS 结构体存储参数信息。这个结构体存储了参数数量、参数值和默认参数信息。

        代码中的第三部分,使用 IDispatch::Invoke 方法调用方法。这个方法需要传入方法的标识符、参数信息和一个 VARIANT 变量用于存储方法的返回值。如果调用成功,就可以使用这个 VARIANT 变量访问方法的返回值。

        最后,代码中还有一个内存释放的步骤,在使用完参数数组后,需要使用 delete[] 释放内存。

2.5 RemoteInvoke

        IDispatch::RemoteInvoke 方法是一个用于通过网络连接调用 OLE 自动化对象上的方法的方法。下面是一个使用 IDispatch::RemoteInvoke 方法的示例代码,假设当前有一个 OLE 自动化对象,在另一个计算机上,我们想要调用它的名为 "Add" 的方法,并传入两个参数 2 和 3:

#include <Windows.h>

int main() {
  // 首先需要获取远程计算机的 IP 地址和端口号
  const char* remoteIp = "192.168.1.100";
  const int remotePort = 1234;
  
  // 连接远程计算机
  SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(remotePort);
  addr.sin_addr.S_un.S_addr = inet_addr(remoteIp);
  if (connect(sock, (sockaddr*)&addr, sizeof(addr)) != 0) {
    // 连接失败
    return -1;
  }
  
  // 发送调用方法的请求
  // 这里假设已经定义了发送数据的函数 sendData(SOCKET, const void*, int)
  const char* request = "{\"method\":\"Add\",\"params\":[2,3]}";
  sendData(sock, request, strlen(request));
  
  // 接收返回结果
  // 这里假设已经定义了接收数据的函数 recvData(SOCKET, void*, int)
  char response[256];
  recvData(sock, response, sizeof(response));
  
  // 关闭连接
  closesocket(sock);
  
// 解析返回结果
// 这里假设已经定义了解析 JSON 的函数 parseJson(const char*),返回一个 JSON 对象
  json j = parseJson(response);
  if (j.count("error") > 0) {
  // 调用方法出错
  printf("Error: %s\n", j["error"].get<std::string>().c_str());
  return -1;
 } else {
  // 调用方法成功
  printf("Result: %d\n", j["result"].get<int>());
  return 0;
}

三、总结

        IDispatch 接口是 OLE 自动化的核心接口,提供了一组方法用于动态调用 OLE 自动化对象的方法和属性。通过中间层来实现不同种类语言的互动,突然想起了一段话:其实计算机的核心就是中间件,好多互相无法互动的东西都可以通过中间件来实现,如果你无法直接实现某个功能那就寻找中间件来帮助。快过年了,祝大家都能平安到家吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thomas_Lbw

欣赏我就赏赐给我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值