在.NET中释放COM对象的"所有权"

6
我有一个客户/合作伙伴,他们试图使用我们暴露的COM功能将其应用程序与我们的应用程序进行链接。到目前为止,他们已经有了一个代表我们软件包实例的COM对象,然后使用我们的COM方法根据他们在应用程序中所做的内容来自动构建用户界面。实质上是一个“导出”功能。
他们要求我的是,我不知道如何做到让用户决定何时关闭该实例。我的意思是,当我们的软件包加载时,用户可以查看它,并与之交互。当他们完成操作后,他们会自然地单击右上角的交叉按钮以退出软件。但这并不起作用,因为COM对象仍在他们的应用程序中“活跃”。只有通过在任务管理器中结束进程才能关闭我们的软件包,而加载它的应用程序仍然保持开启状态。由于COM调用,似乎他们的应用程序“拥有”我们的应用程序。
我已经在C#中制作了一个快速的演示应用程序,尝试使用一些东西,例如Marshal.FinalReleaseComObject(myObject),但没有成功。
我意识到,对于这种情况,使用COM并不是其预期用途,但希望有某种解决方法?客户/合作伙伴正在使用VB.NET,但C#也可以。

请澄清一下您的应用程序是从内部自动化(类似于在VS或Office应用程序中使用VBA脚本)还是从外部自动化(类似于使用Word自动化从您自己的控制台应用程序/脚本创建新文档)? - Alexei Levenkov
他们不能关闭窗口?这没有意义,你需要澄清。 - Hans Passant
这是外部的操作,他们使用VB通过引用我们的exe并从中实例化COM对象来启动一个新进程。我们有一个“打开”功能,它做的就是打开我们软件包的可用窗口。关于无法关闭窗口的问题,你不能点击右上角的红色X,即使你点击了也没有反应。只有当他们的VB应用程序关闭时,它才会关闭。我对COM互操作不是很了解,但似乎VB应用程序拥有该进程或是其父进程。 - sxthomson
听起来他们的应用程序有问题。他们应该在使用完您的对象后立即处理它。可能您的应用程序应该通过处理“Form.Closing Event”并设置一些状态来通知他们关闭了。 - Joshua Drake
你能更精确地解释一下应用程序之间的交互吗?当我在.NET中创建一个作为COM使用的东西(注册为COM互操作),它是一个.dll文件(以及我从VB6使用的.tlb文件)。它在调用它的进程中运行。但是你提到在任务管理器中停止。这个应用程序是否启动了一个新进程?难道没有漏掉一些process.close吗? - IvanH
1个回答

3
您在关于您自己的应用程序方面留下了一些关键信息,最重要的是您如何实现向客户端应用程序公开的COM接口。您描述中的一个问题是我注意到的红旗是实例化您的应用程序。您说客户端应用程序是“使用VB通过引用我们的exe启动新进程”。如果您已经正确地实现和注册了COM服务器,则不需要这样做。让我告诉您我将用来完成您要求的架构,希望这对您有用。
首先,如果您想从单独的进程中提供COM对象,则正确的方法是实现COM外部进程服务器。使用外部进程服务器时,当客户端通过COM请求您的接口之一时,COM会自动启动您的应用程序。外部进程服务器的实现要求之一是在最后一个COM客户端释放其最后一个接口指针时自动关闭。
为了从外部进程服务器公开用户界面,您需要使用具有消息循环的单线程公寓(STA)线程。这将允许您关闭STA线程上的任何窗口,包括主窗口,而不会杀死您的COM服务器。这是因为COM服务器实现将运行自己的多线程公寓(MTA)消息循环线程以支持COM外部过程调用。 MTA线程是主应用程序线程,并且当最后一个客户端接口被释放时,服务器将关闭它。
COM服务器不应该在接口仍未释放的情况下关闭。这意味着客户端应用程序有责任正确释放接口。但是您的.NET测试框架应该已经做到了这一点,因此似乎您的实现存在问题。
假设您遵循此指南,您将需要处理以下情况的方案:最后一个客户端接口被释放,但您仍然拥有打开的UI窗口。一旦您正确设置了所有内容,这不应该是一个主要问题。例如,您可以在UI打开时简单地引用自己的一个接口,这将防止MTA关闭。

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