【问题标题】:What happens between the .NET interop layer and COM?.NET 互操作层和 COM 之间会发生什么?
【发布时间】:2011-08-21 13:25:58
【问题描述】:

我在我的 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() 方法后是否可以看到发生了什么。

我还阅读了以下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"

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

编辑:添加一个有效的 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;

【问题讨论】:

  • 您遇到了什么错误?什么不是“按预期工作”?
  • @Simon Mourier:感谢您的回复。我没有收到任何错误。该方法执行得很好,但数组没有被填充。 COM 代码已经用 delphi 测试过并且工作正常,所以这让我相信在 com-interop 层中发生了一些事情?
  • 我想如果你希望能够在 COM 层填充数组,你的 COM 原型应该是不同的。您需要通过引用来定义 SAFEARRAY 或将 [in, out] 添加到 .IDL 我想。 .NET Marshaler(与任何其他 marshaler BTW 一样)认为这只是一种单向调用。
  • @Simon:再次感谢您的回复。我将如何通过引用定义 SAFEARRAY?另外,如果我需要更改 IDL,这是否意味着重新创建 COM dll?

标签: c# .net com com-interop tlbimp


【解决方案1】:

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

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


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

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


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

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

【讨论】:

  • GetTags() 方法不允许我将对象传递给它,因为它需要一个 System.Array?
  • @shane:是的,COM dll的接口有问题(你确定Delphi的家伙测试了同样的方法吗?关于*:这取决于COM的语言组件已实现。我熟悉c ++,“按值”和“按引用”的概念与C#中完全不同。在c ++中,我主要使用[out, retval, satype(BYTE)] SAFEARRAY** p_Buffer。所以在这种情况下它甚至需要2 *s 工作。我认为这是因为它也是一个 [retval],所以在你的情况下,如果它只是一个 [out] 值,我认为一个就足够了。
  • @shane:刚刚从您的上一个问题 (stackoverflow.com/questions/5910061/…) 开始处理这个新问题... fretje 说的很对,GetTags 原则上不能像您期望的那样工作。 * 表示指针:指向 SAFEARRAY 的指针在堆栈上传递给方法,而不是 SAFEARRAY 结构本身。由于您的 GetTags 方法不是这样编写的,因此您无法更改签名以使其通过引用接受 SAFEARRAY。
  • @fretje/Chris Dickson:非常感谢您对此问题的调查。我将与用 delphi 编写的 COM dll 的开发人员取得联系,并要求他对其进行修改,使其成为一个输出参数并通过引用。如果一切正常,我会在这里发布。再次感谢。
  • @shane/fretje:只是为了解决 *** 的问题:如果客户端创建 SAFEARRAY,您需要一个 * - 指向 SAFEARRAY 的指针在堆栈上传递。如果该方法创建了 SAFEARRAY(即 [out] 方法),那么您需要两个 ** - 客户端必须在堆栈上传递一个指针,该指针指向该方法可以将指针的值写入 SAFEARRAY 的位置。跨度>
【解决方案2】:

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

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

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

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

如果可以的话,最好将 COM 组件更改为正常工作。

【讨论】:

  • 我正在等待与 delphi 开发人员交谈并尝试更改 COM 组件。然而,当我上次发言时,他说我需要创建 SAFEARRAY 并将其传递给他的函数,因为他的代码不处理创建/销毁 SAFEARRAY。因为他是这样写的。我已经添加了 delphi 测试脚本(在我关于这个问题的问题中),他用它测试它并说它运行良好。
猜你喜欢
  • 1970-01-01
  • 2010-12-24
  • 1970-01-01
  • 2012-03-31
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-11
相关资源
最近更新 更多