diff --git a/PInvoke/Kernel32/WinBase.Resource.cs b/PInvoke/Kernel32/WinBase.Resource.cs
index 159406dc..19674d13 100644
--- a/PInvoke/Kernel32/WinBase.Resource.cs
+++ b/PInvoke/Kernel32/WinBase.Resource.cs
@@ -5,65 +5,70 @@ namespace Vanara.PInvoke
{
public static partial class Kernel32
{
- /// Retrieves a handle that can be used by the UpdateResource function to add, delete, or replace resources in a binary module.
+ ///
+ /// Retrieves a handle that can be used by the UpdateResource function to add, delete, or replace resources in a binary module.
+ ///
///
/// Type: LPCTSTR
///
- /// The binary file in which to update resources. An application must be able to obtain write-access to this file; the file referenced by pFileName
- /// cannot be currently executing. If pFileName does not specify a full path, the system searches for the file in the current directory.
+ /// The binary file in which to update resources. An application must be able to obtain write-access to this file; the file
+ /// referenced by pFileName cannot be currently executing. If pFileName does not specify a full path, the system searches for the
+ /// file in the current directory.
///
///
///
/// Type: BOOL
///
- /// Indicates whether to delete the pFileName parameter's existing resources. If this parameter is TRUE, existing resources are deleted and the
- /// updated file includes only resources added with the UpdateResource function. If this parameter is FALSE, the updated file includes
- /// existing resources unless they are explicitly deleted or replaced by using UpdateResource.
+ /// Indicates whether to delete the pFileName parameter's existing resources. If this parameter is TRUE, existing resources
+ /// are deleted and the updated file includes only resources added with the UpdateResource function. If this parameter is
+ /// FALSE, the updated file includes existing resources unless they are explicitly deleted or replaced by using UpdateResource.
///
///
///
/// Type: HANDLE
///
- /// If the function succeeds, the return value is a handle that can be used by the UpdateResource and EndUpdateResource functions. The
- /// return value is NULL if the specified file is not a PE, the file does not exist, or the file cannot be opened for writing. To get extended
- /// error information, call GetLastError.
+ /// If the function succeeds, the return value is a handle that can be used by the UpdateResource and EndUpdateResource
+ /// functions. The return value is NULL if the specified file is not a PE, the file does not exist, or the file cannot be
+ /// opened for writing. To get extended error information, call GetLastError.
///
///
- // HANDLE WINAPI BeginUpdateResource( _In_ LPCTSTR pFileName, _In_ BOOL bDeleteExistingResources);
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms648030(v=vs.85).aspx
+ // HANDLE WINAPI BeginUpdateResource( _In_ LPCTSTR pFileName, _In_ BOOL bDeleteExistingResources); https://msdn.microsoft.com/en-us/library/windows/desktop/ms648030(v=vs.85).aspx
[DllImport(Lib.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
[PInvokeData("Winbase.h", MSDNShortId = "ms648030")]
- public static extern UpdateResourceHandle BeginUpdateResource(string pFileName, [MarshalAs(UnmanagedType.Bool)] bool bDeleteExistingResources);
+ public static extern SafeHUPDRES BeginUpdateResource(string pFileName, [MarshalAs(UnmanagedType.Bool)] bool bDeleteExistingResources);
/// Commits or discards changes made prior to a call to UpdateResource.
///
/// Type: HANDLE
- /// A module handle returned by the BeginUpdateResource function, and used by UpdateResource, referencing the file to be updated.
+ ///
+ /// A module handle returned by the BeginUpdateResource function, and used by UpdateResource, referencing the file to
+ /// be updated.
+ ///
///
///
/// Type: BOOL
///
- /// Indicates whether to write the resource updates to the file. If this parameter is TRUE, no changes are made. If it is FALSE, the
- /// changes are made: the resource updates will take effect.
+ /// Indicates whether to write the resource updates to the file. If this parameter is TRUE, no changes are made. If it is
+ /// FALSE, the changes are made: the resource updates will take effect.
///
///
///
/// Type: BOOL
///
- /// Returns TRUE if the function succeeds; FALSE otherwise. If the function succeeds and fDiscard is TRUE, then no resource updates
- /// are made to the file; otherwise all successful resource updates are made to the file. To get extended error information, call GetLastError.
+ /// Returns TRUE if the function succeeds; FALSE otherwise. If the function succeeds and fDiscard is TRUE, then
+ /// no resource updates are made to the file; otherwise all successful resource updates are made to the file. To get extended error
+ /// information, call GetLastError.
///
///
- // BOOL WINAPI EndUpdateResource( _In_ HANDLE hUpdate, _In_ BOOL fDiscard);
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms648032(v=vs.85).aspx
+ // BOOL WINAPI EndUpdateResource( _In_ HANDLE hUpdate, _In_ BOOL fDiscard); https://msdn.microsoft.com/en-us/library/windows/desktop/ms648032(v=vs.85).aspx
[DllImport(Lib.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
[PInvokeData("Winbase.h", MSDNShortId = "ms648032")]
[return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool EndUpdateResource([In] UpdateResourceHandle hUpdate, [MarshalAs(UnmanagedType.Bool)] bool fDiscard);
+ public static extern bool EndUpdateResource([In] HUPDRES hUpdate, [MarshalAs(UnmanagedType.Bool)] bool fDiscard);
///
- /// Adds, deletes, or replaces a resource in a portable executable (PE) file. There are some restrictions on resource updates in files that contain
- /// Resource Configuration (RC Config) data: language-neutral (LN) files and language-specific resource (.mui) files.
+ /// Adds, deletes, or replaces a resource in a portable executable (PE) file. There are some restrictions on resource updates in
+ /// files that contain Resource Configuration (RC Config) data: language-neutral (LN) files and language-specific resource (.mui) files.
///
///
/// Type: HANDLE
@@ -72,33 +77,34 @@ namespace Vanara.PInvoke
///
/// Type: LPCTSTR
///
- /// The resource type to be updated. Alternatively, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID is an integer value
- /// representing a predefined resource type. If the first character of the string is a pound sign (#), then the remaining characters represent a decimal
- /// number that specifies the integer identifier of the resource type. For example, the string "#258" represents the identifier 258.
+ /// The resource type to be updated. Alternatively, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID
+ /// is an integer value representing a predefined resource type. If the first character of the string is a pound sign (#), then the
+ /// remaining characters represent a decimal number that specifies the integer identifier of the resource type. For example, the
+ /// string "#258" represents the identifier 258.
///
/// For a list of predefined resource types, see Resource Types.
///
///
/// Type: LPCTSTR
///
- /// The name of the resource to be updated. Alternatively, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID is a
- /// resource ID. When creating a new resource do not use a string that begins with a '#' character for this parameter.
+ /// The name of the resource to be updated. Alternatively, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID),
+ /// where ID is a resource ID. When creating a new resource do not use a string that begins with a '#' character for this parameter.
///
///
///
/// Type: WORD
///
- /// The language identifier of the resource to be updated. For a list of the primary language identifiers and sublanguage identifiers that make up a
- /// language identifier, see the MAKELANGID macro.
+ /// The language identifier of the resource to be updated. For a list of the primary language identifiers and sublanguage identifiers
+ /// that make up a language identifier, see the MAKELANGID macro.
///
///
///
/// Type: LPVOID
///
- /// The resource data to be inserted into the file indicated by hUpdate. If the resource is one of the predefined types, the data must be valid and
- /// properly aligned. Note that this is the raw binary data to be stored in the file indicated by hUpdate, not the data provided by LoadIcon,
- /// LoadString, or other resource-specific load functions. All data containing strings or text must be in Unicode format. lpData must not point to
- /// ANSI data.
+ /// The resource data to be inserted into the file indicated by hUpdate. If the resource is one of the predefined types, the data
+ /// must be valid and properly aligned. Note that this is the raw binary data to be stored in the file indicated by hUpdate, not the
+ /// data provided by LoadIcon, LoadString, or other resource-specific load functions. All data containing strings or
+ /// text must be in Unicode format. lpData must not point to ANSI data.
///
/// If lpData is NULL and cbData is 0, the specified resource is deleted from the file indicated by hUpdate.
///
@@ -110,53 +116,53 @@ namespace Vanara.PInvoke
/// Type: BOOL
/// Returns TRUE if successful or FALSE otherwise. To get extended error information, call GetLastError.
///
- // BOOL WINAPI UpdateResource( _In_ HANDLE hUpdate, _In_ LPCTSTR lpType, _In_ LPCTSTR lpName, _In_ WORD wLanguage, _In_opt_ LPVOID lpData, _In_ DWORD cbData);
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms648049(v=vs.85).aspx
+ // BOOL WINAPI UpdateResource( _In_ HANDLE hUpdate, _In_ LPCTSTR lpType, _In_ LPCTSTR lpName, _In_ WORD wLanguage, _In_opt_ LPVOID
+ // lpData, _In_ DWORD cbData); https://msdn.microsoft.com/en-us/library/windows/desktop/ms648049(v=vs.85).aspx
[DllImport(Lib.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
[PInvokeData("Winbase.h", MSDNShortId = "ms648049")]
[return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool UpdateResource([In] UpdateResourceHandle hUpdate, string lpType, string lpName, ushort wLanguage, [In] IntPtr lpData, uint cbData);
+ public static extern bool UpdateResource([In] HUPDRES hUpdate, SafeResourceId lpType, SafeResourceId lpName, ushort wLanguage, [In] IntPtr lpData, uint cbData);
- /// Provides a handle that can be used by UpdateResource.
+ /// Provides a handle to an update resource.
[StructLayout(LayoutKind.Sequential)]
- public struct UpdateResourceHandle : IHandle
+ public struct HUPDRES : IHandle
{
private IntPtr handle;
- /// Initializes a new instance of the struct.
+ /// Initializes a new instance of the struct.
/// An object that represents the pre-existing handle to use.
- public UpdateResourceHandle(IntPtr preexistingHandle) => handle = preexistingHandle;
+ public HUPDRES(IntPtr preexistingHandle) => handle = preexistingHandle;
- /// Returns an invalid handle by instantiating a object with .
- public static UpdateResourceHandle NULL => new UpdateResourceHandle(IntPtr.Zero);
+ /// Returns an invalid handle by instantiating a object with .
+ public static HUPDRES NULL => new HUPDRES(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 .
+ /// Performs an explicit conversion from to .
/// The handle.
/// The result of the conversion.
- public static explicit operator IntPtr(UpdateResourceHandle h) => h.handle;
+ public static explicit operator IntPtr(HUPDRES h) => h.handle;
- /// Performs an implicit conversion from to .
+ /// Performs an implicit conversion from to .
/// The pointer to a handle.
/// The result of the conversion.
- public static implicit operator UpdateResourceHandle(IntPtr h) => new UpdateResourceHandle(h);
+ public static implicit operator HUPDRES(IntPtr h) => new HUPDRES(h);
/// Implements the operator !=.
/// The first handle.
/// The second handle.
/// The result of the operator.
- public static bool operator !=(UpdateResourceHandle h1, UpdateResourceHandle h2) => !(h1 == h2);
+ public static bool operator !=(HUPDRES h1, HUPDRES h2) => !(h1 == h2);
/// Implements the operator ==.
/// The first handle.
/// The second handle.
/// The result of the operator.
- public static bool operator ==(UpdateResourceHandle h1, UpdateResourceHandle h2) => h1.Equals(h2);
+ public static bool operator ==(HUPDRES h1, HUPDRES h2) => h1.Equals(h2);
///
- public override bool Equals(object obj) => obj is UpdateResourceHandle h ? handle == h.handle : false;
+ public override bool Equals(object obj) => obj is HUPDRES h ? handle == h.handle : false;
///
public override int GetHashCode() => handle.GetHashCode();
@@ -164,5 +170,31 @@ namespace Vanara.PInvoke
///
public IntPtr DangerousGetHandle() => handle;
}
+
+ /// Provides a for that is disposed using .
+ public class SafeHUPDRES : SafeHANDLE
+ {
+ /// Initializes a new instance of the class and assigns an existing handle.
+ /// An object that represents the pre-existing handle to use.
+ ///
+ /// to reliably release the handle during the finalization phase; otherwise, (not recommended).
+ ///
+ public SafeHUPDRES(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { }
+
+ /// Initializes a new instance of the class.
+ private SafeHUPDRES() : base() { }
+
+ /// Indicates whether to discard any changes rather than write the resource updates to the file.
+ /// true to ignore changes; otherwise, false.
+ public bool IgnoreChanges { get; set; } = false;
+
+ /// Performs an implicit conversion from to .
+ /// The safe handle instance.
+ /// The result of the conversion.
+ public static implicit operator HUPDRES(SafeHUPDRES h) => h.handle;
+
+ ///
+ protected override bool InternalReleaseHandle() => EndUpdateResource(handle, IgnoreChanges);
+ }
}
}
\ No newline at end of file
diff --git a/UnitTests/PInvoke/Kernel32/Kernel32.csproj b/UnitTests/PInvoke/Kernel32/Kernel32.csproj
index eed23566..a446d89d 100644
--- a/UnitTests/PInvoke/Kernel32/Kernel32.csproj
+++ b/UnitTests/PInvoke/Kernel32/Kernel32.csproj
@@ -48,6 +48,7 @@
+
diff --git a/UnitTests/PInvoke/Kernel32/WinBase.ResourceTests.cs b/UnitTests/PInvoke/Kernel32/WinBase.ResourceTests.cs
new file mode 100644
index 00000000..841a26b7
--- /dev/null
+++ b/UnitTests/PInvoke/Kernel32/WinBase.ResourceTests.cs
@@ -0,0 +1,51 @@
+using NUnit.Framework;
+using static Vanara.PInvoke.Kernel32;
+
+namespace Vanara.PInvoke.Tests
+{
+ [TestFixture]
+ public partial class WinBaseTests_Resource
+ {
+ private const string ResExe = @"C:\Temp\DummyResourceExe.exe";
+ private const int ResId1 = 103;
+ private const int ResId2 = 129;
+ private const int ResType = (int)ResourceType.RT_DIALOG;
+
+ [Test]
+ public void UpdateResourceTest()
+ {
+ using (var tmp = new TempFile())
+ {
+ Assert.That(CopyFile(ResExe, tmp.FullName, false), ResultIs.Successful);
+
+ // Load the .EXE file that contains the dialog box you want to copy.
+ using (var hExe = LoadLibraryEx(ResExe, LoadLibraryExFlags.LOAD_LIBRARY_AS_DATAFILE | LoadLibraryExFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE))
+ {
+ Assert.That(hExe, ResultIs.ValidHandle);
+
+ // Locate the resource in the .EXE file.
+ var hRes = FindResource(hExe, ResId1, ResType);
+ Assert.That(hRes, ResultIs.ValidHandle);
+
+ // Load the resource into global memory.
+ var hResLoad = LoadResource(hExe, hRes);
+ Assert.That(hResLoad, ResultIs.ValidHandle);
+
+ // Lock the resource into global memory.
+ var lpResLock = LockResource(hResLoad);
+ Assert.That(lpResLock, ResultIs.ValidHandle);
+
+ // Open the file to which you want to add the resource resource.
+ using (var hUpdateRes = BeginUpdateResource(tmp.FullName, false))
+ {
+ Assert.That(hUpdateRes, ResultIs.ValidHandle);
+
+ // Add the resource resource to the update list.
+ Assert.That(UpdateResource(hUpdateRes, ResType, ResId2, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
+ lpResLock, SizeofResource(hExe, hRes)), ResultIs.Successful);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file