如何在VC++中调用msvbvm60.dll/vba6.dll的DateTime模块Now函数
在VC++中调用msvbvm60.dll/VBA6.DLL的COM对象(以DateTime::Now为例)
核心前提
msvbvm60.dll包含VBA的类型库,实际函数实现位于VBA6.DLL中,可通过oleview.exe导出完整IDL文件,获取函数的导出序号、参数和返回值信息。
方法一:通过类型库自动生成包装类调用
步骤1:补全IDL文件
将oleview.exe导出的IDL片段补全关键枚举定义(否则编译会报错),保存为vba6.idl:
// Generated .IDL file (by the OLE/COM Object Viewer) // typelib filename: msvbvm60.dll [ uuid(000204EF-0000-0000-C000-000000000046), version(6.0), helpstring("Visual Basic For Applications"), helpfile("VBA.HLP"), helpcontext(0x000f6791) ] library VBA { importlib("stdole2.tlb"); // 补全缺失的枚举定义 typedef enum tagVbDayOfWeek { vbSunday = 1, vbMonday = 2, vbTuesday = 3, vbWednesday = 4, vbThursday = 5, vbFriday = 6, vbSaturday = 7 } VbDayOfWeek; typedef enum tagVbFirstWeekOfYear { vbFirstJan1 = 1, vbFirstFourDays = 2, vbFirstFullWeek = 3 } VbFirstWeekOfYear; typedef enum tagVbCalendar { vbCalGregorian = 0, vbCalHijri = 1 } VbCalendar; // Forward declare all types defined in this typelib interface _HiddenInterface; interface _ErrObject; interface _Collection; // 保留原有的DateTime模块定义 [ dllname("VBA6.DLL"), uuid(37D75300-2BCC-1069-82D8-00DD010EDFAA), helpcontext(0x000f6ebf) ] module DateTime { [entry(610), propget, helpcontext(0x000f6d5e)] VARIANT _stdcall _B_var_Date(); [entry(549), propput, helpcontext(0x000f6d5e)] void _stdcall _B_str_Date([in] BSTR rhs); [entry(548), propput, helpcontext(0x000f6d5e)] void _stdcall _B_var_Date([in] VARIANT* rhs); [entry(609), propget, helpcontext(0x000f6d5e)] BSTR _stdcall _B_str_Date(); [entry(538), helpcontext(0x000f64f8)] VARIANT _stdcall DateSerial( [in] short Year, [in] short Month, [in] short Day); [entry(540), helpcontext(0x000f64f9)] VARIANT _stdcall DateValue([in] BSTR Date); [entry(542), helpcontext(0x000f64fa)] VARIANT _stdcall Day([in] VARIANT* Date); [entry(543), helpcontext(0x000f652b)] VARIANT _stdcall Hour([in] VARIANT* Time); [entry(544), helpcontext(0x000f654e)] VARIANT _stdcall Minute([in] VARIANT* Time); [entry(545), helpcontext(0x000f6551)] VARIANT _stdcall Month([in] VARIANT* Date); [entry(546), propget, helpcontext(0x000f6556)] VARIANT _stdcall Now(); [entry(547), helpcontext(0x000f6573)] VARIANT _stdcall Second([in] VARIANT* Time); [entry(612), propget, helpcontext(0x000f6d5f)] VARIANT _stdcall _B_var_Time(); [entry(551), propput, helpcontext(0x000f6d5f)] void _stdcall _B_str_Time([in] BSTR rhs); [entry(550), propput, helpcontext(0x000f6d5f)] void _stdcall _B_var_Time([in] VARIANT* rhs); [entry(611), propget, helpcontext(0x000f6d5f)] BSTR _stdcall _B_str_Time(); [entry(535), propget, helpcontext(0x000f6593)] single _stdcall Timer(); [entry(539), helpcontext(0x000f6594)] VARIANT _stdcall TimeSerial( [in] short Hour, [in] short Minute, [in] short Second); [entry(541), helpcontext(0x000f6595)] VARIANT _stdcall TimeValue([in] BSTR Time); [entry(552), helpcontext(0x000f65a2)] VARIANT _stdcall Weekday( [in] VARIANT* Date, [in, optional, defaultvalue(1)] VbDayOfWeek FirstDayOfWeek); [entry(553), helpcontext(0x000f65a7)] VARIANT _stdcall Year([in] VARIANT* Date); [entry(661), helpcontext(0x000f7566)] VARIANT _stdcall DateAdd( [in] BSTR Interval, [in] double Number, [in] VARIANT* Date); [entry(662), helpcontext(0x000f74d6)] VARIANT _stdcall DateDiff( [in] BSTR Interval, [in] VARIANT* Date1, [in] VARIANT* Date2, [in, optional, defaultvalue(1)] VbDayOfWeek FirstDayOfWeek, [in, optional, defaultvalue(1)] VbFirstWeekOfYear FirstWeekOfYear); [entry(663), helpcontext(0x000f74d7)] VARIANT _stdcall DatePart( [in] BSTR Interval, [in] VARIANT* Date, [in, optional, defaultvalue(1)] VbDayOfWeek FirstDayOfWeek, [in, optional, defaultvalue(1)] VbFirstWeekOfYear FirstWeekOfYear); [entry(700), propget, helpcontext(0x00110c12)] VbCalendar _stdcall Calendar(); [entry(699), propput, helpcontext(0x00110c12)] void _stdcall Calendar([in] VbCalendar rhs); }; // 保留原有的其他定义(如Collection coclass) [ uuid(A4C4671C-499F-101B-BB78-00AA00383CBB), helpcontext(0x000f8c67) ] coclass Collection { [default] interface _Collection; }; };
步骤2:编译IDL生成类型库
打开VS命令提示符,执行MIDL编译命令:
midl vba6.idl /tlb vba6.tlb
步骤3:在VC++项目中调用
在代码中导入类型库,直接调用模块函数:
#include <comdef.h> #include <iostream> #include <windows.h> // 导入VBA类型库,自动生成包装类 #import "vba6.tlb" no_namespace named_guids int main() { HRESULT hr = CoInitialize(nullptr); if (FAILED(hr)) { std::cout << "COM初始化失败" << std::endl; return -1; } try { // 调用DateTime模块的Now函数 VARIANT varNow = DateTime::Now(); if (varNow.vt == VT_DATE) { SYSTEMTIME st; VariantTimeToSystemTime(varNow.date, &st); std::cout << "当前时间:" << st.wYear << "-" << st.wMonth << "-" << st.wDay << " " << st.wHour << ":" << st.wMinute << ":" << st.wSecond << std::endl; } VariantClear(&varNow); } catch (_com_error& e) { std::cout << "调用失败:" << e.ErrorMessage() << std::endl; } CoUninitialize(); return 0; }
方法二:手动声明函数原型直接调用
如果不想编译IDL,可直接根据IDL中的entry序号从VBA6.DLL加载函数:
代码示例
#include <windows.h> #include <comdef.h> #include <iostream> // 根据IDL声明Now函数的原型 typedef VARIANT (__stdcall *PFN_NOW)(); int main() { HRESULT hr = CoInitialize(nullptr); if (FAILED(hr)) { std::cout << "COM初始化失败" << std::endl; return -1; } // 加载VBA6.DLL(注意:64位系统需编译为x86项目,VBA6.DLL是32位) HMODULE hVbaDll = LoadLibrary(L"VBA6.DLL"); if (!hVbaDll) { std::cout << "加载VBA6.DLL失败,错误码:" << GetLastError() << std::endl; CoUninitialize(); return -1; } // 通过导出序号获取Now函数地址(IDL中entry=546) PFN_NOW pfnNow = reinterpret_cast<PFN_NOW>(GetProcAddress(hVbaDll, reinterpret_cast<LPCSTR>(546))); if (!pfnNow) { std::cout << "获取Now函数地址失败,错误码:" << GetLastError() << std::endl; FreeLibrary(hVbaDll); CoUninitialize(); return -1; } // 调用函数并处理结果 VARIANT varNow = pfnNow(); if (varNow.vt == VT_DATE) { SYSTEMTIME st; VariantTimeToSystemTime(varNow.date, &st); std::cout << "当前时间:" << st.wYear << "-" << st.wMonth << "-" << st.wDay << " " << st.wHour << ":" << st.wMinute << ":" << st.wSecond << std::endl; } VariantClear(&varNow); FreeLibrary(hVbaDll); CoUninitialize(); return 0; }
注意事项
- 64位系统下,VBA6.DLL仅提供32位版本,因此VC++项目需设置为x86平台编译,否则会加载失败
- 所有调用必须在COM初始化(
CoInitialize/CoInitializeEx)后执行,调用结束后需释放COM资源(CoUninitialize) - 对于带参数的函数(如
DateSerial),需严格按照IDL中的参数类型声明函数原型,例如:
然后通过typedef VARIANT (__stdcall *PFN_DATESERIAL)(short Year, short Month, short Day);GetProcAddress(hVbaDll, reinterpret_cast<LPCSTR>(538))获取地址(IDL中entry=538)
内容的提问来源于stack exchange,提问作者leakerlee
相关产品推荐
相关产品推荐

