Porting (unmanaged) C ++ in C # compared to C ++ as a DLL in a C # application

I have a code library written in plain old C ++ (without .NET / managed code) and I am porting an application that uses this code for C #. I came across two options:

  • Rewrite C ++ code in C # to achieve the same functionality;
  • Compile C ++ as a DLL and use it as a library in a C # application.

I'm relatively new to C # and quite unfamiliar with the consequences of using an unmanaged code library in a C # application (or even where it is). The code itself is moderate in size; most likely it will take only a few days to rewrite to C #, but I thought that leaving the code as that would allow me to use it in other applications (and compile it on UNIX, etc.).

What should I know about this when making this decision? Are there any significant flaws or gotchas for using a DLL in a C # application?

+6
c ++ c # dll
source share
3 answers

I would make a wrapper library using C ++ / CLI to open the library for C #. This can leave your library unchanged and just wrap it for use with .NET, providing the best of both options.

+6
source share

One thing I found useful is to learn C ++ / CLI when working with unmanaged C ++ libraries. Create a managed shell using the C ++ / CLI and call it from C # code. A managed shell can include a library (I assume that it is statically linked) in its DLL, and a project link is all you need for your C # code.

+2
source share

No need to write a wrapper in C ++ / CLI. You can directly use Platform Invoke from C #:

http://msdn.microsoft.com/en-us/library/aa288468%28VS.71%29.aspx

EDIT: if you do this using the C ++ / CLI, you will need to make LoadLibrary calls and create function pointers. This is greatly simplified in C #. This is from the MSDN tutorial linked above, but with my own added comments:

class PlatformInvokeTest { [DllImport("msvcrt.dll")] // Specify the DLL we're importing from public static extern int puts(string c); // This matches the signature of the DLL function. The CLR automatically marshals C++ types to C# types. [DllImport("msvcrt.dll")] internal static extern int _flushall(); public static void Main() { puts("Test"); _flushall(); } } 

EDIT: Complex types can also be marshaled, although structures need to be defined. This example is taken from my own code that calls GDI +. I cut it off a bit.

 private static int SRCCOPY = 0x00CC0020; private static uint BI_RGB = 0; private static uint DIB_RGB_COLORS = 0; [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); [StructLayout(LayoutKind.Sequential)] private struct BITMAPINFO { public uint biSize; public int biWidth; public int biHeight; public short biPlanes; public short biBitCount; public uint biCompression; public uint biSizeImage; public int biXPelsPerMeter; public int biYPelsPerMeter; public uint biClrUsed; public uint biClrImportant; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public uint[] cols; } public static Bitmap Downsample(Bitmap input, int bpp) { Bitmap retval = null; // We will call into this GDI functionality from C#. Our plan: // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed) // (2) Create a GDI monochrome hbitmap // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above) // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed) IntPtr inputHandle = input.GetHbitmap(); // // Step (2): create the monochrome bitmap. // BITMAPINFO bmi = new BITMAPINFO(); bmi.biSize = 40; // the size of the BITMAPHEADERINFO struct bmi.biWidth = input.Width; bmi.biHeight = input.Height; bmi.biPlanes = 1; bmi.biBitCount = (short)bpp; // 1bpp or 8bpp bmi.biCompression = BI_RGB; bmi.biSizeImage = (uint)(((input.Width + 7) & 0xFFFFFFF8) * input.Height / 8); bmi.biXPelsPerMeter = 0; // not really important bmi.biYPelsPerMeter = 0; // not really important // // Create the color palette. // uint numColors = (uint)1 << bpp; // 2 colors for 1bpp; 256 colors for 8bpp bmi.biClrUsed = numColors; bmi.biClrImportant = numColors; bmi.cols = new uint[256]; if (bpp == 1) { bmi.cols[0] = MAKERGB(0, 0, 0); bmi.cols[1] = MAKERGB(255, 255, 255); } else { for (int i = 0; i < numColors; i++) { bmi.cols[i] = MAKERGB(i, i, i); } } // // Now create the indexed bitmap // IntPtr bits0; IntPtr indexedBitmapHandle = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0); IntPtr sourceDC = GetDC(IntPtr.Zero); IntPtr hdc = CreateCompatibleDC(sourceDC); IntPtr hdc0 = CreateCompatibleDC(sourceDC); SelectObject(hdc, inputHandle); SelectObject(hdc0, indexedBitmapHandle); BitBlt(hdc0, 0, 0, input.Width, input.Height, hdc, 0, 0, SRCCOPY); retval = Bitmap.FromHbitmap(indexedBitmapHandle); // // Dispose of the crud // DeleteDC(hdc); DeleteDC(hdc0); ReleaseDC(IntPtr.Zero, sourceDC); DeleteObject(inputHandle); DeleteObject(indexedBitmapHandle); return retval; } 
0
source share

All Articles