2009-07-22 11 views
6

Mam bibliotekę kodów napisaną w zwykłym starym C++ (bez kodu NET/managed) i przenosię aplikację, która używa tego kod do C#. Mam do wyboru dwie opcje:Przenoszenie (niezarządzanego) C++ do C# vs używanie C++ jako DLL w aplikacji C#

  1. Przepisz kod C++ w C#, aby osiągnąć tę samą funkcjonalność;
  2. Skompiluj C++ jako bibliotekę DLL i użyj jej jako biblioteki w aplikacji C#.

Jestem stosunkowo nowy w języku C# i jestem całkiem obeznany z konsekwencjami używania niezarządzanej biblioteki kodów w aplikacji C# (lub nawet jeśli są jakieś). Sam kod ma umiarkowany rozmiar; prawdopodobnie zajmie to tylko kilka dni, aby napisać coś w języku C#, ale myślę, że pozostawienie kodu jako takiego umożliwiłoby użycie go również w innych aplikacjach (i skompilowanie go w systemie UNIX itp.).

Co powinienem wiedzieć o podejmowaniu tej decyzji? Czy są jakieś poważne wady lub problemy z używaniem biblioteki DLL w aplikacji C#?

+1

Mono pozwala również na "port"/uruchom na * nix. – Tim

Odpowiedz

6

Uczyniłbym otwieracz biblioteki przy użyciu C++/CLI, aby odsłonić biblioteki do C#. To może pozostawić twoją bibliotekę niezmienioną i po prostu ją zapakować do użytku z .NET, zapewniając najlepszą z obu opcji.

+1

Pokonaj mnie 20 sekund. Ale * I * poszedłem i znalazłem link. :) – Randolpho

+1

Używanie wrapperów C++/CLI jest * najlepszym sposobem. P/Invoke daje problemy z ładowaniem biblioteki i wersjami. Użycie biblioteki niezarządzanej spowoduje, że linia stanie się czysta dla zarządzanej niezarządzanej kompilacji. Uważaj jednak na ekspozycję klas STL. Może się okazać, że twój kod niskiego poziomu, który oczekuje użycia niezarządzanego STL, zostanie zakończony przy użyciu zarządzanych wersji z wieloma przejściami zarządzanymi/niezarządzanymi. – plinth

2

Jedną z rzeczy, które uznałem za użyteczne, jest zagłębienie się w C++/CLI podczas pracy z niezarządzanymi bibliotekami C++. Utwórz zarządzane opakowanie za pomocą C++/CLI i wywołaj je ze swojego kodu C#. Zarządzane opakowanie może zawierać bibliotekę (zakładam, że jest statycznie połączona) w swojej bibliotece DLL, a odniesienie do projektu jest wszystkim, czego potrzebujesz do kodu C#.

+0

FYI: DLL nie są statycznie połączone. Biblioteka linków dynamicznych. – Amy

+1

@ yodaj007: Sądzę, że sugerował statyczne powiązanie oryginalnej biblioteki z zespołem C++/CLI, co jest poprawne. –

+0

@ yodaj007: @Reed Copsey powiedział to lepiej niż ja mogłem. Ale nadal będę próbować: oryginalna biblioteka, oparta na rzeczach wspomnianych w pierwotnym pytaniu, wydaje się być statycznie połączoną biblioteką. Oznacza to, że jeśli biblioteka jest używana przez projekt C++/CLI, zostanie skompilowana do (i podciągana przez) C++/CLI .DLL. – Randolpho

0

Nie trzeba pisać wrappera w C++/CLI. Można bezpośrednio używać Platform Invoke z C#:

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

EDIT: Jeśli zrobisz to za pomocą C++/CLI, musisz się robi połączeń LoadLibrary i tworzenia wskaźników funkcji. Jest to znacznie łatwiejsze w języku C#. Jest to z samouczka MSDN połączonego powyżej, ale z moich własnych dodanych opinii:

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: Złożone typy mogą być również marshaled, jeśli jest to konieczne do określenia konstrukcjom. Ten przykład pochodzi z mojego własnego kodu, który wywołuje GDI +. Zrobiłem to trochę.

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

To wygląda dość prosto. A co z porządkowaniem rzeczy takich jak kontenery STL i złożone typy niestandardowe? –

+3

Nie musisz zajmować się LoadLibrary lub wskaźnikami funkcji - C++/CLI sprawia, że ​​owijanie złożonej biblioteki C++ jest proste.P/Invoke działa świetnie, pod warunkiem, że biblioteka udostępnia C api, ale nie ma (łatwego) sposobu na oblewanie instancji klasy C++ za pomocą P/Invoke. Jest świetny dla C, ale nie jest tak wspaniały dla klas C++. –

+0

Może być bardzo trudno obliczyć sygnatury P/Invoke, jeśli przechodzisz w bardziej skomplikowanych strukturach. Zawijanie w zarządzanej bibliotece DLL C++/CLI pozwoli uprościć przekazywane struktury, jeśli nie uda się znaleźć praktycznego sposobu na uporządkowanie struktur. –

Powiązane problemy