2012-01-09 14 views
13

Tôi đang cố gắng tìm một cách hay để phát hiện xem một đối tượng địa lý có tồn tại trước khi P/Invoking không. Ví dụ: gọi hàm gốc StrCmpLogicalW:Phát hiện tính năng khi P/Gọi trong C# và .NET

[SuppressUnmanagedCodeSecurity] 
internal static class SafeNativeMethods 
{ 
    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] 
    public static extern int StrCmpLogicalW(string psz1, string psz2); 
} 

sẽ gặp sự cố trên một số hệ thống không có tính năng này.

i don't want to perform version checking, vì đó là thực hành không tốt và đôi khi có thể sai (ví dụ: khi chức năng được truyền lại hoặc khi chức năng có thể được gỡ cài đặt).

Cách đúng, là để kiểm tra sự hiện diện của việc xuất khẩu từ shlwapi.dll:

private static _StrCmpLogicalW: function(String psz1, String psz2): Integer; 
private Boolean _StrCmpLogicalWInitialized; 

public int StrCmpLogicalW(String psz1, psz2) 
{ 
    if (!_StrCmpLogialInitialized) 
    { 
     _StrCmpLogicalW = GetProcedure("shlwapi.dll", "StrCmpLogicalW"); 
     _StrCmpLogicalWInitialized = true; 
    } 

    if (_StrCmpLogicalW) 
     return _StrCmpLogicalW(psz1, psz2) 
    else 
     return String.Compare(psz1, psz2, StringComparison.CurrentCultureIgnoreCase); 
} 

Vấn đề, tất nhiên, là C# không hỗ trợ chức năng gợi ý, ví dụ:

_StrCmpLogicalW = GetProcedure("shlwapi.dll", "StrCmpLogicalW"); 

không thể thực hiện được.

Vì vậy, tôi đang cố gắng tìm cú pháp thay thế để thực hiện cùng một logic trong .NET. Tôi có mã giả sau đây cho đến nay, nhưng tôi đang nhận được stymied:

[SuppressUnmanagedCodeSecurity] 
internal static class SafeNativeMethods 
{ 
    private Boolean IsSupported = false; 
    private Boolean IsInitialized = false; 

    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, Export="StrCmpLogicalW", CaseSensitivie=false, SetsLastError=true, IsNative=false, SupportsPeanutMandMs=true)] 
    private static extern int UnsafeStrCmpLogicalW(string psz1, string psz2); 

    public int StrCmpLogicalW(string s1, string s2) 
    { 
     if (!IsInitialized) 
     { 
      //todo: figure out how to loadLibrary in .net 
      //todo: figure out how to getProcedureAddress in .net 
      IsSupported = (result from getProcedureAddress is not null); 
      IsInitialized = true; 
     } 

     if (IsSupported) 
      return UnsafeStrCmpLogicalW(s1, s2); 
     else 
      return String.Compare(s1, s2, StringComparison.CurrentCultureIgnoreCase); 
    } 
} 

và tôi cần một số trợ giúp.


Một ví dụ khác của một số kim ngạch xuất khẩu mà tôi muốn để phát hiện sự hiện diện của sẽ là:

  • dwmapi.dll::DwmIsCompositionEnabled
  • dwmapi.dll::DwmExtendFrameIntoClientArea
  • dwmapi.dll::DwmGetColorizationColor
  • dwmapi.dll::DwmGetColorizationParameters (cung cấp tài liệu , chưa xuất khẩu theo tên, thứ tự 127)
  • dwmapi.dll::127 (không có giấy tờ , DwmGetColorizationParameters)

như của Windows 7 SP1

Hiện đã phải là một mẫu thiết kế trong .NET để kiểm tra sự hiện diện của các tính năng hệ điều hành. Bất cứ ai có thể chỉ cho tôi một ví dụ về cách ưa thích trong. NET để thực hiện tính năng phát hiện?

+0

Mẫu thiết kế trong mã nguồn .NET Framework là để kiểm tra số phiên bản hệ điều hành, nhưng để làm như vậy * thông minh * như Larry Osterman kết thúc lên trong bài đăng trên blog của mình. Tôi đồng ý rằng giải pháp của Johann có lẽ là một giải pháp tốt hơn, nhưng tôi cũng là một anh chàng Win32. 'LoadLibrary' và' GetProcAddress' chỉ * có ý nghĩa * đối với tôi. Tôi dành phần lớn thời gian của tôi viết P/Gọi định nghĩa khi tôi viết mã .NET. Tôi không chắc đó có phải là một điều tốt hay không. –

+0

@Cody: * Tôi không chắc đó có thực sự là một điều hay không * - có lẽ không, không. :-) –

+0

