EnterBS

This is interface that must be implemented:
import "oaidl.idl" ;
[
  object,
  uuid(1F1217B0-DEE0-11d2-A5E5-000086339399),
  pointer_default(unique)
]
interface IComponentInterface : IUnknown
{
    HRESULT GetValues(
 [out]         DWORD *pdwCount,
 [out, size_is(,*pdwCount), string] LPWSTR **ppszCustomString,
 );
}
[
    uuid(1F1217BA-DEE0-11d2-A5E5-000086339399),
    version(1.0),
]
library Component
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");
   
    interface ComponentInterface ;
}

COM server must be implemented using C#:

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ComponentInterface
{
    void ComFunction(
        [Out, MarshalAs(UnmanagedType.U4)] out uint count,
        [Out, MarshalAs(UnmanagedType.LPWStr)] out IntPtr customString);
}


Next class implements this interface:

[ClassInterface(ClassInterfaceType.None)]
public class ComponentClass : ComponentInterface
{
    void ComFunction(out uint count, out IntPtr customString)
    {
        count = 55; // test value, nothing important
        // PLACE FOR CODE I AM ASKING FOR HELP
    }
}

This component must be COM visible, added to registry, added to GAC so it can work.

COM client uses this interface as proxy/stub generated by midl.
COM client is made in C++ and looks like this:

//code snippet
IComponentInterface* pComponent;

long hr = CoCreateInstance( clsid, NULL, CLSCTX_SERVER,
 IID_IComponentInterface, (void **) &pComponent);
if (SUCCEEDED(hr))
{
 IUnknown* pComponentUnk;
 DWORD* pCount;
 DWORD count = 0;
    pCount = &count;
    LPWSTR* pCustomString;
 
    HRESULT hrqi = pComponent->QueryInterface(IID_IComponentInterface, (void**) &pComponentUnk);
 HRESULT hrs = pComponent->GetValues(&count, &pCustomString);
}
//end code snippet

When i call QueryInterface, HRESULT is 0 and no problem there.
When i call GetValues, i get count = 55, as I put in .NET component and HRESULT is 0, but LPWSTR* does not hold any value I pass.

How this can be done
I tried using Marshal.StringTo... methods, which does not report errors, but also does not return value passes.


Thanks in advance.



Re: Common Language Runtime string as out parameter

nobugz

Tough function signature with LPWSTR**. Who allocates the buffer And who releases it





Re: Common Language Runtime string as out parameter

EnterBS

Well, technically speaking, that is what i ask.





Re: Common Language Runtime string as out parameter

EnterBS

I have managed to do next.
In C++ client i have done next:

Instead of LPWSTR* pCustomString i added this LPWSTR* pCustomString = new LPWSTR[1];
This is pointer to one member array of LPWSTR.

In C# i added next below count = 55;

customString = Marshal.StringToHGlobalAuto Method("teststring");

I have managed to pass values without exception.

Trouble now is that i need to pass array of strings, not just one string as pointer, since idl requires that.

Has anyone had something like this





Re: Common Language Runtime string as out parameter

EnterBS

Is there any way to allocate space from managed code on unmanaged memory and to write to that memory
I tried to do so, but i have no idea how i can do that. I think it would be solution to my problem.

Right now ,i managed to pass that array of strings to C++ client, by initializing LPWSTR* ppCustomString in C++ client, like this:
**ppCustomString = new LPWSTR[noOfStrings];

This is not good. Neither i do know how many strings will be returned, nor what is their maximum number.
But in this case, i must initialize LPWSTR* parameter and than pass it as parameter and than i will be able to see some results.

Right now, trouble is next.

How to allocate memory from managed to in unmanaged memory (i know there is Marshal.AllocHGlobal(size))

If i allocate memory, how to write into that memory

If i create C# string[], how to write that array in memory which is previoud allocated in unmanaged memory





Re: Common Language Runtime string as out parameter

Martin Xie - MSFT

Hi,

1. How to allocate memory from managed to in unmanaged memory

Yes, Use Marshal.AllocHGlobal().

2. If you allocate memory, how to write into that memory

Use the following methods of the Marshal class:

WriteByte

WriteInt16

WriteInt32

WriteInt64

WriteIntPtr

3. If you create C# string[], how to write that array in memory which is previous allocated in unmanaged memory

Follow suggestions:

1) you can convert the strings to byte arrays and then write the bytes.

2) You can pass directly string parameters into the function calls, instead of using WriteIntPtr.

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

public interface ComponentInterface

{

void ComFunction(

[Out, MarshalAs(UnmanagedType.U4)] out int count,

[Out, MarshalAs(UnmanagedType.LPWStr)] out string customString);

}

[ClassInterface(ClassInterfaceType.None)]

public class ComponentClass : ComponentInterface

{

public void ComFunction(out int count, out string customString)

{

count = 55; // test value, nothing important

// PLACE FOR CODE I AM ASKING FOR HELP

customString = "Hello";

}

}

3) You also can deal string array with pointer directly.

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

public interface ComponentInterface

{

void ComFunction(

[Out, MarshalAs(UnmanagedType.U4)] out int count,

[Out, MarshalAs(UnmanagedType.LPWStr)] out string customString,

[Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out string[] strings);

}

[ClassInterface(ClassInterfaceType.None)]

public class ComponentClass : ComponentInterface

{

public void ComFunction(out int count, out string customString, out string[] strings)

{

count = 55; // test value, nothing important

// PLACE FOR CODE I AM ASKING FOR HELP

customString = "Hello";

strings = new string[] { "Hello", "World" };

}

}






Re: Common Language Runtime string as out parameter

EnterBS

Thanks, i will try this and return with answers.



Re: Common Language Runtime string as out parameter

EnterBS

Hi,
i tried what was told me but i could not manage to pass that string array without allocating it in C++ client.

So, i tried this:
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

public interface ComponentInterface

{

void ComFunction(

[Out, MarshalAs(UnmanagedType.U4)] out int count,

[Out, MarshalAs(UnmanagedType.LPArray)] out string[] ppCustomString);

}

[ClassInterface(ClassInterfaceType.None)]

public class ComponentClass : ComponentInterface

{

public void ComFunction(out int count, out string customString, out string[] ppCustomString)

{

count = 55;

ppCustomString = new string[] { "Hello", "World" };

}

}

Same thing, but for some reason this works and not if it is safe array.

I also tried to write to previously allocated memory, but also could not do this, for some reason i received HRESULT i never seen before (0x 8013....)

I thank on your help, Martin, and i will mark both this and your post as answers, cause i believe that both are correct, i am sure i missed something when tried your method.





Re: Common Language Runtime string as out parameter

Martin Xie - MSFT

Thank you EnterBS for sharing your experience with us!