示例#1
0
 public ElevPriv(string[] privs, HPROCESS hProc = default, TokenAccess access = TokenAccess.TOKEN_ADJUST_PRIVILEGES | TokenAccess.TOKEN_QUERY)
 {
     if (hProc.IsNull)
     {
         hProc = GetCurrentProcess();
     }
     tok = SafeHTOKEN.FromProcess(hProc, access);
     ElevatePrivileges(privs);
 }
示例#2
0
            /// <summary>Duplicates an object handle.</summary>
            /// <typeparam name="THandle">The type of the handle.</typeparam>
            /// <typeparam name="TAccess">The type of the access value (enum or uint).</typeparam>
            /// <param name="hSourceHandle">
            /// The handle to be duplicated. This is an open object handle that is valid in the context of the source process. For a list of
            /// objects whose handles can be duplicated, see the following Remarks section.
            /// </param>
            /// <param name="lpTargetHandle">
            /// <para>
            /// A pointer to a variable that receives the duplicate handle. This handle value is valid in the context of the target process.
            /// </para>
            /// <para>
            /// If hSourceHandle is a pseudo handle returned by <c>GetCurrentProcess</c> or <c>GetCurrentThread</c>, <c>DuplicateHandle</c>
            /// converts it to a real handle to a process or thread, respectively.
            /// </para>
            /// <para>
            /// If lpTargetHandle is <c>NULL</c>, the function duplicates the handle, but does not return the duplicate handle value to the
            /// caller. This behavior exists only for backward compatibility with previous versions of this function. You should not use
            /// this feature, as you will lose system resources until the target process terminates.
            /// </para>
            /// </param>
            /// <param name="dwDesiredAccess">
            /// <para>
            /// The access requested for the new handle. For the flags that can be specified for each object type, see the following Remarks section.
            /// </para>
            /// <para>
            /// This parameter is ignored if the dwOptions parameter specifies the DUPLICATE_SAME_ACCESS flag. Otherwise, the flags that can
            /// be specified depend on the type of object whose handle is to be duplicated.
            /// </para>
            /// </param>
            /// <param name="hSourceProcessHandle">
            /// <para>A handle to the process with the handle to be duplicated.</para>
            /// <para>The handle must have the PROCESS_DUP_HANDLE access right. For more information, see Process Security and Access Rights.</para>
            /// </param>
            /// <param name="hTargetProcessHandle">
            /// A handle to the process that is to receive the duplicated handle. The handle must have the PROCESS_DUP_HANDLE access right.
            /// </param>
            /// <param name="bInheritHandle">
            /// A variable that indicates whether the handle is inheritable. If <c>TRUE</c>, the duplicate handle can be inherited by new
            /// processes created by the target process. If <c>FALSE</c>, the new handle cannot be inherited.
            /// </param>
            /// <param name="dwOptions">
            /// <para>Optional actions. This parameter can be zero, or any combination of the following values.</para>
            /// <para>
            /// <list type="table">
            /// <listheader>
            /// <term>Value</term>
            /// <term>Meaning</term>
            /// </listheader>
            /// <item>
            /// <term>DUPLICATE_CLOSE_SOURCE0x00000001</term>
            /// <term>Closes the source handle. This occurs regardless of any error status returned.</term>
            /// </item>
            /// <item>
            /// <term>DUPLICATE_SAME_ACCESS0x00000002</term>
            /// <term>Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle.</term>
            /// </item>
            /// </list>
            /// </para>
            /// </param>
            /// <returns>
            /// <para>If the function succeeds, the return value is nonzero.</para>
            /// <para>If the function fails, the return value is zero. To get extended error information, call <c>GetLastError</c>.</para>
            /// </returns>
            public static bool DuplicateHandle <THandle, TAccess>(THandle hSourceHandle, out THandle lpTargetHandle, TAccess dwDesiredAccess,
                                                                  HPROCESS hSourceProcessHandle = default, HPROCESS hTargetProcessHandle = default, bool bInheritHandle = false, DUPLICATE_HANDLE_OPTIONS dwOptions = 0)
                where THandle : SafeKernelHandle where TAccess : struct, IConvertible
            {
                var ret = Kernel32.DuplicateHandle(hSourceProcessHandle == default ? GetCurrentProcess() : hSourceProcessHandle, hSourceHandle.DangerousGetHandle(),
                                                   hTargetProcessHandle == default ? GetCurrentProcess() : hTargetProcessHandle, out IntPtr h, Convert.ToUInt32(dwDesiredAccess), bInheritHandle, dwOptions);

                lpTargetHandle = (THandle)Activator.CreateInstance(typeof(THandle), h, true);
                return(ret);
            }
