.NET "wrapper" dll

Started by brain2cpu, September 13, 2013, 08:27:20 AM

Previous topic - Next topic

brain2cpu

Hi all,
I'd like to add my little contribution to this wonderful project, so here comes ExiftoolWrapper, a .NET dll with source code (C#), to bring a step closer exiftool to .NET developers.
The main goal was to use the "-stay_open" flag in the most transparent way. 
The simplest usage example would be:


#using BBCSharp;

using(ExifToolWrapper extw = new ExifToolWrapper())
{
    extw.Start();
    Dictionary<string, string> d = extw.FetchExifFrom(@"d:\Temp\D800-DSC_8059.NEF");
}


The dictionary above will contain all data exfitool can extract. The Start method loads exiftool(-k).exe (must be in your application's startup folder in case of the parameterless constructor), Dispose will stop it. Start needs some time, so this approach is advised when you need to extract exif from multiple files.
Binary and source code are free to use in any way you like (though I'll appreciate an email just to let me know).

After download please replace the empty exiftool(-k).exe files in the archive with the real one, posting the full zip (around 7MB) fails.

PH Edit: See this post for the most recent version of the wrapper.

Phil Harvey

Nice!  I have added a link from the ExifTool home page.

- 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 ($).

rajesh

I have to updated exif keywords but not able to update IPTC keywords using C#.
Please let me know to can get Exif keyword's value and add to IPTC keywords?

thanks

iaremrsir

Hello, I've tried using your wrapper with success in reading metadata, but I'd like to be able to write metadata as well, which I'm having a hard time with. I've easily used your FetchExifFrom function. But now I'm trying to write metadata using the SendCommand function and it does nothing when I send strings to it.

Here's what my code block looks like now. Reading works fine, but I just can't write anything. My goal is to edit 3 tags of all DNG files in a directory. Any advice?

using (ExifToolWrapper exiftool = new ExifToolWrapper())
{
  exiftool.Start();
  foreach (string dir in workingDir)
  {
    // Get first DNG to check for ISO 100
    string[] dng = System.IO.Directory.GetFiles(dir, "*.dng");
    if (dng.Length == 0) continue;

    Dictionary<string, string> exif = exiftool.FetchExifFrom(dng[0]);

    // Build EXIF write command
    string command = "\n-Exif:BaselineExposure=2.4 " + dir + "\\*.dng\n-Exif:CalibrationIlluminant1=\"Standard Light A\" " + dir + "\\*.dng\n-Exif:CalibrationIlluminant2=\"D65\" " + dir + "\\*.dng\n";
    if (100 == Convert.ToInt32(exif["Exposure Index"]))
      command += "-Exif:WhiteLevel=3660 " + dir + "\\*.dng\n-stay_open\nFalse\n";

    exiftool.SendCommand(command);
  }
}

Phil Harvey

I have no idea what you are trying to do, and I don't know how the DLL API works, but you are not being consistent in the arguments you are sending exiftool.  Either the API takes arguments from a -argfile intput separated by newlines (likely), or as a command line separated by spaces.  You have done both.  I suspect that the code should look more like this (but I am just guessing):

    string command = dir + "\\*.dng\n-Exif:BaselineExposure=2.4\n-Exif:CalibrationIlluminant1=\"Standard Light A\"\n-Exif:CalibrationIlluminant2=\"D65\"\n";
    if (100 == Convert.ToInt32(exif["Exposure Index"]))
      command += "-Exif:WhiteLevel=3660\n";

    exiftool.SendCommand(command);


Also, I'm not sure why you were doing -stay_open False, and why this was only if the exposure index was 100, but I have removed this.

- 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 ($).

iaremrsir

Quote from: Phil Harvey on December 21, 2014, 09:45:21 AM
I have no idea what you are trying to do, and I don't know how the DLL API works, but you are not being consistent in the arguments you are sending exiftool.  Either the API takes arguments from a -argfile intput separated by newlines (likely), or as a command line separated by spaces.  You have done both.  I suspect that the code should look more like this (but I am just guessing):

    string command = dir + "\\*.dng\n-Exif:BaselineExposure=2.4\n-Exif:CalibrationIlluminant1=\"Standard Light A\"\n-Exif:CalibrationIlluminant2=\"D65\"\n";
    if (100 == Convert.ToInt32(exif["Exposure Index"]))
      command += "-Exif:WhiteLevel=3660\n";

    exiftool.SendCommand(command);


Also, I'm not sure why you were doing -stay_open False, and why this was only if the exposure index was 100, but I have removed this.

- Phil

I'm trying to add metadata to a directory of Cinema DNG files, but I haven't been able to write a string to send to the wrapper that actually does anything. So thanks for the suggestion, I'll try it to see how it works.

iaremrsir

Okay so the string you gave me worked for a single command. The string only added -Exif:BaselineExposure, and it ignored the rest of the commands. So I think I'll have to split the command calling into loop of some sort. Anyway that fixed the problem of writing metadata, but now I have a new problem. Writing to the DNG files caused exiftool to automatically create a .dng_original file for each dng. I read the documentation and remember seeing the command line argument -overwrite_original. Would I want to add this to the command I send to stay open or could I just add it to the argument list that starts the exiftool process?

iaremrsir

Okay, so adding -overwrite_original to the arguments list of the wrapper worked. But splitting the string that Phil suggested into singular commands didn't work. I'm going to fiddle around some more and update if I figure something out. Thanks for the help.

brain2cpu

#8
I'd like to post ExiftoolWrapper v2; main changes: direct get/set for orientation, get creation date as DateTime, the SetExifInto method, thread-safe communication with the exiftool process; you can find more in the attached source code.

PH Edit: See this post for the most recent version of the wrapper.

CodeWorks

SetExifInto isn't working.   What am i doing wrong?    The code executes with no errors but the metadata doesn't update.

            using (ExifToolWrapper extw = new ExifToolWrapper())
            {
                // Start ExifTool
                extw.Start();
                // Get Metadata
                Dictionary<string, string> d = extw.FetchExifFrom(Application.StartupPath + "/photos/SAI01.jpg");
                // Remove existing Lens Model
                d.Remove("Lens Model");
                // Add new Lens Model
                d.Add("Lens Model", "lens1234");
                //Write new Metadata
                extw.SetExifInto(Application.StartupPath + "/photos/SAI01.jpg", d, true);
            }

Thanks

Phil Harvey

I'm no expert on this interface, but I would guess that it should use tag names, in which case you should use "LensModel" and not "Lens Model" (ie. remove the space).  If this is the problem, reading ExifTool FAQ 2 may help in determining the appropriate tag names to use.

- 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 ($).

brain2cpu

Quote from: CodeWorks on May 22, 2015, 04:46:47 PM
Dictionary<string, string> d = extw.FetchExifFrom(Application.StartupPath + "/photos/SAI01.jpg");
.........
d.Add("Lens Model", "lens1234");
extw.SetExifInto(Application.StartupPath + "/photos/SAI01.jpg", d, true);
You can see existing tags as keys of dictionary d, you should check if you really have "Lens Model". Arbitrary keys and values can not be set as exif tags!
SetExifInto returns true if succeeded, please check returned value.
If you want to modify a single key the
SetExifInto(string path, string key, string val, bool overwriteOriginal = true)
method should be used, the override above will try to overwrite all tags with existing values.

CodeWorks

Thanks Phil and brain2cpu.   Removing the space between "Lens Model" worked.

I put a space in it because all the existing tags as Keys (such as "Lens Model") in dictionary d have a space.   Weird.  Perhaps FetchExifFrom isn't using the exact spelling of the Tag name for the Key name.

Thanks again


SarahC

brain2cpu, could you spare half an hour to help me work through this hanging problem I've got?   :)

I'm using your wonderful wrapper with my cataloging system - but I've got a hanging problem I've not been able to trace.

Would you be able to give me some pointers about what may be happening?

Thank you so much!   :D


https://exiftool.org/forum/index.php/topic,6524.0.html


(I've just noticed you've released a V2 of it - I've not had a chance to try that one.)

brain2cpu

SarahC, I just downloaded your source code and noticed you converted the C# code to vb, did you tried using the dll instead? I'm using it in my mp3Manager without problems.

SarahC

Quote from: brain2cpu on May 27, 2015, 05:51:22 AM
SarahC, I just downloaded your source code and noticed you converted the C# code to vb, did you tried using the dll instead? I'm using it in my mp3Manager without problems.

Sorry, I've been away from my dev environment!

I'll try that, for sure! That may have cured it - but the error only occurs on huge lists - is your MP3 collection large?

(and how do I get notifications on here?! I'm off to check "properties".)

rkamarowski

I'm getting the following error when the Start() method is executed:

The specified executable is not a valid application for this OS platform.


                ExifToolWrapper etw = new ExifToolWrapper();

                etw.Start();


I'm running VS 2015 on Windows 10

brain2cpu

Did you replaced the dummy exiftool exe with the real one?

rkamarowski


lyrico

Sorry to resurrect an old thread.  I've been playing around with the tool and it works great :)  However, I was wondering if there's any way to pass in a stream to a file instead of the physical file itself?  Often case, the only thing I get is a byte array of the file.  So instead of saving all the bytes to a temporary file, I would like to just pass in that byte array as a stream.

Thanks!

Phil Harvey

This is a basic functionality for the Image::ExifTool Perl library, but unfortunately the .NET wrapper goes through the exiftool application.  Here, the only thing you could possibly do would be to create some sort of pipe from your bytes array (which may be possible with .NET, I don't know), but then you would probably run into troubles with the .NET wrapper not supporting pipes (I'm guessing the wrapper probably uses pipes for the command-line arguments, and if so you probably can't pipe the images too).

So without knowing how the .NET wrapper works, I am guessing that it won't do what you want.

You could, however, launch ExifTool directly piping the image if you can figure out how to do that, without the ExifTool .NET wrapper.

- 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 ($).

brain2cpu

Unfortunately the wrapper does not support byte array, it works with filenames only.

macdonnie

Fantastic Tool and Wrapper!

I'm using the wrapper and the code executes until I call SetExifInto(). At that point my C# code pauses and will not continue. Am I doing something wrong? Do I need to call the function in a different manner or is it something else?

Some notes:
1) I can see that d is being populated and changed properly.
2) The SetExifInto is working and changing the 'Make' in the image's properties
3) When I call Start() two different exittool(-k).exes start up. And remain open the whole time. The first one has a memory footprint of 44MBs and the 2nd has a memory footprint of 0.6MBs


        public static void writeTag5()
        {
            using (BBCSharp.ExifToolWrapper extw = new ExifToolWrapper())
            {
                // Start ExifTool
                extw.Start();
                // Get Metadata
                Dictionary<string, string> d = extw.FetchExifFrom("C:\\Users\\donal\\Desktop\\P1020661_Fixed_b_w2.jpg");
                // Remove existing Lens Model
                d.Remove("Make");
                // Add new Lens Model
                d.Add("Make", "testing");
                //Write new Metadata
                extw.SetExifInto("C:\\Users\\donal\\Desktop\\P1020661_Fixed_b_w2.jpg", d, true);

            }
        }


Thanks,
-Don

brain2cpu

Everything seems alright in your code and notes, but it is not the best way to do it. You should send to SetExifInto() only things you want to change:

extw.SetExifInto("C:\\Users\\donal\\Desktop\\P1020661_Fixed_b_w2.jpg", new Dictionary<string, string> { ["Make"] = "testing" }, true);

however the code should not stop.

brain2cpu

Please use this new version, a possible stall in SetExifInto is fixed.

brain2cpu

ExiftoolWrapper is hosted on GitHub
https://github.com/brain2cpu/ExiftoolWrapper
a NuGet package is available too:
https://www.nuget.org/packages/ExiftoolWrapper/

The latest version can handle non-ascii filenames (discussed in https://exiftool.org/forum/index.php?topic=8382.0).

Phil Harvey

Great, thanks for the update!

- 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 ($).

VeryKross

I'm using the latest ExiftoolWrapper from GitHub with the latest Exiftool; reading data with FetchExifFrom seems to work great but when I try to write it back to a new file via SetExifInto things are a bit odd. What I'm finding is that many of the fields appear to be missing (e.g. "Author" will be filled in but "Date Taken" will be gone, "Camera maker" will be there but "Camera model" will be gone. I'm not doing anything to modify the Dictionary that's originally retrieved; we're creating a fresh new JPG based on the original so the new file has no metadata at all and we want to copy it across. One we have that working, THEN we'll want to support editing some of the metadata fields and include those changes when we save the file.

For now I'd just like to figure out why these gaps appear and I'm not quite sure how to proceed.

VeryKross

I'm having trouble reading multi-line Descriptions with ExifTool via the ExifToolWrapper and I'm not sure it's an issue with ExifTool or the wrapper itself. What I'm seeing is that if you go into something like Adobe Bridge and add a multi-line Description to an image (just press ENTER between each line) then you'll find that description is correctly saved and displays as multi-line when you re-load the image into Bridge or other metadata viewers. When I try and read it, what seems to happen is that somewhere along the way the "Newlines" (CR/LF or however its stored ... maybe an ASCII 13) are being converted to a period (.) and so I just get back a string with the lines concatenated with a period in between.

I'd love to know if this is a limitation of ExifTool or if it's coming from the wrapper; just helps give me a direction to investigate and resolve the issue.

Thanks much!

Phil Harvey

The default output of the exiftool app converts newlines to a period.  To get the newlines, the -b, -j, -X, -php or -csv option may be used.  The C++ interface for ExifTool uses the -php option when extracting information.

- 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 ($).

VeryKross

Thanks Phil - I'll play around with those options; hopefully the .Net wrapper will respect them :)

obetz

a quick look at the source code raised some questions, e.g.

Why does it use "exiftool(-k).exe"? As it is used with -stay_open, -k makes no sense IMO.

What happens if somebody passes a command dealing with more than one file? The return string is checked against "1 image files updated".

VeryKross

Hi Phil,

I wanted to loop back on this as it's proving to be a bit more troublesome to get this working with the C# wrapper than I first thought. I was wondering if there was any option in ExifTool to override the default newline conversion; instead of converting to periods, convert to a character(s) that are specified in the option (thinking along the lines of the -sep option).  This would allow me to specify an "unlikely" character, e.g. ~, that could be easily identified in the output string and converted back. Of course, that doesn't address saving multi-line text back to the metadata field, unless of course by specifying the same option while writing that you'd convert back to the proper newline character(s).

Thoughts?

Ken

Phil Harvey

Hi Ken,

Did you see FAQ 21?  There are many different alternatives for dealing with newlines.  I try to avoid adding new options unless absolutely necessary.

BTW, it isn't just newlines that are converted to ".".

- 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 ($).

StarGeek

Quote from: VeryKross on June 23, 2019, 12:49:10 AMI was wondering if there was any option in ExifTool to override the default newline conversion; instead of converting to periods, convert to a character(s) that are specified in the option (thinking along the lines of the -sep option).

I didn't read the whole thread, so I might be missing the point...

Take a look at adding the RepNL helper function to your .exiftool_config file.  You can then add -api "Filter=RepNL($_)" to the command and all New Lines, Carriage Returns, and Tabs will be escaped to \n, \r, and \t.  You can edit it to change to some other character sequence if you like.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

Phil Harvey

#35
@StarGeek: Very smart.  I'll add the API Filter option technique to FAQ 21.

- Phil

Edit:  FAQ updated.
...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 ($).

jodi319

Hi Brian2cpu. I am doing development with C# (specifically Xamarin.forms). I am interested in your ExifTool wrapper. Question: can I use your wrapper to extract Exif data in JSON format; just like the original ExifTool can?

brain2cpu

Hi, no, there is no direct json support here, but you can easily convert the internal data structures to json.

nemethv

hi all -- appreciating the topic has been quiet for a while but 2 questions regarding the wrapper extension for vscode/c#.
- 1: is it possible and if so how to override the starting arguments and
- 2: is it possible to define on a per-extension basis that some exif-saves should go into xmp while others should be embedded?
thanks.

ps. "explain to me like to a five year old" pls.

brain2cpu

hello, I'm not familiar with vscode extensions so can't be of any help on this.