What happens between the .NET interop layer and COM?

3

I am using COM in my C# .NET project.
However one of the methods I call is not acting as expected.
So I am curious to see what is happening between my .NET code, the Interop layer and COM.
I know the tlbimp.exe generates the metadata wrapper for the COM component and I can see these generated methods in the Object browser.
Am I able to see/debug what happens when one of these wrapper methods is called?

I pass an Array to the method below, and expect that this array will be populated, however the Array does not get populated. I am calling the following tlbimp.exe generated method with unexpected results:

int GetTags(System.Array buffer)
    Member of CServer.IUser

Method IDL:

[id(0x000000d5)]
HRESULT GetTags(
                [in] SAFEARRAY(long) buffer, 
                [out, retval] long* retval);  

.NET code calling this method:

Array tagsArray = Array.CreateInstance(typeof(int), tagsLength);
userWrapper.GetTags(tagsArray);

Other COM methods I call work fine. However when I call any method which expects an Array as a parameter it does not work as expected.
I am presuming that there is something funny going in with the COM interop marshaller.
So I would like to know if I can see what is happening after I call the GetTags() method.

Also I have read the following here.

"if you are not satisified with the COM Interop marshaller, you can "override" just about every aspect of it through the very large and useful System::Runtime::InteropServices namespace"

How can I achieve the above?

EDIT: Adding a Delphi test script which works

procedure TComTestForm.TestUserBtnClick(Sender: TObject);
var
  nCnt :integer;
  User :IUser;
  Persona :IUserPersona;
  ArrayBounds :TSafeArrayBound;
  ArrayData :Pointer;
  TagList :PSafeArray;
  nSize :integer;
begin
  User := Session.GetUser;

  ArrayBounds.lLbound   := 0;
  ArrayBounds.cElements := 0;

  TagList := SafeArrayCreate( varInteger, 1, ArrayBounds );
  User.GetTags( TagList );
  if SafeArrayAccessData( TagList, ArrayData ) = S_OK then
    begin
      nSize := TagList.rgsabound[0].cElements;
      OutLine( '----Available Tags, ' + IntToStr(nSize) + ' tags' );
  for nCnt := 0 to nSize - 1 do
    begin
      OutLine( IntToStr( IntegerArray(ArrayData)[nCnt] ) );
    end;
  OutLine( '----');

  SafeArrayUnAccessData( TagList );
  SafeArrayDestroy( TagList );
    end;

end;
c#
.net
com
com-interop
tlbimp
asked on Stack Overflow May 9, 2011 by shane87 • edited May 9, 2011 by shane87

2 Answers

2

Another update: I just realize it might be that you mean that the GetTags itself should populate that Array (from the COM code). But this can never work as that parameter is an [in] parameter.

For the COM component to be able to fill up that Array, It should be passed as an [in, out] parameter, and by reference (SAFEARRAY*).


Update: Ok, apparently I was mixing the creation of a COM component in .NET with calling a COM component from .NET.

The CCW (com callable wrapper) indeed takes a .NET Array for COM SafeArray's. I see you create your array in the code in your question, but you don't show how you actually populate it. Maybe something's wrong with that code? Could you share it?


Not sure if this is a solution to your problem, but I've experienced problems with COM-interop and SAFEARRAY's in the past.

One thing I learned from it is that the .NET equivalent of a COM SAFEARRAY should always be object, so try passing your array as an object in stead of as an Array.

answered on Stack Overflow May 9, 2011 by fretje • edited May 9, 2011 by fretje
1

I hesitate to suggest this as an answer, but...

If the Delphi test code really does work, as noted elsewhere, this means the GetTags method must not be playing properly by SAFEARRAY rules. If the COM method is always called in-process, you MAY be able to get the .NET code to work by doing some custom marshalling "by hand", following exactly what the unmanaged Delphi test code does.

As a rough outline, I would imagine that this would involve:

  • allocating an unmanaged buffer to hold the array values
  • calling Ole Automation SAFEARRAY initialization APIs via P/Invoke to allocate a SAFEARRAY structure and attach the array buffer to it as its pData member
  • invoking the GetTags method with this SAFEARRAY
  • marshalling your unamanaged buffer into a managed array, before...
  • calling the Win32 API to destroy the SAFEARRAY

But much better to get the COM component changed to do things properly, if you can.

answered on Stack Overflow May 9, 2011 by Chris Dickson

User contributions licensed under CC BY-SA 3.0