Search code examples
javadllitunesjacobcom4j

Converting Java code thats calls iTunes DLLs from using Com4j to Jacob


I currently use Com4j to talk to iTunes from my Java app, unfortunately it does not work with 64bit Java and looks like it never will, so Im trying to use an alternative called Jacob instead.

Both libraries provide a tool to generate Java classes from a DLL, and the resultant classes are very similar and its been straightforward to change most of the code but Im failing on how to find subtypes

IITPlaylist object = itunes.createFolder(TextLabel.SAVE_ITUNES_PLAYLIST_FOLDER.getMsg());
IITUserPlaylist playlistFolder = object.queryInterface(IITUserPlaylist.class);

Both libraries have created IITPlaylist and IITUSerPlaylist classes but only com4j provides the queryInterface class, and no IITUserPlaylist is not actually a subclass of IITPlaylist.

Also com4j provides an is method, but jacob does not

if (next.is(IITFileOrCDTrack.class))

Anyone know how to resolve these issues ?

EDIT: Made some progress but still not got it working, there is a QueryInterface method that takes the guid of the class (include the curly brackets) , I found the guid by looking at the jacobgenlog.txt file which is created when you run jacobgen on the iTunes executable

This then returns another Dispatch object that is meant to relate to the subclass, however the simple cast Ive done is invalid, whats the mising step ?

 private static final String USER_PLAYLIST_GUID      = "{0A504DED-A0B5-465A-8A94-50E20D7DF692}";
IITPlaylist object = itunes.createFolder(TextLabel.SAVE_ITUNES_PLAYLIST_FOLDER.getMsg());
IITUserPlaylist playlistFolder = (IITUserPlaylist)object.QueryInterface(USER_PLAYLIST_GUID);

The is() functionality is replaced by checking the kind

IITTrack next = tracks.getItem(i);
if(next.getKind().equals(ITTrackKind.ITTrackKindFile))

A spanner in the works is that jacobgen getKind() methods are invalid Java because they try to return a new interface, and of course you cannot instantiate an interface, so I had to modify them as follows

ITPlayListKind goes from

public interface ITPlaylistKind extends __MIDL___MIDL_itf_iTunesCOMInterface_0001_0081_0001 {

}

to

public enum ITPlaylistKind {
    ITPlaylistKindUnknown,
    ITPlaylistKindLibrary,
    ITPlaylistKindUser,
    ITPlaylistKindCD,
    ITPlaylistKindDevice,
    ITPlaylistKindRadioTuner;
}

Within IITUserPlaylist

public ITPlaylistKind getKind() {
        return new ITPlaylistKind(Dispatch.get(this, "Kind").toDispatch());
    }

to

public ITPlaylistKind getKind() {
        return  ITPlaylistKind.values()[Dispatch.get(this, "Kind").getInt()];
    }

this wasnt an original idea by me, I got the idea from http://dot-totally.co.uk/software/itunescon/ which appears to be a modified version of the iTunes classes created by jacobgen, I didnt find it added that much and decided to stick with the jacobgen generated classes.


Solution

  • As soon as I set a bounty I work out the answer for myself.

    Simply just use the constructor

    IITPlaylist object = itunes.createFolder
            (TextLabel.SAVE_ITUNES_PLAYLIST_FOLDER.getMsg());
    IITUserPlaylist playlistFolder = new IITUserPlayList(object);
    

    The QueryInterface and GUID sctrings re not required.

    I was also having a problem working out how to add a track to a playlist, but you just need to construct a Variant from the track ( I dont have to do this anywhere else)

    IITTrack next = itunes.getLibraryPlaylist().getTracks()
          .getItemByPersistentID(persistentId.getHighBit(), 
                                 persistentId.getLowBit());
    playlist.addTrack(new Variant(nextTrack));