exiftool .Net wrapper (written in vb.net)

Started by Curtis, June 09, 2014, 05:31:48 PM

Previous topic - Next topic


I have a wrapper for exiftool for .Net.  I wrote it in vb.net and have been using it for sometime now and it works very well for me.  I recently noticed some posts related to using exiftool with .Net so I decided to make a small demo program to show how to use the wrapper (ExifToolIO.dll).  (the program I'm using ExifToolIO in is too big to post here)

I believe the code runs nearly as fast as possible, I have tried to optimize it for speed.  Some of the features are:
A of version exiftool.exe is included in the ExifToolIO.dll so no separate exiftool.exe is needed.  Optionally  a separate exiftool.exe file can be used.

I/O to the exiftool process is by default via stdinp and stdout so no disk file I/O is involved, making it very fast. Optionally a text file may be used and then viewed later to see what commands were actually sent to exiftool.

Very simple to use, simply call Initiailize, send commands using Cmd method then call Execute which returns the results of exiftool.

Sample vb.net code:

Dim result as String = ExifToolIO.Execute("C:\pic.jpg")

Once ExifToolIO.Initiailize() is called as many ExifToolIO.Cmd and ExifToolIO.Execute calls can be made as needed.  Then before exiting the program call
ExifToolIO.Close.  The demo program shows several ways to get tag values including using the -X XML exiftool output format.

I have attached the VS 2012 VB.Net Solution that has all the source code to ExifToolIODemo and ExitToolIO.  Also attached to this post are the executable files (.exe and .dll)  Also see the attached screen shot of the demo program. (I have also put it up on CodeProject and the source can be looked at and downloaded from there https://workspaces.codeproject.com/curtis1757/exiftool_wrapper/code, someday I may write a CodeProject article for this)

Possible issues: I've only run this on my Window 7 machine, Framework 4.0 is required.

I welcome any questions, comments, bug reports or suggestions..... please post them here.... (or my email address is in the source code)


Update: Added JSON format output example in demo program.


Recently I have had some inquiry about the ExifToolIO.Cmd method and spaces in its input string.  Since when Exiftool is running in the -stay_open mode, it wants each of it's commands on a separate line (check the Exiftool documentation for -stay_open and the -@ commands for details on this), the ExifToolIO.Cmd method by default converts spaces to CrLf so you can give the ExifToolIO.Cmd method a whole string of commands in one call.

ExifToolIO.Cmd("-X -t -l")

But some strings sent to ExifTool need spaces, such as tag values and file names.  When this is the case then set the optional second argument to the ExifToolIO.Cmd method to False and then spaces will be preserved.  The ExifToolIO.Cmd method  adds a CrLf after each call to it.  Also note that for the file name given in the ExifToolIO.Execute method, spaces are not converted to CrLf.

Below is sample code fragment that shows a simple way to set tag values in a file. Before calling this SaveChangedTags method ExifToolIO.Initialize needs to have been called.

Imports ExifToolLib

Public Class Sample
    Public Class TagValue
        Public Name As String
        Public Value As String()
    End Class

    '--Some Exiftool commands that I commonly use....

    Private Const CmdOverWriteOriginal = "-overwrite_original"
    Private Const CmdQuiet = "-q"
    Private Const CmdPrintFormat = "-p"
    Private Const CmdSep = "-sep"                                '--set seperator string for between array values
    Private Const CmdListx = "-listx"                            '--gets tag info
    Private Const CmdListff = "-listf"                           '--all supported file extensions
    Private Const CmdListr = "-listr"                            '--all recognized file extensions
    Private Const CmdListwf = "-listwf"                          '--all writable file extensions
    Private Const Cmdl = "-l"                                    '---l expands list to give descriptions too
    Private Const CmdListxFlags = "-f"
    Private Const CmdDisablePrintConversion = "-n"
    Private Const CmdXMLOutput = "-X -t -l"                      '--XML output with table names and get prt and val values at same time
    Private Const CmdBinary = "-b"                               '--get binary values (as base64 text)

    '--Sample routine to save tag values

    Public Sub SaveChangedTags(fn As String, TagValues As TagValue(), Optional OverWriteOriginal As Boolean = False, Optional TagValSep As String = "*")
        If TagValues.Length = 0 Then Return

        Dim first As Boolean = True

        '--write out all the tag values....

        For Each t As TagValue In TagValues
            If first Then

                '--These commands only need to be done once


                '--Note the above 3 lines could be converted to:
                'ExifToolIO.Cmd(CmdQuiet & " " & CmdSep & " " & TagValSep)
                '-- OR
                'ExifToolIO.Cmd("-q -sep " & TagValSep)
                '--(all are equivalent, keep in mind ExifTool expects each command to be on a separate line, which is why by default .Cmd converts spaces to CrLf)

                If OverWriteOriginal Then ExifToolIO.Cmd(CmdOverWriteOriginal)

                first = False
            End If

            '--Write out the commands to change tag values (check for value being an array)
            '--Note that the second argument in .Cmd is set to false so spaces are not converted to CrLf (tag values may certainly contain spaces),
            '--this is also needed with ExifTool commands that take a file name since file names may have spaces

            If t.Value.Length > 1 Then

                '--We have an array, so join it into a single string

                ExifToolIO.Cmd("-" & t.Name & "=" & String.Join(TagValSep, t.Value), False)
                ExifToolIO.Cmd("-" & t.Name & "=" & t.Value(0), False)
            End If

        '--now Execute the previous commands on the given file to update the tag values

    End Sub
End Class

I hope this clears up a few questions concerning this and hope you find the code useful.



Thank you very much for your code, Curtis!

Could you please provide a code example how to write back a change in one or more tags?


Here is some example code (very hard coded, but hopefully you get the idea) using the routine in my previous post. The example shows writing  tags to a file (the second tag being a list type (array).

  Public Sub TestSaveTags()
        Dim fn As String = "C:\mypic.jpg"
        Dim Tags() As TagValue = {New TagValue With {.Name = "IDF0:Software", .Value = {"MySoftware!"}}, New TagValue With {.Name = "XMP-mwg-rs:RegionName", .Value = {"Curtis*Bob*Joe"}}}

        SaveChangedTags(fn, Tags, True)
    End Sub




How can I write an export file with your
Private Const CmdListx = "-listx"
and flags like
Private Const CmdListxFlags = "-f"

Such commands doesn't need a file name.
Whatever I try, I only get a "nothing to write" as .LastErrorMsg, but no written file at all.


Hi FixEUser,

The -listx command does not get tag values from an image file.  It simply gets information (properties) of tags that exiftool supports.  So no file is needed for input.  If this is what you want (probably not) the output of the -listx command is given in the string returned by the  ExifToolIO.Execute() function. (Note: the fn parameter is optional and you would not need it here since no input file is needed).  Once you have the string result from the ExifToolIO.Execute() function you could write it to a file if that is what you want to do.

If what you are looking for is the value of a tag in a particular image file, then you would do something like this:

Dim result as String = ExifToolIO.Execute("C:\mypic.jpg")

and result will have the value of the IFD0:Artist tag, which you could change and then write back to the C:\mypic.jpg file with code similar to what I showed in my previous post.



Thank you very much for your fast answer, Curtis.

I try to read & parse the output of the -listx command to verify if a specific tag is writable or not.
If not, I display this information or change the color of such a tag.

I think, I don't have to write an outputfile at all if I can get the result as a string. Let me check it .-)


You could also use the  -listw command to get only the writable tag names.  You could get them all once, store them and then simply check the list of writable tag names you stored to see if a tag you got from an image file is writable.

Check out the -list documentation here: www.exiftool.org/exiftool_pod.html if you have not already.



It's me again.

Could you please show us how to use the Me.tbxErrorMsgs.Text &= ExifToolIO.LastErrorMsg

I can't loop through a list of filenames and get the .LastErrorMsg for every single processed file with newly written tags.
It seems that .LastErrorMsg accumulate the messages (and use some strange & vbCrLf Syntax) if the result has two lines like:
Quote"Nothing to do." & vbCrLf & "Warning: Sorry, Composite:AdvancedSceneMode doesn't exist or isn't writable" & vbCrLf



Sorry did not respond earlier, I have been away for a few weeks.

Not really sure what your question is, if you have not resolved this and still need help, could you post a more complete code sample?



My question is:
How can I get error messages like"Nothing to do." & vbCrLf & "Warning: Sorry, Composite:AdvancedSceneMode doesn't exist or isn't writable" & vbCrLfas one single string, not as multiple strings?

Just try a For Each loop with multiple files that generate error messages like the above. Try to display the error message for every file in a textbox.

Or maybe you can show us a code example how to handle error messages with more then one line?



I think what you want is for each file the error string to not have any CrLf's in it.  What is happening in ExifToolIO in Sub ErrorOutputHandler each time it gets Err output from exiftool it appends a CrLf to it so that it separates the individual err messages for a given execution of exiftool (ie for each file), otherwise it all runs together and is harder to understand.  I believe most users would want it this way and I would suggest not changing the Sub ErrorOutputHandler.

The simple fix, for what I think you want, is to remove the CrLf's from the returned LastErrMsg string.  To my Demo code I added a button on the main form which executes the following code:
   Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        For Each fn As String In Me.lbxFiles.Items
            Me.tbxErrorMsgs.Text &= ExifToolIO.LastErrorMsg.Replace(ControlChars.CrLf, " ") & ControlChars.CrLf
    End Sub

This will replace the CrLf's within each LastErrorMsg with a space and then append a CrLf at the end for each file (fn). So then the Me.tbxErrorMsgs.Text will show the LastErrorMsg as one line for each file.




May I ask you to scan my https://exiftool.org/forum/index.php/topic,7801.msg39567.html#msg39567 issue posted today (11 Nov) on a problem I have with StdError text getting mixed up with StdOut text?

, having stuck with VBA inside MS Office.

I don't fancy a huge learning curve (at age 77!!). I never graduated to .NET code; just stuck to plain MS Office.
I'm hoping you might be able to help  OR suggest a design improvement.

BTW - My "customer" is still using Office 2000 under Vista, so whatever I do, it needs to work for that
(and there's no money involved either  :) )



Spilly.....  see my response in the thread you referenced.....


Hi Curtis,

I know its been some time since this thread was active.  I tried using the link to codeproject you provided but it isnt working, is it still there?  I am using VS2010 so I cant open your attached project files.  I have written software that lets me tether a Nikon camera, and I would like to know the shutter count (as part of the UI) as I am taking the photos.  Your wrapper looks like it would definitely hand the shutter count back to a string at the time I transfer the image from camera memory, can your wrapper do that?

regards Tom