将C#结构体暴露给COM时,VB6应用程序出现故障

4

最近更新:2009年8月11日下午2:30

几天前,我发布了一个关于一些非常奇怪问题的问题。好吧,我找出了具体导致在某些机器上无法运行构建甚至想出了解决方法,但现在留下了一个很好的、具体的问题:为什么?

为了重现这个问题,我创建了一个新的InteropUserControl并执行以下操作:

  1. 添加一个新的public struct MyStruct
  2. 给它一个GUID和ComVisible属性
  3. InteropUserControl中添加一个GetMyStruct成员并实现它。

MyStruct

[Guid("49E803EC-BED9-4a08-B42B-E0499864A169")]
[ComVisible(true)]
public struct MyStruct {
    public int mynumber;
}

_InteropUserControl.GetMyStruct():

[DispId(7)]
void getMyStruct( int num, ref MyStruct data );

我已经尝试过返回 MyStruct 而不是通过引用传递,也实现了 InteropUserControl.GetMyStruct() 方法:
public void getMyStruct( int num, ref MyStruct data ) {
    data = new MyStruct();
    data.mynumber = num * 2;
}

我还将程序集签名并安装到GAC,并使用Regasm进行注册。将其添加到新的VB6项目并调用GetMyStruct(),然后在我们的构建机上编译后,它拒绝在其他机器上运行。
为了解决这个问题,我不得不将结构体暴露给COM而不是类,并且基本上将GetMyStruct更改为以下内容:
public void GetMyData( int num, MyClass data ) {
    data.mynumber = num * 2;
}

在我的实际项目中,我从内部检索结构体,然后将所有字段值从结构体复制到由客户端传递给方法的类实例的匹配成员上。
那么为什么结构体会导致这种行为,而类却可以正常工作?暴露结构体供VB6使用有什么神奇的地方吗?
我认为这可能与OLE自动化有关。
注意:我还尝试返回结构体而不是使用ref参数,但这并没有改变行为。
编辑以添加项目模板链接: Interop Forms Toolkit 2.0是原始的VB.NET项目模板和dll。我没有引用该dll,因此您可能不需要安装它。

CodeProject上的C#模板翻译是我用来创建我的项目模板(而不是项模板)的。VB.NET版本会自动生成__InteropUserControl事件接口、_InteropUserControl接口和一些相关属性,而这些在C#版本中需要显式编码,这就是两者之间唯一的区别。


1
在 getMyStruct 函数中,ms 是什么?它应该改为 data 吗? - SwDevMan81
1
你能提供一个简短但是完整的复现吗? - AnthonyWJones
@AnthonyWJones:如果我们可以上传源文件和项目的压缩包,那就太好了,这样我们就可以提供完整的场景重现,并避免过度依赖 SO 外部资源的想法。 - Joel B Fant
1
在代码顶部,public class MyStruct 应该改为 public struct MyStruct,是吗?我不是想表现得很刻薄,只是想确认一下我有正确的东西。 - SwDevMan81
没错,SwDevMan81。我肯定是复制了错误的测试版本(将其改为类后它就能正常工作)。 - Joel B Fant
显示剩余3条评论
2个回答

2
我认为我找到了解决这个问题的方法。我遇到了完全相同的问题,当通过传递一个结构体调用Interop库的方法时,vb6会出错。这是我为测试DLL互操作性创建的一个项目,所以我在项目中只有一个表单。但我有另一个项目(主应用程序),它具有相同的引用并且可以正常工作。
在阅读Joel的帖子后,我想测试他的解决方案,实际上它确实起作用了(使用类代替结构体)。但是我有其他的Interop,我正在使用结构体,所以我非常担心我的应用程序可能会在任何时候失败。此外,我不想做额外的工作来创建和公开接口和类来替换结构体。
因此,我将代码从我的表单中移动到模块中的公共子程序中。它立即起作用了。顺便说一下,这就是我在主应用程序中实现调用的方式,它可以正常工作。
我希望这可能会帮助其他人。

有趣。我得再试一次设置这样一个测试项目,并从VB6中的一个模块进行调用。不幸的是,这对我的应用程序没有帮助,因为主要的互操作对象是一个必须驻留在表单上(可视化显示)的控件。但是,这些信息非常有意思。 - Joel B Fant
我决定继续并将您的答案标记为已接受。在我的情况下,将调用代码移动到模块中是不可能的,但这似乎是两个解决方案中的一个。 - Joel B Fant

1
有没有什么方法可以将一个结构体暴露给COM以在VB6中使用?
MSDN上的文章COM数据类型*说结构体是被支持的。具体来说,MSDN文章指出COM结构体定义为:
ByRef VALUETYPE< MyStruct >

页面底部还有一些关于自定义COM可调用包装器的文章,您可能希望查阅。

  • 编辑(2016年):原链接已失效,我将其修复为.NET Framework 3.5的版本。

+1:我很感激您的尝试。我知道它们应该是有效的(并且在某种程度上确实如此)。我还尝试过许多自定义,特别是使用 MarshalAsAttribute,但没有一项改变了我所描述的行为。 - Joel B Fant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接