.NET 互操作层和 COM 之间发生的情况

本文关键字:情况 之间 COM 互操作 操作层 NET | 更新日期: 2023-09-27 17:55:48

我在我的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方法工作正常。但是,当我调用任何期望将数组作为参数的方法时,它无法按预期工作。
我假设 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"

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

编辑:添加一个德尔福测试脚本,该脚本有效

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;

.NET 互操作层和 COM 之间发生的情况

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

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


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

CCW(com可调用包装器)确实为COM SafeArray采用.NET数组。我看到您在问题中的代码中创建数组,但您没有显示实际填充它的方式。也许该代码有问题?你能分享一下吗?


不确定这是否是您问题的解决方案,但我过去遇到过 COM 互操作和 SAFEARRAY 的问题。

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

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

如果 Delphi 测试代码确实有效(如其他地方所述),这意味着 GetTags 方法不能按照 SAFEARRAY 规则正确播放。如果始终在进程中调用 COM 方法,则可以通过"手动"执行一些自定义封送来使 .NET 代码正常工作,完全遵循非托管 Delphi 测试代码的功能。

作为一个粗略的概述,我想这将涉及:

  • 分配非托管缓冲区以保存数组值
  • 通过 P/Invoke 调用 Ole Automation SAFEARRAY 初始化 API 来分配 SAFEARRAY 结构并将数组缓冲区作为其 pData 成员附加到该结构
  • 使用此安全数组调用 GetTags 方法
  • 将非托管缓冲区封送到托管阵列中,之前...
  • 调用 Win32 API 销毁安全阵列

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

相关文章: