File I/O Plugins: A Language Agnostic Guide

** LINKS UPDATED **

Ciao Jan,

I’m currently working on a language agnostic presentation of the File I/O plugin interface, in a dev branch of the PM Assets project:

The idea is to simplify developing a file i/o plugin across different languages by providing explicit data types and functions descriptions. The document is based on some note works I had jotted down when trying to establish which data types I had to use in PureBasic, by looking into the Delphi and C++ types. So, hopefully it’s going to be time-saver for people coming from languages other than C/Delphi.

I’m also planning to write a more detailed explanation of how the communication and data exchange occurs between PM and the plugin, to make looking up things easier during work, and to cover better some aspects which are not to clear in the present documentation (small things mainly). Again, I’m going to be using my annotation from the various experiments carried so far, which lead me to look into these details.

I’ll be using this thread to ask you some clarifications on some aspects which I’m still not quite sure about; and to get some feedback on wether this document is being accurate or not.

I though of posting the links straight away, so that work on the document might be followed by anyone interested, and get some feedback and advise too, before merging it into the master branch.

Here my first question:

  • Which is the lowest version of Pro Motion supporting the current file i/o interface? (ie. from which versions of PM will plugins be usable)

Thanks

Doubts on Setting Errors

Ciao Jan,

I have a question to ask you to clarify how file i/o plugins handle setting errors. I must be getting something wrong here because I keep getting unexpected errors and error messages are not always being displayed as expected.

The point which is unclear to me is wether DLL functions that “can set error” do this by either:

  1. By ONLY setting the Error String that will be returned via getErrorMessage() (i.e. getErrorMessage() is called after every call to a function that “can set error”, to see wether it returns a null pointer or a pointer to a string).
  2. By both setting the Error String AND returning false. (i.e. PM will only call getErrorMessage() in case a function that “can set error” returns false)

Up to know, I’ve been using the second approach, but I’m starting to realize that some functions which are said to be able to “set error” are declared as void (at least in the documentation). So I’m starting to think that the first approach might be the correct one — and that this might be the cause for the problems that had me running in circles for a while (which I was attributing to DLL thread safety so far).

Thanks.

Ciao Tristano,

great that you put some energy into the plugin system. I did not find the time myself to create useful plugins, but here are a couple of image/animation file types that are worth being addressed, e.g. Amiga IFF, C64 or types that might be used along with game engines.
I’m sure that using the interface will lead to more needs of exchanging certain data.

Which is the lowest version of Pro Motion supporting the current file i/o interface? (ie. from which versions of PM will plugins be usable)

V6.5

The point which is unclear to me is wether DLL functions that “can set error” do this by either:

  1. By ONLY setting the Error String that will be returned via getErrorMessage() (i.e. getErrorMessage() is called after every call to a function that “can set error”, to see wether it returns a null pointer or a pointer to a string).
  2. By both setting the Error String AND returning false . (i.e. PM will only call getErrorMessage() in case a function that “can set error” returns false )

The first approach is the one that is used. In theory every function could set an error but only after calling the functions that are allowed to do so, the getErrorMessage() function is called. If a function sets an error message without being allowed to do so then the next function that is allowed will raise the error.
The result of a failing function is dismissed.
But… there is an exception: initialize. Because the sole purpose of this function is to activate the plugin its result says if this was successful or not. I updated the docs.

I also checked the function docs to see if there is something wrong and this was the case. I also had to modify some code and the changes will be available with 7.2 (incl. next preview beta). Despite of the changes I made to the code I think it’s not relevant to the overall plugin behavior. That means even if a plugin works as previously described in the docs it will still work. That’s why I won’t change the interface version number.

initialize
canHandle
Doc said that you can set an error but an error message would not have displayed immediately.
Fixed.

setProgressCallback
getWidth
getHeight
Doc said you can set an error, but it does not make sense. For setting a callback there is no actual reason for an error.
getWidth/getHeight must work without error because loadBasicData() prepares them and this is enough for throwing an error at that time.
So the docs and logic has been changed to not allow for errors.

getFileTypeId
getFileBoxDescription
getFileExtension
isReadSupported
isWriteSupported
canExtractPalette
Doc said that you can’t set an error but an error message would have been displayed immediately if you used one. These function should not need any error handling. It has been removed from the code now.

Hope this helps you.

-Jan

1 Like

Thanks Jan,

your reply and the updates to the Developer Interface document were very useful. Now the problem is much clearer and this should help me fix the plugins I was working on.

