在Windows或ASP.NET服务中的System.Drawing

17
根据MSDN,在Windows Service或ASP.NET Service中使用System.Drawing命名空间内的类不是一个特别好的想法。现在我正在开发一个类库,可能需要访问这个特定的命名空间(用于测量字体),但不能保证主机进程不是服务。
现在有一种不太理想的方法可以回退到,如果System.Drawing不可用,但如果可能的话,我宁愿使用System.Drawing中的类。所以我想做的是在运行时确定System.Drawing是否安全,如果是,就使用它,否则回退到次优选项。
我的问题是:我怎么可能检测System.Drawing是否安全使用?
我认为我应该:
  • 检测当前进程是否为Windows Service或ASP.NET服务
  • 检测GDI是否可用
  • 或者也许有一种方法可以询问System.Drawing.dll本身是否安全使用
不幸的是,我无法想出实现任何这些方法的方式。有没有人有什么想法?

为什么需要检查与字体相关的事情呢?据我了解,Windows服务没有UI界面。请帮助理解原因。 - shahkalpesh
不详细介绍,这个类库生成报告,可能由主进程通过电子邮件发送。其中一个函数需要测量字符串的渲染宽度。重点是它可能会在其他计算机上显示,而不是服务器本身。 - Tamas Czinege
我发现GDI+(System.Drawing)是安全的,只要您正确处理所有可处置对象、具有正确的错误处理并避免所有的错误。使用GDI+动态调整图像的imageresizing.net库(默认情况下使用GDI+)运行了10K-60K个网站,但尚未出现与System.Drawing使用相关的任何稳定性问题。然而,除非您正确使用System.Drawing,否则可能会轻松地创建稳定性问题。 - Lilith River
还有WPF变体,位于System.Windows.Media命名空间中,没有附加相同的警告。我不知道你是否已经拥有了所需的一切,但我知道你可以使用它将文本呈现为轮廓。无论是更好地使用它还是缺少警告只是一个疏忽,我不知道。 - John
5个回答

19
为了消除任何混淆,System.Drawing在ASP.NET和服务下确实可以工作,只是不受支持。可能存在高负载(耗尽未托管资源)、内存或资源泄漏(不良实现或调用Dispose模式)的问题,以及在没有桌面可供显示时弹出对话框的情况。
测试将解决后者,监控将警告您前者。但是,如果/当您遇到问题时,不要指望能够打电话给PSS并要求修复。
那么,您有哪些选择?嗯,如果您不需要完全支持的路线,并且您不期望极大的负载-很多人已经忽略了MSDN的警告并成功地使用了System.Drawing。其中一些人被咬了,但是成功的故事比失败的故事要多得多。
如果您想要受支持的内容,那么您需要知道自己是否正在交互式运行还是非交互式运行。就个人而言,我可能会让托管应用程序在某个位置设置一个非交互标志。毕竟,该应用程序最适合确定它们是否处于托管环境和/或是否愿意冒险使用GDI+的问题。
但是,如果您想要自动检测您的环境,我认为这里在SO上提供的答案并不比其他更糟。总结一下,您可以检查EntryAssembly以查看它是否继承自ServiceBase,或尝试访问System.Console。对于ASP.NET,沿着同样的线路,检测HttpContext.Current应该足够了。
我认为会有一个托管或p/invoke方式来查找桌面(我认为这实际上是所有问题的决定性因素),或者从AppDomain中得到一些暗示。但我不确定它是什么,MSDN上也没有提供太多信息。

编辑: 在恶作剧 MSDN 后,我想起了重要的一点是窗口站(它托管一个桌面)。有了这个信息,我能够找到GetProcessWindowStation(),它返回当前窗口站的句柄。将该句柄传递给GetUserObjectInformation()将会得到一个USEROBJECTFLAGS结构,其中应该有一个具有WSF_VISIBLE的dwFlags,如果你有一个可见的桌面。

或者,EnumWindowsStations将为您提供一个可以检查的工作站列表-WinSta0 是互动的。

但是,是的,我仍然认为让应用程序设置属性或其他东西是更容易的路线...

再次编辑: 7 年后,我发现了 Environment.UserInteractive,其中 MS 为您描述的 GetProcessWindowStation 舞蹈做了完全相同的事情。我仍然建议委托给托管应用程序(他们很可能希望使用更快但稍微有一些风险的System.Drawing路径),但是UserInteractive似乎是一个很好的默认值,而无需自己调用pinvoke。


4

尽管官方不支持,但我已经在高负载的Web服务器(包括Web应用程序和Web服务)上广泛使用System.Drawing类多年,而且没有引起任何性能或可靠性问题。

我认为确定代码是否安全使用的唯一方法是进行测试、监控,并将任何具有外部资源的对象包装在using{}语句中。


1
你可以使用TextRenderer。我知道在ASP.NET中使用System.Windows.Forms的东西很奇怪,但它似乎没有关于不支持ASP.NET的警告。我已经在ASP.NET应用程序中使用了TextRenderer和Graphics.MeasureString来测量字符串,所以它们都可以工作。我从未见过有关在ASP.NET中不建议使用System.Drawing的警告。
顺便说一下,TextRenderer比Graphics.MeasureString慢得多。

System.Drawing在ASP.NET Web应用程序中可用,但在ASP.NET Web服务中不可用。 - Tamas Czinege
不太明白你的意思。System.Drawing可以从ASP.NET Web应用程序和ASP.NET Web服务中使用。我刚试了一下,确保它可行。 - MusiGenesis
它确实可用,只是不建议使用。请查看 http://msdn.microsoft.com/en-us/library/system.drawing.aspx(寻找一个大蓝框)。 - Tamas Czinege
我会选择TextRenderer,因为MSDN没有说不要使用它。 - MusiGenesis

0

这可能与GDI子系统需要STA线程有关。如果是这种情况,请调查在涉及aspx页面的@PAGE指令中指定ASPCOMPAT=TRUE。这将在STA线程中运行aspx页面,如果我没记错的话。

-Oisin


-1

你可以尝试将 System.Drawing.dll 复制到应用程序的 bin 目录中,然后以这种方式使用它。这可能会保证其可用性。

只需右键单击应用程序中的引用,然后将 Copy Local 选项更改为 true

不过我可能误解了问题...


1
问题不在于可用性,因为System.Drawing.dll是.NET Framework的一部分。问题在于没有GDI存在,它无法正常运行。 - Tamas Czinege

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