diff --git a/src/AdvApi32.Desktop/AdvApi32+SECURITY_INFORMATION.cs b/src/AdvApi32.Desktop/AdvApi32+SECURITY_INFORMATION.cs new file mode 100644 index 00000000..1514f7b1 --- /dev/null +++ b/src/AdvApi32.Desktop/AdvApi32+SECURITY_INFORMATION.cs @@ -0,0 +1,81 @@ +// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + +namespace PInvoke +{ + using System; + + /// + /// Contains the nested type. + /// + public partial class AdvApi32 + { + /// + /// Identifies the object-related security information being set or queried. + /// + [Flags] + public enum SECURITY_INFORMATION + { + /// + /// The resource properties of the object being referenced. The resource properties are stored in + /// SYSTEM_RESOURCE_ATTRIBUTE_ACE types in the SACL of the security descriptor. + /// + /// + /// Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: + /// This bit flag is not available. + /// + ATTRIBUTE_SECURITY_INFORMATION, + + /// + /// All parts of the security descriptor. This is useful for backup and restore software that needs to preserve + /// the entire security descriptor. + /// + /// + /// Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: + /// This bit flag is not available. + /// + BACKUP_SECURITY_INFORMATION, + + /// The DACL of the object is being referenced. + DACL_SECURITY_INFORMATION, + + /// The primary group identifier of the object is being referenced. + GROUP_SECURITY_INFORMATION, + + /// + /// The mandatory integrity label is being referenced. The mandatory integrity label is an ACE in the SACL of the + /// object. + /// + /// Windows Server 2003 and Windows XP: This bit flag is not available. + LABEL_SECURITY_INFORMATION, + + /// The owner identifier of the object is being referenced. + OWNER_SECURITY_INFORMATION, + + /// The DACL cannot inherit access control entries (ACEs). + PROTECTED_DACL_SECURITY_INFORMATION, + + /// The SACL cannot inherit ACEs. + PROTECTED_SACL_SECURITY_INFORMATION, + + /// The SACL of the object is being referenced. + SACL_SECURITY_INFORMATION, + + /// + /// The Central Access Policy (CAP) identifier applicable on the object that is being referenced. Each CAP + /// identifier is stored in a SYSTEM_SCOPED_POLICY_ID_ACE type in the SACL of the SD. + /// + /// + /// Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: + /// This bit flag is not available. + /// + SCOPE_SECURITY_INFORMATION, + + /// The DACL inherits ACEs from the parent object. + UNPROTECTED_DACL_SECURITY_INFORMATION, + + /// The SACL inherits ACEs from the parent object. + UNPROTECTED_SACL_SECURITY_INFORMATION + } + } +} \ No newline at end of file diff --git a/src/AdvApi32.Desktop/AdvApi32+TOKEN_ELEVATION_TYPE.cs b/src/AdvApi32.Desktop/AdvApi32+TOKEN_ELEVATION_TYPE.cs new file mode 100644 index 00000000..3f54f038 --- /dev/null +++ b/src/AdvApi32.Desktop/AdvApi32+TOKEN_ELEVATION_TYPE.cs @@ -0,0 +1,35 @@ +// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + +namespace PInvoke +{ + /// + /// Contains the nested type. + /// + public partial class AdvApi32 + { + /// + /// Indicates the elevation type of token being queried by the function. + /// + public enum TOKEN_ELEVATION_TYPE + { + /// + /// Standard user that don't require UAC as he doesn't have any elevated attributes in it's + /// security token. + /// + TokenElevationTypeDefault = 1, + + /// + /// Process executing with full elevated rights, either UAC is disable or the process is + /// executing in "Run as administrator" mode. + /// + TokenElevationTypeFull, + + /// + /// Process executing under UAC, the current user got some elevated right but they can't + /// be used in the process as the token is "split". + /// + TokenElevationTypeLimited + } + } +} diff --git a/src/AdvApi32.Desktop/AdvApi32+TOKEN_INFORMATION_CLASS.cs b/src/AdvApi32.Desktop/AdvApi32+TOKEN_INFORMATION_CLASS.cs new file mode 100644 index 00000000..fa8f1cf0 --- /dev/null +++ b/src/AdvApi32.Desktop/AdvApi32+TOKEN_INFORMATION_CLASS.cs @@ -0,0 +1,59 @@ +// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + +namespace PInvoke +{ + /// + /// Contains the nested type. + /// + public partial class AdvApi32 + { + /// + /// Specify the type of information being assigned to or retrieved from an access token. + /// + public enum TOKEN_INFORMATION_CLASS + { + TokenUser = 1, + TokenGroups, + TokenPrivileges, + TokenOwner, + TokenPrimaryGroup, + TokenDefaultDacl, + TokenSource, + TokenType, + TokenImpersonationLevel, + TokenStatistics, + TokenRestrictedSids, + TokenSessionId, + TokenGroupsAndPrivileges, + TokenSessionReference, + TokenSandBoxInert, + TokenAuditPolicy, + TokenOrigin, + TokenElevationType, + TokenLinkedToken, + TokenElevation, + TokenHasRestrictions, + TokenAccessInformation, + TokenVirtualizationAllowed, + TokenVirtualizationEnabled, + TokenIntegrityLevel, + TokenUiAccess, + TokenMandatoryPolicy, + TokenLogonSid, + TokenIsAppContainer, + TokenCapabilities, + TokenAppContainerSid, + TokenAppContainerNumber, + TokenUserClaimAttributes, + TokenDeviceClaimAttributes, + TokenRestrictedUserClaimAttributes, + TokenRestrictedDeviceClaimAttributes, + TokenDeviceGroups, + TokenRestrictedDeviceGroups, + TokenSecurityAttributes, + TokenIsRestricted, + MaxTokenInfoClass + } + } +} diff --git a/src/AdvApi32.Desktop/AdvApi32+TokenAccessRights.cs b/src/AdvApi32.Desktop/AdvApi32+TokenAccessRights.cs new file mode 100644 index 00000000..df381d41 --- /dev/null +++ b/src/AdvApi32.Desktop/AdvApi32+TokenAccessRights.cs @@ -0,0 +1,100 @@ +// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + +namespace PInvoke +{ + using System; + + /// + /// Contains the nested type. + /// + public partial class AdvApi32 + { + /// + /// The different access rights allowed to access an access token. + /// + [Flags] + public enum TokenAccessRights : uint + { + /// The right to delete the object. + DELETE = 0x00010000, + + /// + /// The right to read the information in the object's security descriptor, not including the information in the + /// system access control list (SACL). + /// + READ_CONTROL = 0x00020000, + + /// The right to modify the discretionary access control list (DACL) in the object's security descriptor. + WRITE_DAC = 0x00040000, + + /// The right to change the owner in the object's security descriptor. + WRITE_OWNER = 0x00080000, + + /// Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access. + STANDARD_RIGHTS_REQUIRED = 0x000F0000, + + /// Currently defined to equal READ_CONTROL. + STANDARD_RIGHTS_READ = READ_CONTROL, + + /// Currently defined to equal READ_CONTROL. + STANDARD_RIGHTS_WRITE = READ_CONTROL, + + /// Currently defined to equal READ_CONTROL. + STANDARD_RIGHTS_EXECUTE = READ_CONTROL, + + /// + /// Required to attach a primary token to a process. The SE_ASSIGNPRIMARYTOKEN_NAME privilege is also required to + /// accomplish this task. + /// + TOKEN_ASSIGN_PRIMARY = 0x0001, + + /// Required to duplicate an access token. + TOKEN_DUPLICATE = 0x0002, + + /// Required to attach an impersonation access token to a process. + TOKEN_IMPERSONATE = 0x0004, + + /// Required to query an access token. + TOKEN_QUERY = 0x0008, + + /// Required to query the source of an access token. + TOKEN_QUERY_SOURCE = 0x0010, + + /// Required to enable or disable the privileges in an access token. + TOKEN_ADJUST_PRIVILEGES = 0x0020, + + /// Required to adjust the attributes of the groups in an access token. + TOKEN_ADJUST_GROUPS = 0x0040, + + /// Required to change the default owner, primary group, or DACL of an access token. + TOKEN_ADJUST_DEFAULT = 0x0080, + + /// Required to adjust the session ID of an access token. The SE_TCB_NAME privilege is required. + TOKEN_ADJUST_SESSIONID = 0x0100, + + /// Combines STANDARD_RIGHTS_READ and TOKEN_QUERY. + TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY, + + /// Combines STANDARD_RIGHTS_WRITE, TOKEN_ADJUST_PRIVILEGES, TOKEN_ADJUST_GROUPS, and TOKEN_ADJUST_DEFAULT. + TOKEN_WRITE = STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT, + + /// Required to wait for the process to terminate using the wait functions. + ACCESS_SYSTEM_SECURITY = 0x01000000, + + /// Combines STANDARD_RIGHTS_EXECUTE and TOKEN_IMPERSONATE. + TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE | TOKEN_IMPERSONATE, + + /// Combines all possible access rights for a token. + TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | + TOKEN_ASSIGN_PRIMARY | + TOKEN_DUPLICATE | + TOKEN_IMPERSONATE | + TOKEN_QUERY | + TOKEN_QUERY_SOURCE | + TOKEN_ADJUST_PRIVILEGES | + TOKEN_ADJUST_GROUPS | + TOKEN_ADJUST_DEFAULT + } + } +} \ No newline at end of file diff --git a/src/AdvApi32.Desktop/AdvApi32.Desktop.csproj b/src/AdvApi32.Desktop/AdvApi32.Desktop.csproj index 635923e9..0c1ff765 100644 --- a/src/AdvApi32.Desktop/AdvApi32.Desktop.csproj +++ b/src/AdvApi32.Desktop/AdvApi32.Desktop.csproj @@ -39,6 +39,7 @@ + @@ -57,6 +58,9 @@ + + + diff --git a/src/AdvApi32.Desktop/AdvApi32.Helpers.cs b/src/AdvApi32.Desktop/AdvApi32.Helpers.cs index 5db926e5..9be8e9e8 100644 --- a/src/AdvApi32.Desktop/AdvApi32.Helpers.cs +++ b/src/AdvApi32.Desktop/AdvApi32.Helpers.cs @@ -5,6 +5,8 @@ namespace PInvoke { using System; using System.Runtime.InteropServices; + using System.Security.AccessControl; + using static Kernel32; /// /// Methods and nested types that are not strictly P/Invokes but provide @@ -96,5 +98,119 @@ public static void UninstallService(string serviceName) } } } + + /// Get the elevation type of a token via . + /// + /// A handle to an access token from which information is retrieved. The handle must have + /// TOKEN_QUERY access. + /// + /// The token elevation type + /// is NULL. + /// If the call to fails. + public static TOKEN_ELEVATION_TYPE GetTokenElevationType(SafeObjectHandle TokenHandle) + { + if (TokenHandle == null) + { + throw new ArgumentNullException(nameof(TokenHandle)); + } + + var elevationType = default(TOKEN_ELEVATION_TYPE); + + bool success; + unsafe + { + uint returnLength; + success = GetTokenInformation( + TokenHandle, + TOKEN_INFORMATION_CLASS.TokenElevationType, + &elevationType, + (uint)sizeof(TOKEN_ELEVATION_TYPE), + out returnLength); + } + + if (!success) + { + throw new Win32Exception(); + } + + return elevationType; + } + + /// + /// Retrieves a copy of the security descriptor associated with a service object. You can also use the + /// GetNamedSecurityInfo function to retrieve a security descriptor. + /// + /// + /// A handle to the service control manager or the service. Handles to the service control manager + /// are returned by the function, and handles to a service are returned by either the + /// or function. The handle must have the READ_CONTROL access + /// right. + /// + /// + /// A set of bit flags that indicate the type of security information to retrieve. This + /// parameter can be a combination of the flags, with the exception that this + /// function does not support the value. + /// + /// + /// A copy of the security descriptor of the specified service object. The calling process must have the + /// appropriate access to view the specified aspects of the security descriptor of the object. + /// + /// is NULL. + /// If the call to the native method fails fails. + public static RawSecurityDescriptor QueryServiceObjectSecurity(SafeServiceHandle hService, SECURITY_INFORMATION dwSecurityInformation) + { + if (hService == null) + { + throw new ArgumentNullException(nameof(hService)); + } + + var securityDescriptor = new byte[0]; + uint bufSizeNeeded; + QueryServiceObjectSecurity(hService, dwSecurityInformation, securityDescriptor, 0, out bufSizeNeeded); + + var lastError = GetLastError(); + if (lastError != Win32ErrorCode.ERROR_INSUFFICIENT_BUFFER) + { + throw new Win32Exception(lastError); + } + + securityDescriptor = new byte[bufSizeNeeded]; + var success = QueryServiceObjectSecurity(hService, dwSecurityInformation, securityDescriptor, bufSizeNeeded, out bufSizeNeeded); + + if (!success) + { + throw new Win32Exception(); + } + + return new RawSecurityDescriptor(securityDescriptor, 0); + } + + /// The SetServiceObjectSecurity function sets the security descriptor of a service object. + /// + /// A handle to the service. This handle is returned by the or + /// function. The access required for this handle depends on the security information + /// specified in the parameter. + /// + /// + /// Specifies the components of the security descriptor to set. This parameter can be a + /// combination of the following values : , + /// , + /// , + /// . Note that flags not handled by + /// SetServiceObjectSecurity will be silently ignored. + /// + /// The new security information. + public static void SetServiceObjectSecurity( + SafeServiceHandle hService, + SECURITY_INFORMATION dwSecurityInformation, + RawSecurityDescriptor lpSecurityDescriptor) + { + var binaryForm = new byte[lpSecurityDescriptor.BinaryLength]; + lpSecurityDescriptor.GetBinaryForm(binaryForm, 0); + if (!SetServiceObjectSecurity(hService, dwSecurityInformation, binaryForm)) + { + throw new Win32Exception(); + } + } } } diff --git a/src/AdvApi32.Desktop/AdvApi32.cs b/src/AdvApi32.Desktop/AdvApi32.cs index 55b17ecd..cb11bfaa 100644 --- a/src/AdvApi32.Desktop/AdvApi32.cs +++ b/src/AdvApi32.Desktop/AdvApi32.cs @@ -5,6 +5,8 @@ namespace PInvoke { using System; using System.Runtime.InteropServices; + using System.Security.AccessControl; + using static Kernel32; /// /// Exported functions from the AdvApi32.dll Windows library @@ -196,6 +198,160 @@ public static partial class AdvApi32 [DllImport(nameof(AdvApi32), SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool StartService(SafeServiceHandle hService, int dwNumServiceArgs, string lpServiceArgVectors); + /// Opens the access token associated with a process. + /// + /// A handle to the process whose access token is opened. The process must have the + /// PROCESS_QUERY_INFORMATION access permission. + /// + /// + /// Specifies an access mask that specifies the requested types of access to the access token. + /// These requested access types are compared with the discretionary access control list (DACL) of the token to + /// determine which accesses are granted or denied. + /// + /// A handle that identifies the newly opened access token when the function returns. + /// + /// If the function succeeds, the return value is a nonzero value. + /// + /// If the function fails, the return value is zero. To get extended error information, call + /// . + /// + /// + [DllImport(nameof(AdvApi32), SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool OpenProcessToken( + IntPtr processHandle, + TokenAccessRights desiredAccess, + out SafeObjectHandle tokenHandle); + + /// + /// The GetTokenInformation function retrieves a specified type of information about an access token. The calling + /// process must have appropriate access rights to obtain the information. + /// + /// To determine if a user is a member of a specific group, use the CheckTokenMembership function. To determine + /// group membership for app container tokens, use the CheckTokenMembershipEx function. + /// + /// + /// + /// A handle to an access token from which information is retrieved. If TokenInformationClass + /// specifies TokenSource, the handle must have TOKEN_QUERY_SOURCE access. For all other TokenInformationClass values, + /// the handle must have TOKEN_QUERY access. + /// + /// + /// Specifies a value from the TOKEN_INFORMATION_CLASS enumerated type to identify the + /// type of information the function retrieves. Any callers who check the TokenIsAppContainer and have it return 0 + /// should also verify that the caller token is not an identify level impersonation token. If the current token is not + /// an app container but is an identity level token, you should return AccessDenied. + /// + /// + /// A pointer to a buffer the function fills with the requested information. The structure + /// put into this buffer depends upon the type of information specified by the + /// parameter. + /// + /// + /// Specifies the size, in bytes, of the buffer pointed to by the TokenInformation + /// parameter. If is NULL, this parameter must be zero. + /// + /// + /// A pointer to a variable that receives the number of bytes needed for the buffer pointed to by the TokenInformation + /// parameter. If this value is larger than the value specified in the TokenInformationLength parameter, the function + /// fails and stores no data in the buffer. + /// + /// If the value of the parameter is + /// and the token has no default DACL, the function sets + /// the variable pointed to by ReturnLength to sizeof(TOKEN_DEFAULT_DACL) and sets the DefaultDacl member of the + /// TOKEN_DEFAULT_DACL structure to NULL. + /// + /// + /// + /// If the function succeeds, the return value is a nonzero value. + /// + /// If the function fails, the return value is zero. To get extended error information, call + /// . + /// + /// + [DllImport(nameof(AdvApi32), SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern unsafe bool GetTokenInformation( + SafeObjectHandle TokenHandle, + TOKEN_INFORMATION_CLASS TokenInformationClass, + void* TokenInformation, + uint TokenInformationLength, + out uint ReturnLength); + + /// + /// The QueryServiceObjectSecurity function retrieves a copy of the security descriptor associated with a service + /// object. You can also use the GetNamedSecurityInfo function to retrieve a security descriptor. + /// + /// + /// A handle to the service control manager or the service. Handles to the service control manager + /// are returned by the function, and handles to a service are returned by either the + /// or function. The handle must have the READ_CONTROL access + /// right. + /// + /// + /// A set of bit flags that indicate the type of security information to retrieve. This + /// parameter can be a combination of the flags, with the exception that this + /// function does not support the value. + /// + /// + /// A pointer to a buffer that receives a copy of the security descriptor of the + /// specified service object. The calling process must have the appropriate access to view the specified aspects of the + /// security descriptor of the object. The SECURITY_DESCRIPTOR structure is returned in self-relative format. + /// + /// + /// The size of the buffer pointed to by the parameter, in + /// bytes. The largest size allowed is 8 kilobytes. + /// + /// + /// A pointer to a variable that receives the number of bytes needed to return the requested + /// security descriptor information, if the function fails. + /// + /// + /// If the function succeeds, the return value is a nonzero value. + /// + /// If the function fails, the return value is zero. To get extended error information, call + /// . + /// + /// + [DllImport(nameof(AdvApi32), SetLastError = true)] + public static extern bool QueryServiceObjectSecurity( + SafeServiceHandle hService, + SECURITY_INFORMATION dwSecurityInformation, + byte[] lpSecurityDescriptor, + uint cbBufSize, + out uint pcbBytesNeeded); + + /// The SetServiceObjectSecurity function sets the security descriptor of a service object. + /// + /// A handle to the service. This handle is returned by the or + /// function. The access required for this handle depends on the security information + /// specified in the parameter. + /// + /// + /// Specifies the components of the security descriptor to set. This parameter can be a + /// combination of the following values : , + /// , + /// , + /// . Note that flags not handled by + /// SetServiceObjectSecurity will be silently ignored. + /// + /// + /// A pointer to a SECURITY_DESCRIPTOR structure that contains the new security + /// information. + /// + /// + /// If the function succeeds, the return value is a nonzero value. + /// + /// If the function fails, the return value is zero. To get extended error information, call + /// . + /// + /// + [DllImport(nameof(AdvApi32), SetLastError = true)] + public static extern bool SetServiceObjectSecurity( + SafeServiceHandle hService, + SECURITY_INFORMATION dwSecurityInformation, + byte[] lpSecurityDescriptor); + /// /// Closes a handle to a service control manager or service object. /// diff --git a/src/Kernel32.Desktop/Kernel32+SafeLibraryHandle.cs b/src/Kernel32.Desktop/Kernel32+SafeLibraryHandle.cs new file mode 100644 index 00000000..0edc42a0 --- /dev/null +++ b/src/Kernel32.Desktop/Kernel32+SafeLibraryHandle.cs @@ -0,0 +1,46 @@ +// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + +namespace PInvoke +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Contains the nested type. + /// + public static partial class Kernel32 + { + /// + /// Represents a library handle that can be closed with . + /// + public class SafeLibraryHandle : SafeHandle + { + /// + /// Initializes a new instance of the class. + /// + public SafeLibraryHandle() + : base(INVALID_HANDLE_VALUE, true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// An object that represents the pre-existing handle to use. + /// to reliably release the handle during the finalization + /// phase; to prevent reliable release. + public SafeLibraryHandle(IntPtr preexistingHandle, bool ownsHandle) + : base(INVALID_HANDLE_VALUE, ownsHandle) + { + this.SetHandle(preexistingHandle); + } + + /// + public override bool IsInvalid => this.handle == INVALID_HANDLE_VALUE || this.handle == IntPtr.Zero; + + /// + protected override bool ReleaseHandle() => FreeLibrary(this.handle); + } + } +} diff --git a/src/Kernel32.Desktop/Kernel32.Desktop.csproj b/src/Kernel32.Desktop/Kernel32.Desktop.csproj index 9421dae7..c06d92bf 100644 --- a/src/Kernel32.Desktop/Kernel32.Desktop.csproj +++ b/src/Kernel32.Desktop/Kernel32.Desktop.csproj @@ -39,6 +39,7 @@ + diff --git a/src/Kernel32.Desktop/Kernel32.cs b/src/Kernel32.Desktop/Kernel32.cs index 94dbfb8a..437c0f9a 100644 --- a/src/Kernel32.Desktop/Kernel32.cs +++ b/src/Kernel32.Desktop/Kernel32.cs @@ -732,5 +732,67 @@ public static extern bool CreatePipe( [DllImport(nameof(Kernel32), SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool K32EmptyWorkingSet(SafeObjectHandle hProcess); + + /// Retrieves the window handle used by the console associated with the calling process. + /// + /// The return value is a handle to the window used by the console associated with the calling process or + /// if there is no such associated console. + /// + [DllImport(nameof(Kernel32))] + public static extern IntPtr GetConsoleWindow(); + + /// + /// Loads the specified module into the address space of the calling process. The specified module may cause other + /// modules to be loaded. + /// For additional load options, use the LoadLibraryEx function. + /// + /// + /// The name of the module. This can be either a library module (a .dll file) or an executable module (an .exe file). + /// The name specified is the file name of the module and is not related to the name stored in the library module + /// itself, as specified by the LIBRARY keyword in the module-definition (.def) file. + /// If the string specifies a full path, the function searches only that path for the module. + /// + /// If the string specifies a relative path or a module name without a path, the function uses a standard search + /// strategy to find the module. + /// + /// + /// If the function cannot find the module, the function fails. When specifying a path, be sure to use + /// backslashes (\), not forward slashes (/). + /// + /// + /// If the string specifies a module name without a path and the file name extension is omitted, the function + /// appends the default library extension .dll to the module name. To prevent the function from appending .dll to + /// the module name, include a trailing point character (.) in the module name string. + /// + /// + /// + /// If the function succeeds, the return value is a nonzero value. + /// + /// If the function fails, the return value is zero. To get extended error information, call + /// . + /// + /// + [DllImport(nameof(Kernel32), SetLastError = true, CharSet = CharSet.Unicode)] + public static extern SafeLibraryHandle LoadLibrary(string lpFileName); + + /// + /// Frees the loaded dynamic-link library (DLL) module and, if necessary, decrements its reference count. When the + /// reference count reaches zero, the module is unloaded from the address space of the calling process and the handle + /// is no longer valid. + /// + /// + /// A handle to the loaded library module. The LoadLibrary, LoadLibraryEx, GetModuleHandle, or + /// GetModuleHandleEx function returns this handle. + /// + /// + /// If the function succeeds, the return value is a nonzero value. + /// + /// If the function fails, the return value is zero. To get extended error information, call + /// . + /// + /// + [DllImport(nameof(Kernel32), SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool FreeLibrary(IntPtr hModule); } } diff --git a/src/Kernel32.Tests/Kernel32.cs b/src/Kernel32.Tests/Kernel32.cs index ee1e8af7..06b95495 100644 --- a/src/Kernel32.Tests/Kernel32.cs +++ b/src/Kernel32.Tests/Kernel32.cs @@ -659,7 +659,6 @@ public void IsWow64Process_ReturnExpectedValue() Assert.Equal(expected, actual); } - [Fact] public void CreatePipe_ReadWrite() { @@ -682,4 +681,20 @@ public void K32EmptyWorkingSet_Run() Assert.True(K32EmptyWorkingSet(pid)); } } + + [Fact] + public void LoadLibrary_And_FreeLibrary() + { + using (var library = LoadLibrary("kernel32.dll")) + { + Assert.False(library.IsInvalid); + } + } + + [Fact] + public void GetConsoleWindow_DoesNotThrow() + { + // No assert possible as the answer depends on the test runner, we only want to know that the method can be called successfully. + GetConsoleWindow(); + } }