2008-09-16 11 views
31

Tôi có chương trình Windows C# sử dụng dll C++ cho dữ liệu i/o. Mục tiêu của tôi là triển khai ứng dụng dưới dạng một EXE duy nhất.Làm cách nào để một cửa sổ C++ có thể được hợp nhất thành một ứng dụng exe C#?

Các bước để tạo tệp thực thi như vậy là gì?

+0

Bạn nên sử dụng BoxedAppPacker hoặc BoxedApp SDK. Nó phải giúp đỡ. – MastAvalons

+1

Bản sao của http://stackoverflow.com/questions/666799/embedding-unmanaged-dll-into-a-managed-c-sharp-dll/11038376#11038376 –

Trả lời

14

Độc thân hội Triển khai quản lý, Switch Mã Chủ Nhật 4 Tháng Hai, 2007

NET nhà phát triển yêu triển khai xcopy. Và họ yêu thích các thành phần lắp ráp đơn lẻ. Ít nhất tôi luôn cảm thấy không thoải mái, nếu tôi phải sử dụng một số thành phần và cần nhớ một danh sách các tệp cũng bao gồm với assembly chính của thành phần đó. Vì vậy, khi gần đây tôi đã phát triển một thành phần mã được quản lý và phải tăng thêm một số mã không được quản lý từ một DLL C (thx tới Marcus Heege để giúp tôi với điều này!), Tôi đã nghĩ về cách làm thế nào để triển khai hai tệp DLL dễ dàng hơn . Nếu đây chỉ là hai hội chúng tôi có thể đã sử dụng ILmerge để đóng gói chúng chỉ trong một tập tin. Nhưng điều này không làm việc cho các thành phần mã hỗn hợp với các DLL được quản lý cũng như không được quản lý.

Vì vậy here's những gì tôi đã đưa ra một giải pháp:

tôi bao gồm bất cứ điều gì DLL tôi muốn triển khai với component's tôi lắp ráp chính là nguồn lực nhúng. Sau đó, tôi thiết lập một hàm tạo lớp để trích xuất các tệp DLL như dưới đây. Các ctor lớp được gọi là chỉ một lần trong mỗi AppDomain vì vậy nó là một chi phí đáng tin cậy, tôi nghĩ.

namespace MyLib 
{ 
    public class MyClass 
    { 
     static MyClass() 
     { 
      ResourceExtractor.ExtractResourceToFile("MyLib.ManagedService.dll", "managedservice.dll"); 
      ResourceExtractor.ExtractResourceToFile("MyLib.UnmanagedService.dll", "unmanagedservice.dll"); 
     } 

     ... 

Trong ví dụ này tôi bao gồm hai DLL như nguồn lực, một là một DLL unmanaged code, và một là một DLL mã số quản lý (chỉ dành riêng cho mục đích biểu tình), để hiển thị, làm thế nào kỹ thuật này làm việc cho cả hai loại mã .

Mã để giải nén các file DLL vào các tập tin của riêng mình rất đơn giản:

public static class ResourceExtractor 
{ 
    public static void ExtractResourceToFile(string resourceName, string filename) 
    { 
     if (!System.IO.File.Exists(filename)) 
      using (System.IO.Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) 
       using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Create)) 
       { 
        byte[] b = new byte[s.Length]; 
        s.Read(b, 0, b.Length); 
        fs.Write(b, 0, b.Length); 
       } 
    } 
} 

Làm việc với một hội đồng quản lý mã như thế này là như nhau như thường lệ - gần. Bạn tham chiếu nó (ở đây: ManagedService.dll) trong dự án chính của thành phần của bạn (ở đây: MyLib), nhưng đặt thuộc tính Sao chép Địa phương thành sai. Ngoài ra bạn liên kết trong assembly như một mục hiện có và thiết lập Build Action thành Embedded Resource.

Đối với mã không được quản lý (ở đây: UnmanagedService.dll) bạn chỉ cần liên kết trong DLL dưới dạng Mục hiện có và đặt Hành động Xây dựng thành Tài nguyên được nhúng. Để truy cập các chức năng của nó, hãy sử dụng thuộc tính DllImport như bình thường, ví dụ:

[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b); 

Đó là điều đó! Ngay sau khi bạn tạo cá thể đầu tiên của lớp với ctor tĩnh, các DLL nhúng được trích xuất thành các tệp của riêng chúng và sẵn sàng sử dụng như thể bạn đã triển khai chúng dưới dạng các tệp riêng biệt. Miễn là bạn có quyền ghi cho thư mục thực thi, điều này sẽ làm việc tốt cho bạn. Ít nhất là đối với mã nguyên mẫu, tôi nghĩ cách triển khai lắp ráp đơn này khá thuận tiện.

Tận hưởng!

http://weblogs.asp.net/ralfw/archive/2007/02/04/single-assembly-deployment-of-managed-and-unmanaged-code.aspx

+1

