dotnet interop:
Hi,
I've encountered a very basic problem when dealing with User Defined Types (UDT) in a COM class.
If a COM method returns a UDT in a VARIANT, the calling .NET application receives an
exception "The specified record cannot be mapped to a managed value class.".
In order to reproduce this problem, create a new C++ ATL project that implements an inproc-server (dll) component.
The name of the project is API.
Then create a simple C# console application 'TestApp' and add the COM object as a reference.
-- This is the contents of the idl file API.idl:
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(4DA8E86C-C8D2-443D-8940-B52C2151E37C),
dual,
nonextensible,
helpstring("ITest Interface"),
pointer_default(unique)
]
interface ITest : IDispatch {
HRESULT Test([in] BSTR message, [out,retval] VARIANT* result);
};
[
uuid(1ECB0F02-F896-473D-ADFE-44BB1602F08D),
version(1.0),
helpstring("API 1.0 Type Library")
]
library APILib
{
importlib("stdole2.tlb");
typedef [uuid(5523d721-5fe5-4a40-892e-8824b14aea0d)] struct MyStruct
{
double X;
double Y;
double Z;
} MyStruct;
[
uuid(C0A1CB57-1726-4729-874A-3C607E90F0EC),
]
coclass Test
{
[default] interface ITest;
};
}
-- This is the implementation of COM object Test (file Test.cpp)
#include "stdafx.h"
#include "Test.h"
#include ".\test.h"
void convertStructToVariant(VARIANT& var, const MyStruct& s)
{
CComPtr<IRecordInfo> pRecInfo;
HRESULT hr;
GUID structUuid;
hr = CLSIDFromString(L"{5523d721-5fe5-4a40-892e-8824b14aea0d}", &structUuid);
hr = GetRecordInfoFromGuids(LIBID_APILib, 1, 0, GetUserDefaultLCID(), structUuid, &pRecInfo);
if (pRecInfo)
{
VariantClear(&var);
var.vt = VT_RECORD;
var.pvRecord = pRecInfo->RecordCreate();
var.pRecInfo = pRecInfo.Detach();
*((MyStruct*)var.pvRecord) = s;
}
}
STDMETHODIMP CTest::Test(BSTR message, VARIANT* result)
{
USES_CONVERSION;
CComVariant var;
if (!wcsicmp(message, L"RETURN-STRING"))
{
var = CComVariant("TEST-RESULT");
}
else if (!wcsicmp(message, L"RETURN-STRUCT"))
{
MyStruct s;
s.X = 1;
s.Y = 2;
s.Z = 3;
convertStructToVariant(var, s);
}
var.Detach(result);
return S_OK;
}
-- TestApp.cs
using System;
using System.Windows.Forms;
namespace TestApp
{
class Application
{
[STAThread]
static void Main(string[] args)
{
APILib.Test test = new APILib.Test();
object result;
try
{
result = test.Test("RETURN-STRING");
MessageBox.Show(result.ToString(), "String Test");
}
catch (Exception e)
{
MessageBox.Show("Caught exception \"" + e.ToString() + "\"", "Struct Test");
}
try
{
result = test.Test("RETURN-STRUCT");
MessageBox.Show(result.ToString(), "Struct Test");
}
catch (Exception e)
{
MessageBox.Show("Caught exception \"" + e.ToString() + "\"", "Struct Test");
}
}
}
}
If I run TestApp.exe, 2 MessageBoxes appear:
The first one for the string test prints "TEST-RESULT".
The second one shows:
Caught exception "System.ArgumentException: The specified record cannot be mapped to a managed value class.
at APILib.TestClass.Test(String message)
at TestApp.Application.Main(String[] args) in c:\temp\udt\testapp\testapp.cs:line 28"
Any help would be greatly appreciated.
Thanks,
- Jürgen