如何在C++中通过OLE Automation获取Word中ActiveX控件的名称
C++ 遍历Word文档中ActiveX控件并获取名称的解决方案
在C#中通过OLE Automation实现该需求十分直接,但C++里OLEFormat.Object属于抽象的IDispatch类型,无法直接访问name属性,需要借助COM的IDispatch机制或类型转换来解决,以下是具体实现方案:
前置准备
- 引入Word类型库(通常为
MSWORD.OLB)和Microsoft Forms 2.0类型库(MSFORMS.TLB)到C++项目,确保能调用对应的接口定义。 - 初始化COM环境:调用
CoInitialize(NULL)或CoInitializeEx(NULL, COINIT_MULTITHREADED)。
实现思路
Word中的ActiveX控件(如ComboBox、TextBox等)均属于Microsoft Forms控件范畴,它们都实现了MSForms::IControl接口,该接口内置Name属性。我们可以将OLEFormat.Object返回的IDispatch*转换为MSForms::IControl*直接获取名称;若转换失败,再通过IDispatch的GetIDsOfNames和Invoke方法动态读取属性值。
代码示例
#include <iostream> #include <atlbase.h> // 需根据Office版本和系统位数调整类型库路径 #import "C:\Program Files\Microsoft Office\root\Office16\MSWORD.OLB" rename("ExitWindows", "WordExitWindows") #import "C:\Windows\SysWOW64\MSFORMS.TLB" rename("TabIndex", "FormsTabIndex") rename("Timer", "FormsTimer") using namespace Word; using namespace MSForms; int main() { HRESULT hr = CoInitialize(NULL); if (FAILED(hr)) { std::cerr << "COM初始化失败" << std::endl; return -1; } CComPtr<_Application> wordApp; hr = wordApp.CoCreateInstance(__uuidof(Application)); if (FAILED(hr)) { std::cerr << "创建Word应用实例失败" << std::endl; CoUninitialize(); return -1; } wordApp->Visible = VARIANT_TRUE; CComPtr<Document> doc; hr = wordApp->Documents->Open(CComVariant(L"sample.docm"), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &doc); if (FAILED(hr)) { std::cerr << "打开文档失败" << std::endl; wordApp->Quit(&variantFalse, &variantFalse, &variantFalse); CoUninitialize(); return -1; } // 遍历Shapes中的ActiveX控件 CComPtr<Shapes> shapes; hr = doc->get_Shapes(&shapes); if (SUCCEEDED(hr)) { long count = 0; shapes->get_Count(&count); for (long i = 1; i <= count; ++i) { CComPtr<Shape> shape; hr = shapes->Item(CComVariant(i), &shape); if (SUCCEEDED(hr)) { MsoShapeType type; shape->get_Type(&type); // 仅处理OLE控件类型 if (type == msoOLEControlObject) { CComPtr<OLEFormat> oleFormat; hr = shape->get_OLEFormat(&oleFormat); if (SUCCEEDED(hr)) { CComPtr<IDispatch> dispObj; hr = oleFormat->get_Object(&dispObj); if (SUCCEEDED(hr)) { // 优先转换为MSForms::IControl强类型访问 CComPtr<IControl> control; hr = dispObj->QueryInterface(__uuidof(IControl), (void**)&control); if (SUCCEEDED(hr)) { CComBSTR name; hr = control->get_Name(&name); if (SUCCEEDED(hr)) { std::wcout << L"控件名称:" << name.m_str << std::endl; } } else { // 强类型转换失败时,通过IDispatch动态获取属性 DISPID dispid; LPOLESTR propName = L"Name"; hr = dispObj->GetIDsOfNames(IID_NULL, &propName, 1, LOCALE_USER_DEFAULT, &dispid); if (SUCCEEDED(hr)) { VARIANT result; VariantInit(&result); DISPPARAMS dp = { NULL, NULL, 0, 0 }; hr = dispObj->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dp, &result, NULL, NULL); if (SUCCEEDED(hr) && result.vt == BSTR) { std::wcout << L"控件名称:" << result.bstrVal << std::endl; } VariantClear(&result); } } } } } } } } // 处理InlineShapes中的嵌入式控件(逻辑与Shapes一致) CComPtr<InlineShapes> inlineShapes; hr = doc->get_InlineShapes(&inlineShapes); if (SUCCEEDED(hr)) { long count = 0; inlineShapes->get_Count(&count); for (long i = 1; i <= count; ++i) { CComPtr<InlineShape> inlineShape; hr = inlineShapes->Item(CComVariant(i), &inlineShape); if (SUCCEEDED(hr)) { MsoShapeType type; inlineShape->get_Type(&type); if (type == msoOLEControlObject) { CComPtr<OLEFormat> oleFormat; hr = inlineShape->get_OLEFormat(&oleFormat); if (SUCCEEDED(hr)) { CComPtr<IDispatch> dispObj; hr = oleFormat->get_Object(&dispObj); if (SUCCEEDED(hr)) { CComPtr<IControl> control; hr = dispObj->QueryInterface(__uuidof(IControl), (void**)&control); if (SUCCEEDED(hr)) { CComBSTR name; hr = control->get_Name(&name); if (SUCCEEDED(hr)) { std::wcout << L"嵌入式控件名称:" << name.m_str << std::endl; } } else { DISPID dispid; LPOLESTR propName = L"Name"; hr = dispObj->GetIDsOfNames(IID_NULL, &propName, 1, LOCALE_USER_DEFAULT, &dispid); if (SUCCEEDED(hr)) { VARIANT result; VariantInit(&result); DISPPARAMS dp = { NULL, NULL, 0, 0 }; hr = dispObj->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dp, &result, NULL, NULL); if (SUCCEEDED(hr) && result.vt == BSTR) { std::wcout << L"嵌入式控件名称:" << result.bstrVal << std::endl; } VariantClear(&result); } } } } } } } } // 清理资源 doc->Close(&variantFalse, &variantFalse, &variantFalse); wordApp->Quit(&variantFalse, &variantFalse, &variantFalse); CoUninitialize(); return 0; }
关键说明
- 优先使用
QueryInterface转换到MSForms::IControl,强类型访问效率更高、可靠性更强。 - 针对非Microsoft Forms的特殊ActiveX控件,需通过IDispatch动态调用方式读取
Name属性。 - 使用
CComPtr管理COM对象,可自动释放接口,避免内存泄漏。 - 代码中的类型库路径需根据Office版本(如Office16对应Office 2016及以上)和系统位数(32位系统用
C:\Windows\System32\MSFORMS.TLB)调整。
内容的提问来源于stack exchange,提问作者Achim Schön
相关产品推荐
相关产品推荐