示例#3
0
        public void NtQueryInformationProcessTest()
        {
            var      curProc = Process.GetCurrentProcess();
            HPROCESS hProc   = curProc.Handle;

            NtQueryResult <PROCESS_BASIC_INFORMATION> pbi = null;

            Assert.That(() => pbi = NtQueryInformationProcess <PROCESS_BASIC_INFORMATION>(hProc, PROCESSINFOCLASS.ProcessBasicInformation), Throws.Nothing);
            Assert.That(pbi, Is.Not.Null);
            // Can do AsRef here since PROCESS_BASIC_INFORMATION has no managed types
            ref var rpbi = ref pbi.AsRef();
示例#4
0
        public ElevPriv(string priv, HPROCESS hProc = default, TokenAccess access = TokenAccess.TOKEN_ADJUST_PRIVILEGES | TokenAccess.TOKEN_QUERY)
        {
            if (hProc.IsNull)
            {
                hProc = GetCurrentProcess();
            }
            tok = SafeHTOKEN.FromProcess(hProc, access);
            var newPriv = new PTOKEN_PRIVILEGES(LUID.FromName(priv), PrivilegeAttributes.SE_PRIVILEGE_ENABLED);

            prevState = PTOKEN_PRIVILEGES.GetAllocatedAndEmptyInstance();
            if (!AdjustTokenPrivileges(tok, false, newPriv, (uint)prevState.Size, prevState, out var retLen))
            {
                Win32Error.ThrowLastError();
            }
            prevState.Size = (int)retLen;
        }
示例#5
0
        /// <summary>
        /// <para>Retrieves information about the specified process.</para>
        /// </summary>
        /// <typeparam name="T">The type of the structure to retrieve.</typeparam>
        /// <param name="ProcessHandle">A handle to the process for which information is to be retrieved.</param>
        /// <param name="ProcessInformationClass">
        /// <para>
        /// The type of process information to be retrieved. This parameter can be one of the following values from the
        /// <c>PROCESSINFOCLASS</c> enumeration.
        /// </para>
        /// <list type="table">
        /// <listheader>
        /// <term>Value</term>
        /// <term>Meaning</term>
        /// </listheader>
        /// <item>
        /// <term>ProcessBasicInformation <br/> 0</term>
        /// <term>
        /// Retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a
        /// unique value used by the system to identify the specified process. Use the CheckRemoteDebuggerPresent and GetProcessId functions
        /// to obtain this information.
        /// </term>
        /// </item>
        /// <item>
        /// <term>ProcessDebugPort <br/> 7</term>
        /// <term>
        /// Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the process
        /// is being run under the control of a ring 3 debugger. Use the CheckRemoteDebuggerPresent or IsDebuggerPresent function.
        /// </term>
        /// </item>
        /// <item>
        /// <term>ProcessWow64Information <br/> 26</term>
        /// <term>
        /// Determines whether the process is running in the WOW64 environment (WOW64 is the x86 emulator that allows Win32-based
        /// applications to run on 64-bit Windows). Use the IsWow64Process2 function to obtain this information.
        /// </term>
        /// </item>
        /// <item>
        /// <term>ProcessImageFileName <br/> 27</term>
        /// <term>
        /// Retrieves a UNICODE_STRING value containing the name of the image file for the process. Use the QueryFullProcessImageName or
        /// GetProcessImageFileName function to obtain this information.
        /// </term>
        /// </item>
        /// <item>
        /// <term>ProcessBreakOnTermination <br/> 29</term>
        /// <term>Retrieves a ULONG value indicating whether the process is considered critical.</term>
        /// </item>
        /// <item>
        /// <term>ProcessSubsystemInformation <br/> 75</term>
        /// <term>
        /// Retrieves a SUBSYSTEM_INFORMATION_TYPE value indicating the subsystem type of the process. The buffer pointed to by the
        /// ProcessInformation parameter should be large enough to hold a single SUBSYSTEM_INFORMATION_TYPE enumeration.
        /// </term>
        /// </item>
        /// </list>
        /// </param>
        /// <returns>The structure and associated memory for any allocated sub-types.</returns>
        /// <exception cref="System.ArgumentException">Mismatch between requested type and class.</exception>
        public static NtQueryResult <T> NtQueryInformationProcess <T>([In] HPROCESS ProcessHandle, PROCESSINFOCLASS ProcessInformationClass) where T : struct
        {
            if (!CorrespondingTypeAttribute.CanGet(ProcessInformationClass, typeof(T)))
            {
                throw new ArgumentException("Mismatch between requested type and class.");
            }
            var mem    = new NtQueryResult <T>();
            var status = NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, mem, mem.Size, out var sz);

            if (status.Succeeded)
            {
                return(mem);
            }
            if (status != NTStatus.STATUS_INFO_LENGTH_MISMATCH || sz == 0)
            {
                throw status.GetException();
            }
            mem.Size = sz;
            NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, mem, mem.Size, out _).ThrowIfFailed();
            return(mem);
        }
