朋友们周末好,在这总结一下这周的比较难的问题,到现在仍然是懵的,因为技术太老了,几乎没人用的那种程度。如JS调用C++的代码,JS是脚本语言,而C++是过程语言,IDispatch提供了协议让他们之间能够沟通,相互调用,其实换成其他语言也行,只要是IDispatch支持的都可。那么就简单介绍吧。
目录
一、转载而来的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. 举例:具体看下方链接
以下是我搜到的信息:
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, ¶ms, &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 自动化对象的方法和属性。通过中间层来实现不同种类语言的互动,突然想起了一段话:其实计算机的核心就是中间件,好多互相无法互动的东西都可以通过中间件来实现,如果你无法直接实现某个功能那就寻找中间件来帮助。快过年了,祝大家都能平安到家吧。