views:

384

answers:

3

I have a COM interface with a following method definition (IDL notation):

SCODE GetText( [in, out] ULONG* pcwcBuffer,
              [out, size_is(*pcwcBuffer)] WCHAR* awcBuffer );

Typelib marshaling is used for COM+, the type library is registered, other methods of the interface work allright when called through COM+, but not this method.

The server side copies an array of WCHARs into the awcBuffer and its length into pwcBuffer, no buffer overrun ever occurs.

static const wchar_t* Text = L"Sample";
STDMETHODIMP CImpl::GetText( ULONG* bufferLength, WCHAR* buffer )
{
    const int length = wcslen( Text );
    *bufferLength = length;
    memcpy( buffer, Text, length * sizeof( WCHAR ) );
    return S_OK;
}

When the client calls this method through COM+ the buffer contents gets lost. Specifically only the first wide char is preserved - if the server copies "Sample" wide character string, the client only receives "S" string. The return value on the client size is S_OK, the buffer length returned to the client is exactly the same as what the server copied.

I finally switched to BSTR to workaround this problem but it's really interesting why the whole valid looking construct doesn't work.

What's the possible reason of the described behaviour?

A: 

Couple of questions:

  • Why aren't you using BSTR?
  • Do you have the sources of the GetText function?
  • What is the size of the buffer returned by the function?
arul
Now I'm using BSTR, but it's interesting why the original version doesn't work. Updated the question to clarify on all points you asked.
sharptooth
+2  A: 

IIRC, the typelib marshaller ignores the size_is attribute -- thus, only 1 char is marshaled.

Johannes Passing
+2  A: 

J. Passing is right. For the typelib marshaller to work, the COM interface must be OLE Automation compatible. The typelib marshaller is implemented in oleaut32.dll, so I guess there's a clue in the name.

[size_is] is perfectly valid IDL, and compiles into a valid typelib, but the typelib marshaler can only handle a subset of valid interfaces. That subset is usually referred to as OLE Automation. As an aside, VB6 clients can only speak OLE Automation, so they wouldn't be able to consume your interface either.

Try marking your interface with the [oleautomation] attribute in your IDL. It should give you a warning or error message that might point you to more information on the subject.

In "normal" COM, you could generate a proxy/stub DLL from your IDL to do the marshalling, but I'm afraid I don't remember whether COM+ would use your custom marshalling code even if you bothered to build it.

Update: in Juval Lowy's book "COM and .NET Component Services", I found this statement: "...configured components cannot use interfaces that require custom marshaling". So I guess that interface will never work in COM+. If you can, re-write to use a BSTR instead.

Martin
The interface has both "dual" and "oleautomation" attributes, but MIDL doesn't emit any warnings.
sharptooth
You're right. I just tried it too, and it doesn't warn. Sorry fo having too much faith in MIDL ;-) I'm still confident that the universal marshaler can't do [size_is] though.
Martin
Concerning the quote: While it is true that custom marshaling cannot be used for COM+, you may of course use a proxy/stub DLL generated from the IDL for COM+. This is a common misconception, by the way.
Johannes Passing