示例#6
0
 public static extern bool InitializeEnclave(HPROCESS hProcess, IntPtr lpAddress, IntPtr lpEnclaveInformation, uint dwInfoLength, ref uint lpEnclaveError);
示例#7
0
 public static extern void CreateEnclave(HPROCESS hProcess, IntPtr lpAddress, SizeT dwSize, SizeT dwInitialCommittment, EnclaveType flEnclaveType, IntPtr lpEnclaveInformation, uint dwInfoLength, out uint lpEnclaveError);
示例#8
0
 public static extern bool DuplicateHandle(
     [In] HPROCESS hSourceProcessHandle, [In] IntPtr hSourceHandle, [In] HPROCESS hTargetProcessHandle, out HANDLE lpTargetHandle, uint dwDesiredAccess,
     [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, DUPLICATE_HANDLE_OPTIONS dwOptions);
示例#9
0
 public static extern bool OpenProcessToken([In] HPROCESS ProcessHandle, TokenAccess DesiredAccess, out SafeHTOKEN TokenHandle);
示例#10
0
 public static extern bool IsWow64Process2([In] HPROCESS hProcess, out IMAGE_FILE_MACHINE pProcessMachine, out IMAGE_FILE_MACHINE pNativeMachine);
示例#11
0
 public static extern bool IsWow64Process([In] HPROCESS hProcess, [MarshalAs(UnmanagedType.Bool)] out bool Wow64Process);
示例#12
0
 public static extern bool QueryProcessCycleTime(HPROCESS ProcessHandle, out ulong CycleTime);
示例#13
0
 public static extern uint GetSystemDpiForProcess(HPROCESS hProcess);
示例#14
0
 public static extern bool SetProcessWorkingSetSize([In] HPROCESS hProcess, SizeT dwMinimumWorkingSetSize, SizeT dwMaximumWorkingSetSize);
示例#15
0
 public static extern bool SetProcessAffinityMask([In] HPROCESS hProcess, UIntPtr dwProcessAffinityMask);
示例#16
0
 public static extern bool GetProcessWorkingSetSize([In] HPROCESS hProcess, [Out] out SizeT lpMinimumWorkingSetSize, [Out] out SizeT lpMaximumWorkingSetSize);
示例#17
0
 public static extern bool GetProcessIoCounters([In] HPROCESS hProcess, out IO_COUNTERS lpIoCounters);
示例#18
0
 public static extern bool GetProcessAffinityMask([In] HPROCESS hProcess, out UIntPtr lpProcessAffinityMask, out UIntPtr lpSystemAffinityMask);
示例#19
0
 public static extern HRESULT GetProcessDpiAwareness([In, Optional] HPROCESS hprocess, out PROCESS_DPI_AWARENESS value);
示例#20
0
 public static extern bool LoadEnclaveData(HPROCESS hProcess, IntPtr lpAddress, IntPtr lpBuffer, SizeT nSize, MEM_PROTECTION flProtect, IntPtr lpPageInformation, uint dwInfoLength, out SizeT lpNumberOfBytesWritten, out uint lpEnclaveError);
示例#21
0
 public static extern HRESULT EncodeRemotePointer(HPROCESS ProcessHandle, IntPtr Ptr, out IntPtr EncodedPtr);
示例#22
0
 public static extern NTStatus NtQueryInformationProcess([In] HPROCESS ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, [Out] IntPtr ProcessInformation, uint ProcessInformationLength, out uint ReturnLength);
示例#23
0
        public void NtQueryInformationProcessTest()
        {
            HPROCESS hProc       = Kernel32.GetCurrentProcess();
            var      procIsWow64 = hProc.IsWow64();
            var      procIs64    = Environment.Is64BitProcess;
            var      osIs64      = Environment.Is64BitOperatingSystem;

            using var pbi = NtQueryInformationProcess <PROCESS_BASIC_INFORMATION>(hProc, PROCESSINFOCLASS.ProcessBasicInformation);
            Assert.That(pbi, ResultIs.ValidHandle);
            // Can get pointer here since PROCESS_BASIC_INFORMATION has no managed types
            unsafe
            {
                var rpbi = (PROCESS_BASIC_INFORMATION *)pbi;
                Assert.That(rpbi->UniqueProcessId.ToInt32(), Is.EqualTo(Kernel32.GetCurrentProcessId()));
                Assert.That(rpbi->PebBaseAddress, Is.Not.EqualTo(IntPtr.Zero));
                // Have to use ToStructure here since PEB has managed types
                var peb = rpbi->PebBaseAddress.ToStructure <PEB>();
                // Have to use ToStructure here since RTL_USER_PROCESS_PARAMETERS has managed types
                var upp = peb.ProcessParameters.ToStructure <RTL_USER_PROCESS_PARAMETERS>();
                Assert.That(upp.CommandLine.ToString(hProc), Is.Not.Empty);
                TestContext.WriteLine($"Img: {upp.ImagePathName.ToString(hProc)}; CmdLine: {upp.CommandLine.ToString(hProc)}");
            }

            NtQueryResult <IntPtr> pdp = null;

            Assert.That(() => pdp = NtQueryInformationProcess <IntPtr>(hProc, PROCESSINFOCLASS.ProcessDebugPort), Throws.Nothing);
            Assert.That(pdp, ResultIs.ValidHandle);
            TestContext.WriteLine($"DbgPort: {pdp.Value.ToInt64()}");

            NtQueryResult <BOOL> pwi = null;

            Assert.That(() => pwi = NtQueryInformationProcess <BOOL>(hProc, PROCESSINFOCLASS.ProcessWow64Information), Throws.Nothing);
            Assert.That(pwi, ResultIs.ValidHandle);
            Assert.That(pwi.Value.Value, Is.True);

            NtQueryResult <UNICODE_STRING> pfn = null;

            Assert.That(() => pfn = NtQueryInformationProcess <UNICODE_STRING>(hProc, PROCESSINFOCLASS.ProcessImageFileName), Throws.Nothing);
            Assert.That(pfn, ResultIs.ValidHandle);
            TestContext.WriteLine($"Fn: {pfn.Value.ToString(hProc)}");

            NtQueryResult <BOOL> pbt = null;

            Assert.That(() => pbt = NtQueryInformationProcess <BOOL>(hProc, PROCESSINFOCLASS.ProcessBreakOnTermination), Throws.Nothing);
            Assert.That(pbt, ResultIs.ValidHandle);
            Assert.That(pbt.Value.Value, Is.False);

            NtQueryResult <SUBSYSTEM_INFORMATION_TYPE> psi = null;

            // This is documented, but fails on Win10
            Assert.That(() => psi = NtQueryInformationProcess <SUBSYSTEM_INFORMATION_TYPE>(hProc, PROCESSINFOCLASS.ProcessSubsystemInformation), Throws.ArgumentException);
            //Assert.That(psi, ResultIs.ValidHandle);
            //Assert.That(Enum.IsDefined(typeof(SUBSYSTEM_INFORMATION_TYPE), psi.Value), Is.True);
            //TestContext.WriteLine($"SubSys: {psi.Value}");

            // Try undocumented fetch
            NtQueryResult <uint> ppb = null;

            Assert.That(() => ppb = NtQueryInformationProcess <uint>(hProc, PROCESSINFOCLASS.ProcessPriorityBoost), Throws.Nothing);
            TestContext.WriteLine($"Priority boost: {ppb.Value}");
        }
示例#24
0
 public static extern NTStatus NtWow64QueryInformationProcess64([In] HPROCESS ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, [Out] int ProcessInformation, [In] ulong ProcessInformationLength, out ulong ReturnLength);
示例#25
0
 /// <summary>Determines whether the specified process is running under WOW64.</summary>
 /// <param name="hProc">
 /// <para>
 /// A handle to the process. The handle must have the PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION access right.
 /// For more information, see Process Security and Access Rights.
 /// </para>
 /// <para><c>Windows Server 2003 and Windows XP:</c> The handle must have the PROCESS_QUERY_INFORMATION access right.</para>
 /// </param>
 /// <returns>
 /// <see langword="true"/> if the process is running under WOW64. If the process is running under 32-bit Windows, the value is set
 /// to <see langword="false"/>. If the process is a 64-bit application running under 64-bit Windows, the value is also set to <see langword="false"/>.
 /// </returns>
 public static bool IsWow64(this HPROCESS hProc) => Environment.OSVersion.Version >= new Version(5, 1) && Kernel32.IsWow64Process(hProc, out var b) && b;
示例#26
0
 /// <summary>A call to <c>NtQueryInformationProcess</c> for the supplied process requires WOW64 structs.</summary>
 /// <param name="ProcessHandle">The process handle.</param>
 /// <returns><see langword="true"/> if structures returned from <c>NtQueryInformationProcess</c> must be configured exclusively for 64-bit use.</returns>
 public static bool NtQueryInformationProcessRequiresWow64Structs(HPROCESS ProcessHandle) => IsWow64(Kernel32.GetCurrentProcess()) && !IsWow64(ProcessHandle);
示例#27
0
 public static extern bool CheckRemoteDebuggerPresent([In] HPROCESS hProcess, [MarshalAs(UnmanagedType.Bool)] out bool pbDebuggerPresent);
示例#28
0
 internal static bool IsWow64(HPROCESS hProc) => (Environment.OSVersion.Version.Major >= 6 || (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1)) && Kernel32.IsWow64Process(hProc, out var b) && b;
示例#29
0
        /// <summary>
        /// <para>Retrieves information about the specified process.</para>
        /// </summary>
        /// <typeparam name="T">The type of the structure to retrieve.</typeparam>
        /// <param name="ProcessHandle">A handle to the process for which information is to be retrieved.</param>
        /// <param name="ProcessInformationClass">
        /// <para>
        /// The type of process information to be retrieved. This parameter can be one of the following values from the
        /// <c>PROCESSINFOCLASS</c> enumeration.
        /// </para>
        /// <list type="table">
        /// <listheader>
        /// <term>Value</term>
        /// <term>Meaning</term>
        /// </listheader>
        /// <item>
        /// <term>ProcessBasicInformation <br/> 0</term>
        /// <term>
        /// Retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a
        /// unique value used by the system to identify the specified process. Use the CheckRemoteDebuggerPresent and GetProcessId functions
        /// to obtain this information.
        /// </term>
        /// </item>
        /// <item>
        /// <term>ProcessDebugPort <br/> 7</term>
        /// <term>
        /// Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the process
        /// is being run under the control of a ring 3 debugger. Use the CheckRemoteDebuggerPresent or IsDebuggerPresent function.
        /// </term>
        /// </item>
        /// <item>
        /// <term>ProcessWow64Information <br/> 26</term>
        /// <term>
        /// Determines whether the process is running in the WOW64 environment (WOW64 is the x86 emulator that allows Win32-based
        /// applications to run on 64-bit Windows). Use the IsWow64Process2 function to obtain this information.
        /// </term>
        /// </item>
        /// <item>
        /// <term>ProcessImageFileName <br/> 27</term>
        /// <term>
        /// Retrieves a UNICODE_STRING value containing the name of the image file for the process. Use the QueryFullProcessImageName or
        /// GetProcessImageFileName function to obtain this information.
        /// </term>
        /// </item>
        /// <item>
        /// <term>ProcessBreakOnTermination <br/> 29</term>
        /// <term>Retrieves a ULONG value indicating whether the process is considered critical.</term>
        /// </item>
        /// <item>
        /// <term>ProcessSubsystemInformation <br/> 75</term>
        /// <term>
        /// Retrieves a SUBSYSTEM_INFORMATION_TYPE value indicating the subsystem type of the process. The buffer pointed to by the
        /// ProcessInformation parameter should be large enough to hold a single SUBSYSTEM_INFORMATION_TYPE enumeration.
        /// </term>
        /// </item>
        /// </list>
        /// </param>
        /// <returns>The structure and associated memory for any allocated sub-types.</returns>
        /// <exception cref="System.ArgumentException">Mismatch between requested type and class.</exception>
        public static NtQueryResult <T> NtQueryInformationProcess <T>([In] HPROCESS ProcessHandle, PROCESSINFOCLASS ProcessInformationClass) where T : struct
        {
            var validTypes = CorrespondingTypeAttribute.GetCorrespondingTypes(ProcessInformationClass, CorrespondingAction.Get).ToArray();

            if (validTypes.Length > 0 && Array.IndexOf(validTypes, typeof(T)) == -1)
            {
                throw new ArgumentException("Mismatch between requested type and class.");
            }
#if x64
            // Check if the target is a 32 bit process running in WoW64 mode.
            if (IsWow64(ProcessHandle))
            {
                // We are 64 bit. Target process is 32 bit running in WoW64 mode.
                throw new PlatformNotSupportedException("Unable to query a 32-bit process from a 64-bit process.");
            }
#else
            if (NtQueryInformationProcessRequiresWow64Structs(ProcessHandle))
            {
                if (validTypes.Length > 1 && !TypeIsWow())
                {
                    throw new ArgumentException("Type name must end in WOW64 to indicate it was configured exclusively for 64-bit use.");
                }
                var mem    = new NtQueryResult <T>();
                var status = NtWow64QueryInformationProcess64(ProcessHandle, ProcessInformationClass, ((IntPtr)mem).ToInt32(), mem.Size, out var sz);
                if (status.Succeeded)
                {
                    return(mem);
                }
                if (status != NTStatus.STATUS_INFO_LENGTH_MISMATCH || sz == 0)
                {
                    throw status.GetException();
                }
                mem.Size = sz;
                NtWow64QueryInformationProcess64(ProcessHandle, ProcessInformationClass, ((IntPtr)mem).ToInt32(), mem.Size, out _).ThrowIfFailed();
                return(mem);
            }
#endif
            // Target process is of the same bitness as us.
            else
            {
                if (validTypes.Length > 1 && TypeIsWow())
                {
                    throw new ArgumentException("Type name must not end in WOW64 should be configured for 32 or 64-bit use.");
                }
                var mem    = new NtQueryResult <T>();
                var status = NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, mem, mem.Size, out var sz);
                if (status.Succeeded)
                {
                    return(mem);
                }
                if (status != NTStatus.STATUS_INFO_LENGTH_MISMATCH || sz == 0)
                {
                    throw status.GetException();
                }
                mem.Size = sz;
                NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, mem, mem.Size, out _).ThrowIfFailed();
                return(mem);
            }

            bool TypeIsWow() => typeof(T).Name.EndsWith("WOW64");
        }
