3

我在我的 C# .NET 项目中使用 COM。
但是,我调用的其中一种方法没有按预期运行。
所以我很想知道我的 .NET 代码、互操作层和 COM 之间发生了什么。
我知道tlbimp.exe为 COM 组件生成元数据包装器,我可以在对象浏览器中看到这些生成的方法。
当调用这些包装器方法之一时,我是否能够查看/调试会发生什么?

我将一个数组传递给下面的方法,并期望这个数组会被填充,但是数组不会被填充。我正在调用以下tlbimp.exe生成的方法,结果出乎意料:

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

方法 IDL:

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

.NET 代码调用此方法:

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

我称之为的其他 COM 方法工作正常。但是,当我调用任何需要 Array 作为参数的方法时,它不会按预期工作。
我假设 COM 互操作编组器有一些有趣的地方。
所以我想知道我打电话后是否能看到发生了什么GetTags()方法后是否可以看到发生了什么。

我也在这里阅读了以下内容。

"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"

我怎样才能实现上述目标?

编辑:添加一个有效的 Delphi 测试脚本

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;
4

2 回答 2

2

另一个更新:我只是意识到这可能是您的意思是它GetTags本身应该填充该数组(来自 COM 代码)。但这永远不会起作用,因为该参数是一个[in]参数。

为了使 COM 组件能够填充该数组,它应该作为 [in, out] 参数并通过引用 (SAFEARRAY*) 传递。


更新:好的,显然我将在.NET 中创建COM 组件与从.NET 调用COM 组件混合在一起。

CCW(com callable wrapper)确实为 COM SafeArray 提供了一个 .NET Array。我看到您在问题的代码中创建了数组,但没有显示实际填充它的方式。也许该代码有问题?你能分享一下吗?


不确定这是否可以解决您的问题,但我过去曾遇到过 COM-interop 和 SAFEARRAY 的问题。

我从中学到的一件事是 COM SAFEARRAY 的 .NET 等效项应该始终是 .NET object,因此请尝试将数组作为 anobject而不是Array.

于 2011-05-09T09:48:41.547 回答
1

我犹豫是否建议将此作为答案,但是...

如果 Delphi 测试代码确实有效,正如其他地方所指出的,这意味着 GetTags 方法一定不能按照 SAFEARRAY 规则正常运行。如果 COM 方法总是在进程内调用,您可以通过“手动”执行一些自定义编组来使 .NET 代码工作,完全按照非托管 Delphi 测试代码的操作。

作为一个粗略的大纲,我想这将涉及:

  • 分配一个非托管缓冲区来保存数组值
  • 通过 P/Invoke 调用 Ole Automation SAFEARRAY 初始化 API 以分配 SAFEARRAY 结构并将数组缓冲区作为其 pData 成员附加到它
  • 使用此 SAFEARRAY 调用 GetTags 方法
  • 在...之前将您的非托管缓冲区编组到托管数组中
  • 调用 Win32 API 来销毁 SAFEARRAY

但是,如果可以的话,最好将 COM 组件更改为正确执行操作。

于 2011-05-09T11:53:07.593 回答