2009-08-23 67 views
8

Tôi đang sử dụng DllImport trong giải pháp của mình.
Vấn đề của tôi là tôi có hai phiên bản của cùng một DLL được xây dựng cho 32 bit và một phiên bản khác cho 64 bit.Đặt dllimport lập trình trong C#

Cả hai đều hiển thị cùng một chức năng với tên giống hệt nhau và chữ ký giống hệt nhau. Vấn đề của tôi là tôi phải sử dụng hai phương pháp tĩnh để hiển thị chúng và sau đó tại thời gian chạy sử dụng IntPtr kích thước để xác định đúng để gọi.

private static class Ccf_32 
{ 
    [DllImport(myDllName32)] 
    public static extern int func1(); 
} 

private static class Ccf_64 
{ 
    [DllImport(myDllName64)] 
    public static extern int func1(); 
} 

tôi phải làm điều này vì myDllName32myDllName64 phải được liên tục và tôi đã không tìm thấy một cách để thiết lập nó tại thời gian chạy.

Có ai có giải pháp thanh lịch cho điều này để tôi có thể loại bỏ trùng lặp mã và kiểm tra kích thước không đổi IntPtr không đổi.

Nếu tôi có thể đặt tên tệp, tôi sẽ chỉ phải kiểm tra một lần và tôi có thể loại bỏ một tấn mã lặp lại.

+0

Không có ý nghĩa trong cách chọn nó trong thời gian chạy nếu sự khác biệt là là toàn bộ biên soạn . – Havenard

Trả lời

12

Bạn có thể đạt được điều này với từ khóa #if. Nếu bạn định nghĩa một biểu tượng trình biên dịch có điều kiện gọi là win32, đoạn code sau sẽ sử dụng win32-block, nếu bạn loại bỏ nó nó sẽ sử dụng các khối khác:

#if win32 
    private static class ccf_32 
    { 
     [DllImport(myDllName32)] 
     public static extern int func1(); 
    } 
#else  
    private static class ccf_64 
    { 
     [DllImport(myDllName64)] 
     public static extern int func1(); 
    } 
#endif 

này có thể có nghĩa rằng bạn có thể loại bỏ các gói lớp mà bạn hiện có:

private static class ccf 
    { 
#if win32 
     [DllImport(myDllName32)] 
     public static extern int func1(); 
#else  
     [DllImport(myDllName64)] 
     public static extern int func1(); 
#endif 
    } 

Để thuận tiện, tôi đoán bạn có thể tạo cấu hình xây dựng để kiểm soát biểu tượng biên dịch.

+3

Vâng, nhưng tôi muốn giữ tùy chọn 'Bất kỳ CPU' thay vì có phiên bản 32 và 64 bit. – Matt

+0

Trong khi nó hoạt động, cách tiếp cận này vẫn dẫn đến việc sao chép mã mà áp phích gốc muốn tránh, cộng với hạn chế bạn biên dịch các phiên bản riêng biệt cho 32 và 64 bit. Bạn có thể loại bỏ mã trùng lặp và tiếp tục nhắm mục tiêu cả phiên bản 32 và 64 bit với một biên dịch DLL/EXE bằng cách sử dụng phương pháp deanis để sử dụng SetDllDirectory, hoặc của tôi để sử dụng LoadLibrary. –

2

Tại sao không quấn chúng vào một phương pháp?

private static class ccf_32_64 
{ 
    private static class ccf_32 
    { 
     [DllImport(myDllName32)] 
     private static extern int func1(); 
    } 

    private static class ccf_64 
    { 
     [DllImport(myDllName64)] 
     private static extern int func1(); 
    } 

    public static int func1() 
    { 
     if (32bit) 
     { 
      return ccf_32.func1(); 
     } 
     else 
     { 
      return ccf_64.func1(); 
     } 
    } 
} 
+0

Đó là bản chất những gì tôi có bây giờ :-) – Matt

+0

Vâng, một khi bạn quấn nó lên, bạn không phải lo lắng về nó. – ChaosPandion

1

Một lựa chọn khác là có cả 32 và 64-bit phiên bản của DLL không được quản lý có cùng tên, nhưng có họ sống trong các thư mục riêng biệt trong xây dựng đầu ra của bạn (ví dụ x86 \ và x64 \).

Sau đó, trình cài đặt của bạn hoặc cách khác bạn đang phân phối này được cập nhật để nó biết cài đặt đúng DLL cho nền tảng mà nó đang cài đặt.

0

Bạn không thể làm điều này theo cách bạn muốn. Bạn cần phải nghĩ đến thuộc tính DllImport làm siêu dữ liệu được sử dụng tại thời gian biên dịch. Kết quả là bạn không thể thay đổi DLL nó đang nhập động.

Nếu bạn muốn giữ mã được quản lý của mình được nhắm mục tiêu đến "CPU bất kỳ" thì bạn cần phải nhập cả hai thư viện 32 bit và 64 bit được bao bọc dưới dạng hai hàm khác nhau mà bạn có thể gọi tùy thuộc vào môi trường thời gian chạy hoặc sử dụng một số cuộc gọi API Win32 bổ sung để tải trễ phiên bản chính xác của hội đồng không được quản lý trong thời gian chạy và các cuộc gọi Win32 bổ sung để thực thi các phương thức được yêu cầu. Nhược điểm là bạn sẽ không có hỗ trợ thời gian biên dịch cho bất kỳ loại mã nào cho an toàn loại, v.v.

