2012-01-09 24 views
13

Özellik algılama özelliği P/Invoking'den önce bir özellik olup olmadığını tespit etmenin iyi bir yolunu bulmaya çalışıyorum.P/Invoking C# ve .NET

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

bu özelliği yok bazı sistemlerde kilitlenmesine: Örneğin yerli StrCmpLogicalW fonksiyonunu çağırarak.

i don't want to perform version checking

, bu kötü bir uygulamadır ve (işlevsellik geri portlu olduğunda veya işlevsellik kaldırılabilir zaman örneğin) bazen sadece yanlış olabilir olarak.

doğru yolu, shlwapi.dll gelen ihracat varlığı için kontrol etmektir:

:

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

sorun, tabii ki, C# işlev işaretçileri, yani desteklemiyor olmasıdır

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

yapılamaz.

.NET'te aynı mantığı gerçekleştirmek için alternatif sözdizimi bulmaya çalışıyorum. ben şimdiye kadar aşağıdaki sözde kod var ama ben stymied alıyorum:

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

ve biraz yardıma ihtiyacım var.


i varlığını tespit etmek isteyen bazı ihracatın başka örnek olacaktır:

  • dwmapi.dll::DwmIsCompositionEnabled
  • dwmapi.dll::DwmExtendFrameIntoClientArea
  • dwmapi.dll::DwmGetColorizationColor
  • dwmapi.dll::DwmGetColorizationParameters (, belgesiz henüz dışa İsme göre, ordinal 127)
  • dwmapi.dll::127 (belgesiz , DwmGetColorizationParameters) Windows 7 SP1

    itibariyle

zaten işletim sistemi özelliklerinin varlığını kontrol etmek için .NET bir tasarım deseni olmalı. Özellik algılama işlemini gerçekleştirmek için bana .NET'te tercih edilen yolun bir örneğini işaret edebilir miyim?

+0

.NET Framework kaynak kodundaki tasarım deseni, işletim sistemi sürüm numaralarını kontrol etmektir, ancak bunu yapmak için * akıllıca * Larry Osterman'ın blog yayınında sona ermesiyle sona erer. Johann'un çözümünün muhtemelen daha iyi olduğuna katılıyorum, ama ben de bir Win32 adamıyım. 'LoadLibrary' ve' GetProcAddress'' için * sadece * anlamlıdır. .NET kod yazarken zamanımın çoğunu P/Invoke tanımlarını yazarak geçiriyorum. Aslında iyi bir şey olup olmadığından emin değilim. –

+0

@Cody: * Gerçekten iyi bir şey olup olmadığından emin değilim * - muhtemelen hayır, hayır. :-) –

+0

@CodeGray Sürüm numaralarına güvenemezsiniz. Bir özellik geriye dönük olarak bir işletim sistemine yüklenmiş olabilir (sürüm numarası yanlış). Bir özellik ayrıca kullanıcı tarafından yüklenemedi (sürüm numarası yanlış). –

cevap

6

shlwapi.dll dosyasını yüklemek için LoadLibraryW P/Invoke ve/veya "StrCmpLogicalW" bulmak için P/Invoke için GetProcAddressW çağırırsınız. NULL döndürüldüğünde, o zaman değil.

GetProcAddressW'dan döndürülen gerçek değere ihtiyacınız yoktur - NULL olmadığı sürece, seçtiğiniz P/Invoke bildirimini kullanabilirsiniz.

Not: GetProcAddressW, sıra değerle dışa aktarılan işlevleri de destekler.

DÜZENLEME: Eğer desen çeşit takip etmek istiyorsanız, o zaman bu işe yarayabilecek:

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); 
} 
: Bir yöntem, bir kütüphanede varsa

İlk anlatır bir yardımcı sınıf NativeMethodResolver tanımlamak

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

yukarıdaki yardımcı sınıfı kazanında yardımcı bazı ortak malzeme kaplama SafeNativeMethod türetilmiş sınıfları tarafından tüketilebilir

Kendi Invoke yöntemini tanımlayan türetilmiş bir sınıf, istenen yerel yöntemin dönüş değeri yerine varsayılan değer (veya varsayılan uygulama) döndürülmesi gerekip gerekmediğini görmek için CanInvoke tabanını çağırabilir. Sorunuzun, ben Shlwapi.dll/StrCmpLogicalW ve dwmapi.dll alacağım/SafeNativeMethod için örnek uygulamaları gibi DwmIsCompositionEnabled: Bu iki zaman bu gibi kullanılabilir

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

Ayrıca, döndürülen adresi bir temsilci haline getirmek için Marshal.GetDelegateForFunctionPointer() yöntemini de kullanabilirsiniz. – Hans

+0

@Hans: .NET Compact Framework'ü kullanmadığınız sürece. Bu yöntem "Marshal" sınıfında desteklenmiyor. –

+0

LoadLibrary, GetProcAddress ve FreeLibrary için sözdizimi için pinvoke.net adresine bakın. – dgvid