背景
我有一个 Windows 服务,它使用各种第三方 DLL 来处理 PDF 文件。这些操作会占用相当多的系统资源,并且在发生错误时偶尔会出现内存泄漏。 DLL 是其他非托管 DLL 的托管包装器。
当前解决方案
在一种情况下,我已经通过在专用控制台应用程序中包装对其中一个 DLL 的调用并通过 Process.Start() 调用该应用程序来缓解此问题。如果操作失败并且存在内存泄漏或未释放的文件句柄,则无关紧要。该过程将结束,操作系统将恢复句柄。
我想将同样的逻辑应用到我的应用程序中使用这些 DLL 的其他地方。但是,我对在我的解决方案中添加更多控制台项目并编写更多样板代码来调用 Process.Start() 并解析控制台应用程序的输出并不感到非常兴奋。
新解决方案
专用控制台应用程序和 Process.Start() 的一个优雅替代方案似乎是使用 AppDomains,如下所示:http://blogs.geekdojo.net/richard/archive/2003/12/10/428.aspx .
我已经在我的应用程序中实现了类似的代码,但单元测试并不乐观。我在单独的 AppDomain 中为测试文件创建了一个 FileStream,但不处理它。然后我尝试在主域中创建另一个 FileStream,但由于未释放的文件锁定而失败。
有趣的是,向工作域添加一个空的 DomainUnload 事件会使单元测试通过。无论如何,我担心创建“worker”AppDomains 可能无法解决我的问题。
想法?
代码
/// <summary>
/// Executes a method in a separate AppDomain. This should serve as a simple replacement
/// of running code in a separate process via a console app.
/// </summary>
public T RunInAppDomain<T>( Func<T> func )
{
AppDomain domain = AppDomain.CreateDomain ( "Delegate Executor " + func.GetHashCode (), null,
new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory } );
domain.DomainUnload += ( sender, e ) =>
{
// this empty event handler fixes the unit test, but I don't know why
};
try
{
domain.DoCallBack ( new AppDomainDelegateWrapper ( domain, func ).Invoke );
return (T)domain.GetData ( "result" );
}
finally
{
AppDomain.Unload ( domain );
}
}
public void RunInAppDomain( Action func )
{
RunInAppDomain ( () => { func (); return 0; } );
}
/// <summary>
/// Provides a serializable wrapper around a delegate.
/// </summary>
[Serializable]
private class AppDomainDelegateWrapper : MarshalByRefObject
{
private readonly AppDomain _domain;
private readonly Delegate _delegate;
public AppDomainDelegateWrapper( AppDomain domain, Delegate func )
{
_domain = domain;
_delegate = func;
}
public void Invoke()
{
_domain.SetData ( "result", _delegate.DynamicInvoke () );
}
}
[Test]
public void RunInAppDomainCleanupCheck()
{
const string path = @"../../Output/appdomain-hanging-file.txt";
using( var file = File.CreateText ( path ) )
{
file.WriteLine( "test" );
}
// verify that file handles that aren't closed in an AppDomain-wrapped call are cleaned up after the call returns
Portal.ProcessService.RunInAppDomain ( () =>
{
// open a test file, but don't release it. The handle should be released when the AppDomain is unloaded
new FileStream ( path, FileMode.Open, FileAccess.ReadWrite, FileShare.None );
} );
// sleeping for a while doesn't make a difference
//Thread.Sleep ( 10000 );
// creating a new FileStream will fail if the DomainUnload event is not bound
using( var file = new FileStream ( path, FileMode.Open, FileAccess.ReadWrite, FileShare.None ) )
{
}
}
最佳答案
应用程序域和跨域交互是一件非常简单的事情,所以在做任何事情之前,应该确保他真的了解事情是如何工作的......嗯......让我们说,“非标准”:-)
首先,您的流创建方法实际上是在您的“默认”域上执行的(出乎意料!)。为什么?简单:您传入的方法 AppDomain.DoCallBack在 AppDomainDelegateWrapper 上定义对象,并且该对象存在于您的默认域中,因此这是执行其方法的地方。 MSDN 没有提到这个小“功能”,但检查起来很容易:只需在 AppDomainDelegateWrapper.Invoke 中设置一个断点即可。 .
因此,基本上,您必须在没有“包装器”对象的情况下凑合。对 DoCallBack 的参数使用静态方法。
但是您如何将您的“func”参数传递到另一个域,以便您的静态方法可以选择并执行它?
最明显的方法是使用AppDomain.SetData , 或者你可以推出你自己的,但不管你怎么做,还有另一个问题:如果“func”是一个非静态方法,那么它定义的对象必须以某种方式传递到另一个应用程序域。它可以通过值(而它被逐个字段地复制)或通过引用(创建具有远程处理的所有美感的跨域对象引用)传递。要做到前者,必须用 [Serializable] 标记该类。属性。要做到后者,它必须继承自 MarshalByRefObject .如果该类都不是,则在尝试将对象传递给另一个域时将引发异常。但是请记住,通过引用传递几乎会扼杀整个想法,因为您的方法仍将在对象所在的同一域上调用 - 即默认域。
结束上面的段落,您有两个选择:要么传递在标有 [Serializable] 的类上定义的方法。属性(并记住对象将被复制),或者传递一个静态方法。我怀疑,为了您的目的,您将需要前者。
以防万一它没有引起您的注意,我想指出您的第二次过载 RunInAppDomain (采用 Action 的方法)传递定义在未标记为 [Serializable] 的类上的方法.没有看到那里有任何类(class)吗?您不必:使用包含绑定(bind)变量的匿名委托(delegate),编译器将为您创建一个。碰巧的是,编译器不会费心标记自动生成的类 [Serializable] .不幸的是,但这就是生活:-)
说了这么多(很多话,不是吗?:-),并假设你发誓不通过任何非静态和非 [Serializable]方法,这是您的新 RunInAppDomain方法:
/// <summary>
/// Executes a method in a separate AppDomain. This should serve as a simple replacement
/// of running code in a separate process via a console app.
/// </summary>
public static T RunInAppDomain<T>(Func<T> func)
{
AppDomain domain = AppDomain.CreateDomain("Delegate Executor " + func.GetHashCode(), null,
new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory });
try
{
domain.SetData("toInvoke", func);
domain.DoCallBack(() =>
{
var f = AppDomain.CurrentDomain.GetData("toInvoke") as Func<T>;
AppDomain.CurrentDomain.SetData("result", f());
});
return (T)domain.GetData("result");
}
finally
{
AppDomain.Unload(domain);
}
}
[Serializable]
private class ActionDelegateWrapper
{
public Action Func;
public int Invoke()
{
Func();
return 0;
}
}
public static void RunInAppDomain(Action func)
{
RunInAppDomain<int>( new ActionDelegateWrapper { Func = func }.Invoke );
}
关于c# - 用 AppDomains 替换 Process.Start,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1510466/
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio
假设我在Store的模型中有这个非常简单的方法:defgeocode_addressloc=Store.geocode(address)self.lat=loc.latself.lng=loc.lngend如果我想编写一些不受地理编码服务影响的测试脚本,这些脚本可能已关闭、有限制或取决于我的互联网连接,我该如何模拟地理编码服务?如果我可以将地理编码对象传递到该方法中,那将很容易,但我不知道在这种情况下该怎么做。谢谢!特里斯坦 最佳答案 使用内置模拟和stub的rspecs,你可以做这样的事情:setupdo@subject=MyCl
我有很多这样的文档:foo_1foo_2foo_3bar_1foo_4...我想通过获取foo_[X]的所有实例并将它们中的每一个替换为foo_[X+1]来转换它们。在这个例子中:foo_2foo_3foo_4bar_1foo_5...我可以用gsub和一个block来做到这一点吗?如果不是,最干净的方法是什么?我真的在寻找一个优雅的解决方案,因为我总是可以暴力破解它,但我觉得有一些正则表达式技巧值得学习。 最佳答案 我(完全)不懂Ruby,但类似这样的东西应该可以工作:"foo_1foo_2".gsub(/(foo_)(\d+)/
我有以下内容:text.gsub(/(lower)(upper)/,'\1\2')我可以将\2替换为大写吗?类似于:sed-e's/\(abc\)/\U\1/'这在Ruby中可行吗? 最佳答案 查看gsub文档:str.gsub(模式){|匹配|block}→new_str在block形式中,当前匹配字符串作为参数传入,$1、$2、$`、$&、$'等变量将被适当设置。block返回的值将替换为每次调用的匹配项。"alowerupperb".gsub(/(lower)(upper)/){|s|$1+""+$2.upcase}
我在RailsController操作中有下面的代码序列。在IF之前,params包含请求参数,正如预期的那样。在它之后,params为零。谁能解释一下这里发生了什么?iffalseparams={:user=>{:name=>"user",:comment=>'comment'}}end谢谢。 最佳答案 params其中包含请求参数实际上是一个方法调用,它返回包含参数的散列。你的params=行正在分配给一个名为params的局部变量.iffalse之后block,Ruby已经看到了本地params变量,所以当你引用params时