开发者

Invoke C++ method from VB.Net with string handled as string array

开发者 https://www.devze.com 2023-01-25 18:47 出处:网络
I have a C++ method with this signatur开发者_如何学Goe: STDMETHODIMP ClassName::FunctionName(long number, BSTR* names, long* status)

I have a C++ method with this signatur开发者_如何学Goe:

STDMETHODIMP ClassName::FunctionName(long number, BSTR* names, long* status)

Inside the method the names variable is accessed as an array of strings, i.e.

char *  tempString = NULL;

for (int n = 0; n < number; n++)
{
    tempString = OLE2T(names[n]);
...

I compile the C++ project, which generates a dll, I then register this dll and add a reference to it in the VB project. When I add the reference, an Interop assembly is automatically generated, and the signature of the method in the Interop assembly is as follows:

FunctionName (number as Integer, ByRef names as String) As Integer

From VB.Net I invoke the method like this:

result = FunctionName (number, names(0))

Where names is a string array with multiple elements, and number and result are Integers.

The problem is that when the C++ code tries to access the rest of the elements in the names array (names[1] and ahead) it starts getting "garbage" on those fields.

My question is, how do I send the whole string array instead of just the first value.

The C++ code is a library that I cannot modify, therefore any change that I do must be on the VB.Net code.

I was thinking that maybe using PInvoke to call the method might do the trick (declaring a correct signature), but I was hoping for something better.

Any ideas?

Thanks!

Edit:

I'm no expert in Interop/Marshaling but I checked the IDL definition of the method and it is as follows:

[id(60), helpstring("method FunctionName")]
        HRESULT FunctionName(
                    [in] long number, 
                    [in, size_is(number)] BSTR* names,
                    [out, retval] long* status);

Shouldn't the size_is indicate that the names parameter is an array, and thus, when the Interop assembly is generated act accordingly?

Thanks again


Nope, [size_is] is an attribute that only midl.exe knows how to use. It does so when it generates the proxy/stub for the interface, something that's used when you want to make the call across process boundaries.

It isn't otherwise expressible in a type library. The argument gets emitted as "pointer to BSTR" which could either indicated a BSTR passed by reference or an array of BSTRs. Tlbimp.exe can't tell the difference, it picks the former. It has to, it cannot reasonably infer the array size. You get junk at runtime because the CLR interop layer only passes a single element of the array. Any COM client has a big problem using this method, it isn't .NET specific. The .NET pinvoke marshaller has a workaround for this problem, note the SizeParamIndex property of the [MarshalAs] attribute.

If you cannot modify the C++ code then you'll need to edit the interop library by hand to inject that [MarshalAs] attribute. That's quite fugly, you have to decompile the library with ildasm.exe, edit the .il and put it back together with ilasm.exe. You don't want to do that often.

If you can then you should use the Automation compatible way to pass arrays. Use a SAFEARRAY. This is fully supported by type libraries and the CLR interop plumbing, no work on your end is needed on the managed side. Also note that you now no longer need the number argument, safe arrays know how long they are. Not unlike managed arrays.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号