VS2008,.NET 2,VB.NET,XP ......
我有一个Windows窗体,有一个WebBrowser控件和一个关闭按钮,它只是一个Me.Close.表单的取消按钮设置为关闭按钮,以便我可以按ESC键关闭表单.
我DocumentText在load事件中设置WebBrowser控件的属性,并显示HTML.
从Visual Studio运行应用程序,如果单击"关闭"按钮,表单将关闭,不会出现任何错误.
如果我按下ESC按钮,我会得到
检测到RaceOnRCWCleanup消息:已尝试释放正在使用的RCW.RCW正在活动线程或另一个线程上使用.尝试释放正在使用的RCW可能会导致损坏或数据丢失.
如果我在VS外面运行应用程序,我没有错误.
任何想法a)为什么错误,和b)如何预防或抑制它?
提前谢谢了.
我有一个使用Office互操作程序集的应用程序.我知道运行时管理的"运行时可调用包装器(RCW)".但我不太确定引用计数如何增加.MSDN说,
RCW只保留对包装的COM对象的一个引用,而不管调用它的受管客户端的数量.
如果我理解正确,在以下示例中,
using Microsoft.Office.Interop.Word;
static void Foo(Application wrd)
{
/* .... */
}
static void Main(string[] args)
{
var wrd = new Application();
Foo(wrd);
/* .... */
}
Run Code Online (Sandbox Code Playgroud)
我将实例传递wrd给另一个方法.但这不会增加内部引用计数.所以我想知道引用计数增加的场景是什么?任何人都可以指出引用计数增加的情况吗?
另外我读了一些博客,说在使用COM对象编程时避免使用双点.像,wrd.ActiveDocument.ActiveWindow.作者声称编译器创建单独的变量来保存将增加引用计数器的值.恕我直言,这是错误的,第一个例子证明了这一点.那是对的吗?
任何帮助都会很棒!
执行以下两个测试用例后,将向控制台打印COM执行.我究竟做错了什么?
如果我单独运行任一测试,或者如果我同时运行两个测试,则会将异常写入控制台一次.这让我怀疑是否存在某种我不会清理的每AppDomain资源.
我已尝试使用NUnit和MSTest进行测试,在两种环境中都具有相同的行为.(实际上,我不确定在MSTest中运行这两个测试是否会导致一个或两个异常打印输出.)
例外:
System.Runtime.InteropServices.InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.
at System.Windows.Input.TextServicesContext.StopTransitoryExtension()
at System.Windows.Input.TextServicesContext.Uninitialize(Boolean appDomainShutdown)
at System.Windows.Input.TextServicesContext.TextServicesContextShutDownListener.OnShutDown(Object target)
at MS.Internal.ShutDownListener.HandleShutDown(Object sender, EventArgs e)
Run Code Online (Sandbox Code Playgroud)
测试代码:
using NUnit.Framework;
namespace TaskdockSidebarTests.Client
{
[TestFixture, RequiresSTA]
public class ElementHostRCWError
{
[Test]
public void WinForms()
{
var form = new System.Windows.Forms.Form();
var elementHost = new System.Windows.Forms.Integration.ElementHost();
form.Controls.Add(elementHost);
// If the form is not shown, the exception is not printed.
form.Show();
// These lines are optional. The exception is …Run Code Online (Sandbox Code Playgroud) 使用此代码,我打开 excel(使用visible = false,以便用户看不到它),写入工作簿,然后在脚本结束后打开 excel(使其可见)或完全关闭它而不保存。当我保存 Excel、使其保持打开状态、结束脚本,然后稍后手动关闭 Excel 时,任务管理器中没有后台进程。但是,当我使用脚本关闭 Excel 时,它仍保留在任务管理器中。
以下是我开始使用 Excel 的方法:
$script:excel = new-object -ComObject excel.application # create excel object
$excel.visible = $false # hide excel window
$script:workbook = $excel.Workbooks.Add() # add excel file
$script:ws1 = $workbook.Worksheets.Item(1) # create new sheet
Run Code Online (Sandbox Code Playgroud)
这是我关闭它的方法:
[gc]::Collect()
[gc]::WaitForPendingFinalizers()
if ($script:closeOnX) {
#only do this if not keeping excel open
Write-Host "Closing Excel"
$excel.Quit()
}
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel)
Run Code Online (Sandbox Code Playgroud)
closeOnX 只是一个标志,因此它仅在某些情况下实际关闭 Excel 应用程序。其余部分在每次脚本结束时执行。
当我结束脚本并同时关闭 Excel 时,我只想关闭当前的 Excel 进程(这就是我不想停止进程的原因),而不关闭用户可能正在处理的其他工作簿。
当我结束脚本、保存并打开 Excel 时,我希望当用户手动关闭 Excel 时所有进程都消失。(这是工作)
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestDemo
{
[TestClass]
public class UnitTest1
{
private static Microsoft.Office.Interop.PowerPoint.ApplicationClass
g_app = new Microsoft.Office.Interop.PowerPoint.ApplicationClass();
private TestContext testContextInstance;
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
[TestMethod]
public void Test01()
{
g_app.Visible = Microsoft.Office.Core.MsoTriState.msoCTrue;
}
[TestMethod]
public void Test02()
{
g_app.Visible = Microsoft.Office.Core.MsoTriState.msoCTrue;
}
}
}
Run Code Online (Sandbox Code Playgroud)
Test method TestDemo.UnitTest1.Test02 threw …Run Code Online (Sandbox Code Playgroud) 判断COM对象的引用计数是否达到0的一种方法是尝试访问其中一个成员并捕获生成的InvalidComObjectException,这不是很优雅,并且似乎不能很好地适应.另一种方法是调用Marshal.ReleaseComObject并检查结果,但这需要将Com对象的引用计数减少1.
有简单明了的方法吗?
我正在使用COM interop在使用VS2012/.NET 4.5/Win8.1的非托管应用程序中创建托管插件.所有的互操作似乎都没问题,但是当我关闭应用程序时,我得到了一个MDA异常,告诉我在发布完成期间RCW持有的COM对象时发生了AV.
这是调用堆栈:
clr.dll!MdaReportAvOnComRelease::ReportHandledException() + 0x91 bytes
clr.dll!**SafeRelease_OnException**() + 0x55 bytes
clr.dll!SafeReleasePreemp() + 0x312d5f bytes
clr.dll!RCW::ReleaseAllInterfaces() + 0xf3 bytes
clr.dll!RCW::ReleaseAllInterfacesCallBack() + 0x4f bytes
clr.dll!RCW::Cleanup() + 0x24 bytes
clr.dll!RCWCleanupList::ReleaseRCWListRaw() + 0x16 bytes
clr.dll!RCWCleanupList::ReleaseRCWListInCorrectCtx() + 0x9c bytes
clr.dll!RCWCleanupList::CleanupAllWrappers() + 0x2cd1b6 bytes
clr.dll!RCWCache::ReleaseWrappersWorker() + 0x277 bytes
clr.dll!AppDomain::ReleaseRCWs() + 0x120cb2 bytes
clr.dll!ReleaseRCWsInCaches() + 0x3f bytes
clr.dll!InnerCoEEShutDownCOM() + 0x46 bytes
clr.dll!WKS::GCHeap::**FinalizerThreadStart**() + 0x229 bytes
clr.dll!Thread::intermediateThreadProc() + 0x76 bytes
kernel32.dll!BaseThreadInitThunk() + 0xd bytes
ntdll.dll!RtlUserThreadStart() + 0x1d bytes
Run Code Online (Sandbox Code Playgroud)
我的猜测是应用程序已经销毁了它的COM对象,其中一些引用被传递给托管插件 - 而对RCW的IUnknown :: Release的调用让它变得繁荣.
我可以在输出窗口(VS)中清楚地看到应用程序已经开始卸载其中的一些dll.
'TestHost.exe': Unloaded …Run Code Online (Sandbox Code Playgroud) 我已经在网上阅读了很多关于安全发布RCW的文章,在我看来,没有人能够确切地知道需要按什么顺序做什么,所以我要问你们各位的意见.例如,人们可以这样做:
object target = null;
try {
// Instantiate and use the target object.
// Assume we know what we are doing: the contents of this try block
// do in fact represent the entire desired lifetime of the COM object,
// and we are releasing all RCWs in reverse order of acquisition.
} finally {
if(target != null) {
Marshal.FinalReleaseComObject(target);
target = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
Run Code Online (Sandbox Code Playgroud)
然而,有些人主张在之前进行垃圾收集Marshal.FinalReleaseComObject,有些人在之后,有些则根本没有.是否真的有必要手动GC每个RCW,特别是在它已经从COM对象中分离出来之后?
在我看来,将RCW从COM对象中分离并让RCW自然过期会更简单,更容易:
object target = null;
try {
// Same …Run Code Online (Sandbox Code Playgroud) 直到最近,我相信.NET运行时只在创建运行时可调用包装器时将COM对象的引用计数增加1 ,并且只为任何给定的COM对象创建一个这样的运行时可调用包装器.
如果我没有弄错的话,上面暗示Marshal.FinalReleaseComObject和Marshal.ReleaseComObject在实践中做同样的事情.
但是,今天我正在编写一些测试来验证我的代码是否正确释放了COM对象.我这样做是通过调用所谓的释放对象并检查预期的InvalidComObjectException.事实证明,有些情况下会抛出异常FinalReleaseComObject,但不会抛出异常ReleaseComObject.
这是否意味着.NET 2.0运行时可以包含多个COM对象的引用?如果是这样,它什么时候这样做?
只有时候(我还没有注意到一个模式),当我右键单击一个项目(各种类型的项目)并选择"时,VS2010显示以下错误(我希望属性页的主体是)"属性":

我通常可以在重启VS2010后查看项目页面.我正在使用RTM版本.
什么可能导致此错误?
rcw ×10
com ×5
.net ×4
com-interop ×4
c# ×3
ms-office ×2
comobject ×1
excel ×1
mstest ×1
office-pia ×1
powershell ×1
unit-testing ×1
winforms ×1
wpf ×1