Return to the technote repository Calling a DLL
from Magic
 

Introduction

This technote is about the Windows 32 bits platforms (Windows 95, Windows 98, Windows millennium, Windows NT, Windows 2000, Windows XP, ...).

# DLL type Available from Magic charset
1 Magic specific DLL Magic V7 OEM
2 Windows standard DLL Magic 8.20 (8.30 recommended) OEM
3 ActiveX DLL Magic 8.20 (8.30 recommended) ANSI

Magic Specific DLLs

As they are designed for Magic, they are several advantages

  • Wide compatibility (from Magic V7)
  • Parameter checking : type, number
  • Better reliability (old functionality)
  • Better Magic integration
  • It's for Magic, so it should work (it not always possible to call other DLL types)

Note that Magic DLLs are loaded only once (and unloaded only at Magic shutdown) : this is good for performance if functions from this DLL have be to be often called, it also allows to keep in the DLL some context data.

I wrote some tips about making a Magic Specific DLL with LCC-Win32 or Visual C++, and there is a project with Delphi on Craig Martin site. Once you have been able to make a Magic DLL, it's quite easy to maintain the MAGIC_BIND function and you don't have to maintain a .def file (only the MAGIC_BIND function has to be exported).

You can call

  1. User Defined Function with the UDF() function. A function (UDF) returns a value, but the parameters can't be modified.
  2. User Defined Procedure with the Call UDP operation, an option of the Call operation (like Call Prog and Call Task). A procedure (UDP) doesn't return a value, but the parameters can be modified.

For a DLL called DLLName, a function FunctionName or a procedure ProcedureName from this DLL, The syntax is

  1. UDF('DLLName.FunctionName', Parameter1, Parameter2, ...)
  2. Operation :Call UDP, Name 'DLLName.FunctionName', Prm : put the parameters in the Parameters table.

I provide a tool (in GET.DLL demo application, 'Utilities/DLL' menu) that use the MAGIC_BIND function in the same way than Magic to list available functions of a Magic DLL.

Windows Standard DLLs

You could use the CALLDLL function, but this function is now obsolete, as the UDF function and the Call UDP operation can now handle standard DLLs.

The syntax is the same than for Magic DLLs, except :

  1. A prefix to the DLL Name is needed to specify it's a Windows DLL : @. CALLDLL is for Windows DLLs only, so it doesn't use the @ prefix.
  2. As first parameter, you have to put a string that gives the type of each parameter.

Each item of the type string comes from this list

  • 1 Char
  • 2 Short
  • 4 Long
  • F Float
  • 8 Double
  • D Double pointer
  • E Float pointer
  • L Long pointer
  • A Null terminated string pointer
  • V Void pointer
  • 0 Void

Note that you can't call a function that needs as parameter a structure. As a result, a lot of functions can't be called from Magic, you need to make a Magic specific DLLs to handle this cases.

In most cases, you want to use a Windows function. So, at first you need the Windows Win32 API documentation. You can download an old (but still useful) win32.hlp at LCC-Win32. Downloading an installing the compiler is also a good idea, you'll sometimes have to look in the header (.h files) to find the numeric value from a constant.

For an up-to-date documentation, you can look online at MSDN.

A sample : print a file via his associated program

The function we need is ShellExecute. How did I find the function ? I've found that usenet (comp.os.ms-windows.programmer.win32) is a very useful resource, I use Google Groups to search the archives. Try also MSDN and Google.

So look at Win32.hlp (or at ShellExecute.pdf), there is a "QuickInfo" button, it shows a menu with :

  • Windows versions availability : a lot of functions only work with Windows NT (so Windows 2000 and Windows XP), take care of this if you need to run on Win95 or upper).
  • Import Library : shell32.lib. This means that this function is in shell32.dll DLL.
  • Header File : without interest here.
  • Unicode : WinNT. This is important, it means that this function exists in 2 versions : ANSI or Unicode. As Magic doesn't support Unicode, we want to use the ANSI version. So the real name of the function ends by A. This is the case of the functions that have a parameter that is a string.

With this informations, we know that the function string is : '@Shell32.ShellExecuteA'. The case of the function name is important, the case of the DLL name isn't.

Now we have to determine what are the parameters.

  1. HWND hwnd : it's the parent window handle, so we need the handle of the task that calls this function. We can get it with the Magic function WINHWND(0). Note that the "Open task window" flag must be to "Yes" or WINHWND(0) returns 0. We are in a 32 bit operation system, most handles are 32 bits integers so we take as parameter type '4' (this is true for HWND, DWORD, BOOL, INT, HANDLE, HINSTANCE, int, UINT, ...)
  2. LPCTSTR lpOperation : the documentation states that we must use "print", it's for C (string separator is ") so in Magic (string separator is ') we'll use 'print'. It's a C (null terminated) string, so the parameter type is 'A'.
  3. LPCTSTR lpFile : the file we want to print, for instance 'c:\test.doc'. Logical names won't be interpreted, so we have to expand them before. Parameter type is 'A'.
  4. LPCTSTR lpParameters : we don't have any parameter, so the documentation says we must use NULL. To use NULL (the real type isn't important), use as value 0 and as parameter type '4'.
  5. LPCTSTR lpDirectory : we don't care, so let Windows take the one he wants by setting it as NULL : parameter type is '4' and value is 0.
  6. INT nShowCmd : I don't want to show anything, so I want to use SW_HIDE as parameter. This is a constant, I must look for the numeric value in the .h files (I search for SH_HIDE in *.h files of c:\lcc\include\ folder from Windows explorer, and I obtain some files and find in Windows.h this line : #define SW_HIDE 0). The value is 0 and parameter type is '4' .
  7. return value : HINSTANCE : if the function fail, the value is less than 32. The parameter is a Magic virtual (picture 10) and The parameter type is '4'.

This function is called by using Call UDP '@Shell32.ShellExecuteA', parameters including the first (the type string) and the last (return value H, virtual of picture 10) are :

  1. '4AA4444'
  2. WINHWND(0)
  3. 'print'
  4. 'c:\test.doc'
  5. 0
  6. 0
  7. 0
  8. H

It's a function, so we could also set (init or update) H with

  • UDF('@Shell32.ShellExecuteA', '4AA4444', WINHWND(0), 'print', 'c:\test.doc', 0,0,0)
  • CALLDLL('Shell32.ShellExecuteA', '4AA4444', WINHWND(0), 'print', 'c:\test.doc', 0,0,0)

This function is also implemented in GET.DLL, so you could also use Call UDP 'get.print' 'c:\test.doc'. It illustrates the interest of using a Magic DLL : far less complexity from the Magic side.

calldll.txt : export (Magic 8.30) of this sample.

ActiveX DLLs

Visual Basic can only generate such DLLs.

It's a lot harder to call an ActiveX DLL, you need to use the mg_ocx stuff, and I can't help on this subject, this MagicDemo.zip complete sample (with a VB project) may help.

Some utilization of mg_ocx are available on magicu-l shared folder

ANSI/OEM

There is an issue with Magic V7/V8 (OEM) and Windows (ANSI) : they don't use the same charset. So you need to convert before or after using a string. With GET.DLL you can use the ansi_to_oem, oem_to_ansi or load_convert procedure (that may rely on Windows API or Magic conversion table), the Windows functions are CharToOem and OemToChar.

Using a Magic DLL from another tool ?

This is possible, you just have to call the function like Magic.

I've done it from C for my Magic cross reference printing shareware tool, where I'm able to call hotfudge bmp to jpg function.

I've put the function I use in mgproc.dll (part of GET.DLL package).