[ Last Update 12 March 2003 20.41 EST ]

Compiling Lightwave Plugins using
Borland C++ Builder v 3.0

by Carl Looper


Builder was released a few years ago as a freeby in computer mags, (to promote Version 4). I'm still using it as of 3 Feb 2003.

This discussion covers the main steps required to compile a Lightwave plugin using Builder v 3. The source code can be any of the SDK examples but this discussion assumes the SDK box example.

The SDK files are assumed to be located as follows:

C:\Lwsdk\include
C:\Lwsdk\source
C:\Lwsdk\sample

and that we will use the following folder as the location for our plugin project:

C:\MyPlugin\

Start Builder and select: File -> New...

We see the following window.

Choose DLL and click OK.
Builder generates the following code:

 

We can delete the comments in box.cpp. They are irrelevant to this project.

We save this project as "Box.bpr" in "C:\MyPlugin". The result is that the above file becomes "box.cpp"

 

OK, we now copy/paste the SDK box source code (C:\Lwsdk\sample\boxes\box1\box.c) into our source code.

The result is the following:

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

/*
======================================================================
box.c

A Modeler plug-in that makes a box.

Ernie Wright 28 May 01
====================================================================== */

#include <lwserver.h>
#include <lwcmdseq.h>
#include <stdio.h>


XCALL_( int )
Activate( long version, GlobalFunc *global, LWModCommand *local,
void *serverData )
{
char cmd[ 128 ];

if ( version != LWMODCOMMAND_VERSION )
return AFUNC_BADVERSION;

sprintf( cmd, "MAKEBOX <%g %g %g> <%g %g %g> <%d %d %d>",
-0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 1, 1, 1 );
local->evaluate( local->data, cmd );

return AFUNC_OK;
}

ServerRecord ServerDesc[] = {
{ LWMODCOMMAND_CLASS, "Tutorial_Box1", Activate },
{ NULL }
};

//---------------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
return 1;
}
//---------------------------------------------------------------------------

 

Now we need to add some files to this project. All Lightwave plugins can use these files so the SDK suggests building a library of the files but, instead, we'll be explicitly adding them to this project.

To do so we need the "Project Manager"

The following window becomes visible:

We right click on "box" (not on "box.cpp") to get a "context menu' of options. The one we want is "Add".

We navigate to the "source" directory of the SDK (C:\Lwsdk\source) and choose all the files there:

The following lines appear in our box.cpp source code, just below our include statements:

USEUNIT("..\Lwsdk\source\username.c");
USEUNIT("..\Lwsdk\source\servdesc.c");
USEUNIT("..\Lwsdk\source\servmain.c");
USEUNIT("..\Lwsdk\source\shutdown.c");
USEUNIT("..\Lwsdk\source\startup.c");
USEDEF("..\Lwsdk\source\serv.def");

Now we need to tell Builder where to find the SDK include files.
Select: Project -> Options ...

Select the Directories/Conditionals" tab ...

Click on the button for the include path. The resulting window allows us to tell Builder the location of the SDK include files.

Enter the path and click Add. The path is added to the list of include paths.

We are now ready to build our plugin.

Unfortunately, this results in the following error:

[C++Error] box.cpp(44): Cannot convert 'int (*)(long,void * (*)(const char *,int),st_LWModCommand *,void *)' to 'int (*)(long,void * (*)(const char *,int),void *,void *)'.

Hmmm. Perhaps we need to recast "Activate".

So, comment out the original Server Record block and put in an alternative.

My thanks to Sensei for this one. (My original alternative here, while it worked, was quite mad)

/*
ServerRecord ServerDesc[] = {
{ LWMODCOMMAND_CLASS, "Tutorial_Box1", Activate },
{ NULL }
};

*/

ServerRecord ServerDesc[] = {
{ LWMODCOMMAND_CLASS,
"Tutorial_Box1",
(ActivateFunc *) //<------ recasting Activate
Activate
},
{ NULL }
};

Rebuild ...

 

The DLL succeeds in building. That's good.

A quick test in Modeller, and, good grief, it works.


But while the above approach works we had to force a cast on "Activate". As it turns out, this was because we had wrapped box.c inside box.cpp, ie. as a C++ file.

Under C++, Builder regarded the attempt at Activate's pointer conversion as illegal. So we had to explicitly cast it.

What we really need to do is compile box.c as a C file instead. This way we only get a "suspicious pointer conversion" warning (which is much better than an error). It means we don't have to change any source code.

So the much better alternative approach is as follows:

1. Select a DLL project as before but save the project under another name, ie. other than box, eg. as "mybox.bpr"

2. Leave the Builder generated "mybox.cpp" as is. Don't change anything.

3. As before, use the Project Manager to "add" all the files in the SDK source folder (serv.def etc.) to the project. But this time, also add box.c (C:\Lwsdk\sample\boxes\box1\box.c).

4. Finally build "mybox"

The resulting DLL is "mybox.dll".

And it works in Modeller as expected.


Checking with the Lightwave Plugin Mailing List we get the following response from Ernie regarding the above tutorial.

Excellent--I wish more people would do stuff like this.

The only question I'd have is about adding the SDK source/ files to the project, which is how I always do it. Ordinarily you wouldn't want to add servdesc.c or username.c. You only need those if you *don't* have a ServerRecord in your plug-in somewhere.

- Ernie                           http://mywebpages.comcast.net/erniew

Hmmm. So this explains the Linker warning we had:

[LinkerWarning] Public symbol '_ServerDesc' defined in both module C:\MYPLUGIN\BOX.OBJ and C:\LWSDK\SOURCE\SERVDESC.OBJ.

So, armed with Ernie's advice we remove "username.c" and "servdesc.c" from the project ...

And rebuild "mybox".

The only warning left now is the suspicious pointer conversion:

[C++Warning] box.c(33): Suspicious pointer conversion.

The pointer conversion is intentional so the warning can be safely ignored.

 

A quick test in Modeller ... and once again, "mybox.dll" works in Modeller as expected.


What about the Preprocessor Defines mentioned in the SDK? While it didn't seem to be necessary we had better put them in ...

Select: Project -> Options -> Directories/Conditionals ...

In "Conditional defines" add: _X86_;_WIN32;

 


EXTRA STUFF ...


OBJECT FILES

When Builder compiles the DLL it generates temporary "object" files in the same location as your source files. If you want Builder to put these object files elsewhere (ie. rather than throughout your source folders) you can specify a folder for such.

For example, if we want Builder to put all the temporary object files in a folder called "myobjects" then we would do the following:

Select: Project -> Options -> Directories/Conditionals

Enter "myobjects" for "Intermediate output":

Click "OK".

If the folder doesn't already exist Builder will seek confirmation to create it:

Click "Yes"

 

Now, when we build "mybox" all the object files will be written to:

C:\MyPlugin\myobjects


STANDALONE DLL

If we are not careful we may find that when we ship our DLL to another machine we get a "cannot find xxxx.dll" error. While none of the SDK examples will have this problem our own plugins might.

The simplest solution is to produce a "standalone" DLL. Any Builder specific components we might use are embedded in the DLL itself rather than requiring a link (ie. to another possibly absent DLL) at runtime:

Step 1. Select: Project -> Options -> Packages

If the "Build with runtime packages" checkbox is checked (lower left) then uncheck it.

Step 2. Select the Project Options Linker tab ...

If the "Use dynamic RTL" checkbox is checked (upper left) then uncheck it.