我正在尝试找到一种在 P/调用之前检测功能是否存在的好方法。例如调用 native StrCmpLogicalW
功能:
[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int StrCmpLogicalW(string psz1, string psz2);
}
在某些没有此功能的系统上会崩溃。
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
(未记录1,尚未按名称导出,序号 127)dwmapi.dll::127
(未记录1,DwmGetColorizationParameters)
1 从 Windows 7 SP1 开始
.NET 中必须已经有一个设计模式来检查操作系统功能的存在。谁能指出在 .NET 中执行特征检测的首选方式的示例?
最佳答案
您可以 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
来查看是否应该返回默认值(或默认实现)来代替所寻找的 native 方法的返回值。根据您的问题,我将以 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();
}
关于c# - C#和.NET中P/Invoking时的特性检测,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8794991/