Porting (unmanaged) C++ to C# vs. using the C++ as

2019-04-07 17:10发布

I have a code library written in plain old C++ (no .NET/managed code) and I'm porting the application that uses this code to C#. I'm faced with two options:

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

I'm relatively new to C# and am pretty unfamiliar with the implications of using an unmanaged code library in a C# app (or if there even are any). The code itself is moderate in size; it will likely take only a few days to rewrite in C#, but my thought is that leaving the code as a it is would allow me to use it in other applications as well (and to compile it on UNIX, etc).

What sort of things should I be aware of when making this decision? Are there any major drawbacks or gotchas to using the DLL in the C# application?

标签: c# c++ dll
3条回答
放我归山
2楼-- · 2019-04-07 17:46

It's not necessary 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 it using C++/CLI, you'll need to be doing LoadLibrary calls and creating function pointers. This is significantly easier 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, though it's necessary to define the structs. This example taken from my own code that invokes GDI+. I've snipped it 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;
}
查看更多
Rolldiameter
3楼-- · 2019-04-07 17:49

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

查看更多
唯我独甜
4楼-- · 2019-04-07 17:59

One thing I've found useful is to delve into C++/CLI when dealing with unmanaged C++ libraries. Create a managed wrapper using C++/CLI and call it from your C# code. The managed wrapper can include the library (I assume it's statically linked) in its DLL, and a project reference is all you need for your C# code.

查看更多
登录 后发表回答