Executing ExifTool from C# with -stay_open option

Started by lokatz, June 06, 2020, 07:03:14 AM

Previous topic - Next topic


Hope it is ok to use this forum to share some hard-won wisdom.  With great help from StarGeek and Phil (thanks again, guys!), I spent the last day-and-a-half to figure out how to run ExifTool from within my C# application.  Other posts on this forum suggest that I'm not the only one struggling with this, so I thought I should share my newfound expertise by giving a code example that shows how this can be done in the most simple of ways, without the use of a wrapper or library:

    bool ExifToolHasBeenLoaded = false;
    Process pExifTool = new Process();

    private void RunExiftoolWhileStayingOpen()
        //  Start ExifTool and keep it in memory if that has not been done yet.
        if (!ExifToolHasBeenLoaded)
            string command = "\"" + ExifToolFolderPath + "exiftool.exe\" -stay_open true -@ args.txt";

            pExifTool.StartInfo = new ProcessStartInfo("cmd", String.Format("/c \"{0}\"", @command));
            pExifTool.StartInfo.RedirectStandardOutput = true;

            //  NOTE:  If you do not implement an asynchronous error handler like in this example, instead simply using pExifTool.StandardError.ReadLine()
            //         in the following, you risk that your program might stall.  This is because ExifTool sometimes reports failure only through a
            //         StandardOutput line saying something like "0 output files created" without reporting anything in addition via StandardError, so
            //         pExifTool.StandardError.ReadLine() would wait indefinitely for an error message that never comes.
            pExifTool.StartInfo.RedirectStandardError = true;
            pExifTool.ErrorDataReceived += new DataReceivedEventHandler(ETErrorHandler);

            pExifTool.StartInfo.UseShellExecute = false;
            pExifTool.StartInfo.CreateNoWindow = true;
            pExifTool.BeginErrorReadLine();  //  This command starts the error handling, meaning ETErrorHandler() will now be called whenever ExifTool reports an error.

            ExifToolHasBeenLoaded = true;

        //  Append the args file for Exiftool to start reading and executing the command.
        //  NOTE:  NEVER use WriteAllLines here - ExifTool expects the args file to be appended continually, not re-written.

        string[] args = new string[] { ImageFileName, "-execute" };  //  This tells ExifTool to read out all of the image's metadata.
        File.AppendAllLines(ExifToolFolderPath + "args.txt", args);  //  args.txt gets written into the folder where exiftool.exe resides here.

        string line;
            line = pExifTool.StandardOutput.ReadLine();

            //  NOTE:  Depending on the command you issued, line will either contain a progress report of an operation (e.g., "1 output files created"),
            //         give line-by-line data, such as an image's metadata (e.g. "Orientation                     : Horizontal (normal)"), or
            //         read "{ready}", which indicates that executing the last command in args.txt has completed.
                ...  do something with the information provided in line ...
        while (!line.Contains("{ready}"));

        //  At this point, you can issue the next command for ExifTool, following the very same approach.  For instance, this tells ExifTool to
        //  extract a PreviewImage from a RAW image, using the trick of adding "%0f" at the beginning of the new filename as a way to
        //  give the PreviewImage a different name from the orginal file ...
        args = new string[] { "-b", "-PreviewImage", "-w", ImagePreviewFolderPath + "%0f" + ImagePreviewFileName, RAWImageFileName, "-execute" };
        File.AppendAllLines(ExifToolFolderPath + "args.txt", args);

        ...  read and process pExifTool.StandardOutput again as per the above ...

        //  ... and this starts the conversion of an appropriate original image, for instance a .NEF, to .JPG:
        args = new string[] { "-b", "-JpgFromRaw", "-w", JPGImageFolderPath + "%0f" + JPGImageName, RAWImageFileName, "-execute" };
        File.AppendAllLines(ExifToolFolderPath + "args.txt", args);

        ...  read and process pExifTool.StandardOutput again as per the above ...

    //  Finally, this is the asynchronous error handler

    private void ETErrorHandler(object sendingProcess, DataReceivedEventArgs errLine)
        if (!String.IsNullOrEmpty(errLine.Data))
            ...  do something with the information provided in errLine.Data ...

A cakewalk once you figured it all out.   ;)

Phil Harvey

Thanks!  I've added a link from the exiftool home page to this post.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).