I am porting our component which is written in C++ at its core and has both an ActiveX and a .Net shell. The component internally uses the VARIANT type in many places. Some public properties (get/set) and methods of this component's arguments are of the VARIANT type in the ActiveX implementation and System::Object in the .Net implementation. Internally in our code we use the VARIANT directly.
When implementing the ActiveX component, I did not need to do any marshaling since VARIANT is an OLE/COM type.
When implementing .Net component, I used similar to this:
VARIANT var;
//...
//Initialize the VARIANT value
//...
System::IntPtr p( &var );
System::Object ^o = System::Runtime::InteropServices::Marshal::GetObjectForNativeVariant(p );
return o;
In WinRT, there does not seem to be any similar Marshal class that will do the job. According to MSDN "The WinRT Platform::Runtime::InteropServices namespace is intended for internal use only, and is not intended to be used for development."
What are my options? Surely there must be an existing class to do the work of marshalling a VARIANT across ABI boundaries. I don't want to write such a marshaller and then find out that it already exists !
Any help would be much appreciated.
Thanks for your reply. I ended up writing a utility class using Windows::Foundation::IPropertyValue.
I posted the code here together with a link back to this thread:
Marshalling a VARIANT in a WinRT component
Example:
// in a C# we can write:
string [] sarray = new string[2];
sarray[0] = "abc";
sarray[1] = "def";
SetValue(sarray);
//in C++/CX we write:
void SetValue( Object ^value )
{
VARIANT var;
VariantInit( &var );
var = acMarshall::MarshalObjectToVariant( value );
//
// We will have a SAFEARRAY of BSTRs in the VARIANT...
//
VariantClear( &var );
}
// and the reverse, in C++/CX
Object ^ GetValue()
{
VARIANT var;
VariantInit(&var);
v.vt = VT_BSTR | VT_ARRAY;
SAFEARRAYBOUND sab;
sab.cElements = 2;
sab.lLbound = 0;
SAFEARRAY *psa = SafeArrayCreate( VT_BSTR, 1, &sab );
LPVOID p = NULL;
SafeArrayAccessData( psa, &p );
((BSTR *) p)[0] = SysAllocString(L"string one");
((BSTR *) p)[1] = SysAllocString(L"string two");
SafeArrayUnaccessData( psa );
var.parray = psa;
Object ^ obj = acMarshall::MarshalVariantToObject( var );
VariantClear( &var );
return obj;
}
//and in C#:
object obj = GetValue() //obj will contain a string array...
Regards,
Roger
There is no "VARIANT" equivalent in the Windows Runtime. At the ABI level, System::Object is represented as an object implementing IInspectable, that may be a mechanism that you can use to replace your variant.
It is possible to use IReference to create an IInspectable based interface which represents your higher level objects but it won't have the weakly typed semantics that VARIANT does.
Related
I have a function that return a pointer to an interface pointer via a paramater (project) :
CreateProject(std::string str, IDispatch** project);
Given two other Interface that implement IDispatch : A and B, is the following code legit given that the real type of project is A. ( I am trying to work with COM VCProjectEngine.CreateProject)
A** a;
B** b;
CreateProject("test.vcxproj", a); //should work but I don't know why
CreateProject("test.vcxproj", b); //should not work but I don't know why
Can someone explain me how this kind of thing is suppose to work ? I am sorry I am a little bit new with COM objects.
IDispatch** project argument typically assumes that you pass a pointer to IDispatch* variable, which is to be filled with actual interface pointer:
IDispatch* pDispatch;
pDispatch = NULL; // Sanity, optional
CreateProject("test.vcxproj", &pDispatch);
assert(pDispatch != NULL); // Filled by call above
// ...
pDispatch->Release();
Since dealing COM interface pointers make you care about proper reference counting, you typically want to use wrapper classes, instead of raw pointers:
CComPtr<IDispatch> pDispatch;
CreateProject("test.vcxproj", &pDispatch);
ATLASSERT(pDispatch != NULL);
Read up on CComPtr on MSDN.
I've been researching passing a struct as a parameter from a C++ client to a C++ server using COM. I've found many examples but none that really explained it to me like I'm five nor any that really provided a firm understanding of how to do what I want, which is simply pass a C++ struct through a COM interface where both sides are C++. Should be easy, right?
I have established my struct as follows in the IDL file on server-side:
[
uuid(7F0C9A48-3C41-425B-B4E6-8156B61D5355),
version(1.0)
]
typedef struct xxxData
{
int iWidth;
int iHeight;
SafeArray(short) pxxxData;
} xxxData;
// Fix for UUID DECLARATION FOR _uuidof() functionality
// From http://go4answers.webhost4life.com/Example/error-c2787-no-guid-been-associated-158947.aspx
cpp_quote("struct __declspec(uuid(\"{7F0C9A48-3C41-425B-B4E6-8156B61D5355}\")) xxxData;")
Which works, so far as I can tell.
Now my client calls GetImageData which is shown as follows:
[id(16)] HRESULT GetImageData([in,out] VARIANT* pData);
Now my client call is as follows with this function:
VARIANT* pData = new VARIANT;
VariantInit( pData );
xxxData* data = new xxxxData;
HRESULT hr = mpCOMEvents->GetImageData(pData);
data = (FBIS_ImageData*)(pData->pvRecord);
int length = data->iWidth * data->iHeight;
However, length is giving me an incorrect address location. This makes me wonder if my use of pvRecord is incorrect and if I can really typecast it?
Here is my COM server side:
xxxData data;
//SAFEARRAY *psa;
IRecordInfo *pRI;
HRESULT hr;
/* Pass in Structure Information */
data.iHeight = 100;
data.iWidth = 100;
// Used http://vcfaq.mvps.org/com/4.htm as reference
hr = GetRecordInfoFromGuids(LIBID_xxxLib, 1, 0, 0x409, _uuidof(xxxData), &pRI);
VariantInit(pData);
pData->vt = VT_RECORD;
pData->pvRecord = &data;
pData->pRecInfo = pRI;
pRI = NULL;
There's some confusion here.
If you're not aiming to be automation friendly, change your IDL to:
[size_is=iWidth*iHeight] unsigned short* pxxxData;
and don't use SAFEARRAY API on this. For marshalling, you'll have to compile a proxy/stub DLL and register it.
If you're aiming to be automation friendly, change your IDL to:
SAFEARRAY(short) pxxxData;
and do use the SAFEARRAY API on this. For marshalling, you'll have to compile a typelib (optionally, embed it) and register it. This also enables early-binding (e.g. VB6, tlbimp).
This will work for languages/environments that support user-defined types. For the ones that don't (e.g. scripting languages), you'll have to use an oleautomation/dual/IDispatch-based interface instead of a struct (and implement in in the server).
EDIT: Based on the changes you made to your question.
You should declare the pData parameter as out only, GetImageData will populate it, not use it and possibly replace it. It also only requires marshaling on return, not on the call. Here's a suggestion:
[id(16)] HRESULT GetImageData([out] VARIANT* pData);
Your client code has a memory leak, it always creates an xxxData. Here's a suggestion:
// If pData is in-out, this is not safe, use CoTaskMemAlloc(sizeof(VARIANT)) instead.
// The callee may override the buffer by assuming it was CoTaskMemAlloc'ed, thus
// assuming it can CoTaskMemFree the original location and set the pointer to a new
// CoTaskMemAlloc'ed location.
// The callee may be a proxy.
// Assuming it's out only, we can provide any location with enough space for a VARIANT.
VARIANT vData;
VariantInit( &vData );
xxxData* data; // remove memory leak
HRESULT hr = mpCOMEvents->GetImageData(&vData);
// error handling removed for clarity (I hope)
data = (xxxData*)(vData.pvRecord);
int length = data->iWidth * data->iHeight;
// ... use data ...
// Don't forget to clear the variant, or there'll be a memory leak
// It implies:
// vData.pRecInfo->RecordDestroy(vData.pvRecord);
// This should recursively release memory allocated in each field
// and finally release the memory allocated for the struct itself.
// vData.pRecInfo->Release();
VariantClear( &vData );
// don't use data past this point
Your server code is setting pData->pvRecord to point to the stack, which means that it will potentially be overwritten by a caller or some other invoked function. Here's a suggestion:
xxxData* data; // Changed to pointer
IRecordInfo *pRI;
HRESULT hr;
// data.iHeight = 100; // removed
// data.iWidth = 100; // removed
hr = GetRecordInfoFromGuids(LIBID_xxxLib, 1, 0, 0x409, _uuidof(xxxData), &pRI);
// error handling removed for clarity (I hope)
VariantInit(pData);
// This will allocate memory for the struct itself
// For fields that require memory allocation, follow "normal" COM rules,
// such as using CoTaskMemAlloc for buffers, SysAllocString or similar for BSTRs,
// etc.
// For each inner (pointed to) structure, you should call RecordCreate on the
// respective IRecordInfo instance for that type.
data = (xxxData*)pRI->RecordCreate();
data->iHeight = 100; // new
data->iWidth = 100; // new
// If pData is in-out, this will leak, use VariantClear instead.
// Assuming it's out only, use VariantInit as it points to (allocated) garbage.
VariantInit(pData);
pData->vt = VT_RECORD;
pData->pvRecord = data; // data is already a pointer
pData->pRecInfo = pRI;
pRI = NULL;
// This won't (normally) leak, the caller must call VariantClear on the out VARIANT.
// The caller may be a stub.
I want to call a method in a COM component from C# using COM interop. This is the methods signature:
long GetPrecursorInfoFromScanNum(long nScanNumber,
LPVARIANT pvarPrecursorInfos,
LPLONG pnArraySize)
and this is sample code (which I checked is really working) to call it in C++:
struct PrecursorInfo
{
double dIsolationMass;
double dMonoIsoMass;
long nChargeState;
long nScanNumber;
};
void CTestOCXDlg::OnOpenParentScansOcx()
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = 0;
m_Rawfile.GetPrecursorInfoFromScanNum(m_nScanNumber,
&vPrecursorInfos,
&nPrecursorInfos);
// Access the safearray buffer
BYTE* pData;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pData);
for (int i=0; i < nPrecursorInfos; ++i)
{
// Copy the scan information from the safearray buffer
PrecursorInfo info;
memcpy(&info,
pData + i * sizeof(MS_PrecursorInfo),
sizeof(PrecursorInfo));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
}
And here's the corresponding C# signature after importing the typelib of the COM component:
void GetPrecursorInfoFromScanNum(int nScanNumber, ref object pvarPrecursorInfos, ref int pnArraySize);
If I'm not mistaken, I need to pass in null for pvarPrecursorInfos to make COM interop marshal it as the expected VT_EMPTY variant. When I'm doing it, I get a SafeArrayTypeMismatchException - not really surprising, looking at how the result is expected to be handled in the sample. So I was trying to use a custom marshaler. Since a cannot alter the component itself, I tried to introduce it this way:
[Guid("06F53853-E43C-4F30-9E5F-D1B3668F0C3C")]
[TypeLibType(4160)]
[ComImport]
public interface IInterfaceNew : IInterfaceOrig
{
[DispId(130)]
int GetPrecursorInfoFromScanNum(int nScanNumber, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshaler))] ref object pvarPrecursorInfos, ref int pnArraySize);
}
The TypeLibType and DispID attribute are the same as in the original version. This works as far as that the MyMarshaller.GetInstance() method is called, but I do not get a callback in MyMarshaller.NativeToManaged. Instead, an access violation is reported. So is this a reliable approach? If yes - how can I make it work? If no: are there any alternatives?
(Just a footnote: in theory I could try to use managed C++ to call the component natively. However, there are lots of other methods in it that work fine with COM interop, so I would very much like to stick with C# if there is any way.)
Since someone asked for it, here's my solution in Managed C++.
array<PrecursorInfo^>^ MSFileReaderExt::GetPrecursorInfo(int scanNumber)
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = -1;
//call the COM component
long rc = pRawFile->GetPrecursorInfoFromScanNum(scanNumber, &vPrecursorInfos, &nPrecursorInfos);
//read the result
//vPrecursorInfos.parray points to a byte sequence
//that can be seen as array of MS_PrecursorInfo instances
//(MS_PrecursorInfo is a struct defined within the COM component)
MS_PrecursorInfo* pPrecursors;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pPrecursors);
//now transform into a .NET object
array<PrecursorInfo^>^ infos = gcnew array<PrecursorInfo^>(nPrecursorInfos);
MS_PrecursorInfo currentPrecursor;
for (int i=0; i < nPrecursorInfos; ++i)
{
currentPrecursor = pPrecursors[i];
infos[i] = safe_cast<PrecursorInfo^>(Marshal::PtrToStructure(IntPtr(¤tPrecursor), PrecursorInfo::typeid));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
return infos;
}
I look at the github code mzLib, which I believe is related to this topic. The code looks good, where it calls
pin_ptr<const wchar_t> wch = PtrToStringChars(path);
I think it may cause some problem, better use
pin_ptr<const wchar_t> pathChar = static_cast<wchar_t*>(System::Runtime::InteropServices::Marshal::StringToHGlobalUni(path).ToPointer());
The code then seems to be worked just fine when compiles. However, it might run in problem when imported as dll. I worked on that by adding a constructor,such as
public ref class ThermoDLLClass
{
public:
ThermoDLLClass();
PrecursorInfo GetPrecursorInfo(int scanNum, String^ path);
};
Then, it seems to get precursorInfo and parameters appropriately.
I need to make COM IntetrOp at runtime using reflections. My native COM Object's exposed methods have some parameters as pointers (DWORD*) and some double pointers (DWORD**) and some are user defined types(e.g SomeUDTType objSmeUDTType) and vice versa its pointer(i.e. SomeUDTType *pSomeUDTType).
Now for dynamic method invocation, we have single option for passing parameters as array of object i.e object[] and filling this array statically.
But I need to pass pointers and references and pointers to pointers. For now how can I be able to populate object array as mixed data of simple data types, pointers or references and pointers to pointers.
Working Example:
Native COM exposed method :
STDMETHODIMP MyCallableMethod(DWORD *value_1,BSTR *bstrName,WESContext **a_wesContext)
Translated by tlbimp.exe (COMInterop)
UDTINIDLLib.IRuntimeCalling.MyCallableMethod(ref uint, ref string, System.IntPtr)
Now calling these methods at runtime using reflection at runtime,
See here :
Assembly asembly = Assembly.LoadFrom("E:\\UDTInIDL\\Debug\\UDTINIDLLib.dll");
Type[] types = asembly.GetTypes();
Type type = null;
//foreach (Type oType in types)
{
try
{
type = asembly.GetType("UDTINIDLLib.RuntimeCallingClass");
}
catch (TypeLoadException e)
{
Console.WriteLine(e.Message);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
object parameters = new object[3];
Type CustomType = asembly.GetType("UDTINIDLLib.WESContext");
object oCustomType = Activator.CreateInstance(CustomType);
FieldInfo fieldInfo = CustomType.GetField("MachineName", BindingFlags.Public | BindingFlags.Instance);
string MachineName = "ss01-cpu-102";
string MachineIp = "127.0.0.1";
string Certificate = "UK/78T";
fieldInfo.SetValue(oCustomType, MachineName);
fieldInfo.SetValue(oCustomType, MachineIp);
fieldInfo.SetValue(oCustomType, Certificate);
object obj = Activator.CreateInstance(type);
MethodInfo mInfo = type.GetMethod("MyCallableMethod");
int lengthOfParams = mInfo.GetParameters().Length;
ParameterInfo [] oParamInfos = mInfo.GetParameters();
object[] a_params = new object[lengthOfParams];
int ValueType = 0;
for(int iCount = 0; iCount<lengthOfParams; iCount++)
{
a_params[iCount] = ???; //Now here this array should be populated with corresponding pointers and other objects (i.e WESContext's obj)
}
mInfo.Invoke(obj, a_params);
Hope code will clarifies ...If any any confusion do let me know I'll edit my question accordingly.
I am stuck here , I'll be obliged if you help me out.(I am confused about "dynamic" keyword might hope it solves the problem)
Is there any need to generate C++/CLI wrappers? and if in which context?
Regards
Usman
Just put the values of your arguments directly into the array. For out/ref parameters, the corresponding elements of the array will be replaced by new values returned by the function.
For the double pointer, by far the easiest approach is to use /unsafe and unmanaged pointers, like so (assuming the parameter is used by the method to return a value):
WESContext* pWesContext; // the returned pointer will end up here
IntPtr ppWesContext = (IntPtr)&pWesContext;
// direct call
MyCallableMethod(..., ppWesContext);
// reflection
a_params[3] = ppWesContext;
mInfo.Invoke(obj, a_params);
After you'll get the pointer to struct in pWesContext, you can use -> to access the members in C#. I'm not sure what memory management rules for your API are, though; it may be that you will, eventually, need to free that struct, but how exactly to do that should be described by the documentation of the API you're trying to call.
I am a COM object written in ATL that is used from a C++ application, and I want to pass an array of BYTEs between the two. My experience of COM/IDL so far is limited to passing simple types (BSTRs, LONGs, etc.).
Is there a relatively easy way to have the COM object pass an array to the caller? For example, I want to pass a raw image (TIFF) instead of messing with temporary files.
Try passing a safearray variant to the COM Object. Something like this to put a BYTE array inside a safearray variant....
bool ArrayToVariant(CArray<BYTE, BYTE>& array, VARIANT& vtResult)
{
SAFEARRAY FAR* psarray;
SAFEARRAYBOUND sabounds[1];
sabounds[0].lLbound=0;
sabounds[0].cElements = (ULONG)array.GetSize();
long nLbound;
psarray = SafeArrayCreate(VT_UI1, 1, sabounds);
if(psarray == NULL)
return false;
for(nLbound = 0; nLbound < (long)sabounds[0].cElements ; nLbound++){
if(FAILED(SafeArrayPutElement(psarray, &nLbound, &array[nLbound]))){
SafeArrayDestroy(psarray);
return false;
}
}
VariantFree(vtResult);
vtResult.vt = VT_ARRAY|VT_UI1;
vtResult.parray = psarray;
return true;
}
SAFEARRAYs are the way to go if you want OLE-Automation compliance, and maybe use the COM interface from other languages such as VB6. But there is an alternative in IDL, for example: -
void Fx([in] long cItems, [in, size_is(cItems)] BYTE aItems[]);
This describes a method where the marshalling code can infer the number of bytes to be transfered by inspecting the value of the first parameter.
This is fine if your clients are all written in C/C++, but i think that an interface containing this would not be automation-compliant, so not usable from VB6, and possibly the standard marshaler will not be able to do the marshaling, so you'd need to generate your own proxy/stub DLL from the IDL. Not hard to do, but a bit harder than using SAFEARRAYs.
Check out using safearrays. Here's some example code:
The safearray is returned as a pointer to a VARIANT
[id(1), helpstring("LogCache")] HRESULT LogCache([out,retval] VARIANT* logCache);
Safearrays are pretty easy to use. Here's some example code which is a cache of the latest 1000 log messages of some application:
safearray_t<bstr_t> m_logCache;
...
if (m_logCache.size() > 1000)
{
m_logCache.pop_back();
}
m_logCache.push_front(Msg.str(), 0);
variant_t LogCache()
{
if (!m_logCache.is_empty())
{
variant_t cache(m_logCache);
return cache;
}
}
Note that the syntax in your case is almost certainly going to be different since I'm using the comet COM library, but the ideas/concepts are the same.
You can use BSTR to pass an array of bytes.
BYTE array[buffer_size];
...
BSTR toBePassed = SysAllocStringByteLen((OLECHAR*)array,length);
YourCOMMethod(toBePassed);
SysFreeString(toBePassed);
In your method:
BYTE* pData = (BYTE*)bstrPassed;
DWORD dataLength = SysStringByteLen(bstrPassed);