Blitz2D Intermediates: Creating PureBasic DLLs for Blitz's CallDLL
by Morduun

But... why?

Yeah, I know, there's cooler ways for Blitz to interface with DLLs now. With the advent of .decls files it's child's play to add new functionality to Blitz -- and since the IDE even highlights new functions added this way, it begs the question of why you'd even want to do this.

There's two reasons, and they're both decent ones. For one, users of Blitz2D never got the .decls update, so they're pretty much stuck using CallDLL to access DLLs with. And two, .decls are lovely but they totally leave out one thing: dynamic loading at runtime. If you don't know in advance exactly what the name of the DLL you're accessing is, then there's no way you can write a .decls file for it, and once again you're stuck with CallDLL as the only way you've got to access DLLs under these conditions.

Okay... so, how?

Pure and Blitz actually play pretty nicely with each other once you've got the calling conventions sorted out. We'll cover Pure's end of the syntax first -- as well as the quirks you need to deal with in order to get a working DLL.

First, the PureBasic procedure heading. It's deadly simple:


The ProcedureDLL heading tells Pure that you'll want that function available to DLL calls (you can have lots of function calls in your DLL, but only the ones you expose as ProcedureDLL will be accessable via the DLL call). Blitz sends a DLL procedure pointers to a pair of banks, but you don't need to tell Pure that they're pointers, specifically. Just let Pure think it's being passed a pair of ints and Pure will do the rest via peeking and poking, like so:


Now, technically, Blitz is not expecting the in bank to be modified -- only the out bank, so I'd advise against modifying the in bank. Just use it as parameters to be passed the DLL and leave it at that. Some people report freely being able to modify the in bank as well, but... well, don't and you'll sleep better at night.

As for the out bank, you'll do the same thing in reverse -- use the Poke methods to alter the allocated memory space before returning out of the DLL routine. Just be sure to always check the address you're poking against the initial address of out and the provided out_size -- Pure isn't nearly as finicky about letting you Poke around where you don't belong, and you can easily bluescreen yourself if you're not careful. When you're done and the procedure returns, Blitz will then be able to read the altered Bank and report on its data.

The Pure Compiler

Before you compile your DLL, be sure you've gone into the Compiler menu, clicked on Compiler Options and selected "Shared Dll" from the drop-down for executable format. Once you've done that, you should be able to select Create Executable from the Compile menu and Pure will make your DLL wherever you tell it to. You're pretty much done at this point, but there's a few more things you should know before we move on to the Blitz side of things.

DLL Tricks

DO initialize constants and data structures at the top of your code, outside of any functions, but DON'T do anything else out there! Leaving code outside of functions in a Pure DLL can cause it to crash the Windows Kernel when accessed.

DO create a function to initialize all your important data constructs. Blitz does not seem to release a DLL from memroy once it's loaded via CallDLL, so you'll need to be sure that your DLL resets any data it uses to generate information. Especially be sure that struct instances are deleted or cleared, that globals are reset to defaults and arrays are re-dimmed to clear all their info -- otherwise you'll end up with not just memory leaks, but also odd or unexpected behavior. I have an init() function that I call every time my main DLL functions get called, just to be sure memory is properly cleared. If you're sneaky, this =can= be useful as it creates a sort of pseudo-state within the DLL -- if you call several methods from a DLL, the fact that the first sets and does not erase the DLL's internal state can be used to your advanrage. But it can also be a bother if you don't plan for it -- so plan for it.

Testing -- Here's a little code snippet that allows you to simulate calling the DLL when you're still compiling as a Windows executable (again, in the Compiler Menu under Compiler Options) -- this will help you track down potential bugs without having to compile a DLL every time you need to hunt down a bug:


Blitz's turn

This is really as simple as you can get it. All you need to do is initialize two banks, put the info you want to pass to the DLL into one, leave the other one blank, and call the DLL. In this example, we're passing two Ints and letting the DLL return the result of their addition -- so we'd do something like this:


With that done, when Blitz regains control, myOutputBank will contain the information that your Pure DLL put there -- in this case, the results of this addition.

Code example

Grab this file: Click Here. It contains purebasic and blitz basic source for a working CallDLL interface, as well as the working DLL, already compiled in PB. If you're looking for a skeleton to work from, this will be just what you're after.

Last notes

Using Pure to do cool things for you outside of Blitz is fine, but creating a "wrapper" DLL that basically does nothing more than wrap all of Pure's functionality for Blitz to use is not. Please be sure to read the terms of use at the PureBasic site and if you have any questions about Pure itself or the terms of its license then visit their forums and ask for yourself. The developer of Pure is a good fellow and seems open to all reasonable requests.

And that's it --you should be done now. Some of those tips up there are hard-won, so, obvious though they may be, I hope they're of use to someone. If you'd like to discuss this, or have found some new or better ways to do this, leave a message on this thread or drop me a line via email (morduun@hotmail.com). Thanks for reading!


For a printable copy of this article, please click HERE.


This site is Copyright© 2000-2004, BlitzCoder. All rights reserved.