将.NET中COM对象包装器的标准错误输出重定向

4
我正在尝试在.NET库中使用ImageMagick COM对象(ImageMagickObject)。这个库旨在从IronRuby中调用,但这不是非常重要。我选择这种方法是因为它可以与我的现有调用相匹配,而当前调用将ImageMagick二进制文件作为外部进程调用。COM对象将使用与二进制文件相同的参数,但将保存进程创建,并且整体速度快了约5倍。
我的唯一问题是,COM对象的“Compare”方法将其结果返回到STDERR。这也是二进制文件的问题,但很容易将其回传到STDOUT,我本来期望这样做。使用COM对象时,我从函数返回值获取结果。
如何将“Compare”的结果重定向到字符串缓冲区甚至文件而不是STDERR?
我尝试了以下操作,它确实阻止输出到达STDERR,但未按预期写入文件:
using ImageMagickObject;
...

public class ImageMagickCOM
{
    [DllImport("Kernel32.dll", SetLastError = true)]
    public static extern int SetStdHandle(int device, IntPtr handle);

    private const int STDOUT_HANDLE = -11;
    private const int STDERR_HANDLE = -12;

    private ImageMagickObject.MagickImage magickImage = null;

    private FileStream filestream = null;
    private StreamWriter streamwriter = null;

    public ImageMagickCOM()
    {
        IntPtr handle;
        int status;

        filestream = new FileStream("output.txt", FileMode.Create);
        streamwriter = new StreamWriter(filestream);
        streamwriter.AutoFlush = true;

        //handle = filestream.Handle; // deprecated
        handle = filestream.SafeFileHandle.DangerousGetHandle(); // replaces filestream.handle
        status = SetStdHandle(STDOUT_HANDLE, handle);
        status = SetStdHandle(STDERR_HANDLE, handle);

        Console.SetOut(streamwriter);
        Console.SetError(streamwriter);

        magickImage = new ImageMagickObject.MagickImage();
    }

    public string Compare()
    {
        object[] args = new object[] { "-metric", "AE", "-fuzz", "10%", "imageA.jpg", "imageB.jpg", "diff.png" };
        return (string)this.magickImage.Compare(ref args);
    }

    public void Close()
    {
        if (this.magickImage != null)
        {
            Marshal.ReleaseComObject(magickImage);
            this.magickImage = null;
        }
        if (this.streamwriter != null)
        {
            this.streamwriter.Flush();
            this.streamwriter.Close();
            this.streamwriter = null;
            this.filestream = null;
        }
    }
}

只有“比较”操作似乎使用 STDERR 发送结果(它使用返回值作为成功指示器)。所有其他方法(Identify、Convert、Mogrify 等)都按照您的期望工作。

供参考,它被称为类似于以下方式(来自 IronRuby):

require 'ImagingLib.dll'
im = ImagingLib::ImageMagickCOM.new
im.compare # returns nil
im.close

输出文件output.txt已创建,但为空。没有任何内容被打印到STDOUT或STDERR。

编辑:为了更清楚地说明streamwriter的flush / close以及如何从IronRuby使用示例。

4个回答

1
你试过释放(或刷新)写入器和流吗?它可能在缓冲区中卡死了。使用 using 块可能会有所帮助。
    using(filestream = new FileStream("output.txt", FileMode.Create))
    using(streamwriter = new StreamWriter(filestream))
    {

...

这段代码与编程有关。

我已经尝试过在调用Compose之前和之后显式刷新缓冲区,但是没有任何运气。 - cgyDeveloper

1

在添加了debug选项后,我终于在文件中得到了一些文本。

这是您期望的结果吗?如果是,请参见compare命令行工具的debug选项。

请注意,将Console.ErrorConsole.Out设置为streamwriter会导致异常,如果您尝试直接或间接地使用上述属性记录任何内容。

如果您确实需要设置这些属性,请将它们设置为另一个StreamWriter实例。如果您不需要它们,请省略对Console.SetOutConsole.SetError的调用。

我还注意到,一旦创建了ImageMagickObject.MagickImage实例,就无法重定向标准错误。如果需要撤消标准错误重定向,或多次执行该操作,请尝试在另一个应用程序域中执行代码。


比较明显的是写入 STDERR。如果我删掉 SetStdHandle 调用,它将每次都写入 STDERR。但是,如果那些调用存在,则永远不会这样做(也不会写入文件)。 - cgyDeveloper
关于你的问题,这里使用的关键语句非常标准,如下所示: fprintf(stderr, "%g", distortion); fprintf(stderr, "\n"); - cgyDeveloper
根据stdin,stdout,stderr的说明,stderr是一个常量。如果是这种情况,SetStdHandle将不足以解决问题。我看过一个可能有帮助的示例,一旦我再次找到它,就会发布出来。 - user595010
非常奇怪的是只有在使用“debug”时,标准错误输出才会被重定向到文件中。顺便说一下,如果SetStdHandle起作用,我实际上并不需要Console.Out和Console.Error重定向。 我将不得不深入研究IM源代码,看看调试输出是如何被处理的。感谢您的挖掘 :) - cgyDeveloper
如果不重定向句柄,stderr 绝对会有输出。我在 IronRuby 控制台中看到它,而且我认为你会在为该进程创建的任何其他 .NET 控制台中看到它。 - cgyDeveloper
显示剩余11条评论

0

对于关闭StreamWriter我感到很满意。由于VS给出了弃用警告(它们似乎是等效的),我也尝试了两种句柄方法。 - cgyDeveloper

0
根据文档,“对于没有FileShare参数的FileStream构造函数,FileShare.Read是默认值”…也许在FileStream c-tor中设置FileAccess.Read、FileAccess.Write、FileShare.Read和FileShare.Write会有所帮助?
我没有ImageMagick exe来进行测试…如果你能把链接发给我,那就太好了。

文件权限在这种情况下不是问题,ImageMagick在这里:http://imagemagick.org/script/binary-releases.php#windows。确保在安装期间安装COM对象以像示例中一样使用它。 - cgyDeveloper

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