Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirect STDERR output from COM object wrapper in .NET

I'm trying to make use of the ImageMagick COM object (ImageMagickObject) in a .NET library. This library is intended to be called from IronRuby, but that isn't all that important. I want to take this approach because it will fit with my existing calls, which currently call the ImageMagick binaries as external processes. The COM object will take the same arguments as the binaries, but will save the process creation and are about 5x faster overall.

My only hurdle is that the "Compare" method for the COM object returns its result to STDERR. This is also a problem with the binary, but it's easy to pipe that back into STDOUT, where I was expecting it. With the COM object, I'm getting my results from function return values.

How can I redirect the result from "Compare" to a string buffer or even a file instead of STDERR?

I have tried the following, which does stop the output from reaching STDERR, but it doesn't write to the file as expected:

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;
        }
    }
}

Only the "Compare" action seems to use STDERR to send a result (it uses the return value as a success indicator). All of the other methods (Identify, Convert, Mogrify, etc) work as you would expect.

For reference, it gets called something like this (from IronRuby):

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

And output.txt is created, but empty. Nothing gets printed to STDOUT or STDERR.

EDITS: For clarity regarding streamwriter flush/close and how the sample is used from IronRuby.

like image 988
cgyDeveloper Avatar asked Nov 14 '22 03:11

cgyDeveloper


1 Answers

Did you try Disposing the(or flushing) writer and stream? It could have died stuck in the buffer. A using block might help there.

    using(filestream = new FileStream("output.txt", FileMode.Create))
    using(streamwriter = new StreamWriter(filestream))
    {

... }

like image 182
James Michael Hare Avatar answered Dec 15 '22 07:12

James Michael Hare