Liên kết đã chết. Đó là lý do tại sao bạn nên ** luôn ** [trích dẫn các phần có liên quan nhất của trang được liên kết] (http://stackoverflow.com/help/how-to-answer) thay vì đăng câu trả lời chỉ có liên kết. – BackSlash

0

Thinstall là một giải pháp. Đối với một ứng dụng cửa sổ bản địa tôi sẽ đề nghị nhúng DLL như một đối tượng tài nguyên nhị phân, sau đó giải nén nó vào thời gian chạy trước khi bạn cần nó.

+2

Thinstall cực kỳ tốn kém và gặp sự cố với một số cài đặt Windows và tiện ích mở rộng vỏ bên thứ ba. chúng tôi đã sử dụng nó với thành công hạn chế đối với các mã không phải của tập đoàn, nhưng tìm thấy ClickOnce và MSI cài đặt tốt nhất cho .Net. – user7116

+2

Nếu chủ yếu của bạn đang tìm kiếm cài đặt, tôi muốn giới thiệu Inno Setup, chúng tôi đã bỏ InstallShield trong vài năm trước và không bao giờ nhìn lại. http://www.innosetup.com/isinfo.php – titanae

1

Bạn đã thử ILMerge chưa?http://research.microsoft.com/~mbarnett/ILMerge.aspx

ILMerge là một tiện ích có thể được sử dụng để hợp nhất nhiều hội đồng .NET vào một hội đồng duy nhất. Nó có sẵn miễn phí để sử dụng từ trang Công cụ & Tiện ích tại Trung tâm Nhà phát triển Microsoft .NET Framework.

Nếu bạn đang xây dựng cáC++ DLL C với /clr cờ (tất cả hoặc một phần C++/CLI), sau đó nó sẽ làm việc:

ilmerge /out:Composite.exe MyMainApp.exe Utility.dll 

Nó sẽ không làm việc với một bình thường (bản địa) của Windows DLL tuy nhiên.

+1

Khá chắc chắn nó yêu cầu '/ clr: pure' hoặc cao hơn trên DLL. –

3

Hãy thử boxedapp; nó cho phép tải tất cả các tệp DLL từ bộ nhớ. Ngoài ra, có vẻ như bạn thậm chí có thể nhúng thời gian chạy .net. Tốt để tạo ra một ứng dụng thực sự độc lập ...

+0

sdk tuyệt vời. Tôi cũng sử dụng nó. –

-2

PostBuild từ Xenocode có thể đóng gói cả quản lý và unmanged thành một exe duy nhất.

1

Chỉ cần nhấp chuột phải vào dự án của bạn trong Visual Studio, chọn Thuộc tính dự án -> Tài nguyên -> Thêm tài nguyên -> Thêm tệp hiện có… Và bao gồm mã bên dưới vào App.xaml.cs hoặc tương đương.

public App() 
{ 
    AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve); 
} 

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
{ 
    string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll",""); 

    dllName = dllName.Replace(".", "_"); 

    if (dllName.EndsWith("_resources")) return null; 

    System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly()); 

    byte[] bytes = (byte[])rm.GetObject(dllName); 

    return System.Reflection.Assembly.Load(bytes); 
} 

Dưới đây là bài đăng blog ban đầu của tôi: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/

+2

xin vui lòng không đăng chính xác cùng một bài nhiều lần, đặc biệt là nếu bạn sẽ đăng nó vào câu hỏi rất cũ. Nếu câu trả lời chính xác giống như bài đăng trước là thích hợp, hãy gắn cờ câu hỏi là trùng lặp. Nếu bạn tiếp tục đăng liên kết blog đó với cùng một văn bản đi kèm, bạn có thể thu hút một số cờ spam. – Mat

+0

Ok .. Cảm ơn bạn. –

0

Smart Assembly thể làm điều này và nhiều hơn nữa. Nếu dll của bạn có mã không được quản lý, nó sẽ không cho phép bạn hợp nhất các dll vào một assembly duy nhất, thay vào đó nó có thể nhúng các phụ thuộc cần thiết làm tài nguyên cho exe chính của bạn. Mặt lật của nó, không phải của nó.

Bạn có thể thực hiện việc này theo cách thủ công bằng cách nhúng dll vào tài nguyên của mình và sau đó dựa vào Hội đồng AppDomain ResolveHandler. Khi nói đến dlls chế độ hỗn hợp, tôi tìm thấy nhiều biến thể và hương vị của phương pháp ResolveHandler không hoạt động đối với tôi (tất cả đều đọc byte dll vào bộ nhớ và đọc từ nó). Tất cả họ đều làm việc cho các dll được quản lý. Đây là những gì đã hiệu quả đối với tôi:

static void Main() 
{ 
    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => 
    { 
     string assemblyName = new AssemblyName(args.Name).Name; 
     if (assemblyName.EndsWith(".resources")) 
      return null; 

     string dllName = assemblyName + ".dll"; 
     string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName); 

     using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName)) 
     { 
      byte[] data = new byte[stream.Length]; 
      s.Read(data, 0, data.Length); 

      //or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length); 

      File.WriteAllBytes(dllFullPath, data); 
     } 

     return Assembly.LoadFrom(dllFullPath); 
    }; 
} 

Chìa khóa ở đây là ghi byte vào một tệp và tải từ vị trí của nó. Để tránh vấn đề về gà và trứng, bạn phải đảm bảo bạn khai báo trình xử lý trước khi truy cập assembly và bạn không truy cập vào các thành viên lắp ráp (hoặc khởi tạo bất kỳ thứ gì phải xử lý assembly) bên trong phần tải (lắp ráp giải quyết). Ngoài ra hãy cẩn thận để đảm bảo GetMyApplicationSpecificPath() không phải là thư mục tạm thời vì các tệp tạm thời có thể bị xóa bởi các chương trình khác hoặc của chính bạn (không phải là nó sẽ bị xóa trong khi chương trình của bạn truy cập vào dll, nhưng ít nhất là phiền toái của nó. vị trí tốt). Cũng lưu ý rằng bạn phải viết các byte mỗi lần, bạn không thể tải từ vị trí chỉ 'cos dll đã nằm ở đó.

Nếu lắp ráp hoàn toàn không được quản lý, bạn có thể xem số link hoặc this về cách tải các dll này.