使用打开的监视窗口将COM对象传递给VB 6时的VariantClear异常

时间:2016-01-21 18:42:42

标签: c++ com vb6 atl

为什么会出现此错误?引用计数增加,线程模型是单个公寓。 Coll-object和EmptyColl函数都位于一个dll中。 ATL项目的默认调用转换是__stdcall。与此dll中的其他对象发生相同的错误。

  

当使用NULL-object清除VARIANT时,VariantClear会抛出异常:
  在VB6.EXE中0x75C14974(oleaut32.dll)抛出异常:0xC0000005:
  访问冲突读取位置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;
}

2 个答案:

答案 0 :(得分: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所期望的。

答案 1 :(得分: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;
}