2016-01-21 50 views
0

爲什麼發生此錯誤?引用計數增加,線程模型是單個公寓。 Coll-object和EmptyColl-function都位於一個dll中。 ATL項目的默認調用轉換是__stdcall。與此dll內的其他對象發生同樣的錯誤。當打開觀察窗口將COM對象傳遞給VB 6時VariantClear異常

與空物體清除VARIANT VariantClear時拋出異常:
異常在VB6.EXE在0x75C14974(的oleaut32.dll)拋出:0000005:
訪問衝突讀取位置0x00000008。

frmMain.frm(錯誤,見下面爲什麼):

Private Sub Form_Load() 
    Dim c As Coll 
    Set c = EmptyColl 
    'error when ends here with variable "c" in the watch window. 
End Sub 

frmMain.frm(沒有錯誤):

Private Sub Form_Load() 
    Dim c2 As Coll 'instead of Coll can be any object of same library 
    Set c2 = New Coll 'creation 
    Set c2 = Nothing 'destroying (optionaly) 
    Dim c As Coll 
    Set c = EmptyColl 
    'no error 
End Sub 

filyus.idl:

[ 
    object, 
    uuid(6FA7FAEB-5CE3-4A80-9288-2667EE5E7596), 
    dual, 
    nonextensible, 
    pointer_default(unique) 
] 
interface IColl : IDispatch{ 
    //some methods 
}; 

[ 
    uuid(157F3D2F-A427-4D5A-B908-87868297EA43), 
    version(1.0), 
] 
library Filyus 
{ 
    importlib("stdole2.tlb"); 
    [ 
    dllname("Filyus") 
    ] 
    module Filyus{ 
    [entry("EmptyColl")] 
    HRESULT EmptyColl([out, retval] IColl** Coll); 
    } 
}; 

filyus.def:

LIBRARY 

EXPORTS 
    DllCanUnloadNow  PRIVATE 
    DllGetClassObject PRIVATE 
    DllRegisterServer PRIVATE 
    DllUnregisterServer PRIVATE 
    DllInstall  PRIVATE 
    EmptyColl 

ole.h:

extern HRESULT EmptyColl(IColl** Coll); 

ole.cpp:

HRESULT EmptyColl(IColl** Coll) { 
    HRESULT hr; CComObject<CColl>* Object; 
    if (Coll != nullptr) { 
    hr = CComObject<CColl>::CreateInstance(&Object); 
    if (hr == S_OK) { 
     Object->AddRef(); 
     *Coll = Object; //same error with using QueryInterface 
    } 
    } 
    else hr = E_POINTER; 
    return hr; 
} 
+0

「*同樣的錯誤與此DLL *內其他物體發生」 - 那麼顯然,你正在做的事情你的DLL裏面根本錯誤的。請提供[最小,完整和可驗證的示例](http://stackoverflow.com/help/mcve),其中顯示了更多的DLL代碼。特別是,它是如何聲明和設置'CColl'類以及其他您遇到問題的類。 –

回答

0

EmptyColl()需要使用__stdcall調用約定:

extern HRESULT __stdcall EmptyColl(IColl** Coll); 

HRESULT __stdcall EmptyColl(IColl** Coll) { 
    //... 
} 

或者,使用STDMETHODCALLTYPE宏,它解析爲__stdcall

extern HRESULT STDMETHODCALLTYPE EmptyColl(IColl** Coll); 

HRESULT STDMETHODCALLTYPE EmptyColl(IColl** Coll) { 
    //... 
} 

沒有調用約定聲明,默認爲C/C++編譯器將使用__cdecl代替,除非配置有所不同。 __cdecl__stdcall以不同方式管理調用堆棧。如果不使用正確的調用約定,將會損壞調用堆棧。 COM標準要求__stdcall,這就是VB所期望的。

+0

我的項目的默認調用轉換是__stdcall。 – Filyus

+0

更好的是在代碼中調用約定而不是依賴項目設置。 –

+0

從COM接口開始的偏移量8是COM對象的Release()方法的vtable條目。在地址'00000008'處的AV意味着在空接口指針上調用Release()。如果VARIANT的類型是VT_UNKNOWN或VT_DISPATCH,VariantClear()會調用Release(),但是如果VARIANT被正確管理,那麼它不應該有一個空接口指針。相反,一個空的VARIANT應該有'VT_EMPTY'的'vt'值。也就是說,如果'CreateInstance()'失敗,我確實看到'EmptyColl()'沒有將'Coll'指針初始化爲空。 –

0

由於訪問對象的權限錯誤,因此發生錯誤。
CComPtr用於客戶端,CComObject用於服務器端(直接訪問,只有當您創建了該庫的任何對象時才獲取它)。

正確ole.cpp:

HRESULT EmptyColl(IColl** Coll) { 
    HRESULT hr; CComPtr<IColl> Object; 
    if (Coll != nullptr) { 
    hr = Object.CoCreateInstance(CLSID_Coll); 
    if (hr == S_OK) { 
     Object.CopyTo(Coll); 
    } 
    } 
    else hr = E_POINTER; 
    return hr; 
} 
+0

'EmptyColl()'實現是服務器端代碼,而不是客戶端代碼。 EmptyColl()不是從另一個庫創建一個COM對象的實例,而是創建一個與'EmptyColl()'在同一個DLL中的'CColl'類的新實例。在這種情況下,OP使用'CComObject'是正確的(提供'CColl'來自'CComObjectRoot'或'CComObjectRootEx',即)。像你所建議的那樣,使用'CComPtr',雖然客戶端代碼正確,但對EmptyColl()的服務器端代碼不正確。 –

+0

@ remy-lebeau查看上面標記爲「frmMain.frm(無錯誤)」的代碼,創建該庫的任何對象(並且可選地銷燬)之後,不會發生錯誤。因此,要使用CComObject,您需要一些已經從客戶端對象創建的,可能的應用程序創建子對象和相關對象。 – Filyus

+0

不,你不需要一個已經存在的活動對象來使用'CComObject'來創建新的對象。我寫了大量的COM服務器,它們在調用時返回新的對象,並且'CComObject'在正確使用時工作得很好。問題不在代碼中顯示,正如「*與此dll *中的其他對象發生相同的錯誤」評論所證明的那樣。該DLL必須做一些其他的錯誤,但這些細節尚未提供。 –

相關問題