2016-01-21 16 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必须做一些其他的错误,但这些细节尚未提供。 –

相关问题