IBM Support

CPLEX Callable Library interface from C#.NET

Question & Answer


How can I use the CPLEX Callable Library interface from C#.NET?


This technote is primarily of interest for versions of CPLEX before 9.0. Starting with version 9.0, CPLEX provides a direct, object oriented API for C# that renders the previous information in this technote obsolete. The previous information only applies in the unlikely event that you need to call CPLEX's C API from C# rather than using the C# API, or if you are using a version before 9.0. If the latter, we encourage you to upgrade to the latest version of CPLEX for the performance improvements as well as the more direct way to use C#. Example programs that use the C# API are provided with the distribution.

It is possible to invoke methods of the CPLEX Callable Library from C#.NET. However, doing so will take a little bit of work. For a basic example of lpex1.c implemented as a C#.NET application, please see the bottom of this FAQ.

C#.NET is considered a managed language and can work directly only with managed objects. The CPLEX Callable Library routines are ordinary unmanaged C routines. To work between them, you must create an appropriate bridge between the two sides. This bridge is created with "platform invoke."

"Platform invoke" is a Microsoft concept that allows managed code to call unmanaged functions implemented in a DLL. It works to find and invoke these unmanaged functions, taking care to "marshal" the parameters and return values from the managed datatypes to their unmanaged counterparts. It can even dynamically allocate memory on the unmanaged heap, copy the contents of a managed object to this heap, let the umanaged code work with the heap, then update the object when it has finished.

It is strongly recommended to review the platform invoke chapters of the .NET Framework documentation. For now, this FAQ will cover the basics to get a simple Callable Library application to work in C#.NET through "platform invoke".

The general idea entails four steps:

  • Find the CPLEX Callable Library routines and constants that you want to use.
    These are the ones that will be invoked from C#.NET

  • Designate a wrapper class in C#.NET to hold these methods and constants.
    You can use an existing class or create a separate class.

  • Initialize the CPLEX constants with the proper values.
    You can refer to the C header file cplex.h to see the correct data types and values for all of the constants.

  • Create prototypes in managed code, using C#.NET data types.
    You will need to create one prototype for each Callable Library routine.

This last step is a little tricky, since you will need to refer to the signature of each function and the corresponding documentation to make sure properly that the marshalling behavior and memory management are handled correctly. Fortunately, the default mappings between the C#.NET data types and the C data types can provide several shortcuts.

General Conversion Chart(1)
int in C double in C char* in C
read-only parameter, passed by value int double String
updatable parameter, passed by reference ref int ref double StringBuffer (3)
read-only array int[] double[] String[]
updatable array(2) [In,Out] int[] [In,Out] double[] See note (4)

  • Note 1: CPXENVptr, and CPXLPptr are IntPtr instances.
    Even though CPXENVptr and CPXLPptr are never dereferenced in user code, it is best to use an IntPtr for both types.
  • Note 2: Updatable arrays are not passed by reference.
    While in C, an updatable array is always passed by reference, the marshalling routines cannot easily handle an array of unknown size. For correctness, we recommend passing the array by value, but also inform the marshaling routines to update the original array with the new values.
  • Note 3: Methods like CPXgeterrorstring need special treatment.
    This method takes in an updateable string buffer, and usually returns a second pointer to the same buffer. If both the parameter and the return value were a StringBuffer or String, the "platform invoke" process would try to free both pointers, and thus try to free the same memory twice. For any routine that involves a pointer that should not be automatically freed by the platform invoke system, consider using the IntPtr datatype. See Memory Management with the Interop Marshaler in the .NET framework documentation for more.
  • Note 4: Methods like CPXgetcolname more specific handling.
    CPXgetcolname expects a contiguous block of memory and will give an array of pointers to the block of memory. For this, you will need to use methods like Marshal.AllocHGlobal and Marshal.FreeHGlobal to create/destroy the block of memory, and the other conversion methods of the Marshal class (like Marshal.PtrToStringAnsi) to read or write to this block. See the documentation for the Marshal class and its methods. Other methods that involve an array of pointers should be handled similarly.

After all this is done, and after the CPLEX Callable Library routines have proper prototypes, then you can freely use them in your code. We have created a version of the CPLEX Callable Library example lpex1.c implemented for C#.NET. You can download this sampleLPex1c.cs - LPex1c.cs and expand on this. Please be sure to review the prototypes and the constants used in the application.

[{"Product":{"code":"SSSA5P","label":"IBM ILOG CPLEX Optimization Studio"},"Business Unit":{"code":"BU001","label":"Analytics Private Cloud"},"Component":"Not Applicable","Platform":[{"code":"PF033","label":"Windows"}],"Version":"9.2;9.1.3;9.1.2;9.1;9.0;8.1;8.0;7.5;7.1;7.0;6.6;12.0;11.2.1;11.2;11.1.1;11.1;11.0.1;11.0;10.3;10.2.1;10.2;10.1.1;10.1;10.0","Edition":""},{"Product":{"code":"SSSA5P","label":"IBM ILOG CPLEX Optimization Studio"},"Business Unit":{"code":"BU001","label":"Analytics Private Cloud"},"Component":"General","Platform":[{"code":"PF002","label":"AIX"},{"code":"PF010","label":"HP-UX"},{"code":"PF016","label":"Linux"},{"code":"PF027","label":"Solaris"},{"code":"PF033","label":"Windows"},{"code":"PF017","label":"Mac OS"}],"Version":"12.2","Edition":"All Editions"}]

Historical Number


Document Information

Modified date:
16 June 2018