Hi,
I have a problem when an unmanaged C++ client gets a COM
object from a C# server, which it in turn has got from a
COM server. It works 75% of the time, but sometimes it
crashes.
The COM .exe server's idl looks something like:
[
object,
uuid(F534D8C9-033A-4E17-B73A-440B6BE7B5EB),
dual,
nonextensible,
pointer_default(unique)
]
interface IItem : IDispatch
{
};
[
object,
uuid(158E2223-4973-4231-A51C-34B49B16858D),
dual,
nonextensible,
pointer_default(unique)
]
interface ICollection : IDispatch
{
[propget, id(1)] HRESULT Count([out, retval] long*
pVal);
[id(2)] HRESULT Add([in] IItem* pItem);
[propget, id(DISPID_VALUE)] HRESULT Item([in] long
Index, [out, retval] VARIANT* pVal);
[propget, id(DISPID_NEWENUM)] HRESULT _NewEnum
([out, retval] LPUNKNOWN* pVal);
};
[
object,
uuid(9E97E74D-6A36-4218-933A-D7DCAC802F61),
dual,
nonextensible,
pointer_default(unique)
]
interface IManager : IDispatch
{
[id(4)] HRESULT GetCollection([in] BSTR someBstr,
[in] BSTR anotherBstr, [out,retval] ICollection** menu);
};
[
uuid(B7A4DD8D-E760-46D2-A699-A623290CE005),
version(1.0),
]
library LIBRARYLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(FF23BFD1-1C9F-487C-8754-0926810DEBD5),
]
coclass Manager
{
[default] interface IManager;
};
[
uuid(08E1F10A-24E8-4037-96C3-ED5BB61531A3),
]
coclass Collection
{
[default] interface ICollection;
};
[
uuid(303AF813-528F-4B6D-B297-517CA6DB0EC5),
]
coclass Item
{
[default] interface IItem;
};
};
The GetCollection method creates some items and puts them
in a collection.
This is then exposed to .Net using:
Tlbimp MyCOMExe.exe /out:MyCOMExe.dll
MyCOMExe.dll is referenced in a C# project. The C# code
looks something like:
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using MyCOMExe;
namespace MyDotNet
{
[InterfaceType
(ComInterfaceType.InterfaceIsIDispatch), Guid("58E50811-
9733-412a-8BFA-9B4960E3467A")]
public interface _DotNetManager
{
[return:MarshalAs
(UnmanagedType.IDispatch)] Object GetCollection(string
someString, Object someObject);
}
[ClassInterface(ClassInterfaceType.None), Guid
("85A834FC-BE33-440e-95A5-40EE78E80693")]
public class DotNetManager : _DotNetManager
{
[return:MarshalAs(UnmanagedType.IDispatch)]
public Object GetCollection(string someString, Object
someObject)
{
Manager mgr = new Manager();
// do some stuff
// Get some stuff from a database
// etc.
Collection coll = mgr.GetCollection
(someString, abotherString);
return coll;
}
}
}
The above is regisetered for COM interop.
Then, in an unmanaged MFC C++ client, I have a header file
similar to:
// Dlg.h
#pragma once
#include "afxwin.h"
#include "TheHeaderFileForMyCOMExe.h"
#import "MyDotNet.tlb"
using namespace MyDotNet;
class CDlg : public CDialog
{
public:
CDlg(CWnd* pParent = NULL);
// ...
protected:
virtual void DoDataExchange(CDataExchange* pDX);
// DDX/DDV support
// Implementation
protected:
// etc. etc.
public:
afx_msg void OnBnClickedButton1();
afx_msg void OnBnClickedButton2();
private:
_DotNetManager* pDot;
CComPtr<ICollection> pCollection;
};
and a CPP file something like:
// Dlg.cpp
#include "stdafx.h"
#include "CPPTest.h"
#include "Dlg.h"
#include "MyCOMExe_i.c"
// ...
void CCPPTestDlg::OnBnClickedButton1()
{
_DotNetManagerPtr p(__uuidof(DotNetManager));
pDot = p;
}
void CCPPTestDlg::OnBnClickedButton2()
{
// Release old collection if necessary:
if (pCollection)
{
pCollection.Release();
pCollection = NULL;
}
// yada yada yada
IDispatch* pDisp = NULL;
pDisp = pDot->GetCollection
(someCString.AllocSysString(), someObjectPtr);
HRESULT hr = pDisp->QueryInterface
(IID_ICollection, (void**)&pCollection);
pDisp->Release();
// Get enumerator for Collection
LPUNKNOWN punkEnum;
hr = pCollection->get__NewEnum(&punkEnum) ;
IEnumVARIANT* pEnum;
hr = punkEnum->QueryInterface(&pEnum);
punkEnum->Release();
VARIANT vntNext;
VariantInit(&vntNext);
// Get the number of items in menu:
long count = 0;
hr = pContextMenu->get_Count(&count);
// Do something with each item:
for (int i = 0; i < count; i++)
{
// Get the next item in the collection
hr = pEnum->Next(1, &vntNext, NULL);
CComQIPtr<IItem> pItem(vntNext.pdispVal);
// do something with the item
}
}
OnBnClickedButton1() is called once, and then
OnBnClickedButton2() is called repeatedly. It always works
the first time OnBnClickedButton2 is called. Sometimes the
second time causes a crash, sometimes the third time, etc.
(Incidentally, from a VB client it never crashes).
When it crashes, I get the following output:
First-chance exception at 0x779db680 in CPPTest.exe:
0xC0000005: Access violation reading location 0xcccccccc.
Unhandled exception at 0x779db680 in CPPTest.exe:
0xC0000005: Access violation reading location 0xcccccccc.
First-chance exception at 0x779db680 in CPPTest.exe:
0xC0000005: Access violation reading location 0xcccccccc.
Unhandled exception at 0x779db680 in CPPTest.exe:
0xC0000005: Access violation reading location 0xcccccccc.
First-chance exception at 0x779db680 in CPPTest.exe:
0xC0000005: Access violation reading location 0xcccccccc.
Unhandled exception at 0x779db680 in CPPTest.exe:
0xC0000005: Access violation reading location 0xcccccccc.
The program '[3336] CPPTest.exe: Native' has exited with
code 0 (0x0).
The program '[3336] CPPTest.exe: DefaultDomain' has exited
with code 0 (0x0).
and the debugger points to a location in the MyDotNet.tli
file, which looks like:
inline IDispatchPtr _DotNetManager::GetCollection (
_bstr_t someBstr, const _variant_t & someObject ) {
IDispatch * _result = 0;
_com_dispatch_method(this, 0x60020000,
DISPATCH_METHOD, VT_DISPATCH, (void*)&_result,
L"\x0008\x000c", (BSTR)someBstr, &someObject ); //
this is the line the debugger points to
return IDispatchPtr(_result, false);
Any ideas as to what may cause this behaviour would be
greatly appreciated.
Best regards,