2013-08-19 43 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; 
}