I’ve also udpated the language-agnostic document draft accordingly.

Q About Plugin File Type Id

Jan, I wanted to ask you a question about getFileTypeId(), mainly out of curiosity. This function, if I’ve understood correctly, is used by PM to obtain a unique Id that can be used as an internal reference to the plugin, and it’s not intended to be shown to users (getFileBoxDescription() is used to define how the plugin will be described to end users in the UI).

Since the Id is not shown to end users (except in crash reports), why doesn’t PM just generate a unique Id for each plugin at initialization time? For example, it could have generated a UUID/GUID at launch time, or even use the plugin DLL filename as a unique Id, since all file I/O plugins reside in the same folder and will therefore all have unique filenames.

Are there some added benefits in allowing developers to define a specific plugin Id, which I’m missing out?

This ID is internally used to identify the file i/o module. For example if the user selects plugin X with “save as” then the same file i/o module must be used with the next “save”. Same goes for preselected file types in project presets etc. . The file extension is typically not sufficient. Yes, I could have used the dll name because it would be unique.

Ciao Jan,

a question about the setFilename function. The official documentation states:

Indicates that a new file is to be processed and gives the corresponding file name.
The plugin should reset internal structures if the file name is different to the one set before.
At this point it is undefined if the file is to be written or read!

The above description isn’t quite clear about the context of the new file.

If I remember correctly from my trial and errors with plugins, this function is called whenever the user selects a file in (one of) the file i/o dialogs — i.e. a file with an extension which matches the file extension handled by the plugin.

Which (if I remember correctly) means that at the time the function is called there is no actual certainity that the selected file will be processed at all, but just that the user has selected it the dialog — and that the plugin shold acknoledge this to be ready (if and when) actual processing functions will be called.

Ciao Tristano,

you are right, basically, but also keep in mind that a user can start a multi file process, e.g. exporting or importing a series of images. So it’s not bound to a file selection done by the user.
Whenever this function is called a new file might be addressed.

-Jan

but also keep in mind that a user can start a multi file process, e.g. exporting or importing a series of images. So it’s not bound to a file selection done by the user.
Whenever this function is called a new file might be addressed.

Thanks for the info, this is very helpful to know, especially in the initial stages of working on a plugin. Knowing the context of the call is always good from a designer point of view. For example, some file filter might need to create a dialog to ask the user to make a choice of some sort (e.g. compression level, interlacing, etc.), so understanding the right point where to do so is crucial.

About Plugin File Type Id (Again)

This ID is internally used to identify the file i/o module. For example if the user selects plugin X with “save as” then the same file i/o module must be used with the next “save”. Same goes for preselected file types in project presets etc. . The file extension is typically not sufficient. Yes, I could have used the dll name because it would be unique.

I suggest that in the future, if the file i/o interface bumps to a new version breaking backward compatibility, the File Type Id might become optional or ignored. If PMNG were to use the DLL name as File Type Id it could spare developers having to declare one.

I know, it’s no big deal, but since this is a hidden internal ID which could really only show up in crash reports, it would make more sense if the report would refer to the DLL filename anyhow (the File Type Id wouldn’t mean anything except to the plugin author).

In the future PMNG could either:

  • Always ignore the pointer returned by getFileTypeId() and always use the DLL name (maybe not even call getFileTypeId() at all).
  • At least tollerate getFileTypeId() returning a null pointer and fallback on the DLL name.

Of course, the main issue here would be compatibility with PM 6, where plugins not returning a valid string pointer from getFileTypeId() would not register correctly. But if the interface switches to a higher version (>= 2) then the new interface would be for PMNG only anyhow, and the getFileTypeId() function could be dropped entirely.

This should be safe even if in the future there’ll be a 64 bit version of PMBG, for the DLLs would live in separate folders, so even if a DLL had the same name for both x86 and x64 it would create conflicts. Again, Windows files being case insensitive there is no risk of two similarly named DLLs either — and, in any case, the ID string could be case sensitive to be on the safe side for Linux running PMNG under Wine or Mono.

IMO, there are less risks of same-ID conflicts using the DLL filename than an arbitrary chosen ID — with the latter, for example, if a user was to rename different versions of the same plugin inside the plugins/ folder, PMNG would end up trying to register the same ID multiple times, while with the former these could coexists without problems.

A side effect is that the ID is also used to define Ids in project presets. If a dll is for whatever reason renamed then it would not work anymore. I favor an explicit definition over such automatic conventions but yes, I can make it an optional value, using the dll name if it’s not defined.