如何调试 Excel 加载 COM 服务器?

How do I debug Excel's loading of a COM server?

我成功写了一个32位Excel RTD server。这是在 Excel 中加载的,并且在单元格中键入 =RTD(...) 有效。但是,我的相同代码的 64 位构建不起作用,我想知道调试它的任何方法。我找不到任何相关文档。

我有一个 64 位 RTD 服务器 DLL。 Dependency walker 没有显示任何缺少 DLL 依赖项的问题,事实上,如果我 link 一个玩具可执行文件,我可以很好地调用这个 DLL。

DLL 已成功注册到 regsvr32。

Excel 甚至看到它但将其列为 "inactive"。过去,在开发 32 位版本时,这通常是由于缺少 DLL 依赖项(但没有错误消息)而发生的。如前所述,我可以 link DLL 并且依赖项 walker 没有显示任何问题。

我还能做些什么来调试这个问题?如果它是开源的,我会查看 Excel 源代码并尝试做它正在做的事情,但显然这不是一个选项。

相同的代码生成 32 位 DLL,32 位 Excel 运行良好。但是64位的Excel好像不能使用64位的DLL。

问题出在 DLL 的注册表项上。要回答我自己的问题(并且在学习了比我想要的更多的 COM 知识之后),最好的方法是编写一个 COM 客户端应用程序并尝试以多种方式实例化 COM 服务器。对于 RTD,下面是我使用的示例客户端应用程序。如果有人遇到类似问题,我建议首先尝试使用 CoCreateInstance,然后查看是否可以从 ProgId 获取 CLSID,然后使用 ProgId 创建实例,因为这就是 Excel 所做的。相应地替换 UUID 并将 "VCRTDServer.RTDFunctions" 替换为您的 ProgId 是什么。代码:

/**
   Small test program to check that the COM DLL was properly
   registered
 */

#include    <objbase.h>

#ifndef RTD_ARCH
#    define RTD_ARCH 32
#endif

//
//Here we do a #import on the DLL ,you can also do a #import on the .TLB
//The #import directive generates two files in the output folders.
//
#import  "bin\VCRTDServer.dll"

#include <iostream>
#include <stdexcept>
#include <string>
#include <tchar.h>
#include "IRTDServer_h.h"

using namespace std;


#define PROGID _T("VCRTDServer.RTDFunctions")
#define CLSID_STRING _T("{8D2EEA35-CBEB-49b1-8F3E-68C8F50F38D8}")

const CLSID CLSID_RTD = {0x8D2EEA35, 0xCBEB, 0x49B1,
                         {0x8F, 0x3E, 0x68, 0xC8, 0xF5, 0x0F, 0x38, 0xD8}};

const CLSID IID_RTD_UpdateEvent = {0xa43788c1, 0xd91b, 0x11d3,
                                   0x8f, 0x39, 0x00, 0xc0, 0x4f, 0x36, 0x51, 0xb8};

const CLSID IID_RTD = {0xec0e6191, 0xdb41, 0x11d3,
                       0x8f, 0xe3, 0x00, 0xc0, 0x4f, 0x36, 0x51, 0xb8};

static string GetLastErrorAsString();
static void run();

int main() {
    try {
        run();
        CoUninitialize();
        return 0;
    } catch(const exception& ex) {
        cerr << "Error: " << ex.what() << endl;
        CoUninitialize();
        return 1;
    }
}

void run() {
    cout << "CoInitializing" << endl;
    CoInitialize(nullptr);

    // if CoCreateInstance doesn't work, nothing else will
    // cout << "Checking CoCreateInstance" << endl;
    // IRtdServer *obj;
    // const auto hr = CoCreateInstance(CLSID_RTD,
    //                                  nullptr,
    //                                  CLSCTX_INPROC_SERVER,
    //                                  IID_RTD_UpdateEvent,
    //                                  (void**)&obj);
    // if(hr != S_OK)
    //     throw runtime_error("CoCreateInstance failed: " + GetLastErrorAsString());

    cout << "Converting prog id to clsid" << endl;
    CLSID clsid;
    const auto ptoc_res = CLSIDFromProgID(L"VCRTDServer.RTDFunctions", &clsid);
    if(ptoc_res != S_OK)
        throw runtime_error("CLSID error: " +  GetLastErrorAsString());
    cout << "Printing out class ID" << endl;
    cout << "Class ID: " << *((int*)&clsid) << endl;

    LPOLESTR progId;
    const auto progIdResult = ProgIDFromCLSID(
        CLSID_RTD, &progId);
    if(progIdResult != S_OK)
        throw runtime_error("Prog ID error: " +  GetLastErrorAsString());

     char buf[40];
     WideCharToMultiByte(CP_ACP, NULL, progId, -1, buf, 40, NULL, NULL);
     cout << "prog id is '" << buf << "'" << endl;

    cout << "Creating instance" << endl;
    RTDServerLib::IRtdServerPtr rtdServer;
    if(rtdServer.CreateInstance(CLSID_RTD) != S_OK)
        throw runtime_error("Could not create instance: " +
                            GetLastErrorAsString());

    cout << "Starting RTD server" << endl;
    const auto startResult = rtdServer->ServerStart(nullptr);
    cout << "Start result was: " << startResult << endl;
}



//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::string GetLastErrorAsString() {
    //Get the error message, if any.
    const auto errorMessageID = ::GetLastError();
    if(errorMessageID == 0)
        return {}; //No error message has been recorded

    LPSTR messageBuffer = nullptr;
    size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, nullptr);

    std::string message(messageBuffer, size);

    //Free the buffer.
    LocalFree(messageBuffer);

    return message;
}