diff --git a/PInvoke/Kernel32/LibLoaderApi.cs b/PInvoke/Kernel32/LibLoaderApi.cs index 4f2c58dd..97ce4ac5 100644 --- a/PInvoke/Kernel32/LibLoaderApi.cs +++ b/PInvoke/Kernel32/LibLoaderApi.cs @@ -1176,7 +1176,7 @@ namespace Vanara.PInvoke [PInvokeData("Winbase.h", MSDNShortId = "ms648044")] [Obsolete] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool FreeResource([In] SafeResourceDataHandle hglbResource); + public static extern bool FreeResource([In] HRSRCDATA hglbResource); /// /// @@ -1250,7 +1250,7 @@ namespace Vanara.PInvoke public static string GetModuleFileName(HINSTANCE hModule) { var buffer = new StringBuilder(MAX_PATH); - Label_000B: + Label_000B: var num1 = GetModuleFileName(hModule, buffer, (uint)buffer.Capacity); if (num1 == 0) throw new Win32Exception(); @@ -1324,7 +1324,7 @@ namespace Vanara.PInvoke [DllImport(Lib.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] [PInvokeData("Winbase.h", MSDNShortId = "ms683200")] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG dwFlags, [Optional] string lpModuleName, out IntPtr phModule); + public static extern bool GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG dwFlags, [Optional] string lpModuleName, out HINSTANCE phModule); /// Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL). /// @@ -1743,7 +1743,7 @@ namespace Vanara.PInvoke [DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)] [PInvokeData("Winbase.h", MSDNShortId = "ms648046")] [SuppressUnmanagedCodeSecurity] - public static extern SafeResourceDataHandle LoadResource(HINSTANCE hModule, HRSRC hResInfo); + public static extern HRSRCDATA LoadResource(HINSTANCE hModule, HRSRC hResInfo); /// Retrieves a pointer to the specified resource in memory. /// @@ -1764,16 +1764,12 @@ namespace Vanara.PInvoke [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] [PInvokeData("Winbase.h", MSDNShortId = "ms648047")] [SuppressUnmanagedCodeSecurity] - public static extern IntPtr LockResource(SafeResourceDataHandle hResData); + public static extern IntPtr LockResource(HRSRCDATA hResData); - /// - /// Determines whether the specified function in a delay-loaded DLL is available on the system. - /// + /// Determines whether the specified function in a delay-loaded DLL is available on the system. /// - /// /// A handle to the calling module. Desktop applications can use the GetModuleHandle or GetModuleHandleEx function to get this - /// handle. Windows Store apps should set this parameter to . - /// + /// handle. Windows Store apps should set this parameter to static_cast<HMODULE>(&__ImageBase). /// /// /// The file name of the delay-loaded DLL that exports the specified function. This parameter is case-insensitive. @@ -1782,17 +1778,11 @@ namespace Vanara.PInvoke /// than kernel32.dll. /// /// - /// - /// The name of the function to query. This parameter is case-sensitive. - /// - /// - /// This parameter is reserved and must be zero (0). - /// + /// The name of the function to query. This parameter is case-sensitive. + /// This parameter is reserved and must be zero (0). /// - /// /// TRUE if the specified function is available on the system. If the specified function is not available on the system, this /// function returns FALSE. To get extended error information, call GetLastError. - /// /// /// /// @@ -1822,12 +1812,12 @@ namespace Vanara.PInvoke /// available on the system. /// /// - // https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi2/nf-libloaderapi2-queryoptionaldelayloadedapi BOOL - // QueryOptionalDelayLoadedAPI( HMODULE hParentModule, LPCSTR lpDllName, LPCSTR lpProcName, DWORD Reserved ); - [DllImport(Lib.KernelBase, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Ansi)] + // https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi2/nf-libloaderapi2-queryoptionaldelayloadedapi + // BOOL QueryOptionalDelayLoadedAPI( HMODULE hParentModule, LPCSTR lpDllName, LPCSTR lpProcName, DWORD Reserved ); + [DllImport(Lib.KernelBase, SetLastError = true, ExactSpelling = true)] [PInvokeData("libloaderapi2.h", MSDNShortId = "43690689-4372-48ae-ac6d-230250f05f7c")] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool QueryOptionalDelayLoadedAPI(HINSTANCE hParentModule, string lpDllName, string lpProcName, uint Reserved = 0); + public static extern bool QueryOptionalDelayLoadedAPI(HINSTANCE hParentModule, [MarshalAs(UnmanagedType.LPStr)] string lpDllName, [MarshalAs(UnmanagedType.LPStr)] string lpProcName, uint Reserved = 0); /// Removes a directory that was added to the process DLL search path by using AddDllDirectory. /// The cookie returned by AddDllDirectory when the directory was added to the search path. @@ -1956,6 +1946,54 @@ namespace Vanara.PInvoke public IntPtr DangerousGetHandle() => handle; } + /// Provides a handle to resource data. + [StructLayout(LayoutKind.Sequential)] + public struct HRSRCDATA : IHandle + { + private IntPtr handle; + + /// Initializes a new instance of the struct. + /// An object that represents the pre-existing handle to use. + public HRSRCDATA(IntPtr preexistingHandle) => handle = preexistingHandle; + + /// Returns an invalid handle by instantiating a object with . + public static HRSRCDATA NULL => new HRSRCDATA(IntPtr.Zero); + + /// Gets a value indicating whether this instance is a null handle. + public bool IsNull => handle == IntPtr.Zero; + + /// Performs an explicit conversion from to . + /// The handle. + /// The result of the conversion. + public static explicit operator IntPtr(HRSRCDATA h) => h.handle; + + /// Performs an implicit conversion from to . + /// The pointer to a handle. + /// The result of the conversion. + public static implicit operator HRSRCDATA(IntPtr h) => new HRSRCDATA(h); + + /// Implements the operator !=. + /// The first handle. + /// The second handle. + /// The result of the operator. + public static bool operator !=(HRSRCDATA h1, HRSRCDATA h2) => !(h1 == h2); + + /// Implements the operator ==. + /// The first handle. + /// The second handle. + /// The result of the operator. + public static bool operator ==(HRSRCDATA h1, HRSRCDATA h2) => h1.Equals(h2); + + /// + public override bool Equals(object obj) => obj is HRSRCDATA h ? handle == h.handle : false; + + /// + public override int GetHashCode() => handle.GetHashCode(); + + /// + public IntPtr DangerousGetHandle() => handle; + } + /// Provides a to a that releases a created HINSTANCE instance at disposal using FreeLibrary. [PInvokeData("LibLoaderAPI.h")] public class SafeHINSTANCE : SafeHANDLE @@ -1993,25 +2031,5 @@ namespace Vanara.PInvoke /// protected override bool InternalReleaseHandle() => FreeLibrary(this); } - - /// Represents a loaded resource handle. - /// - public class SafeResourceDataHandle : SafeHANDLE - { - private IntPtr bptr; - - /// Initializes a new instance of the class. - public SafeResourceDataHandle() : base() { } - - /// Initializes a new instance of the class. - /// The handle. - public SafeResourceDataHandle(IntPtr handle) : base(handle, false) { } - - /// Gets the pointer to the memory of the resource. - public IntPtr LockedPtr => bptr != null ? bptr : (bptr = LockResource(this)); - - /// - protected override bool InternalReleaseHandle() => throw new NotImplementedException(); - } } } \ No newline at end of file diff --git a/UnitTests/PInvoke/Kernel32/Kernel32.csproj b/UnitTests/PInvoke/Kernel32/Kernel32.csproj index a8e98707..9b55122e 100644 --- a/UnitTests/PInvoke/Kernel32/Kernel32.csproj +++ b/UnitTests/PInvoke/Kernel32/Kernel32.csproj @@ -46,6 +46,7 @@ + diff --git a/UnitTests/PInvoke/Kernel32/Kernel32Tests.cs b/UnitTests/PInvoke/Kernel32/Kernel32Tests.cs index 16710fa7..ffa68c9e 100644 --- a/UnitTests/PInvoke/Kernel32/Kernel32Tests.cs +++ b/UnitTests/PInvoke/Kernel32/Kernel32Tests.cs @@ -13,8 +13,6 @@ namespace Vanara.PInvoke.Tests [TestFixture] public class Kernel32Tests { - internal const string badlibfn = @"C:\Windows\System32\ole3.dll"; - internal const string libfn = @"ole32.dll"; internal const string tmpstr = @"Temporary"; internal const string fn = @"C:\Temp\help.ico"; @@ -75,18 +73,6 @@ namespace Vanara.PInvoke.Tests File.Delete(fn); } - [Test] - public void EnumResourceNamesTest() - { - using (var hLib = LoadLibraryEx(@"C:\Windows\System32\en-US\aclui.dll.mui", IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE)) - { - var l = EnumResourceNamesEx(hLib, ResourceType.RT_STRING); - Assert.That(l.Count, Is.GreaterThan(0)); - foreach (var resourceName in l) - Assert.That(resourceName.ToString(), Has.Length.GreaterThan(0)); - } - } - [Test] public void FileTimeToSystemTimeTest() { @@ -99,16 +85,6 @@ namespace Vanara.PInvoke.Tests Assert.That(dt.Day, Is.EqualTo(st.wDay)); } - [Test] - public void FindResourceTest() - { - using (var hLib = LoadLibraryEx(@"comctl32.dll", IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE)) - { - var ptr = FindResource(hLib, 65, ResourceType.RT_STRING); - Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero)); - } - } - [Test] public void GetCompressedFileSizeTest() { @@ -150,28 +126,6 @@ namespace Vanara.PInvoke.Tests Assert.That(i.deviceId == GAMING_DEVICE_DEVICE_ID.GAMING_DEVICE_DEVICE_ID_NONE); } - [Test] - public void GetModuleFileNameTest() - { - const string fn = @"C:\Windows\System32\tzres.dll"; - using (var hLib = LoadLibrary(fn)) - { - var f = GetModuleFileName(hLib); - Assert.That(f, Is.SamePath(fn)); - } - } - - [Test] - public void GetProcAddressTest() - { - const string fn = @"C:\Windows\System32\kernel32.dll"; - using (var hLib = LoadLibrary(fn)) - { - var a = GetProcAddress(hLib, "GetNativeSystemInfo"); - Assert.That(a, Is.Not.EqualTo(IntPtr.Zero)); - } - } - [Test] public void GlobalLockTest() { @@ -188,44 +142,6 @@ namespace Vanara.PInvoke.Tests } } - [Test] - public void LoadLibraryTest() - { - var hlib = LoadLibrary(badlibfn); - Assert.That((HINSTANCE)hlib, Is.EqualTo(HINSTANCE.NULL)); - Assert.That(Marshal.GetLastWin32Error(), Is.Not.Zero); - - hlib = LoadLibrary(libfn); - Assert.That((HINSTANCE)hlib, Is.Not.EqualTo(HINSTANCE.NULL)); - } - - [Test] - public void LoadLibraryExTest() - { - var hlib = LoadLibraryEx(badlibfn, IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_DATAFILE); - Assert.That((HINSTANCE)hlib, Is.EqualTo(HINSTANCE.NULL)); - Assert.That(Marshal.GetLastWin32Error(), Is.Not.Zero); - - hlib = LoadLibraryEx(libfn, IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_DATAFILE); - Assert.That((HINSTANCE)hlib, Is.Not.EqualTo(HINSTANCE.NULL)); - } - - [Test] - public void LoadResourceTest() - { - using (var hlib = LoadLibraryEx("ole32.dll", IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_DATAFILE)) - { - var hres = FindResource(hlib, 4, ResourceType.RT_CURSOR); - Assert.That(hres, Is.Not.EqualTo(IntPtr.Zero)); - var sz = SizeofResource(hlib, hres); - Assert.That(sz, Is.GreaterThan(0)); - var pres = LoadResource(hlib, hres); - Assert.That(pres, Is.Not.EqualTo(IntPtr.Zero)); - var pmem = LockResource(pres); - Assert.That(pmem, Is.Not.EqualTo(IntPtr.Zero)); - } - } - [Test] public void QueryDosDeviceTest() { diff --git a/UnitTests/PInvoke/Kernel32/LibLoaderApiTests.cs b/UnitTests/PInvoke/Kernel32/LibLoaderApiTests.cs new file mode 100644 index 00000000..8afad305 --- /dev/null +++ b/UnitTests/PInvoke/Kernel32/LibLoaderApiTests.cs @@ -0,0 +1,190 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using Vanara.InteropServices; +using static Vanara.PInvoke.AdvApi32; +using static Vanara.PInvoke.Kernel32; + +namespace Vanara.PInvoke.Tests +{ + [TestFixture] + public class LibLoaderApiTests + { + internal const string badlibfn = @"C:\Windows\System32\ole3.dll"; + internal const string libfn = @"ole32.dll"; + const string resFile = @"C:\Windows\en-US\regedit.exe.mui"; + + [Test] + public void AddRemoveDllDirectoryTest() + { + var ptr = AddDllDirectory(@"C:\Temp"); + Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero)); + Assert.That(RemoveDllDirectory(ptr), Is.True); + } + + [Test] + public void EnumResourceLanguagesExTest() + { + using (var hLib = LoadLibraryEx(resFile, IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE)) + { + IList l = null; + Assert.That(() => l = EnumResourceLanguagesEx(hLib, ResourceType.RT_STRING, 2), Throws.Nothing); + Assert.That(l.Count, Is.GreaterThan(0)); + TestContext.WriteLine(string.Join(" : ", l)); + } + } + + [Test] + public void EnumResourceTypesExTest() + { + using (var hLib = LoadLibraryEx(resFile, IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE)) + { + IList l = null; + Assert.That(() => l = EnumResourceTypesEx(hLib), Throws.Nothing); + Assert.That(l.Count, Is.GreaterThan(0)); + TestContext.WriteLine(string.Join(" : ", l.Select(i => (ResourceType)i.id))); + } + } + + [Test] + public void EnumResourceNamesTest() + { + using (var hLib = LoadLibraryEx(resFile, IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE)) + { + IList l = null; + Assert.That(() => l = EnumResourceNamesEx(hLib, ResourceType.RT_STRING), Throws.Nothing); + Assert.That(l.Count, Is.GreaterThan(0)); + foreach (var resourceName in l) + Assert.That(resourceName.ToString(), Has.Length.GreaterThan(0)); + TestContext.WriteLine(string.Join(" : ", l.Select(i => i.id))); + } + } + + [Test] + public void FindResourceTest() + { + using (var hLib = LoadLibraryEx(@"comctl32.dll", IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE)) + { + var ptr = (IntPtr)FindResource(hLib, 65, ResourceType.RT_STRING); + Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero)); + } + } + + [Test] + public void FindResourceExTest() + { + using (var hLib = LoadLibraryEx(@"comctl32.dll", IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE)) + { + var ptr = (IntPtr)FindResourceEx(hLib, 65, ResourceType.RT_STRING, 1033); + Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero)); + } + } + + [Test] + public void FindStringOrdinalTest() + { + const string src = "How do you do today?"; + + Assert.That(FindStringOrdinal(SEARCH_FLAGS.FIND_ENDSWITH, src, src.Length, "DAY?", 4, true), Is.GreaterThan(0)); + Assert.That(FindStringOrdinal(SEARCH_FLAGS.FIND_FROMEND, src, src.Length, "do", 2, false), Is.EqualTo(11)); + Assert.That(FindStringOrdinal(SEARCH_FLAGS.FIND_FROMSTART, src, src.Length, "do", 2, false), Is.EqualTo(4)); + Assert.That(FindStringOrdinal(SEARCH_FLAGS.FIND_STARTSWITH, src, src.Length, "how", 2, false), Is.EqualTo(-1)); + Assert.That(FindStringOrdinal(SEARCH_FLAGS.FIND_STARTSWITH, src, src.Length, "how", 2, true), Is.EqualTo(0)); + } + + [Test] + public void FreeLibraryAndExitThreadTest() + { + var t = new System.Threading.Thread(ThreadFunc); + t.Start(); + t.Join(); + + void ThreadFunc() + { + const string fn = @"C:\Windows\System32\kernel32.dll"; + using (var hLib = LoadLibrary(fn)) + { + FreeLibraryAndExitThread(hLib, 20); + hLib.SetHandleAsInvalid(); + } + } + } + + [Test] + public void GetModuleFileNameHandleTest() + { + const string fn = @"C:\Windows\System32\tzres.dll"; + using (var hLib = LoadLibrary(fn)) + { + var f = GetModuleFileName(hLib); + Assert.That(f, Is.SamePath(fn)); + var hmod = GetModuleHandle(fn); + Assert.That(hLib.Equals(hmod), Is.True); + Assert.That(GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG.GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, fn, out hmod), Is.True); + Assert.That(hLib.Equals(hmod), Is.True); + } + } + + [Test] + public void GetProcAddressTest() + { + const string fn = @"C:\Windows\System32\kernel32.dll"; + using (var hLib = LoadLibrary(fn)) + { + var a = GetProcAddress(hLib, "GetNativeSystemInfo"); + Assert.That(a, Is.Not.EqualTo(IntPtr.Zero)); + } + } + + [Test] + public void LoadLibraryTest() + { + using (var hlib = LoadLibrary(badlibfn)) + { + Assert.That((HINSTANCE)hlib, Is.EqualTo(HINSTANCE.NULL)); + Assert.That(Marshal.GetLastWin32Error(), Is.Not.Zero); + } + + using (var hlib = LoadLibrary(libfn)) + Assert.That((HINSTANCE)hlib, Is.Not.EqualTo(HINSTANCE.NULL)); + } + + [Test] + public void LoadLibraryExTest() + { + using (var hlib = LoadLibraryEx(badlibfn, IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_DATAFILE)) + { + Assert.That((HINSTANCE)hlib, Is.EqualTo(HINSTANCE.NULL)); + Assert.That(Marshal.GetLastWin32Error(), Is.Not.Zero); + } + + using (var hlib = LoadLibraryEx(libfn, IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_DATAFILE)) + Assert.That((HINSTANCE)hlib, Is.Not.EqualTo(HINSTANCE.NULL)); + } + + [Test] + public void FindLoadLockSizeofResourceTest() + { + using (var hlib = LoadLibraryEx("ole32.dll", IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_DATAFILE)) + { + var hres = FindResource(hlib, 4, ResourceType.RT_CURSOR); + Assert.That(hres, Is.Not.EqualTo(IntPtr.Zero)); + var sz = SizeofResource(hlib, hres); + Assert.That(sz, Is.GreaterThan(0)); + var pres = LoadResource(hlib, hres); + Assert.That((IntPtr)pres, Is.Not.EqualTo(IntPtr.Zero)); + var pmem = LockResource(pres); + Assert.That(pmem, Is.Not.EqualTo(IntPtr.Zero)); + } + } + + [Test] + public void QueryOptionalDelayLoadedAPITest() + { + Assert.That(() => QueryOptionalDelayLoadedAPI(GetModuleHandle(), "kernel32.dll", "GetNativeSystemInfo"), Throws.Nothing); + } + } +} \ No newline at end of file