2013-05-15 39 views
17

Tôi có một ứng dụng được xây dựng dưới dạng 'Bất kỳ CPU' và có hai Dll của cùng một bên thứ ba của cùng một thư viện được nhắm mục tiêu đến x86 và x64. Tôi muốn bao gồm một trong các thư viện này trong thời gian chạy tùy thuộc vào nền tảng nó sẽ chạy trên máy khách. Điều gì sẽ là cách tốt nhất để đi về nó?Tải x64 hoặc x86 DLL tùy thuộc vào nền tảng?

+1

Có phải các cụm được quản lý hoặc mã gốc không? –

+0

Tại sao bạn không muốn chỉ xây dựng các phiên bản x86 và x64 của ứng dụng? – outcoldman

+0

Tôi muốn nói rằng chúng được trộn lẫn. De-biên dịch họ đã cho tôi rất nhiều mã được viết bằng C nhưng tôi vẫn không chắc chắn. – Ammark

Trả lời

21

Nếu chúng ta đang nói về DLL không được quản lý, khai báo p/gọi như thế này:

[DllImport("DllName.dll")] 
static extern foo(); 

Lưu ý rằng chúng tôi không chỉ định một đường dẫn đến DLL, chỉ cần tên của nó, mà tôi đoán là như nhau cho cả phiên bản 32 bit và 64 bit.

Sau đó, trước khi bạn gọi bất kỳ p/invokes nào của bạn, hãy tải thư viện vào quá trình của bạn. Làm điều đó bằng cách p/gọi hàm LoadLibrary API. Tại thời điểm này, bạn sẽ xác định xem quá trình của bạn là 32 hoặc 64 bit và xây dựng đường dẫn đầy đủ đến DLL cho phù hợp. Đường dẫn đầy đủ đó là những gì bạn chuyển đến LoadLibrary.

Bây giờ, khi bạn gọi p/invokes cho thư viện, chúng sẽ được giải quyết bởi mô đun mà bạn vừa tải.

Đối với các hội đồng quản lý, bạn có thể sử dụng Assembly.LoadFile để chỉ định đường dẫn của hội đồng. Điều này có thể là một chút khó khăn để dàn nhạc, nhưng bài viết tuyệt vời này cho bạn thấy làm thế nào: Automatically Choose 32 or 64 Bit Mixed Mode DLLs. Có rất nhiều chi tiết liên quan đến chế độ hỗn hợp và các phụ thuộc DLL gốc có thể không liên quan đến bạn. Điều quan trọng là trình xử lý sự kiện AppDomain.CurrentDomain.AssemblyResolve.

+0

Rất tuyệt. Tôi không biết điều này. – Ani

+0

Cảm ơn David, Tôi có cần phải tạo một trình bao bọc xung quanh các dll hiện có, vì các phương pháp tôi cần truy cập không phải là tĩnh không? – Ammark

+0

Bạn có sử dụng 'DllImport' ngay bây giờ khi bạn gọi mã của bạn khi bạn biên dịch giống như x86 hay chỉ x64? –

1

Tôi thực sự rất có kinh nghiệm về chủ đề này, vì vậy tôi nghĩ tôi sẽ đăng câu trả lời theo cách tôi đã sử dụng trong Pencil.Gaming. Thứ nhất, bạn phải "DllImport" hai chức năng, một từ dll 32 bit và một từ 64-bit dll (hoặc như vậy, hoặc dylib, bất kỳ nền tảng nào của bạn sử dụng).

static class Foo32 { 
    [DllImport("32bitdll.dll")] 
    internal static extern void Foo(); 
} 
static class Foo64 { 
    [DllImport("64bitdll.dll")] 
    internal static extern void Foo(); 
} 

Sau đó, bạn cần một lớp trung gian có chứa các đại biểu, và nhập khẩu chúng từ interop 32 hoặc 64-bit theo kích thước của một IntPtr (Tôi không sử dụng Environment.Is64BitProcess, vì đó là một chức năng NET 4) :

internal delegate void FooDelegate(); 
static class FooDelegates { 
    internal static FooDelegate Foo; 