@CodeGray Bạn không thể dựa vào số phiên bản. Một tính năng có thể đã được chuyển về một hệ điều hành (làm cho số phiên bản sai). Một tính năng cũng có thể không được cài đặt bởi người dùng (làm cho số phiên bản sai). –

Trả lời

6

Bạn có thể P/Gọi tới LoadLibraryW để tải shlwapi.dll và sau đó P/Gọi tới GetProcAddressW để tìm "StrCmpLogicalW". Nếu NULL được trả về, thì nó không có ở đó.

Bạn không cần giá trị trả lại thực tế từ GetProcAddressW - miễn là nó không phải là NULL, bạn biết bạn có thể sử dụng khai báo P/Invoke mà bạn chọn.

Lưu ý rằng GetProcAddressW cũng hỗ trợ các hàm được xuất theo giá trị thứ tự.

EDIT: Nếu bạn muốn làm theo một số loại mô hình, thì đây có thể làm việc:

Đầu tiên xác định một lớp helper NativeMethodResolver cho bạn biết nếu một phương pháp tồn tại trong thư viện:

public static class NativeMethodResolver 
{ 
    public static bool MethodExists(string libraryName, string methodName) 
    { 
     var libraryPtr = LoadLibrary(libraryName); 
     var procPtr = GetProcAddress(libraryPtr, methodName); 

     return libraryPtr != UIntPtr.Zero && procPtr != UIntPtr.Zero; 
    } 

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
    private static extern UIntPtr LoadLibrary(string lpFileName); 

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)] 
    private static extern UIntPtr GetProcAddress(UIntPtr hModule, string lpProcName); 
} 

Lớp trợ giúp trên có thể được tiêu thụ bởi các lớp dẫn xuất của SafeNativeMethod hỗ trợ trong lò hơi mạ một số công cụ phổ biến:

public abstract class SafeNativeMethod 
{ 
    private readonly string libraryName; 
    private readonly string methodName; 
    private bool resolved; 
    private bool exists; 

    protected SafeNativeMethod(string libraryName, string methodName) 
    { 
     this.libraryName = libraryName; 
     this.methodName = methodName; 
    } 

    protected bool CanInvoke 
    { 
     get 
     { 
      if (!this.resolved) 
      { 
       this.exists = Resolve(); 
       this.resolved = true; 
      } 

      return this.exists; 
     }    
    } 

    private bool Resolve() 
    { 
     return NativeMethodResolver.MethodExists(this.libraryName, this.methodName); 
    } 
} 

Một lớp dẫn xuất xác định phương thức Invoke riêng của nó sau đó có thể gọi cơ số CanInvoke để xem giá trị mặc định (hoặc thực thi mặc định) có được trả về thay cho giá trị trả về của phương thức gốc được tìm kiếm hay không. Từ câu hỏi của bạn, tôi sẽ đưa shlwapi.dll/StrCmpLogicalWDWMAPI.dll/DwmIsCompositionEnabled như triển khai ví dụ cho SafeNativeMethod:

public sealed class SafeStrCmpLogical : SafeNativeMethod 
{ 
    public SafeStrCmpLogical() 
     : base("shlwapi.dll", "StrCmpLogicalW") 
    {   
    } 

    public int Invoke(string psz1, string psz2) 
    { 
     return CanInvoke ? StrCmpLogicalW(psz1, psz2) : 0; 
    } 

    [DllImport("shlwapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
    private static extern int StrCmpLogicalW(string psz1, string psz2); 
} 

public sealed class SafeDwmIsCompositionEnabled : SafeNativeMethod 
{ 
    public SafeDwmIsCompositionEnabled() 
     : base("dwmapi.dll", "DwmIsCompositionEnabled") 
    { 
    } 

    public bool Invoke() 
    { 
     return CanInvoke ? DwmIsCompositionEnabled() : false; 
    } 

    [DllImport("dwmapi.dll", SetLastError = true, PreserveSig = false)] 
    private static extern bool DwmIsCompositionEnabled(); 
} 

Hai sau đó có thể được sử dụng như thế này:

static void Main() 
{ 
    var StrCmpLogical = new SafeStrCmpLogical(); 
    var relation = StrCmpLogical.Invoke("first", "second"); 

    var DwmIsCompositionEnabled = new SafeDwmIsCompositionEnabled(); 
    var enabled = DwmIsCompositionEnabled.Invoke(); 
} 
+3

Bạn cũng có thể sử dụng Marshal.GetDelegateForFunctionPointer() để chuyển đổi địa chỉ trả về cho một đại biểu. – Hans

+0

@Hans: Có, trừ khi bạn đang sử dụng .NET Compact Framework. Phương thức đó không được hỗ trợ trong lớp 'Marshal' ở đó. –

+0

Xem pinvoke.net để biết cú pháp cho LoadLibrary, GetProcAddress và FreeLibrary. – dgvid