2013-08-19 41 views
0

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

首先,这是工作的VB代码:

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

CANape.Application是选择我需要的接口的进程id。

在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; 
} 

有几个问题与此有关。

  • 使用的ClassID从收到CLSIDFromProgIDCoCreateInstance的不工作的第一个参数,它会返回错误:未注册
  • 如果我使用的ProgID类CanapeCom.CanapeCom(我通过在注册表中找到它),CoCreateInstance的作品。但是,当我使用pdisp-> GetIDsOfNames我收到错误消息:未知名称。我认为这意味着该方法未找到。这似乎是合乎逻辑的,因为我使用了不同的ProgID,但我无法弄清楚如何到达我要找的界面。
  • 我也曾尝试使用生成的CLSID从CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID);CoCreateInstance的的第四个参数但这导致了“不支持此接口”错误。

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

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

+1

通常你只是让一些工具来生成周围C++中的COM库的包装和使用。如果您使用的是Visual Studio,请尝试'#import“CANape.tlb”'。 – Alex

+0

检查你没有做一个64位版本,据推测CanApe只有32位。 –

+0

继乔纳森的评论,如果是这样的话,你*可以*使用COM代理工作,但不要指望它是微不足道的(老实说,我不recc它)。 – WhozCraig

回答

1

感谢您的意见。 我已经解决了这个问题,虽然解决方案有点令人尴尬...... 在我的防守中,我仍然是一名学生,对于这类东西来说是新的。

我用Process Monitor来检查当我执行VB脚本时会发生什么。 我看到那里使用的CLSID是由CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID);返回的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; 
}