Using the FMOD API with MASM – Part 3/3

We’ve heard that a million monkeys at a million keyboards could produce the complete works of Shakespeare; now, thanks to the Internet, we know that is not true. — Robert Wilensky



Building our own import library

Now this time we will just build our own import libraries. This will obviously make for more optimized code, as it will not have to call LoadLibraryA and GetProcAddress every time we want to call a FMOD function. It also saves us the hassle of having an extra asm file to include into our project (i.e. if we were to put the prototypes and function definitions in their own asm file and link it).

This topic has been covered by one of the masters of MASM coding already, so what I am writing here is based on his tutorial which you might still be able to find here: http://win32assembly.online.fr/importlib.html. Iczelion also supplied two nifty tools which allow us to extract the function names from VC++ import libs and create import libs for the use with MASM. They are “Lib2Def” and “MLib” respectively. They are included in the linked tutorial and also in the package you will find at the end of this article. Just put the VC++ lib you want to convert into the same directory (in our case this would be “fmodvc.lib”, then run Lib2Def, then MLib and if everything went OK you got your MASM import lib.

If you run into trouble doing this, just as I did, you will have to do it the hard way: typing it all by hand. Fortunately, by reading Iczelions tutorial you know how – and for anyone wanting to code FMOD stuff in MASM even more fortunate I will do this for you and, just in case, explain it step by step.

The reason why those tools wont create the library for us becomes apparent when we look at the output which “Lib2Def” creates (or rather when trying to compile it manually). The linker tells us, that the supplied ordinals are invalid. Ordinal numbers may only be numbers between 1 and 65,535 inclusive, so it is apparent, that the ordinals “Lib2Def” found are wrong. If you fire up some other PE exploring tools, such as PE-Explorer, and check the exports with it, you will see that the export ordinals differ vastly from the ones “Lib2Def” found.

PE Explorer image

Those are the actual ordinals we need to supply in our .DEF file. As to why “Lib2Def” comes up with the wrong ordinals I dont know. I suspect the dll being packed with UPX being the culprit but I might be wrong. On the positive side, this gives us the opportunity to do it by hand and therefore learn more about the topic.

So Lib2Def provided us with a nice listing of all the functions contained in “fmodvc.lib”, only MLib failed to create a MASM lib. Therefore we at least wont have to type all the function names and their amount of parameters ourselves. We need 2 more things: a dll asm source code which contains all those functions and a matching module definition file. For those of you that were to lazy to read Iczelions tutorial: MASM will create an invokable import lib for any dll-project you are building, so basically we create a fake FMOD dll and get a working FMOD lib.

Creating the DLL asm source code

Creating the DLL asm source code “fmod.asm” is pretty much straight forward. All we need to do is to create an empty function with the proper amount of parameters for each of the functions we want to include in the lib:

.386
.model flat,stdcall
.code
function1 proc param1:DWORD, param2:DWORD
function1 endp
function2 proc param1:DWORD
function2 endp
end

 
The .icz file provides us with all the information we need. The function name (omit the leading underscore) delimited by an “@” from the size of the parameters in bytes. From the size of the parameters we can conclude the amount of parameters, as the size of one parameter is always a DWORD (4 bytes). At the end of the line we find the (false) ordinal, which is used to retrieve the address of the symbol to be imported.

So the first few lines of our new asm file would look like this:

.386
.model flat,stdcall
.code
FSOUND_File_SetCallbacks proc a1:DWORD, a2:DWORD, a3:DWORD, a4:DWORD, a5:DWORD
FSOUND_FILE_SetCallbacks endp
FSOUND_SetMemorySystem proc a1:DWORD, a2:DWORD, a3:DWORD, a4:DWORD, a5:DWORD
FSOUND_SetMemorySystem endp
FSOUND_GetMemoryStats proc a1:DWORD, a2:DWORD
FSOUND_GetmemoryStats endp
FMUSIC_SetLooping proc a1:DWORD, a2:DWORD
FMUSIC_SetLooping endp
.
.
.
end

Creating the module definition file

After we finished the daunting task of writing the DLL asm code we have the other daunting task of creating the module definition file “fmod.def”:

LIBRARY <name of dll>
EXPORTS
function1 @ordinal1
function2 @ordinal2
.
.

To find the right ordinals I used PE-Explorers Export Viewer. As it is sorted by ordinals, and the output of “Lib2Def”, which I used so I dont have to type each function name myself, is not, I had to do quite some search and replace, but after a while we do get some output like this:

