2009-07-22 76 views
6

Tôi có thư viện mã được viết bằng ngôn ngữ cũ C++ (không có mã .NET/managed) và tôi đang chuyển ứng dụng sử dụng mã đến C#. Tôi đang phải đối mặt với hai tùy chọn:Chuyển (không được quản lý) C++ sang C# so với sử dụng C++ dưới dạng DLL trong ứng dụng C#

  1. Viết lại mã C++ trong C# để đạt được cùng chức năng;
  2. Biên dịch C++ làm DLL và sử dụng nó làm thư viện trong ứng dụng C#.

Tôi khá mới đối với C# và khá lạ với các tác động của việc sử dụng thư viện mã không được quản lý trong ứng dụng C# (hoặc nếu có). Bản thân mã có kích thước vừa phải; nó có thể sẽ chỉ mất một vài ngày để viết lại trong C#, nhưng suy nghĩ của tôi là để lại mã như nó sẽ cho phép tôi sử dụng nó trong các ứng dụng khác (và biên dịch nó trên UNIX, vv).

Tôi nên biết điều gì khi đưa ra quyết định này? Có bất kỳ hạn chế lớn hoặc gotchas để sử dụng DLL trong ứng dụng C#?

+1

Mono cũng sẽ cho phép bạn "cổng"/chạy trên * nix. – Tim

Trả lời

6

Tôi sẽ tạo một thư viện trình bao bọc bằng cách sử dụng C++/CLI để hiển thị thư viện cho C#. Điều này có thể để thư viện của bạn không thay đổi và chỉ cần bọc nó để sử dụng từ .NET, cung cấp tốt nhất cả hai tùy chọn.

+1

Đánh bại tôi sau 20 giây. Nhưng * I * đã đi và tìm thấy một liên kết. :) – Randolpho

+1

Sử dụng trình bao bọc C++/CLI là * cách * để đi. P/Invoke sẽ cung cấp cho bạn các vấn đề về tải và phiên bản dll. Sử dụng thư viện không được quản lý sẽ làm cho dòng rõ ràng cho việc biên dịch không được quản lý được quản lý. Tuy nhiên, hãy cẩn thận khi phơi bày các lớp STL. Bạn có thể thấy rằng mã cấp thấp của bạn dự kiến ​​sẽ sử dụng STL không được quản lý sẽ kết thúc bằng các phiên bản được quản lý với nhiều chuyển tiếp được quản lý/không được quản lý. – plinth

2

Một điều tôi thấy hữu ích là nghiên cứu kỹ vào C++/CLI khi xử lý thư viện C++ không được quản lý. Tạo một trình bao bọc được quản lý bằng cách sử dụng C++/CLI và gọi nó từ mã C# của bạn. Các wrapper quản lý có thể bao gồm các thư viện (tôi giả định nó tĩnh liên kết) trong DLL của nó, và một tài liệu tham khảo dự án là tất cả những gì bạn cần cho mã C# của bạn.

+0

FYI: DLL không được liên kết tĩnh. Thư viện liên kết động. – Amy

+1

@ yodaj007: Tôi tin rằng anh ta đã gợi ý liên kết tĩnh thư viện gốc vào trong C++/CLI assembly, điều này đúng. –

+0

@ yodaj007: @Reed Copsey nói nó tốt hơn tôi có thể. Nhưng tôi vẫn sẽ cố gắng: thư viện gốc, dựa trên những điều được đề cập trong câu hỏi ban đầu, dường như là một thư viện liên kết tĩnh. Có nghĩa là nếu thư viện được sử dụng bởi dự án C++/CLI, nó sẽ được biên dịch thành (và được gộp lại bởi) C++/CLI .DLL. – Randolpho

0

Không cần thiết phải viết trình bao bọc trong C++/CLI. Bạn có thể trực tiếp sử dụng vBulletin Gọi từ C#:

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

EDIT: Nếu bạn làm điều đó bằng C++/CLI, bạn sẽ cần phải làm các cuộc gọi LoadLibrary và tạo ra con trỏ hàm. Điều này là dễ dàng hơn đáng kể trong C#. Đây là từ hướng dẫn MSDN được liên kết ở trên, nhưng với nhận xét đã thêm của riêng tôi:

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: Các loại phức hợp cũng có thể được sắp xếp, mặc dù cần thiết để xác định cấu trúc. Ví dụ này lấy từ mã của riêng tôi gọi GDI +. Tôi đã cắt nó một chút.

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

Điều này có vẻ khá đơn giản. Điều gì về marshalling của những thứ như container STL và các loại tùy chỉnh phức tạp? –

+3

Bạn không cần phải đối phó với LoadLibrary hoặc hàm con trỏ - C++/CLI làm cho gói một thư viện phức tạp C++ SIMPLE.P/Gọi các tác phẩm lớn, miễn là thư viện cung cấp một api C, nhưng không có cách nào (dễ dàng) để bọc một cá thể lớp C++ bằng cách sử dụng P/Invoke. Nó tuyệt vời cho C, nhưng không tuyệt vời như vậy đối với các lớp C++. –

+0

Có thể thật khó để tìm ra các chữ ký P/Invoke nếu bạn đang đi qua các cấu trúc phức tạp hơn. Gói trong một DLL C++/CLI được quản lý sẽ cho phép bạn đơn giản hóa các cấu trúc đang được truyền, nếu bạn không thể tìm ra một cách khả thi để kết hợp các cấu trúc. –

Các vấn đề liên quan