You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 18:52:38