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.
It looks like
.Handle
is deprecated, have you tried.SafeFileHandle.DangerousGetHandle()
instead?Ref: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle.dangerousgethandle.aspx
(Additionally: can you confirm you close the StreamWriter after the data has been written? Like, if you move it to just before the app closes, to see if the data makes its way out?)
According to the documentation "FileShare.Read is the default for those FileStream constructors without a FileShare parameter"... Perhaps it could help to have FileAccess.Read, FileAccess.Write, FileShare.Read and FileShare.Write set on in the FileStream c-tor?
I don't have ImageMagick exe to play with... if you can post the link, it would be great.
Did you try Disposing the(or flushing) writer and stream? It could have died stuck in the buffer. A using block might help there.
... }
After adding the
debug
option I finally got some text in the file.Is that the result you are expecting? If so, see
debug
option of thecompare
command-line tool.Please note that setting the
Console.Error
orConsole.Out
tostreamwriter
will cause an exception if you try to log anything directly or indirectly using the aforementioned properties.If you really need to set those properties, set them to another instance of
StreamWriter
. If you do not need them, omit the calls toConsole.SetOut
andConsole.SetError
.I have also noticed that once
ImageMagickObject.MagickImage
instance is created, it is not possible to redirect the standard error. If the standard error redirection needs to be undone, or performed multiple times, try executing the code in another application domain.