0

Hmm, tôi tự hỏi liệu bạn có thể tạo giao diện và sau đó là một lớp với phương thức dựa trên các tệp 32 bit và 64 bit.

Tôi không chắc liệu có phương pháp rõ ràng để xác định xem bạn đang chạy 64 bit hay không, nhưng có thể làm việc sau: cho phép mã không an toàn và có chức năng không an toàn nhận con trỏ tới một số địa chỉ con trỏ có kích thước 4 hoặc 8 byte. Dựa trên kết quả xác định thực hiện giao diện cần tạo.

0

Bạn có thể xác định xem bạn đang chạy 64Bits hay không bằng cách kiểm tra kích thước của loại IntPtr (được gọi là kiểu gốc int anyways). Sau đó, bạn có thể tải DLL approriate bằng cách sử dụng cuộc gọi LoadLibraryW đã nhập, nhận con trỏ hàm bằng GetProcAddress, và sau đó, hãy xem Marshal.GetDelegateForFunctionPointer

Điều này gần như không phức tạp. Bạn phải DllImport cả LoadLibraryW và GetProcAddress.

16

Tôi muốn thực hiện điều này bằng cách sử dụng lệnh gọi LoadLibrary từ kernel32.dll để buộc một DLL cụ thể tải từ một đường dẫn cụ thể.

Nếu bạn đặt tên cho 32-bit và 64-bit DLL giống nhau nhưng đặt chúng trong các đường dẫn khác nhau, bạn có thể sử dụng mã sau để tải đúng dựa trên phiên bản Windows bạn đang chạy. Tất cả bạn cần làm là gọi ExampleDllLoader.LoadDll() TRƯỚC bất kỳ mã tham khảo ccf lớp được tham chiếu:

private static class ccf 
{ 
    [DllImport("myDllName")] 
    public static extern int func1(); 
} 

public static class ExampleDllLoader 
{ 
    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] 
    private extern static IntPtr LoadLibrary(string librayName); 

    public static void LoadDll() 
    { 
     String path; 

     //IntPtr.Size will be 4 in 32-bit processes, 8 in 64-bit processes 
     if (IntPtr.Size == 4) 
      path = "c:/example32bitpath/myDllName.dll"; 
     else 
      path = "c:/example64bitpath/myDllName.dll"; 

     LoadLibrary(path); 
    } 
} 
+0

Dường như giải pháp này yêu cầu người dùng có quyền quản trị. Ai đó có thể xác nhận? –

+0

Điều này không yêu cầu quyền quản trị, miễn là người dùng có quyền đọc DLL đang được tải. Tôi đã sử dụng nó trong các tình huống mà một người dùng bình thường đang chạy ứng dụng. –

9

Tôi biết đây là một câu hỏi thực sự cũ (Tôi mới - là nó xấu để trả lời một câu hỏi cũ?), nhưng tôi phải giải quyết vấn đề tương tự này. Tôi đã phải tham khảo động một DLL 32-bit hoặc 64-bit dựa trên hệ điều hành, trong khi .EXE của tôi được biên dịch cho CPU bất kỳ.

Bạn có thể sử dụng DLLImport và bạn không cần sử dụng LoadLibrary().

Tôi đã làm điều này bằng cách sử dụng SetDLLDirectory. Trái với tên, SetDLLDirectory thêm vào đường dẫn tìm kiếm DLL và không thay thế toàn bộ đường dẫn. Điều này cho phép tôi có một DLL có cùng tên ("TestDLL.dll" cho cuộc thảo luận này) trong các thư mục con của Win32 và Win64 và được gọi một cách thích hợp.

public partial class frmTest : Form 
{ 
    static bool Win32 = Marshal.SizeOf(typeof(IntPtr)) == 4; 
    private string DLLPath = Win32 ? @"\Win32" : @"\Win64"; 

    [DllImport("kernel32.dll", SetLastError = true)] 
    public static extern bool SetDllDirectory(string lpPathName); 
    [DllImport("TestDLL.dll", SetLastError = true)] 
    static extern IntPtr CreateTestWindow(); 

    private void btnTest_Click(object sender, EventArgs e) 
    { 
     string dllDir = String.Concat(Directory.GetCurrentDirectory(), DLLPath); 
     SetDllDirectory(dllDir); 

     IntPtr newWindow = CreateTestWindow(); 
    } 
} 
+2

Đây là một cách tiếp cận tuyệt vời, đặc biệt là nếu bạn có nhiều DLL mà bạn cần 32 và 64 bit phiên bản của.Tôi tin rằng tôi thích cách tiếp cận SetDllDirectory thậm chí tốt hơn bằng cách sử dụng phương pháp LoadDll mà tôi đã giải thích trong câu trả lời của tôi. Nó cũng là một mã số ít hơn rất nhiều so với các phương pháp tiếp cận wrapper, và cho phép bạn biên dịch bất kỳ CPU nào. –

1

bạn có thể tạo hai phương pháp và chọn một trong một thời gian chạy, vì vậy bạn có thể giữ Any CPU

public static class Ccf 
{ 
    [DllImport(myDllName32)] 
    private static extern int func32(); 

    [DllImport(myDllName64)] 
    private static extern int func64(); 


    public static int func() 
    { 
     if(Environment.Is64BitProcess) 
     { 
      return func64(); 
     } 
     return func32(); 
    } 

}