The C++ interface for ExifTool provides the source code for a set of objects that allow C++ applications to easily leverage the full power of the exiftool application through a simple interface. This interface handles all the hard work of launching, monitoring, controlling, and communicating with an external exiftool process.
Download Version 1.08 (23 kB) May 24, 2019
The download package contains the source code for the C++ interface for ExifTool, plus a few example programs. Unpack and type "make" to compile the example programs.
-stay_open
feature.The exiftool application must exist on the system. This interface should be platform independent, and has been tested on Mac OS X, Linux, and Windows (Cygwin).
Copyright © 2013-2019, Phil Harvey (philharvey66 at gmail.com)
This is software, in whole or part, is free for use in non-commercial applications, provided that this copyright notice is retained in the code. A licensing fee may be required for use in a commercial application. Contact Phil Harvey for more information.
The ExifTool object provides the C++ application programming interface for running exiftool. All exiftool features are accessible through these public methods of the ExifTool object.
Note that the methods should not be accessed concurrently in multiple threads for a single ExifTool object. Parallelization is accomplished by creating multiple ExifTool objects.
Below is a complete C++ code example of how to use an ExifTool object to extract information from a file (example1.cpp in the distribution):
#include <iostream> #include "ExifTool.h" using namespace std; int main(int argc, char **argv) { if (argc < 2) { cout << "Example1: Read metadata from an image." << endl; cout << "Please specify input file name" << endl; return 1; } // create our ExifTool object ExifTool *et = new ExifTool(); // read metadata from the image TagInfo *info = et->ImageInfo(argv[1]); if (info) { // print returned information for (TagInfo *i=info; i; i=i->next) { cout << i->name << " = " << i->value << endl; } // we are responsible for deleting the information when done delete info; } else if (et->LastComplete() <= 0) { cerr << "Error executing exiftool!" << endl; } // print exiftool stderr messages char *err = et->GetError(); if (err) cout << err; delete et; // delete our ExifTool object return 0; }
And here is the technique for writing tags (from example2.cpp in the distribution):
// set new values of tags to write et->SetNewValue("XMP:Title", title); et->SetNewValue("XMP:Description", desc); // write the information et->WriteInfo(argv[1]); // wait for exiftool to finish writing int result = et->Complete(); if (result <= 0) cerr << "Error executing exiftool!" << endl;
The following example programs are included in the example directory of the distribution:
example1 - extract all metadata from a file
example2 - write XMP:Title and XMP:Description to a file
example3 - read/write metadata from/to files
example4 - read a ThumbnailImage from one file and write it to another
example5 - interleaved command send/receive test
To compile the included example programs, unpack the distribution tarball, then type "make" from within the distribution directory. This will compile the programs in the "example" directory.
See the comments in the source files for more details about each example. To run successfully, these examples require that the exiftool application exists in the current path.
Create ExifTool object and launch an exiftool application as a separate process.
Internally, the arguments sent to exiftool are "-stay_open true -@ -
".
Each new ExifTool object maintains an independent exiftool application.
Prototype ExifTool(const char *exec=NULL, const char *arg1=NULL);
Inputs
const char *exec
Application to execute. If NULL, "exiftool" is assumed. const char *arg1
Optional first argument for application. Returns Pointer to ExifTool object.
Examples:
#include "ExifTool.h" ExifTool *et = new ExifTool();
ExifTool *et = new ExifTool("/Users/phil/Desktop/exiftool");
ExifTool *et = new ExifTool("C:\\WINDOWS\\exiftool.exe");
ExifTool *et = new ExifTool("/usr/bin/perl", "/usr/local/bin/exiftool");
If the directory is not specified in the exec
argument, then
the executable must be located somewhere in the system PATH.
Notes:
This constructor forks and execs an exiftool application process as well as
forking a watchdog process to monitor the main program. The watchdog is used to
terminate the exiftool process if the main program exits for any reason while
the exiftool process is still running. This prevents orphan exiftool processes
from remaining if the main program exits without properly destructing all
ExifTool objects (which could happen if the main program crashes). If
necessary, the public static int ExifTool::sNoWatchdog
may be set
to a non-zero value before creating an ExifTool object to prevent the watchdog
process from being created.
This constructor installs a signal handler to catch SIGPIPE messages to
prevent the main program from being terminated by any communication problem with
the exiftool process. If necessary, the public static int
ExifTool::sNoSigPipe
may be set to a non-zero value before
creating any ExifTool object to bypass installing this SIGPIPE handler.
If IsRunning returns false for a newly created ExifTool object, make sure that the path to exiftool is correct. See the last three examples above for techniques for specifying the path. If the path is not specified, then exiftool must exist in the system PATH when the program is run.
Delete ExifTool object and terminate the associated exiftool process.
Prototype virtual ~ExifTool();
Inputs (none) Returns (none)
Example:
delete et;
It is safe to call the destructor at any time. If the exiftool process is still working, the destructor will wait until all commands have completed before terminating the process. This also terminates the watchdog process if it was created, but does not uninstall the SIGPIPE handler.
Read one or more image files and/or directories, and return the extracted meta information as a linked-list of TagInfo structures. This is the one-step function for retrieving meta information from an image. Internally, ImageInfo calls ExtractInfo to send the appropriate Command to the exiftool process, then GetInfo to Complete the command and convert the result into a linked list of TagInfo structures.
Prototype TagInfo *ImageInfo(const char *file, const char *opts=NULL, double timeout=NEVER);
Inputs
const char *file
Name of files and/or directories to process. Names are separated by newline characters ('\n'). const char *opts
Additional exiftool options, separated by newlines. See notes 2, 3 and 5. Only the following exiftool options are allowed:
-TAG -x -b -c -charset -d -L -lang -listItem -n -sep -sort --a -e -ee -ext -F -fast -fileOrder -i -if -m -password -r -scanForXMP -u -U -z -globalTimeShift -use -@ -apidouble timeout
Maximum number of seconds to wait for exiftool to complete the command. The default of NEVER
waits forever (provided the exiftool process doesn't terminate).NOW
or 0 returns immediately.Returns Pointer to linked list of TagInfo objects (see below). The returned pointer may be NULL if any error occurred during processing or if the timeout time was exceeded (see note 1). The first TagInfo structure for each processed file will have a name
of "SourceFile", and avalue
that gives the file path. Only thename
,value
andnum
pointers are guaranteed to exist. Any other pointer in a TagInfo structure may be NULL. The caller is responsible for deleting the returned TagInfo list when done (deleting the first element deletes the entire list).
struct TagInfo { TagInfo(); virtual ~TagInfo(); char *group[3]; // family 0-2 group names char *name; // tag name char *desc; // tag description char *id; // tag ID char *value; // converted value int valueLen; // length of value in bytes (not including null terminator) char *num; // "numerical" value int numLen; // length of numerical value int copyNum; // copy number for this tag name TagInfo *next; // next TagInfo in linked list };
Examples:
TagInfo *info = et->ImageInfo("image.jpg"); /* << do stuff with the returned TagInfo list here >> */ delete info; // must delete the TagInfo list when done
Extract preview images from all files in a directory (recursively):
TagInfo *info = et->ImageInfo("c:\\images","-b\n-r\n-previewimage");
1. If this routine returns NULL and LastComplete returns 0, then a timeout occurred. In this case, GetInfo may still successfully return the metadata after waiting for the exiftool process to complete the operation. A negative value from LastComplete indicates an (usually unrecoverable) error in the ExifTool process, and if this happens the solution is to delete and recreate the ExifTool object. A NULL return and a positive value from LastComplete indicates that exiftool ran, but returned no information from this file -- in this case, GetError should return an error message from the exiftool process.
2. The -b
option must be used to extract binary information.
3. This routine uses -sep ", "
to combine list items into a
single string, but the separator may be changed by specifying a different
-sep
option in the opts
argument.
4. The metadata returned for the processed files is all stored in memory, so processing a large number of files at once may use a significant amount of memory.
5. Internally, ExtractInfo uses
-php -l -G:0:1:2:4 -D
to retrieve detailed information in PHP
format with group names. This combination implies -a
, so duplicate
tags are extracted by default, but this may be disabled by using the
--a
option.
Send command to exiftool process with the necessary arguments to extract information for reading and parsing by GetInfo. This routine always returns immediately, and the command is queued if the exiftool process wasn't able to receive it immediately.
Prototype int ExtractInfo(const char *file, const char *opts=NULL);
Inputs
const char *file
Name of one or more files and/or directories to write, separated by newlines. const char *opts
Additional exiftool options, separated by newlines. See ImageInfo for more details. Returns ID number for this command (>0), or an error code (<0).
Example:
int cmdNum = et->ExtractInfo("test.jpg", "-b");
Read and parse information returned after call to ExtractInfo.
This routine calls Complete internally to wait for the
command to complete (unless cmdNum
is -1), then reads the output
and converts it into a linked list of TagInfo structures.
Prototype TagInfo *GetInfo(int cmdNum=0, double timeout=NEVER);
Inputs
int cmdNum
Command number returned by ExtractInfo, 0 to process the results from the next available command, or -1 to parse the results of the previously completed command. double timeout
Maximum time in seconds to wait for the response. The default ( NEVER
) will wait forever for the next command to complete. SpecifyNOW
to return immediately (with a return value of NULL if the command hasn't completed).Returns Pointer to linked list of TagInfo structures, or NULL on timeout or error or if exiftool doesn't return any information. The caller is responsible for deleting this list when done. See ImageInfo for more details.
Example:
TagInfo *info = et->GetInfo(cmdNum, 0.5); // wait for up to 0.5 seconds
After calling GetInfo, LastComplete may be called to obtain the command ID number or error code from this operation, and GetError may be called to return any error output from exiftool.
Note:
Responses from commands must be obtained in sequence. If an out-of-sequence command ID is passed to this routine, then earlier command responses are discarded until the response from the specified command is received.
Assign a new value for a tag. This is a convenience routine that automates handling of the TagInfo list passed to WriteInfo. It should be called separately for each tag assignment before calling WriteInfo to actually write the information to file.
Prototype int SetNewValue(const char *tag=NULL, const char *value=NULL, int len=-1);
Inputs
const char *tag
Name of tag to write. May be prefixed by one or more group names, separated by colons, and/or suffixed by a '#' to write the "numerical" value. See the exiftool application documentation for details. Set to NULL to clear the internal list of new values set from previous calls to SetNewValue. const char *value
Value to write for the tag. Set to NULL or an empty string to delete the tag. int len
Length of value in bytes. This must be set only for binary data values. For simple string values, the default of -1 causes the string length to be used. Returns The total number of tag values set up to this point, or -3 on an out-of-memory error.
Examples:
int count = et->SetNewValue("XMP:Title", "some title");
et->SetNewValue("Orientation#", "1"); // set "numerical" value
Write new tag values to one or more files. This routine always returns immediately. Call Complete afterwards to wait for the command to complete before checking the output and error messages with GetOutput and GetError.
Prototype int WriteInfo(const char *file, const char *opts=NULL, TagInfo *info=NULL);
Inputs
const char *file
Name of one or more files and/or directories to write, separated by newlines. const char *opts
Additional exiftool options, separated by newlines. Most writing options are compatible with this routine, provided the -ex
option used internally is not overridden.TagInfo *info
Pointer to linked list of TagInfo structures for the tags to write. See the ImageInfo section for details about the TagInfo structure. If NULL, the internal list of tags generated by previous calls to SetNewValue is used. If not NULL, tag values set by previous calls to SetNewValue have no effect. Returns Command number (>0), or error code (<0).
The input TagInfo list is not modified by this routine, so WriteInfo may be called repeatedly using the same TagInfo list. (This also applies to the internal TagInfo list used when the "info" argument is NULL.)
Examples:
Write values from previous calls to SetNewValue:
int cmdNum = et->WriteInfo("test.jpg", "-overwrite_original");
Copy all metadata from another image (this also writes tags set by SetNewValue, but any same-named tags copied with the-tagsFromFile
take precedence):
int cmdNum = et->WriteInfo("dst.jpg", "-tagsfromfile\nsrc.jpg\n-all:all");
Notes:
1. The exiftool -sep ", "
option is used by this routine to
split values at comma-space combinations when writing List-type tags. The
-ex
option is also used internally to escape characters in the
values from the TagInfo list if necessary to avoid syntax problems in the
arguments passed to exiftool.
2. Any "SourceFile" entries in the input TagInfo list are ignored. (These are generated by GetInfo at the start of each file.)
3. If a tag value
is NULL, the num
value is written
instead, and a "#" is appended to the tag name
. If both
value
and num
are NULL, then the tag is deleted. The
value lengths (valueLen
and numLen
) must be set if the
corresponding pointer is not NULL.
4. Any defined group
names are used to prefix the tag name in
the command sent to exiftool.
5. The TagInfo desc
, id
and copyNum
elements are not used by WriteInfo.
This routine may be used to send an arbitrary command to exiftool. If exiftool is busy, this routine queues the command and always returns immediately. After sending a command, call Complete to receive the command output.
Prototype int Command(const char *cmd=NULL);
Inputs
const char *cmd
Command argument string. Arguments are separated by newlines, in a format identical to the exiftool -@
argfile. Set to NULL to continue writing previously queued commands and check to see if the queue is empty.Returns ID number for this command (>0), an error code (<0), or 0 if the input cmd
is NULL and the command queue is empty.
Example:
int cmdNum = et->Command("-overwrite_original\n-artist=Phil Harvey\nimage.jpg");
This routine adds the necessary arguments to the exiftool command to ensure
proper command/response synchronization, including an -execute
option carrying the command ID number. Before sending the command to exiftool,
this routine first calls IsRunning to ensure that the
process is running, and returns immediately with a value of -1 if it is not.
Commands are queued if the exiftool process is busy, and sent as previous commands are completed. The number of queued commands is limited only by the system memory.
Known Problems:
Under Cygwin in Windows, it seems that the write pipe may block if it becomes full (even though it is explicitly set to O_NONBLOCK). If this happens, this call may hang. To avoid this problem, avoid queuing up a large number of commands before completing them in Windows.
Receive the output of a command from the exiftool process. Call this routine
after issuing a command via either Command or
ExtractInfo. If the returned command ID number is
greater than zero, then the command output strings are available via
GetOutput and GetError,
and the output from ExtractInfo may be parsed
by calling GetInfo with a cmdNum
of -1.
Prototype int Complete(double timeout=NEVER);
Inputs
double timeout
Maximum time in seconds to wait for the response. The default ( NEVER
) will wait forever for the next command to complete. SpecifyNOW
to return immediately (with a return value of 0 if the command hasn't completed).Returns Command ID number for a successful response (>0), an error code (<0), or 0 if the timeout was reached.
Example:
int cmdNum = et->Complete(NOW); // check for complete command without waiting
Note:
This routine returns a positive (>0) command number as long as communication with the exiftool process was completed successfully, even if exiftool itself returned an error message. To determine the success or failure of the exiftool command, GetOutput, GetError and/or GetSummary should be called after Complete has returned a positive number.
Return command number from last call to Complete. This routine is useful to determine the result code from the last Complete call if it was invoked via ImageInfo or GetInfo.
Prototype int LastComplete();
Inputs (none) Returns Command number (>0) or error code (<0) from last call to Complete, or 0 if Complete hasn't been called or hasn't finished yet.
Set value to be returned by next call to LastComplete.
Prototype void SetLastComplete(int lastComplete);
Inputs New value for the last complete command number. Returns (none)
This routine may be useful in some situations to reset the last complete command number after processing a command to aid in synchronization of multithreaded applications.
Get stdout stream from the last complete command.
Prototype char *GetOutput();
Inputs (none) Returns Pointer to a null-terminated string of the stdout output from the last complete exiftool command, or NULL if the most recent call to Complete was not successful.
The returned string is part of an input buffer maintained by the ExifTool object, but the caller is free to change bytes within the length of this string (as is done by GetInfo when parsing the returned information). The string is overwritten on the next call to Complete.
Get length of output data in bytes.
Prototype int GetOutputLen();
Inputs (none) Returns Number of bytes in output data (not including null terminator).
For normal string output this routine is not usually called since the string
is null terminated. However, binary data may be returned if
Command is called with a bare -b
option, in
which case this routine must be used to get the length of the binary data since
it may contain embedded null bytes.
Get stderr stream from the last complete command.
Prototype char *GetError();
Inputs (none) Returns Pointer to a null-terminated string of the stderr output from the last complete exiftool command, or NULL if there was no stderr output or the most recent call to Complete was not successful.
The returned string is part of an input buffer maintained by the ExifTool object, but the caller is free to change bytes within the length of this string. The string is overwritten on the next call to Complete.
Helper function to retrieve the specified count from the exiftool summary statistics. The summary statistics are only available after a command has successfully completed.
Prototype int GetSummary(const char *msg);
Inputs
const char *msg
Summary message string. The macro definitions below are included in ExifTool.h for the common exiftool messages. Returns Count from the specified summary line of the exiftool output, or -1 if the specified message wasn't found.
Definitions of common exiftool message strings (from ExifTool.h):
#define SUMMARY_DIRECTORIES_SCANNED "directories scanned" #define SUMMARY_DIRECTORIES_CREATED "directories created" #define SUMMARY_FILES_FAILED_CONDITION "files failed condition" #define SUMMARY_IMAGE_FILES_CREATED "image files created" #define SUMMARY_IMAGE_FILES_UPDATED "image files updated" #define SUMMARY_IMAGE_FILES_UNCHANGED "image files unchanged" #define SUMMARY_IMAGE_FILES_MOVED "image files moved" #define SUMMARY_IMAGE_FILES_COPIED "image files copied" #define SUMMARY_FILE_UPDATE_ERRORS "files weren't updated due to errors" #define SUMMARY_FILE_CREATE_ERRORS "files weren't created due to errors" #define SUMMARY_IMAGE_FILES_READ "image files read" #define SUMMARY_IMAGE_FILE_ERRORS "files could not be read" #define SUMMARY_OUTPUT_FILES_CREATED "output files created" #define SUMMARY_OUTPUT_FILES_APPENDED "output files appended" #define SUMMARY_HARD_LINKS_CREATED "hard links created" #define SUMMARY_HARD_LINK_ERRORS "hard links could not be created" #define SUMMARY_SYMBOLIC_LINKS_CREATED "symbolic links created" #define SUMMARY_SYMBOLIC_LINK_ERRORS "symbolic links could not be created"
Example:
int num_updated = et->GetSummary(SUMMARY_IMAGE_FILES_UPDATED);
Note:
The exiftool summary message usually goes to stdout (accessed via
GetOutput), but may go to stderr (accessed via
GetError) if certain options are used (ie.
-csv
, -h
, -j
, -p
,
-php
or -X
). This routine will scan both pipes for
the specified message.
Check to see if the ExifTool process is still running. If this routine returns zero, then the exiftool process has died and the ExifTool object should be deleted and recreated.
Prototype int IsRunning();
Inputs (none) Returns Non-zero if the exiftool process is still running.
If the exiftool process dies, other routines may return an error code, or may time out. This routine allows verification that the exiftool process is still running, and may be called at any time.
Specifies time to wait in microseconds each time through the processing loop if there were no commands in the queue. Default is 1000 us.
Prototype void SetWaitTime(int waitTime);
Inputs Wait time in microseconds Returns (none)
Error codes returned by ExifTool methods:
Error Code Description -1 exiftool process is not running -2 error communicating with exiftool process -3 out of memory for buffer -4 timeout waiting for stderr output from exiftool process -5 file pointer is null in function argument