2012-01-09 6 views
13

P/Invokingの前に機能が存在するかどうかを検出するための良い方法を見つけようとしています。C/.NETでのP /呼び出し時の機能検出

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

がこの機能を持っていないいくつかのシステムでクラッシュします。たとえば、ネイティブStrCmpLogicalW関数を呼び出します。

i don't want to perform version checkingこれは悪い習慣であり、間違っていることもあります(たとえば、機能がバックポートされているか、機能をアンインストールできる場合など)。

正しい方法は、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); 
} 

問題は、もちろん、すなわち、C#は関数ポインタをサポートしていないということです。

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

はできません。

私は.NETで同じロジックを実行するための代替構文を見つけようとしています。私はこれまでのところ以下の擬似コードを持っていますが、私は窮地に瀕しています:

[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); 
    } 
} 

私はいくつかの助けが必要です。


私はの存在を検出したいいくつかの輸出の別の例は次のようになります。

  • dwmapi.dll::DwmIsCompositionEnabled
  • dwmapi.dll::DwmExtendFrameIntoClientArea
  • dwmapi.dll::DwmGetColorizationColor
  • dwmapi.dll::DwmGetColorizationParameters(文書化されていない、まだエクスポートされません氏名、序数127)
  • Windows 7のSP1

    のよう

  • dwmapi.dll::127(文書化されていない、DwmGetColorizationParameters)

すでにOSの機能の有無をチェックするための.NETのデザインパターンが存在する必要があります。誰でも、.NETでの機能検出を行うために推奨される方法の例を教えていただけますか?

+0

.NET Frameworkのソースコードのデザインパターンは、OSのバージョン番号を確認することですが、Larry Osterman氏がブログ記事で結論づけるように、*インテリジェントに*行うことです。私はJohannのソリューションがおそらくより優れていることに同意しますが、私もWin32の人です。 'LoadLibrary'と' GetProcAddress'はちょうど*私に意味があります*。私は.NETコードを書くときにP/Invokeの定義を書いている時間のほとんどを費やします。それが本当に良いのかどうかはわかりません。 –

+0

@Cody:*実際には良いのかどうかは分かりません。*おそらくそうではありません。 :-) –

+0

@ CodeGrayバージョン番号は信頼できません。機能が遡及的にOSに移植されている可能性があります(バージョン番号が間違っている)。また、機能がユーザーによってインストールされない可能性もあります(バージョン番号が間違っている)。 –

答えて

6

LoadLibraryWにshlwapi.dllを読み込み、P/InvokeをGetProcAddressWに読み込んで「StrCmpLogicalW」を検索することができます。 NULLが返された場合は、そこにはありません。

GetProcAddressWの実際の戻り値は必要ありません。NULLでない限り、選択したP/Invoke宣言を使用できます。

GetProcAddressWは、序数でエクスポートされた関数もサポートしています。

EDIT:あなたは、パターンのいくつかの種類をフォローしたい場合は、これはうまくいくかもしれない:

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); 
} 

まずメソッドがライブラリ内に存在するかどうかを示しますヘルパークラスNativeMethodResolverを定義

上記ヘルパークラスは、いくつかの一般的なものにボイラーめっきを援助するSafeNativeMethodの派生クラスによって消費することができます。

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); 
    } 
} 

独自のInvokeメソッドを定義している派生クラスは、ベースCanInvokeを呼び出して、求められるネイティブメソッドの戻り値の代わりにデフォルト値(またはデフォルトの実装)が返されるかどうかを確認できます。

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(); 
} 

これら二つは、このように使用することができます:

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

    var DwmIsCompositionEnabled = new SafeDwmIsCompositionEnabled(); 
    var enabled = DwmIsCompositionEnabled.Invoke(); 
} 
SafeNativeMethodのための実装例として、あなたの質問から、私は SHLWAPI.DLL/StrCmpLogicalWdwmapi.dllを取るよ/をDwmIsCompositionEnabled
+3

Marshal.GetDelegateForFunctionPointer()を使用して、返されたアドレスをデリゲートに変換することもできます。 – Hans

+0

@ハンス:はい、.NET Compact Frameworkを使用している場合を除きます。このメソッドは、そこの 'Marshal'クラスではサポートされていません。 –

+0

LoadLibrary、GetProcAddress、およびFreeLibraryの構文については、pinvoke.netを参照してください。 – dgvid

関連する問題