LIBRARY FMOD
EXPORTS
FSOUND_File_SetCallbacks @96
FSOUND_SetMemorySystem @174
FSOUND_GetMemoryStats @111
FSOUND_SetLooping @30
.
.

The use of ordinals is not always needed, as typically when a symbol is imported it looks up the symbol name to get the ordinal to import the proper symbol. This does not work in our case, again I suspect this to be the case due to the dll being packed, and again I might be wrong. Fact is it doesnt work without supplying the ordinals, so lets just make it a habit anyway.

Now all that is left to do is to compile and link the files:

ml /c /coff /Cp fmod.asm
link /DLL /NOENTRY /def:fmod.asm /subsystem:windows fmod.obj

Creating the include file

Now when you include the library at the start of your MASM code and try to invoke the functions, you will naturally be presented with another compiler errror. You will be told, that you have undefined symbols, namely those symbols you were trying to import. This is the same “problem” we had before: the functions are not prototyped yet, so MASM cannot make use of its inherent function wrapper. We could, as we did before, just prototype them at the beginning of our code. This is, especially for larger projects not a perfect solution. Instead we should prototype the functions in their own include file and include that at the start of the code just as we normally do with other include files.

; ============================================
; fmod.inc created by Sascha Hennig 25.02.2008
; ============================================
IFNDEF FMOD_INC
FMOD_INC equ <1>
FSOUND_Init PROTO :DWORD, :DWORD, :DWORD
FMUSIC_LoadSong PROTO :DWORD
FMUSIC_PlaySong PROTO :DWORD
FMUSIC_SetMasterVolume PROTO :DWORD, :DWORD
FMUSIC_StopSong PROTO :DWORD
ELSE
echo -----------------------------------------
echo WARNING Duplicate include file fmod.inc
echo -----------------------------------------
ENDIF

First we check weather the include file is already included. If it is output the error message when building the project. If its not included yet we protoype all the functions. Fortunately there is a tool that will do all the work for us: “lib2inc” which you might still be able to get here. It is also included in the final tutorial package. Simply run the executable from the command line like that: “lib2inc fmod.lib -s” and you should have a working .INC file. You might still want to modify it and add the check weather the file was included already (as described above).

And that is all there is to it. We now do have a working import library and a working include file. All we need to do to use the functions in the dll now, is to include both files at the start of our code.

Talking about Dynamic Link Libraries: It is almost always better to leave the dll in the execuatable directory than copying it to the windows directory. The same is true for ini-files. I never understood what made Microsoft believe that it is a grand idea to spam system folders with application dependent files. For some dlls, shared by many programs, this might make sense (see DirectX), but oftentimes this leads to more trouble than is good. Especially for dlls or ini-files that are only used by a single application and which wont get deleted once the user decides to uninstall the program. The same line of thought can be applied to the use of the registry. It is amazing how overused the registry is – and astonishing how many programs dont clean up their traces in the registry once the user decides to uninstall those programs. So if you are a programmer yourself, think about if you really need to store your data in the operation system area or if the application directory would fully suffice.

And before I forget – here the link to “fmod-masm3.rar”, including the fully finished include and library files. Have a nice day!

[dm]4[/dm]

VN:F [1.9.18_1163]
Rating: 0.0/5 (0 votes cast)
Thursday, January 29th, 2009 at 16:02
82 visits
  • Feb 8th, 2009 at 19:13 | #1

    People tell me that the executable aint working – they get the error-message “The file could not be found!” Note: this is on purpose! As I do mention in the readme.txt there is no “*.mod” file included in the archive (for copyright reasons). So you will need to download or create your own .mod file, rename it “modname.mod” if you dont want to recompile the source and copy it into the folder where the executable is located.

    VN:F [1.9.18_1163]
    Rating: 0.0/5 (0 votes cast)
  • Beyond2000!
    Jul 16th, 2009 at 22:27 | #2

    hi, i can´t download the file, how can i login ?

    VA:F [1.9.18_1163]
    Rating: 0.0/5 (0 votes cast)
    • Jul 19th, 2009 at 22:03 | #3

      Well, thanks for the info actually. Seems like the download manager plugin did break at one of the last WP releases. I am gonna fix this as soon as possible. Until then I have hardcoded the links to the files in the download archive (which you will find in the menu at the top of the page). Sorry for any inconvenience caused.

      VN:F [1.9.18_1163]
      Rating: 0.0/5 (0 votes cast)

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>