Windows服务:使用BitmapEncoder或BitmapDecoder以“操作成功完成”结束

6

我面临一个问题,无法解决或在谷歌上找到解决方案。

我正在运行一个服务,加载或保存图像,并使用BitmapEncoderBitmapDecoder类。一段时间后(取决于我保存/加载图像的频率),服务拒绝保存/加载图像。首先,在事件日志中看到警告:

堆分配失败

我搜索了它的含义,它与Windows服务拥有的GDI对象数量有限有关。可以修改注册表以增加这些对象的数量,但我认为这不是很好的解决方案,而且对我也没有用。

我的代码在保存时抛出以下异常和堆栈跟踪:

Error while storing image : System.ComponentModel.Win32Exception (0x80004005): The operation completed successfully
   at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
   at System.Windows.Threading.Dispatcher..ctor()
   at System.Windows.Threading.DispatcherObject..ctor()
   at System.Windows.Media.Imaging.BitmapEncoder..ctor(Boolean isBuiltIn)
   at Imaging.TiffReadWrite.Save(String filename, Image img)

当加载时

Error while loading image : System.ComponentModel.Win32Exception (0x80004005): The operation completed successfully
   at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
   at System.Windows.Threading.Dispatcher..ctor()
   at System.Windows.Threading.DispatcherObject..ctor()
   at System.Windows.Media.Imaging.BitmapDecoder..ctor(Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Guid expectedClsId)
   at Imaging.TiffReadWrite.Load(String filename)

我的代码用于保存图片,看起来像这样:

public static void Save(string filename, BitmapSource img)
{
    using (FileStream stream = new FileStream(filename, FileMode.Create))
    {
        TiffBitmapEncoder encoder = new TiffBitmapEncoder();
        encoder.Compression = TiffCompressOption.None;

        BitmapFrame frm = BitmapFrame.Create(img);

        encoder.Frames.Add(frm);
        encoder.Save(stream);
    }
}

加载图片的代码如下:

public static BitmapSource Load(string filename)
{
    BitmapSource resultImage = null;

    using (Stream imSource = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        var decoder = new TiffBitmapDecoder(imSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
        resultImage = decoder.Frames[0];
    }
    return resultImage;
}

因此,服务拒绝保存/加载图像。我可以尝试捕获这个异常,以便服务可以继续运行,但是没有图片可以被保存/加载。有时在第一次出现这个异常后,几张图片可以被保存/加载,过了一会儿就不再执行保存/加载操作。

我唯一的解决方法是不在服务中运行此代码,而是在应用程序中运行。然后它运行得非常好,但这不是我要寻找的解决方案。如果有更好的建议,请告诉我。

有一些类似的帖子(异常的堆栈跟踪或多或少相同),实际上并没有解决:


1
那是一个非常无用的异常信息。服务在会话0中运行,它有一个故意小的桌面堆。这使得CreateWindowEx()失败的可能性更大。可能是另一个服务占用了不公平的份额。或者是你的服务,“任务管理器”中的“用户对象”计数器是你的第一个诊断工具。你还面临着垃圾回收器不能经常进行回收的风险,你可能需要通过每N个位图调用GC.Collect()来帮助解决问题。然而,没有有意义的错误代码确实指向了一个环境问题。 - Hans Passant
谢谢您的回复。我知道我的问题描述不够清晰,这也是我无法解决它的原因。我检查了任务管理器,并使用dheapmon跟踪GDI对象,发现只有WinLogon具有高使用率,但仅此而已。可能是另一个服务,但在使用我发布的代码的不同PC上观察到了这种情况,所以可能性不大。我认为我已经调用了足够多的GC.Collect(),所以这也不会有帮助。 - Tomas Kosar
WPF不使用GDI。但这可能与线程模型有关。您的代码是否在STA线程中运行?您能否分享更多代码,以便我们可以重现(PS:WPF不支持服务器代码)? - Simon Mourier
如果您无法提供一个小的完整可复现的代码,我无法帮助您。 - Simon Mourier
1
我知道按照我的说法很难复现,因为它会在一段时间后(几小时、几天)发生。但是除此之外你需要的一切都有了。只需创建一个循环,在其中保存/加载图片(最好大于100MB),并在Windows服务中执行。 - Tomas Kosar
显示剩余3条评论
1个回答

3

操作成功完成

这个令人费解的消息缩小了HwndWrapper构造函数中失败的确切代码范围。WPF在GetStockObject pinvoke声明中存在一个错误。它的SetLastError = true属性是错误的,实际上GetStockObject()并没有产生错误代码。你会看到错误代码0的描述,“没有出错”。

GetStockObject()是一个winapi函数,如果得到正确的参数,它将永远不会失败。存储对象是预先分配的,永远不会被释放。你有很强的证据表明进程状态彻底损坏了。在事件日志中看到“堆分配失败”的消息肯定是那种痛苦的一部分。

如果你不知道是什么原因导致了这种损坏,机器使用可靠的RAM,并且你没有运行任何危险的本机代码,机器也没有运行可能会破坏桌面堆栈的其他服务,那么唯一的选择就是创建一个崩溃进程的minidump。联系Microsoft Support,他们可以跟踪从GetStockObject()故障的迹象。但请注意,你必须通过第一层支持层,他们会告诉你更换机器:)


谢谢您的回复。我很感激您的解释。我可能会尝试联系微软支持,但我并不抱有太大的希望。同时,我也不知道该如何解决这个问题。 - Tomas Kosar

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