    static FooDelegates() { 
     Type interop = (IntPtr.Size == 8) ? typeof(Foo64) : typeof(Foo32); 
     FieldInfo[] fields = typeof(FooDelegates).GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); 
     foreach (FieldInfo fi in fields) { 
      MethodInfo mi = glfwInterop.GetMethod(fi.Name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); 
      Delegate function = Delegate.CreateDelegate(fi.FieldType, mi); 
      fi.SetValue(null, function); 
     } 
    } 
} 

Và sau đó tôi thường sử dụng một lớp học "thật", chứa các chức năng bạn nhập khẩu (mặc dù điều này là không cần thiết về mặt kỹ thuật):

public static class FooApi { 
    public static void Foo() { 
     FooDelegates.Foo(); 
    } 
} 

Đây là một nỗi đau thực sự nếu bạn chỉ cần một hoặc hai chức năng, nhưng cách nhập khẩu các đại biểu thực sự hiệu quả cho các thư viện/ứng dụng quy mô lớn hơn. Bạn có thể muốn kiểm tra Pencil.Gaming trên github, vì nó sử dụng phương pháp này khá rộng rãi (here là một ví dụ về nó được sử dụng rất nhiều).

Một lợi ích khác của phương pháp này là 100% nền tảng chéo và không dựa vào bất kỳ chức năng WinAPI nào.

+0

Bạn không cần bất kỳ đại biểu nào. Bạn có thể thực hiện toàn bộ điều này với 'DllImport' cũ cho tất cả các hàm được nhập. –

+0

@DavidHeffernan Nhưng sau đó bạn không thể sử dụng "CPU bất kỳ". Sau đó, bạn sẽ cần phải phân phối lại các phiên bản riêng biệt của ứng dụng của bạn cho các hệ thống 32 bit và 64 bit. – antonijn

+0

Chắc chắn bạn có thể sử dụng AnyCPU. Câu trả lời của tôi giải thích như thế nào. Bạn cần các DLL để có cùng tên, nhưng ở trong các thư mục khác nhau, hãy nhớ đến bạn. Nhưng đó là điều bình thường đối với các tệp DLL có độ bit 32/64. –

2

Giải pháp hoàn chỉnh của tôi cho vấn đề của tôi là sử dụng liên kết thứ hai do David Heffernan cung cấp. Những gì tôi đã làm là 1. Tham chiếu một dll giả trong dự án. 2. rõ hai pre-build sự kiện

xcopy /y "$(SolutionDir)\Assemblies\Lib\x86\(Assembly name)*" "$(TargetDir)" 
xcopy /y "$(SolutionDir)\Assemblies\Lib\x64\(Assemble name)*" "$(TargetDir)" 

3. và khi khởi động của ứng dụng trong trường hợp lắp ráp quyết tâm thay đổi lắp ráp tương ứng phụ thuộc vào nền tảng này.

 var currentDomain = AppDomain.CurrentDomain; 
     var location = Assembly.GetExecutingAssembly().Location; 
     var assemblyDir = Path.GetDirectoryName(location); 

     if (assemblyDir != null && (File.Exists(Path.Combine(assemblyDir, "(Assembly name).proxy.dll")) 
            || !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x86.dll")) 
            || !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x64.dll")))) 
     { 
      throw new InvalidOperationException("Found (Assembly name).proxy.dll which cannot exist. " 
       + "Must instead have (Assembly name).x86.dll and (Assembly name).x64.dll. Check your build settings."); 
     } 

     currentDomain.AssemblyResolve += (sender, arg) => 
     { 
      if (arg.Name.StartsWith("(Assembly name),", StringComparison.OrdinalIgnoreCase)) 
      { 
       string fileName = Path.Combine(assemblyDir, 
        string.Format("(Assembly).{0}.dll", (IntPtr.Size == 4) ? "x86" : "x64")); 
       return Assembly.LoadFile(fileName); 
      } 
      return null; 
     }; 
+1

Tuy nhiên, điều này chỉ hoạt động đối với dlls .net, không dành cho bất kỳ dll – Tyron

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