示例#30
0
 /// <summary>Executes an action on an attachment.</summary>
 /// <param name="hwnd">
 /// <para>Type: <c>HWND</c></para>
 /// <para>The handle of the parent window.</para>
 /// </param>
 /// <param name="pszVerb">
 /// <para>Type: <c>LPCWSTR</c></para>
 /// <para>
 /// A pointer to a null-terminated string that contains a verb specifying the action to be performed on the file. See the
 /// lpOperation parameter in ShellExecute for valid strings. This value can be <c>NULL</c>.
 /// </para>
 /// </param>
 /// <param name="phProcess">
 /// <para>Type: <c>HANDLE*</c></para>
 /// <para>A pointer to a handle to the source process, used for synchronous operation. This value can be <c>NULL</c>.</para>
 /// </param>
 /// <remarks>
 /// <para>
 /// Before calling <c>IAttachmentExecute::Execute</c>, IAttachmentExecute::SetLocalPath must be called with a valid local path
 /// and the file must be copied to that location.
 /// </para>
 /// <para>
 /// If a prompt is indicated, <c>IAttachmentExecute::Execute</c> calls IAttachmentExecute::Prompt using the
 /// ATTACHMENT_ACTION_EXEC value.
 /// </para>
 /// <para>
 /// <c>IAttachmentExecute::Execute</c> may run virus scanners or other trust services to validate the file before executing it.
 /// Note that these services can delete or alter the file.
 /// </para>
 /// <para><c>IAttachmentExecute::Execute</c> may attach evidence to the local path in its NTFS alternate data stream (ADS).</para>
 /// <para>
 /// If phProcess is not <c>NULL</c>, <c>IAttachmentExecute::Execute</c> operates as a synchronous process and returns an
 /// <c>HPROCESS</c>, if available. If phProcess is <c>NULL</c>, <c>IAttachmentExecute::Execute</c> operates as an asynchronous
 /// process. This implies that the calling application has a message pump and a long-lived window.
 /// </para>
 /// <para>
 /// If the handle pointed to by phProcess is non- <c>NULL</c> when the method returns, the calling application is responsible
 /// for calling CloseHandle to free the handle when it is no longer needed.
 /// </para>
 /// </remarks>
 // https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-iattachmentexecute-execute HRESULT Execute(
 // HWND hwnd, LPCWSTR pszVerb, HANDLE *phProcess );
 void Execute([In] HWND hwnd, [Optional, MarshalAs(UnmanagedType.LPWStr)] string pszVerb, [Out, Optional] out HPROCESS phProcess);