Interface Features
Requirements
License
ExifTool Methods
Programming Examples
Error Codes
Revision History

C++ Interface for ExifTool (by Phil Harvey)

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.

Interface Features

Requirements

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

License

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.


ExifTool Object Methods

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.


Programming Examples

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.


ExifTool (constructor)

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.

PrototypeExifTool(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.
ReturnsPointer 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.


~ExifTool (destructor)

Delete ExifTool object and terminate the associated exiftool process.

Prototypevirtual ~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.


ImageInfo

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.

PrototypeTagInfo *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 -@ -api
double 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.
ReturnsPointer 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 a value that gives the file path. Only the name, value and num 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).

The TagInfo structure:

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");

Notes:

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.


ExtractInfo

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.

Prototypeint 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.
ReturnsID number for this command (>0), or an error code (<0).

Example:

int cmdNum = et->ExtractInfo("test.jpg", "-b");

GetInfo

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.

PrototypeTagInfo *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. Specify NOW to return immediately (with a return value of NULL if the command hasn't completed).
ReturnsPointer 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.


SetNewValue

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.

Prototypeint 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.
ReturnsThe 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

WriteInfo

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.

Prototypeint 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.
ReturnsCommand 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.


Command

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.

Prototypeint 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.
ReturnsID 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.


Complete

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.

Prototypeint 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. Specify NOW to return immediately (with a return value of 0 if the command hasn't completed).
ReturnsCommand 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.


LastComplete

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.

Prototypeint LastComplete();
Inputs(none)
ReturnsCommand number (>0) or error code (<0) from last call to Complete, or 0 if Complete hasn't been called or hasn't finished yet.

SetLastComplete

Set value to be returned by next call to LastComplete.

Prototypevoid SetLastComplete(int lastComplete);
InputsNew 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.


GetOutput

Get stdout stream from the last complete command.

Prototypechar *GetOutput();
Inputs(none)
ReturnsPointer 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.


GetOutputLen

Get length of output data in bytes.

Prototypeint GetOutputLen();
Inputs(none)
ReturnsNumber 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.


GetError

Get stderr stream from the last complete command.

Prototypechar *GetError();
Inputs(none)
ReturnsPointer 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.


GetSummary

Helper function to retrieve the specified count from the exiftool summary statistics. The summary statistics are only available after a command has successfully completed.

Prototypeint 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.
ReturnsCount 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.


IsRunning

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.

Prototypeint IsRunning();
Inputs(none)
ReturnsNon-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.


SetWaitTime

Specifies time to wait in microseconds each time through the processing loop if there were no commands in the queue. Default is 1000 us.

Prototypevoid SetWaitTime(int waitTime);
InputsWait time in microseconds
Returns(none)

Error Codes

Error codes returned by ExifTool methods:

Error CodeDescription
-1exiftool process is not running
-2error communicating with exiftool process
-3out of memory for buffer
-4timeout waiting for stderr output from exiftool process
-5file pointer is null in function argument

Revision History

May 24, 2019 - Version 1.08

May 21, 2019 - Version 1.07

Oct 5, 2018 - Version 1.06

July 24, 2018 - Version 1.05

Sept. 22, 2016 - Version 1.04

June 10, 2016 - Version 1.03

May 22, 2014 - Version 1.02

Dec. 16, 2013 - Version 1.01

Dec. 1, 2013 - Version 1.00