by Bill Florek
Clarion has always had the capability of calling functions and procedures from non-Clarion DLLs. Documentation on the mechanics of how to do this is available within LanguageReference.pdf in the Clarion installation docs directory and does a fair job of explaining how to do things – as long as you are calling Windows API functions or functions in other non-.NET DLLs.
While the .NET frameworks have become a standard means of programming for Windows, the DLLs produced by .NET languages cannot be called from Clarion in the traditional way.
When searching the Internet (and other areas such as newsgroups), I found very little information on this topic. A handful of articles were available, none of which had a complete, tested, documented methodology.
After gleaning as much information as possible from the few articles available, searching the various Clarion newsgroups for answers, and expending many hours of trial and error, I acquired enough information to describe the process. In this article I will show the specific details of how to accomplish this from the Clarion side (using Clarion 9.1) as well as from the .NET side (using C# and Visual Studio 2012). You can apply the technique to other versions of Clarion and to other .NET languages.
As with any software development, there are many approaches and solutions to a problem. Developing this methodology is no different. There are many possible approaches, but this one attempts to stay with "Clarion norms" in terms of calling conventions, primarily using strings. Also, I use this methodology in a large scale real world application (approximately 2000 procedures across 85 DLLs).
Creating/Preparing a C#.NET DLL for use by Clarion
.NET DLLs contain “managed code”, which cannot be used directly by non-.NET applications because of differences in calling conventions.To create a .NET DLL that can be used by Clarion, the functions (which are actually class methods) need to be exported as “unmanaged” assemblies. Once an unmanaged DLL is created, it can then be used by Clarion just like any other non-Clarion DLL. The steps below outline the process of creating an “unmanaged” DLL:
- Create a C#.NET DLL/assembly as you normally would.
- Write the method(s) you wish to call from Clarion.
- Select the processor type to compile for (X86, i64, etc) – you must select a specific processor.
- Import the RGiesecke.DllExport NUGET package into the C# project
- This package is what allows for automated creation of unmanaged exports
- Add a “using” clause to the assembly (see source lines 6/7)
- Add DLLExport code/tags in front of each function to be exported (see source lines 14,21,29). These tags:
- Set the calling convention to stdcall
- Set the name of the function (as Clarion will see it)
- Parameters may need some special handling:
- INT (LONG) and BYTE types can be passed natively
- Strings take special handling – they must be passed/received as BSTRINGS
- Use the MarshalAs BSTR attribute on any passed or returned strings (see source lines 22,23,30,31)
- Build the C#.NET solution.
- The DLL is ready to use, but the .LIB file will need to be created as “Clarion-friendly”.
As a side note, unmanaged exported functions can call other (managed) functions within the C# assembly. For example, you could create a JSON API to do conversions to/from JSON format. Many times it’s much easier to create a .NET assembly to handle a specific task than to try and handle that task directly in Clarion. With these methodologies you can quickly create the .NET code, export it, and write a Clarion function to call the code just as if it were written as a Clarion DLL.
First, create a new class library:
The default CPU is "any".
Edit the CPU...
and change it to x86:
Add Robert Giesecke's UNmanagedExports library using NuGet:
You can also do this from the Package Manager console with
Write the source for the DLL, using the DllExport attribute:
Using a C#.NET DLL in Clarion
After creating the C#.NET DLL with the appropriate exported functions, it’s time to use it in Clarion. The process is outlined below:
- Create a Clarion-friendly .LIB file using LibMaker (see the Clarion help). However the shipping LibMaker won't work - go to the Fushisoft site and downoad Brahn Partridge's version.
- Create a new Clarion app as an EXE – this app will call the C# DLL
- Copy the C# DLL and LIB file (from step 1) to the root of your new app’s folder. You will also need RGiesecke.DllExport.Metadata.dll from the C# project.
- Add the C# DLL/LIB file to the Clarion solution
- In the Global Embeds, Inside Program Map
- Add a module statement that references the C# DLL
- Add the procedure declarations using:
- ,NAME() attribute
- ,PASCAL, RAW, DLL(True) calling convention attributes
- Declare any STRINGs as BSTRINGS, whether passed or received
- Parameter types are required, parameter names are not
- Create a Clarion procedure that will call the C# DLL
- The C# procedure is called like any other Clarion procedure would be called
- Passing and receiving LONG and BYTE data types requires no special handling
- When using strings, they must be passed/received as BSTRINGs, which are relatively undocumented in Clarion:
- To declare a BSTRING variable, no “size” specification is given
- BSTRINGs are assigned data from Clarion strings using bvar = clip(strvar)
- BSTRING data can be converted to a string using strvar = bvar
Here's Brahn Patridge's Libmaker with the C# DLL loaded, before clicking Save as:
Adding the lib to the project:
SDI vs. MDI applications and Recursive Calls to the C# DLL
SDI applications can call the functions in a C# DLL multiple times without issue, but MDI applications that use an application frame and START() procedures from that frame must handle repeated calls to the C# DLL a bit differently.
Methods exported from a C# DLL may only be called once per thread
To handle this:
- Create a Clarion procedure that calls the C# DLL
- Call the procedure by using START() – the process will automatically kill/close the thread when it’s done processing
Defining the procedures in the Clarion map:
Calling the C# DLL from Clarion:
Hopefully this information is as useful to you as it has been to me. Although the steps outlined above are for calling C#.NET assemblies from Clarion, the reverse can also be done. In a follow up article I will give the details on how to go the other direction (C#.NET assemblies calling Clarion DLLs), as well as how to handle calling DLL functions that include windows for display, entry and interaction.