2024-04-03 14:27:35 +08:00
// CustomAction.cpp : Defines the entry point for the custom action.
# include "pch.h"
2024-04-11 12:06:10 +08:00
# include <stdlib.h>
2024-04-06 16:30:33 +08:00
# include <strutil.h>
2024-04-03 14:27:35 +08:00
# include <shellapi.h>
2024-04-11 11:51:35 +08:00
# include <tlhelp32.h>
# include <winternl.h>
2024-04-03 14:27:35 +08:00
UINT __stdcall CustomActionHello (
2024-04-11 11:51:35 +08:00
__in MSIHANDLE hInstall )
2024-04-03 14:27:35 +08:00
{
HRESULT hr = S_OK ;
DWORD er = ERROR_SUCCESS ;
hr = WcaInitialize ( hInstall , " CustomActionHello " ) ;
ExitOnFailure ( hr , " Failed to initialize " ) ;
WcaLog ( LOGMSG_STANDARD , " Initialized. " ) ;
// TODO: Add your custom action code here.
WcaLog ( LOGMSG_STANDARD , " ================= Example CustomAction Hello " ) ;
LExit :
er = SUCCEEDED ( hr ) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE ;
return WcaFinalize ( er ) ;
}
UINT __stdcall RemoveInstallFolder (
2024-04-11 11:51:35 +08:00
__in MSIHANDLE hInstall )
2024-04-03 14:27:35 +08:00
{
HRESULT hr = S_OK ;
DWORD er = ERROR_SUCCESS ;
int nResult = 0 ;
2024-04-06 16:30:33 +08:00
LPWSTR installFolder = NULL ;
LPWSTR pwz = NULL ;
LPWSTR pwzData = NULL ;
2024-04-03 14:27:35 +08:00
hr = WcaInitialize ( hInstall , " RemoveInstallFolder " ) ;
ExitOnFailure ( hr , " Failed to initialize " ) ;
2024-04-06 16:30:33 +08:00
hr = WcaGetProperty ( L " CustomActionData " , & pwzData ) ;
ExitOnFailure ( hr , " failed to get CustomActionData " ) ;
pwz = pwzData ;
hr = WcaReadStringFromCaData ( & pwz , & installFolder ) ;
ExitOnFailure ( hr , " failed to read database key from custom action data: %ls " , pwz ) ;
2024-04-03 14:27:35 +08:00
SHFILEOPSTRUCTW fileOp ;
2024-04-11 11:51:35 +08:00
ZeroMemory ( & fileOp , sizeof ( SHFILEOPSTRUCT ) ) ;
2024-04-03 14:27:35 +08:00
fileOp . wFunc = FO_DELETE ;
2024-04-06 16:30:33 +08:00
fileOp . pFrom = installFolder ;
2024-04-03 14:27:35 +08:00
fileOp . fFlags = FOF_NOCONFIRMATION | FOF_SILENT ;
2024-04-06 16:30:33 +08:00
nResult = SHFileOperation ( & fileOp ) ;
2024-04-03 14:27:35 +08:00
if ( nResult = = 0 )
{
2024-04-06 16:30:33 +08:00
WcaLog ( LOGMSG_STANDARD , " The directory \" %ls \" has been deleted. " , installFolder ) ;
2024-04-03 14:27:35 +08:00
}
else
{
2024-04-06 16:30:33 +08:00
WcaLog ( LOGMSG_STANDARD , " The directory \" %ls \" has not been deleted, error code: 0X%02X. Please refer to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa for the error codes. " , installFolder , nResult ) ;
2024-04-03 14:27:35 +08:00
}
LExit :
2024-04-06 16:30:33 +08:00
ReleaseStr ( installFolder ) ;
2024-04-03 14:27:35 +08:00
er = SUCCEEDED ( hr ) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE ;
return WcaFinalize ( er ) ;
}
2024-04-11 11:51:35 +08:00
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
// **NtQueryInformationProcess** may be altered or unavailable in future versions of Windows.
// Applications should use the alternate functions listed in this topic.
// But I do not find the alternate functions.
// https://github.com/heim-rs/heim/issues/105#issuecomment-683647573
typedef NTSTATUS ( NTAPI * pfnNtQueryInformationProcess ) ( HANDLE , PROCESSINFOCLASS , PVOID , ULONG , PULONG ) ;
bool TerminateProcessIfNotContainsParam ( pfnNtQueryInformationProcess NtQueryInformationProcess , HANDLE process , LPCWSTR excludeParam )
{
bool processClosed = false ;
PROCESS_BASIC_INFORMATION processInfo ;
NTSTATUS status = NtQueryInformationProcess ( process , ProcessBasicInformation , & processInfo , sizeof ( processInfo ) , NULL ) ;
if ( status = = 0 & & processInfo . PebBaseAddress ! = NULL )
{
PEB peb ;
SIZE_T dwBytesRead ;
if ( ReadProcessMemory ( process , processInfo . PebBaseAddress , & peb , sizeof ( peb ) , & dwBytesRead ) )
{
RTL_USER_PROCESS_PARAMETERS pebUpp ;
if ( ReadProcessMemory ( process ,
peb . ProcessParameters ,
& pebUpp ,
sizeof ( RTL_USER_PROCESS_PARAMETERS ) ,
& dwBytesRead ) )
{
if ( pebUpp . CommandLine . Length > 0 )
{
WCHAR * commandLine = ( WCHAR * ) malloc ( pebUpp . CommandLine . Length ) ;
if ( commandLine ! = NULL )
{
if ( ReadProcessMemory ( process , pebUpp . CommandLine . Buffer ,
commandLine , pebUpp . CommandLine . Length , & dwBytesRead ) )
{
if ( wcsstr ( commandLine , excludeParam ) = = NULL )
{
WcaLog ( LOGMSG_STANDARD , " Terminate process : %ls " , commandLine ) ;
TerminateProcess ( process , 0 ) ;
processClosed = true ;
}
}
free ( commandLine ) ;
}
}
}
}
}
return processClosed ;
}
// Terminate processes that do not have parameter [excludeParam]
// Note. This function relies on "NtQueryInformationProcess",
// which may not be found.
// Then all processes of [processName] will be terminated.
bool TerminateProcessesByNameW ( LPCWSTR processName , LPCWSTR excludeParam )
{
HMODULE hntdll = GetModuleHandleW ( L " ntdll.dll " ) ;
if ( hntdll = = NULL )
{
WcaLog ( LOGMSG_STANDARD , " Failed to load ntdll. " ) ;
}
pfnNtQueryInformationProcess NtQueryInformationProcess = NULL ;
if ( hntdll ! = NULL )
{
NtQueryInformationProcess = ( pfnNtQueryInformationProcess ) GetProcAddress (
hntdll , " NtQueryInformationProcess " ) ;
}
if ( NtQueryInformationProcess = = NULL )
{
WcaLog ( LOGMSG_STANDARD , " Failed to get address of NtQueryInformationProcess. " ) ;
}
bool processClosed = false ;
// Create a snapshot of the current system processes
HANDLE snapshot = CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS , 0 ) ;
if ( snapshot ! = INVALID_HANDLE_VALUE )
{
PROCESSENTRY32W processEntry ;
processEntry . dwSize = sizeof ( PROCESSENTRY32W ) ;
if ( Process32FirstW ( snapshot , & processEntry ) )
{
do
{
if ( lstrcmpW ( processName , processEntry . szExeFile ) = = 0 )
{
HANDLE process = OpenProcess ( PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ , FALSE , processEntry . th32ProcessID ) ;
if ( process ! = NULL )
{
if ( NtQueryInformationProcess = = NULL )
{
WcaLog ( LOGMSG_STANDARD , " Terminate process : %ls, while NtQueryInformationProcess is NULL " , processName ) ;
TerminateProcess ( process , 0 ) ;
processClosed = true ;
}
else
{
processClosed = TerminateProcessIfNotContainsParam (
NtQueryInformationProcess ,
process ,
excludeParam ) ;
}
CloseHandle ( process ) ;
}
}
} while ( Process32Next ( snapshot , & processEntry ) ) ;
}
CloseHandle ( snapshot ) ;
}
if ( hntdll ! = NULL )
{
CloseHandle ( hntdll ) ;
}
return processClosed ;
}
UINT __stdcall TerminateProcesses (
__in MSIHANDLE hInstall )
{
HRESULT hr = S_OK ;
DWORD er = ERROR_SUCCESS ;
int nResult = 0 ;
wchar_t szProcess [ 256 ] = { 0 } ;
DWORD cchProcess = sizeof ( szProcess ) / sizeof ( szProcess [ 0 ] ) ;
hr = WcaInitialize ( hInstall , " TerminateProcesses " ) ;
ExitOnFailure ( hr , " Failed to initialize " ) ;
MsiGetPropertyW ( hInstall , L " TerminateProcesses " , szProcess , & cchProcess ) ;
WcaLog ( LOGMSG_STANDARD , " Try terminate processes : %ls " , szProcess ) ;
TerminateProcessesByNameW ( szProcess , L " --install " ) ;
LExit :
er = SUCCEEDED ( hr ) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE ;
return WcaFinalize ( er ) ;
}