i'm trying to find a good way to detect if a feature exists before P/Invoking. For example calling the native StrCmpLogicalW
function:
[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int StrCmpLogicalW(string psz1, string psz2);
}
will crash on some systems that do not have this feature.
i don't want to perform version checking, as that is bad practice, and can sometimes just be wrong (for example when functionality is back-ported, or when the functionality can be uninstalled).
The correct way, is to check for the presence of the export from 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);
}
The problem, of course, is that C# doesn't support function pointers, i.e.:
_StrCmpLogicalW = GetProcedure("shlwapi.dll", "StrCmpLogicalW");
cannot be done.
So i'm trying to find alternative syntax to perform the same logic in .NET. i have the following pseudo-code so far, but i'm getting 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);
}
}
and i need some help.
Another example of some exports that i want to detect presence of would be:
dwmapi.dll::DwmIsCompositionEnabled
dwmapi.dll::DwmExtendFrameIntoClientArea
dwmapi.dll::DwmGetColorizationColor
dwmapi.dll::DwmGetColorizationParameters
(undocumented1, not yet exported by name, ordinal 127)dwmapi.dll::127
(undocumented1, DwmGetColorizationParameters)1 as of Windows 7 SP1
There must already be a design pattern in .NET for checking the presence of OS features. Can anyone point me to an example of the preferred way in .NET to perform feature detection?
You could P/Invoke to LoadLibraryW
to load shlwapi.dll and then P/Invoke to GetProcAddressW
to find "StrCmpLogicalW". If NULL is returned, then it's not there.
You don't need the actual returned value from GetProcAddressW
- as long as it's not NULL, you know you can use the P/Invoke declaration of your choice.
Note that GetProcAddressW
also supports functions exported by ordinal value.
EDIT: If you want to follow some kind of pattern, then this might work:
First define a helper class NativeMethodResolver
that tells you if a method exists in a library:
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);
}
The above helper class can be consumed by derived classes of SafeNativeMethod
that aids in boiler plating some common stuff:
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);
}
}
A derived class that defines its own Invoke
method can then call the base CanInvoke
to see if a default value (or default implementation) should be returned in place of the return value of the sought native method. From your question, I'll take shlwapi.dll/StrCmpLogicalW and dwmapi.dll/DwmIsCompositionEnabled as example implementations for 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();
}
Those two can then be used like this:
static void Main()
{
var StrCmpLogical = new SafeStrCmpLogical();
var relation = StrCmpLogical.Invoke("first", "second");
var DwmIsCompositionEnabled = new SafeDwmIsCompositionEnabled();
var enabled = DwmIsCompositionEnabled.Invoke();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With