【问题标题】:Problems accessing a COM interface in C++在 C++ 中访问 COM 接口的问题
【发布时间】:2013-08-21 16:55:14
【问题描述】:

我要做的是访问一个 COM 接口,然后调用该接口的“Open”方法。 我在 Visual Basic 中有一个可以正常工作的示例代码,但我需要用 C++ 编写它,我似乎无法让它工作。

首先,这是有效的 VB 代码:

Dim CANapeApplication As CANAPELib.Application
CANapeApplication = CreateObject("CANape.Application")
Call CANapeApplication.Open("C:\Users\Public\Documents\Vector\CANape\12\Project", 0)

CANape.Application 是选择我需要的接口的 ProgID。

在阅读了 msdn.microsoft.com 和 this question 上的一些文档后,我编写了以下代码:

void ErrorDescription(HRESULT hr); //Function to output a readable hr error
int InitCOM();
int OpenCANape();

// Declarations of variables used.
HRESULT hresult;
void **canApeAppPtr;
IDispatch *pdisp;
CLSID ClassID;
DISPID FAR dispid;
UINT nArgErr;
OLECHAR FAR* canApeWorkingDirectory = L"C:\\Users\\Public\\Documents\\Vector\\CANape\\12\\Project";

int main(){

    // Instantiate CANape COM interface
    if (InitCOM() != 0) {
        std::cout << "init error";
        return 1;
    }

    // Open CANape
    if (OpenCANape() != 0) {
        std::cout << "Failed to open CANape Project" << std::endl;
        return 1;
    }
    CoUninitialize();
    return 0;
}


void ErrorDescription(HRESULT hr) { 
    if(FACILITY_WINDOWS == HRESULT_FACILITY(hr)) 
        hr = HRESULT_CODE(hr); 
    TCHAR* szErrMsg; 

    if(FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 
        NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
        (LPTSTR)&szErrMsg, 0, NULL) != 0) 
    { 
        _tprintf(TEXT("%s"), szErrMsg); 
        LocalFree(szErrMsg); 
    } else 
        _tprintf( TEXT("[Could not find a description for error # %#x.]\n"), hr); 
}

int InitCOM() {
    // Initialize OLE DLLs.
    hresult = OleInitialize(NULL);
    if (!SUCCEEDED(hresult)) {
        ErrorDescription(hresult);
        return 1;
    }
    // Get CLSID from ProgID
    //hresult = CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID);
    hresult = CLSIDFromProgID(OLESTR("CanapeCom.CanapeCom"), &ClassID);
    if (!SUCCEEDED(hresult)) {
        ErrorDescription(hresult);
        return 1;
    }
    // OLE function CoCreateInstance starts application using GUID/CLSID
    hresult = CoCreateInstance(ClassID, NULL, CLSCTX_LOCAL_SERVER,
        IID_IDispatch, (void **)&pdisp);
    if (!SUCCEEDED(hresult)) {
        ErrorDescription(hresult);
        return 1;
    }
    // Call QueryInterface to see if object supports IDispatch
    hresult = pdisp->QueryInterface(IID_IDispatch, (void **)&pdisp);
    if (!SUCCEEDED(hresult)) {
        ErrorDescription(hresult);
        return 1;
    }

    std::cout << "success" << std::endl;
    return 0;
}

int OpenCANape() {
    //Method name
    OLECHAR *szMember = L"Open";
    // Retrieve the dispatch identifier for the Open method
    // Use defaults where possible
    DISPID idFileExists;
    hresult = pdisp->GetIDsOfNames(
        IID_NULL,
        &szMember,
        1,
        LOCALE_SYSTEM_DEFAULT,
        &idFileExists);
    if (!SUCCEEDED(hresult)) {
        std::cout << "GetIDsOfNames: ";
        ErrorDescription(hresult);
        return 1;
    }

    unsigned int puArgErr = 0;

    VARIANT VarResult;

    VariantInit(&VarResult); 

    DISPPARAMS pParams;
    memset(&pParams, 0, sizeof(DISPPARAMS)); 
    pParams.cArgs = 2; 

    VARIANT Arguments[2];
    VariantInit(&Arguments[0]); 

    pParams.rgvarg = Arguments; 
    pParams.cNamedArgs = 0;
    pParams.rgvarg[0].vt = VT_BSTR;
    pParams.rgvarg[0].bstrVal = SysAllocString(canApeWorkingDirectory);
    pParams.rgvarg[1].vt = VT_INT;
    pParams.rgvarg[1].intVal = 0; // debug mode

    // Invoke the method. Use defaults where possible.
    hresult = pdisp->Invoke(  
        dispid,
        IID_NULL,
        LOCALE_SYSTEM_DEFAULT,
        DISPATCH_METHOD,
        &pParams,
        &VarResult,
        NULL,
        &puArgErr
        );

    SysFreeString(pParams.rgvarg[0].bstrVal);

    if (!SUCCEEDED(hresult)) {
        ErrorDescription(hresult);
        return 1;
    }
    return 0;
}

