13

P/Invoking の前に機能が存在するかどうかを検出する良い方法を見つけようとしています。たとえば、ネイティブStrCmpLogicalW関数を呼び出します。

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

この機能を持たない一部のシステムではクラッシュします。

バージョン チェックを実行したくありません。これは悪い習慣であり、間違っている場合もあります (たとえば、機能がバックポートされている場合や、機能がアンインストールされている場合など)。

正しい方法は、からのエクスポートの存在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(文書化されていない1、まだ名前でエクスポートされていない、序数 127)
  • dwmapi.dll::127(文書化されていない1、DwmGetColorizationParameters)

1 Windows 7 SP1 以降

OS 機能の存在を確認するためのデザイン パターンが .NET に既に存在している必要があります。.NET で機能検出を実行するための推奨される方法の例を誰か教えてもらえますか?

4

1 に答える 1

6

P/Invoke でLoadLibraryWshlwapi.dll をロードしてから、P/Invoke でGetProcAddressW"StrCmpLogicalW" を見つけることができます。NULL が返された場合は、存在しません。

からの実際の戻り値は必要ありませんGetProcAddressW- NULL でない限り、選択した P/Invoke 宣言を使用できることがわかっています。

GetProcAddressW序数値によってエクスポートされる関数もサポートすることに注意してください。

編集: ある種のパターンに従いたい場合、これはうまくいくかもしれません:

最初NativeMethodResolverに、メソッドがライブラリに存在するかどうかを通知するヘルパー クラスを定義します。

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

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検索されたネイティブ メソッドの戻り値の代わりに既定値 (または既定の実装) を返す必要があるかどうかを確認できます。あなたの質問から、shlwapi.dll/StrCmpLogicalWdwmapi.dll/DwmIsCompositionEnabledを の実装例として取り上げ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();
}

これらの 2 つは、次のように使用できます。

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

    var DwmIsCompositionEnabled = new SafeDwmIsCompositionEnabled();
    var enabled = DwmIsCompositionEnabled.Invoke();
}
于 2012-01-09T20:53:28.807 に答える