The Source Code

Parent Previous Next

The Source Code.

Here is the complete source code for the C# class provided to demonstrate one way of encapsulating and using the DLL API.


DISCLAIMER: The source code is provided "AS IS" with no guarantees what so ever. You may copy, modify and use it at your own risk.



using System;

using System.Text;

using System.Runtime.InteropServices;


namespace DLL_Interface

{

   [UnmanagedFunctionPointer(CallingConvention.Cdecl)]

   public delegate Int32 TCallBack(Int32 x, Int32 y, Int32 z);


   class DLL

   {

       /**************************** ENCAPSULATE DLL API AS PRIVATE FUNCTIONS ****************************/


       private const string DLLNAME = "VM32.dll";


       [DllImport(DLLNAME, EntryPoint = "CompilerVersion_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _CompilerVersion();


       [DllImport(DLLNAME, EntryPoint = "MultiThreadMode_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _MultiThreadMode(Int32 mode);


       [DllImport(DLLNAME, EntryPoint = "LastErrorGetStringLength_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _LastErrorGetStringLength(Int32 codePage, ref Int32 len);


       [DllImport(DLLNAME, EntryPoint = "LastErrorGetString_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _LastErrorGetString(Int32 codePage, Int32 len, byte[] textbytes);


       [DllImport(DLLNAME, EntryPoint = "VMCreate_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCreate(Int32 codePage, Int32 len, byte[] asmByteCode, ref Int32 vm);


       [DllImport(DLLNAME, EntryPoint = "VMFree_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMFree(Int32 vm);


       [DllImport(DLLNAME, EntryPoint = "VMSetCallback_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMSetCallback(Int32 vm, TCallBack callBack);


       [DllImport(DLLNAME, EntryPoint = "VMGC_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMGC(Int32 vm, Int32 gcMainClass);


       [DllImport(DLLNAME, EntryPoint = "VMExecute_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMExecute(Int32 vm, Int32 x, Int32 y, Int32 z, ref Int32 returnValue);


       [DllImport(DLLNAME, EntryPoint = "VMClearCells_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMClearCells(Int32 vm);


       [DllImport(DLLNAME, EntryPoint = "VMCellIsInteger_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellIsInteger(Int32 vm, Int32 row, Int32 col, ref Int32 returnValue);


       [DllImport(DLLNAME, EntryPoint = "VMCellIsBytes_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellIsBytes(Int32 vm, Int32 row, Int32 col, ref Int32 returnValue);


       [DllImport(DLLNAME, EntryPoint = "VMCellIsString_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellIsString(Int32 vm, Int32 row, Int32 col, ref Int32 returnValue);


       [DllImport(DLLNAME, EntryPoint = "VMCellGetInteger_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellGetInteger(Int32 vm, Int32 row, Int32 col, ref Int32 returnValue);


       [DllImport(DLLNAME, EntryPoint = "VMCellGetBytesLength_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellGetBytesLength(Int32 vm, Int32 row, Int32 col, ref Int32 len);


       [DllImport(DLLNAME, EntryPoint = "VMCellGetBytes_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellGetBytes(Int32 vm, Int32 row, Int32 col, Int32 len, byte[] bytes);


       [DllImport(DLLNAME, EntryPoint = "VMCellGetStringLength_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellGetStringLength(Int32 vm, Int32 row, Int32 col, Int32 codePage, ref Int32 len);


       [DllImport(DLLNAME, EntryPoint = "VMCellGetString_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellGetString(Int32 vm, Int32 row, Int32 col, Int32 codePage, Int32 len, byte[] textbytes);


       [DllImport(DLLNAME, EntryPoint = "VMCellSetInteger_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellSetInteger(Int32 vm, Int32 row, Int32 col, Int32 value);


       [DllImport(DLLNAME, EntryPoint = "VMCellSetBytes_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellSetBytes(Int32 vm, Int32 row, Int32 col, Int32 len, byte[] bytes);


       [DllImport(DLLNAME, EntryPoint = "VMCellSetString_cdecl", CallingConvention = CallingConvention.Cdecl)]

       private static extern Int32 _VMCellSetString(Int32 vm, Int32 row, Int32 col, Int32 codePage, Int32 len, byte[] textbytes);



       /************************************** PRIVATE SUPPORT FUNCTIONS **************************************/


       private static void RaiseLastError()

       {

           Int32 codePage = Encoding.Unicode.CodePage;

           Int32 len = 0;

           if (_LastErrorGetStringLength(codePage, ref len) != 1) RaiseError("Unable to get the length of the last error");

           if (len != 0)

           {

               byte[] textbytes = new byte[len];

               if (_LastErrorGetString(codePage, len, textbytes) != 1) RaiseError("Unable to get last error");

               string errMsg = Encoding.Unicode.GetString(textbytes);

               RaiseError(errMsg);

           }

           else

           {

               RaiseError("An unspecified error has occurred");

           }

       }


       private static void RaiseError(string errMsg)

       {

           throw new Exception(errMsg);

       }


       private static Int32 BoolToInt(Boolean value)

       {

           if (value) { return 1; } else { return 0; }

       }


       private static Boolean BoolOfInt(Int32 value)

       {

           if (value != 0 & value != 1) RaiseError("Unknown BoolInt value " + value.ToString());

           if (value == 1) { return true; } else { return false; }

       }



       /****************** PUBLIC FUNCTIONS REPRESENT BUILT-IN @VMxxxxx LIBRARY FUNCTIONS ******************/

       

       public static Int32 VMCreate(string asmByteCode)

       {

           Int32 codePage = Encoding.Unicode.CodePage;

           byte[] asmBytes = Encoding.Unicode.GetBytes(asmByteCode);

           Int32 len = asmBytes.Length;

           Int32 vm = 0;

           if (_VMCreate(codePage, len, asmBytes, ref vm) != 1) RaiseLastError();

           return vm;

       }


       public static void VMFree(Int32 vm)

       {

           if (_VMFree(vm) != 1) RaiseLastError();

       }


       public static void VMSetCallback(Int32 vm, TCallBack callBack)

       {

           if (_VMSetCallback(vm, callBack) != 1) RaiseLastError();

       }


       public static void VMGC(Int32 vm, Boolean gcMainClass)

       {

           if (_VMGC(vm, BoolToInt(gcMainClass)) != 1) RaiseLastError();

       }


       public static Int32 VMExecute(Int32 vm, Int32 x, Int32 y, Int32 z)

       {

           Int32 returnValue = 0;

           if (_VMExecute(vm, x, y, z, ref returnValue) != 1) RaiseLastError();

           return returnValue;

       }


       public static void VMClearCells(Int32 vm)

       {

           if (_VMClearCells(vm) != 1) RaiseLastError();

       }


       public static Boolean VMCellIsInteger(Int32 vm, Int32 row, Int32 col)

       {

           Int32 returnValue = 0;

           if (_VMCellIsInteger(vm, row, col, ref returnValue) != 1) RaiseLastError();

           return BoolOfInt(returnValue);

       }


       public static Boolean VMCellIsBytes(Int32 vm, Int32 row, Int32 col)

       {

           Int32 returnValue = 0;

           if (_VMCellIsBytes(vm, row, col, ref returnValue) != 1) RaiseLastError();

           return BoolOfInt(returnValue);

       }


       public static Boolean VMCellIsString(Int32 vm, Int32 row, Int32 col)

       {

           Int32 returnValue = 0;

           if (_VMCellIsString(vm, row, col, ref returnValue) != 1) RaiseLastError();

           return BoolOfInt(returnValue);

       }


       public static Int32 VMCellGetInteger(Int32 vm, Int32 row, Int32 col)

       {

           Int32 returnValue = 0;

           if (_VMCellGetInteger(vm, row, col, ref returnValue) != 1) RaiseLastError();

           return returnValue;

       }


       public static byte[] VMCellGetBytes(Int32 vm, Int32 row, Int32 col)

       {

           Int32 len = 0;

           if (_VMCellGetBytesLength(vm, row, col, ref len) != 1) RaiseLastError();

           byte[] bytes = null;

           if (len != 0)

           {

               bytes = new byte[len];

               if (_VMCellGetBytes(vm, row, col, len, bytes) != 1) RaiseLastError();

           }

           return bytes;

       }


       public static string VMCellGetString(Int32 vm, Int32 row, Int32 col)

       {

           Int32 codePage = Encoding.Unicode.CodePage;

           Int32 len = 0;

           if (_VMCellGetStringLength(vm, row, col, codePage, ref len) != 1) RaiseLastError();

           if (len == 0)

           {

               return "";

           }

           else

           {

               byte[] textbytes = new byte[len];

               if (_VMCellGetString(vm, row, col, codePage, len, textbytes) != 1) RaiseLastError();

               return Encoding.Unicode.GetString(textbytes);

           }

       }


       public static void VMCellSetInteger(Int32 vm, Int32 row, Int32 col, Int32 value)

       {

           if (_VMCellSetInteger(vm, row, col, value) != 1) RaiseLastError();

       }


       public static void VMCellSetBytes(Int32 vm, Int32 row, Int32 col, byte[] bytes)

       {

           if (_VMCellSetBytes(vm, row, col, bytes.Length, bytes) != 1) RaiseLastError();

       }


       public static void VMCellSetString(Int32 vm, Int32 row, Int32 col, string text)

       {

           Int32 codePage = Encoding.Unicode.CodePage;

           byte[] textbytes = Encoding.Unicode.GetBytes(text);

           Int32 len = textbytes.Length;

           if (_VMCellSetString(vm, row, col, codePage, len, textbytes) != 1) RaiseLastError();

       }


       /************************************* OTHER PUBLIC FUNCTIONALITY *************************************/

       public static Int32 CompilerVersion()

       {

           return _CompilerVersion();

       }


       public static void MultiThreadMode(Int32 mode)

       {

           if (_MultiThreadMode(mode) != 1) RaiseLastError();

       }

   }

}



Managed vs. Unmanaged Memory.

If you are a .NET programmer you may have noticed, no concerns have been raised about how to marshal content from the .NET managed memory to the VM DLL unmanaged (or at least differently managed) memory.


This is because most of the involved types are blittable types. Only byte-arrays are of particular concern. However, the API does not expect passed content to exist beyond the end of an API function call (see here), thus avoiding the need for either pinning or using specifically allocated unmanaged memory to protect against the .NET garbage collector.