The TDLL16 component (Version 1.0) ================================== TDLL16 is Copyright(C) 1997 by Simon Chang. Use and redistribute version 1.0 as much as you like. Later versions may, however, be shareware. If you do use version 1.0 in a commercial product, please place an acknowledgement in the help file or the about box. Thank you. What you should have from the zip file: DLL16.OBJ - The component! README.TXT - This file! P_DLL16.cpp - Example project file F_DLL16.cpp - Example form file F_DLL16.h - Example form include file Test16.DLL - A 16-bit DLL used in the example Test16.t - The template file Test16.h - The autocreated header file Test16.inc - The autocreated include file WHAT DOES IT DO? ================ This component is invisible and allows you to call 16-bit DLL functions from your Borland C++ Builder 32-bit programs. This process is known as "thunking". Thunking sucks, so I wrote this component to try and make it easier. WHY WOULD YOU WANT TO DO IT? ============================ The reason I need it is that I'm an engineer and I need to control a bit of hardware which only has a 16-bit DLL to control it and no 32-bit device drivers. Mayby you have a similar problem. Another reason is that you might want to access the I/O ports, something that can only be done in 32-bit mode with a device driver. (I think). You may also have loads of "legacy" code written for 16-bit and you don't want to have to re-write it all at once. Finally you might be one of those strange people who just like to hurt themselves for the sheer punishment of it. HOW DO YOU USE THIS FANTASTIC TDLL16 COMPONENT THEN? ==================================================== There's the simple way and the advanced way. The simple way takes a bit longer and makes messier code, but is easier to understand, so let's look at it first. Step 1. Plonk the DLL16 component on your main form. Step 2. Set the FileName property to the name of the DLL. Step 3. Create function variables to store the addresses of the DLL functions Step 4. Call the CreateFunction method of the control to calculate these addresses. If you have problems with Steps 1 and 2, I suggest you read the Borland C++ builder manuals. I'll go through steps 3 and 4 with an example: Let's say the DLL has a function called "Add" which takes two ints, adds them and returns the result. In pascal, the function might look like this: function Add(x, y : Integer):Integer; begin Result := x + y; end; {Add} Now to store a pointer to this function in c++, we'll need a variable declared as follows: int16 (*Add)(int16 x, int16 y); Don't worry about all that __stdcall, __fastcall stuff. The pointer doesn't _actually_ point to the function, but to a piece of code manufactured by the component which in turn calls the 16-bit DLL. If you're wondering about int16, remember that an int in 16-bit is 16 bits and an int in 32-bit is 32 bits. So int16 is how 16-bit integers are referred to by my component in 32-bit borland c++. Now, the code for creating the function with CreateFunction would be: Add = (int (*)(int,int))DLL161->CreateFunction("int16 (*Add)(int16 x, int16 y)"); ^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1 2 3 4 It looks a bit difficult, but it's really straight-forward. Section 1 is the assignment - what is calculated by the right hand side of the equals sign gets stored in Add. (No I don't think you're stupid, I'm just trying to write this so it's understandable). Section 2 is the "type modifier". The CreateFunc returns a (void *) which means it returns a pointer to nothing in particular. If you leave the type modifier out, it generates a compiler error saying you can't change from (void *) to (int (*)(int,int)). The type modifier is just to tell the compiler that the conversion is OK. No actial code is generated. Section 3 is the call to the function. Section 4 is the string argument which contains the "template" of the desired function (in this case Add). TDLL16 needs this to manufacture the thunking code which calls the DLL. ADVANCED METHOD =============== The advanced method involves using the component to automatically create the c++ code to load the functions as described previously. To do this, you first need to create a "template" file which contains all of the function templates. For example, the Test16.T template file contains: int16 DllTest(int16, int16) double DblTest(double) float FltTest(float) Which are the templates for the three functions in Test16.dll. If you set the "Template File" property of the control to the name of your template file, the component automatically creates two files. The first is the .h file which should be included by every unit which uses functions from the DLL. The .h file created for test16.dll is shown below: //--------------------------------------------------------------------------- #ifndef test16H #define test16H //--------------------------------------------------------------------------- extern int16 (*DllTest)(int16, int16); extern double (*DblTest)(double); extern float (*FltTest)(float); //--------------------------------------------------------------------------- #endif The second, .inc, file contains the code for loading the functions using CreateFunction. This .inc include file is designed to be included _inside_ the event handler "OnLibraryLoad" of the component. There is also a dummy function at the end of the .inc to make all the }'s line up properly. You can ignore it. The .inc file created for test16.dll is shown below: TDLL16 *DLL16=(TDLL16 *)Sender; DllTest = (int16 (*)(int16, int16))DLL16->CreateFunction("int16 DllTest(int16, int16)"); DblTest = (double (*)(double))DLL16->CreateFunction("double DblTest(double)"); FltTest = (float (*)(float))DLL16->CreateFunction("float FltTest(float)"); } int16 (*DllTest)(int16, int16); double (*DblTest)(double); float (*FltTest)(float); void __test16_DUMMY(void) { NOTE: Any code placed after the .inc include but within the event handler will be ignored, so please place all such vital code _before_ the include. REFERENCE: ========== Allowed types in the templates are: void - void char - a byte, basically int16 - a 16 bit integer long16 - a 32 bit integer float - a 32 bit floating point number double - a 64 bit floating point number POINTERS: ========= Pointers require mucking about with special windows functions because 16-bit DLLs can't always access 32-bit pointers. Unfortunately, implementing the thunking of pointer routines is something that will have to wait for later versions of TDLL16. STRINGS: ======== See pointers TDLL16 PROPERTIES: ================== Runtime only: int NumFunctions - Read only Returns the number of functions created by CreateFunction void *Functions[] - Read only Returns a pointer to the indexed function created by CreateFunction. Design Time: AnsiString FileName - Read/Write The name of the DLL. AnsiString TemplatesFile - Read/Write The name of the template file used when autocreating include files (leave it blank if you're using the easy way). TDLL16 EVENTS: ============== TNotifyEvent OnLoadLibrary Called after the DLL is loaded. The idea being that this is the best point to go on and create your functions. TDLL16 METHODS: =============== void *CreateFunction(char *Template); Uses the template given to construct a function which calls the required DLL function and returns a pointer to it. MEMORY CONSIDERATIONS ===================== TDLL16 should be nice and tidy. I'm pretty sure it deletes everything it news when it destructs. TEMPLATE SYNTAX =============== I haven't put much effort into translating the templates, so if you do it wrong, strange things may happen. If you follow the following guidelines, you should be ok. 1. Only use the allowed types described above. (I'll repeat them for convenience): void - void char - a byte, basically int16 - a 16 bit integer long16 - a 32 bit integer float - a 32 bit floating point number double - a 64 bit floating point number 2. Do not put variable names in the templates. My template analyser is too stupid to ignore them. ie DO NOT USE THE FOLLOWING AS A TEMPLATE: int16 func(int16 x, int16 y) -DO NOT WRITE FUNCTION TEMPLATES LIKE THIS 3. Don't try and use &'s or *'s. There is absolutely no support for pointers at this stage, and when it does come, it will work with handles and not *'s and &'s. THANKS FOR USING TDLL16!!! ========================== Well that's all I can think of for now! If you have any problems, write to me at: stc@eee.nott.ac.uk (No spam please) Simon Chang.