2012-01-09 8 views
13

में पी/इनकोकिंग करते समय फ़ीचर पहचान जब मैं यह पता लगाने का एक अच्छा तरीका ढूंढ रहा हूं कि पी/Invoking से पहले कोई सुविधा मौजूद है या नहीं।सी # और .NET

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

समस्या, ज़ाहिर है, कि सी # समारोह संकेत दिए गए, यानी का समर्थन नहीं करता है

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

नहीं किया जा सकता है।

तो मैं .NET में एक ही तर्क करने के लिए वैकल्पिक वाक्यविन्यास खोजने की कोशिश कर रहा हूं। मैं अब तक निम्नलिखित छद्म कोड है, लेकिन मैं 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); 
    } 
} 

और मैं कुछ मदद की जरूरत है।


कुछ निर्यात है कि मैं की उपस्थिति का पता लगाने के लिए चाहते हैं का एक और उदाहरण होगा:

  • dwmapi.dll::DwmIsCompositionEnabled
  • dwmapi.dll::DwmExtendFrameIntoClientArea
  • dwmapi.dll::DwmGetColorizationColor
  • dwmapi.dll::DwmGetColorizationParameters (undocumented , फिर भी निर्यात नहीं नाम से, क्रमिक 127)
  • dwmapi.dll::127 (गैर-दस्तावेजी , DwmGetColorizationParameters)

1 विंडोज 7 SP1

के रूप में वहां पहले से ही ओएस सुविधाओं की उपस्थिति की जाँच के लिए नेट में एक डिजाइन पैटर्न होना चाहिए। क्या कोई मुझे फीचर पहचान करने के लिए .NET में पसंदीदा तरीके के उदाहरण के बारे में बता सकता है?

+0

.NET Framework स्रोत कोड में डिज़ाइन पैटर्न ओएस संस्करण संख्याओं की जांच करना है, लेकिन ऐसा करने के लिए * बुद्धिमानी से * लैरी ओस्टर्मन अपने ब्लॉग पोस्ट में समाप्त होने के समाप्त होने के बाद। मैं मानता हूं कि जोहान का समाधान शायद एक बेहतर है, लेकिन मैं Win32 लड़का भी हूं। 'लोड लाइब्रेरी' और 'GetProcAddress' बस * मुझे समझ में आता है *। जब मैं .NET कोड लिखता हूं तो मैं अपने अधिकांश समय पी/लेखन परिभाषाओं को लिखता हूं। मुझे यकीन नहीं है कि यह वास्तव में एक अच्छी बात है। –

+0

@ कोडी: * मुझे यकीन नहीं है कि यह वास्तव में एक अच्छी बात है * - शायद नहीं, नहीं। :-) –

+0

@CodeGray आप संस्करण संख्याओं पर भरोसा नहीं कर सकते हैं। एक सुविधा को एक ओएस (पूर्व में संस्करण संख्या गलत बनाने) के लिए पूर्ववत रूप से पोर्ट किया गया हो सकता है। उपयोगकर्ता द्वारा एक सुविधा भी स्थापित नहीं की जा सकती है (संस्करण संख्या गलत बना रही है)। –

उत्तर

6

आप shlwapi.dll लोड करने के लिए 0/पर पी/Invoke कर सकते हैं और फिर P/Invroke GetProcAddressW को "StrCmpLogicalW" खोजने के लिए आमंत्रित कर सकते हैं। अगर न्यूल वापस आ गया है, तो यह वहां नहीं है।

आपको GetProcAddressW से वास्तविक लौटाए गए मूल्य की आवश्यकता नहीं है - जब तक यह पूर्ण नहीं है, आप जानते हैं कि आप अपनी पसंद के पी/आमंत्रण घोषणा का उपयोग कर सकते हैं।

ध्यान दें कि GetProcAddressW भी सामान्य मूल्य द्वारा निर्यात किए गए कार्यों का समर्थन करता है।

संपादित करें: यदि आप पैटर्न किसी तरह का पालन करना चाहते हैं, तो इस काम हो सकता है:

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, जिससे आप जान को परिभाषित

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

ऊपर सहायक वर्ग कि बायलर में एड्स कुछ सामान्य सामान चढ़ाना SafeNativeMethod के व्युत्पन्न वर्ग द्वारा उपयोग किया जा

एक व्युत्पन्न वर्ग जो अपनी विधि को परिभाषित करता है, फिर बेस CanInvoke को कॉल करने के लिए कॉल कर सकते हैं यह देखने के लिए कि क्या मूल मूल्य (या डिफ़ॉल्ट कार्यान्वयन) मांगा गया मूल विधि के वापसी मूल्य के स्थान पर वापस किया जाना चाहिए। अपने प्रश्न से, मैं Shlwapi.dll/StrCmpLogicalW और dwmapi.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(); 
} 

उन दो तो इस तरह इस्तेमाल किया जा सकता:

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

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

आप लौटे पते को एक प्रतिनिधि को बदलने के लिए मार्शल.GetDelegateForFunctionPointer() का भी उपयोग कर सकते हैं। – Hans

+0

@ हंस: हां, जब तक आप .NET कॉम्पैक्ट फ्रेमवर्क का उपयोग नहीं कर रहे हों। वह विधि 'मार्शल' कक्षा में असमर्थित है। –

+0

LoadLibrary, GetProcAddress, और FreeLibrary के लिए वाक्यविन्यास के लिए pinvoke.net देखें। – dgvid