Обнаружение функций при P / Invoking в C # и .NET - PullRequest
13 голосов
/ 10 января 2012

Я пытаюсь найти хороший способ определить, существует ли функция перед 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

В .NET уже должен быть шаблон проектирования для проверки наличия функций ОС.Может кто-нибудь указать мне пример предпочтительного способа в .NET для обнаружения функций?

1 Ответ

6 голосов
/ 10 января 2012

Вы можете P / Invoke на LoadLibraryW загрузить shlwapi.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 / 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...