I need to consume an existing service in C++ using C#. Previous clients
used VB6 arrays. To paraphrase, I have a method in C++:
int __stdcall DoSomething( VARIANT *pvArray );
I have tried calling this method using the following:
TRIAL #1
[DllImport("binary.dll")]
private static extern DoSomething( ref [] object vArray );
(...)
Object [] variant;
System.Collections.ArrayList vP = new System.Collections.ArrayList();
vP.Add( (ulong)12345 );
variant = vP.ToArray();
CAScc_CompleteWorkVariant(m_hAgent, "101", 0, ref variant, "");
TRIAL #2
[DllImport("binary.dll")]
private static extern DoSomething( ref object vArray );
(...)
Object variant;
System.Collections.ArrayList vP = new System.Collections.ArrayList();
vP.Add( (ulong)12345 );
variant = vP.ToArray();
CAScc_CompleteWorkVariant(m_hAgent, "101", 0, ref variant, "");
TRIAL #3
[DllImport("binary.dll")]
private static extern DoSomething( ref object vArray );
(...)
Object variant;
System.Collections.ArrayList vP = new System.Collections.ArrayList();
vP.Add( (ulong)12345 );
variant = new VariantWrapper(vP.ToArray());
CAScc_CompleteWorkVariant(m_hAgent, "101", 0, ref variant, "");
I have tried a bunch of other stuff; but I have ommitted them for brevity.
Everytime I debug the C++, by variant pointer is ALWAYS null.
Are there any comprehensive samples out there on how to get something like
this done?
Thanks,
James
Beverly, MA
> I need to consume an existing service in C++ using C#. Previous clients
> used VB6 arrays. To paraphrase, I have a method in C++:
>
> int __stdcall DoSomething( VARIANT *pvArray );
>
> I have tried calling this method using the following:
[...]
> I have tried a bunch of other stuff; but I have ommitted them for brevity.
> Everytime I debug the C++, by variant pointer is ALWAYS null.
I'm not sure what your VARIANT stores (a SAFEARRAY of VT_UI4 ?).
Moreover, is this VARIANT an input only parameter?
However, if you can't find the proper marshaling on the C# side, I would
suggest you to just write a C++/CLI bridging layer wrapper over your
original DoSomething().
e.g.
// in C++/CLI
int DoSomethingWrapper( array<UInt32>^ data )
{
... create a VARIANT to pass to DoSomething().
VARIANT var;
...
fill VARIANT with data stored in 'data' input variable
...
// Call your original DoSomething:
return DoSomething( &var );
}
You can call the DoSomethingWrapper() directly from C#, passing a simple C#
array to it.
HTH,
Giovanni
In answer to your question,
It is a Variant SAFEARRAY of VARIANT SAFEARRAY's.
The inner array has two elements. One a uint, the other either a
String, Date, our Double. With this info, could you come up with a direct
signature?
Let me know,
James
Beverly, MA
>I like your suggestion; but if it is not a managed DLL, would I need to do
> any .NET library initialization?
I don't understand this question well, i.e: I'm not sure I understand what
you mean by "do any .NET library initialization".
My suggestion is to build a C++/CLI class library that exposes methods to
wrap the native DLL exported functions:
Add New Project | Visual C++ | CLR | Class Library.
Then you can build the VARIANT in some method of the C++/CLI class, and pass
this VARIANT to the native DLL.
The C# client talks to the C++/CLI class directly (not to the native DLL;
the C++/CLI class talks to the native DLL).
> In answer to your question,
> It is a Variant SAFEARRAY of VARIANT SAFEARRAY's.
> The inner array has two elements. One a uint, the other either a
> String, Date, our Double. With this info, could you come up with a direct
> signature?
I think that if the native function uses a VARIANT * as parameter, the
corresponding C# signature should be 'ref object'.
There is also a class called VariantWrapper, but I'm not sure that it can
help you in this particular case:
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.variantwrapper.aspx
I still think that the simplest way would be to just use the C++/CLI
bridging technique, and build the VARIANT directly from C++/CLI.
HTH,
Giovanni
That said, my existing binary is not managed. Could I initialize the .NET
library dynamically in a non dotnet C++ dll? If so, how?
Thanks,
James
Beverly, MA
I've never done a DLL that exposes both unmanaged code and managed code at
its public interface, but I think it is possible.
Of course, building a simple test project on Visual Studio would answer
that. You may want to try Add New Project | Visual C++ | CLR | Class Library
template, and then expose also pure C native functions.
Or, if you have an existing native DLL, you may want to go to Project
Properties | Configuration Properties | General and select "Common Language
Runtime Support (/clr)" in "Common Language Runtime support" field, and then
add C++/CLI code, e.g.
namespace TestLibNative {
public ref class SomeWrapperClass
{
public:
void DoSomething()
{
...
}
};
}
HTH,
Giovanni
I think that if you try that, your dll will take a dependency on mscoree and
it won't run anymore except in a .NET program. Maybe you can work around
that by marking the mscoree dependency as delayload. Definitely avoid
compiling your native entrypoints with /clr, if you want native clients to
be able to call them (this requires putting your managed wrapper in a
separate .cpp file, then combining managed code with purely native object
files at the link step).
> customers/3rd parties; so I don't want to add the burden of deploying
> an extra binary.
>
> That said, my existing binary is not managed. Could I initialize the
> .NET library dynamically in a non dotnet C++ dll? If so, how?
You can't use .NET data types directly from native code, because the garbage
collector needs the metadata describing stack variables.