这有几个问题。

  • 使用从CLSIDFromProgID接收的ClassID作为CoCreateInstance的第一个参数不起作用,它返回错误:class not registered
  • 如果我使用 ProgID CanapeCom.CanapeCom(我在注册表中找到它),CoCreateInstance 有效。但是,当我使用 pdisp->GetIDsOfNames 时,我收到错误消息:Unkown name。我认为这意味着找不到该方法。这似乎合乎逻辑,因为我使用了不同的 ProgID,但我就是不知道如何访问我正在寻找的界面。
  • 我还尝试使用来自 CLSIDFromProgID(OLESTR("CANape.Application"), &amp;ClassID); 的结果 CLSID 作为 CoCreateInstance 的第四个参数,但这会导致 “不支持此类接口” 错误。

我需要软件的dll文件吗?在 VB 示例中,dll 文件用于获取接口,然后使用 ProgID 创建一个新对象。我不确定我是否需要在 C++ 中做同样的事情,或者这应该如何工作。

我真的被困在这里,希望有人可以帮助我。

【问题讨论】:

  • 通常你只是让一些工具在 C++ 中围绕 COM 库生成一个包装器并使用它。如果您使用的是 Visual Studio,请尝试 #import "CANape.tlb"
  • 检查你没有制作 64 位版本,因为大概 CanApe 只有 32 位。
  • 根据 Jonathan 的评论,如果是这种情况,您可以使用 COM 代理使其工作,但不要指望它是微不足道的(老实说,我不't recc it)。
  • 您不能随意替换 ProgId,您需要让 CLSID 恢复工作。在 VB 代码和您的代码上都使用 SysInternals 的 ProcMon。您会看到它在注册表中搜索 CLSID 键,差异会很快弹出。
  • 是的,+1 用于使用进程监视器 - 这是停止猜测并开始调试的唯一方法。

标签: c++ com canape


【解决方案1】:

感谢您的 cmets。 我已经解决了这个问题,虽然解决方案有点尴尬...... 在我的辩护中,我仍然是一名学生,对这类东西很陌生。

我使用进程监视器来检查执行 VB 脚本时发生的情况。 我看到那里使用的 CLSID 是 CLSIDFromProgID(OLESTR("CANape.Application"), &amp;ClassID); 返回的 ID,这意味着这必须是正确的 ID,而问题必须在其他地方。我再次查看了 CoCreateInstance,然后查看了其他参数。结果发现上下文 CLSCTX_LOCAL_SERVER 是错误的,它必须是 CLSCTX_INPROC_SERVER。我不知道为什么我首先将它设置为 local_server 或者为什么我从未质疑过它。几天前我写了这部分代码,然后过多地关注 CLSID 和 IID 而不是其他参数。 我还考虑了 Alex 的第一条评论并创建了一个 tlb 文件。

这是代码的简化版本:

#import "CANape.tlb"

int _tmain(int argc, _TCHAR* argv[])
{
    _bstr_t path = "C:\\Users\\Public\\Documents\\Vector\\CANape\\12\\Project";
    CLSID idbpnt; 

    CoInitialize(NULL); 

    HRESULT hr = CLSIDFromProgID (L"CANape.Application", &idbpnt); 
    CANAPELib::IApplication *app; 
    hr = CoCreateInstance(idbpnt,NULL,CLSCTX_INPROC_SERVER,__uuidof(CANAPELib::IApplication),(LPVOID*)&app ); 
    app->Open(path,0);
    CoUninitialize();
    return 0; 
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-30
    • 2010-09-29
    • 2010-10-20
    • 2011-06-07
    • 1970-01-01
    • 2015-10-13
    • 1970-01-01
    • 2014-01-06
    相关资源
    最近更新 更多