이미 실행된 프로그램을 또 실행되지 않게 하기 위해 사용하는 가장 일반적인 방법이
MUTEX를 이용한 방법입니다.
이 방법은 프로그램이 실행될때
이미 생성된 MUTEX가 생성되어 있으면 그냥 종료하고
MUTEX가 없으면 새로 생성한 후 실행되도록 하는 방식입니다.
이 방식 이외에 몇가지 방법이 더 있긴 하지만 이 방법이 가장 많이 사용되는 방법입니다.
하지만 전 이미 실행된 프로그램이 있으면 그냥 종료되는게 아니고
이미 실행된 프로세스를 강제로 종료시키고 재실행되도록 하는 방법이 필요했습니다.
소스코드는 다음과 같습니다.
Procedure GetPIDsByProgramName(AProcessName: string; IncludeSelf: Boolean; ResultList: TStringList);
var
isFound: boolean;
AHandle, AhProcess: THandle;
ProcEntry32: TProcessEntry32;
APath: array[0..MAX_PATH] of char;
begin
AProcessName := ExtractFileName(AProcessName);
ResultList.Clear;
try
AHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
ProcEntry32.dwSize := Sizeof(TProcessEntry32);
isFound := Process32First(AHandle, ProcEntry32);
while isFound do
begin
AhProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, ProcEntry32.th32ProcessID);
GetModuleFileNameEx(AhProcess, 0, @APath[0], sizeof(APath));
CloseHandle(AhProcess);
if (UpperCase(StrPas(APath)) <> UpperCase(AProcessName)) AND
(UpperCase(StrPas(ProcEntry32.szExeFile)) <> UpperCase(AProcessName)) then
begin
isFound := Process32Next(AHandle, ProcEntry32);
Continue;
end;
if ProcEntry32.th32ProcessID = GetCurrentProcessId then
Begin
if NOT IncludeSelf then
begin
isFound := Process32Next(AHandle, ProcEntry32);
Continue;
end;
End;
ResultList.Add(IntToStr(ProcEntry32.th32ProcessID));
isFound := Process32Next(AHandle, ProcEntry32);
end;
finally
CloseHandle(AHandle);
end;
end;
Function KillProcessByPIDList(PIDList: TStringList): Boolean;
Var
KillProcessParam : String;
I: Integer;
CmdPID : PDWORD;
Begin
Result := True;
KillProcessParam := '/F /T ';
for I := 0 to PIDList.Count - 1 do
Begin
KillProcessParam := KillProcessParam + '/PID ' + PIDList[I];
End;
ShellExecute(0, 'Open', 'TaskKill.exe', PChar(KillProcessParam), '', SW_HIDE);
End;
Function KillProcessByProgramName(AProcessName: String = ''; IncludeSelf: Boolean = False): Boolean;
Var
PIDList : TStringList;
Begin
Result := True;
if AProcessName = '' then
Begin
AProcessName := Application.ExeName;
End;
AProcessName := ExtractFileName(AProcessName);
PIDList := TStringList.Create;
GetPIDsByProgramName(AProcessName, IncludeSelf, PIDList);
while PIDList.Count > 0 do
Begin
KillProcessByPIDList(PIDList);
PIDList.Clear;
GetPIDsByProgramName(AProcessName, IncludeSelf, PIDList);
End;
FreeAndNil(PIDList);
End;
이렇게 했으면 이제 프로젝트 소스코드에 이 함수를 사용하시면 됩니다.
Application.Initialize;
KillProcessByProgramName;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
참고로 프로세스를 강제종료하는 부분을 보면
ShellExecute(0, 'Open', 'TaskKill.exe', PChar(KillProcessParam), '', SW_HIDE);
이렇게 TaskKill.exe를 사용했습니다.
SendMessage를 이용하여 WM_Close 메세지를 전송하여 창을 닫는 방법도 있고
TerminateProcess함수를 사용하여 프로세스를 강제로 종료하는 방법도 있는데
굳이 ShellExecute를 이용하여 외부프로그램을 사용한데는 이유가 있습니다.
같은 프로그램을 빠른 속도로 계속 재실행 시키다 보면
SendMessage를 이용하여 WM_Close 메세지를 전송하면 창은 닫히지만 프로세스가 남아 있는 경우가 발생합니다.
그리고 TerminateProcess함수를 이용하면
빠른 속도로 프로그램을 계속 재실행하는 경우 약 50번 정도 재실행 되기 전에 반드시 에러메세지가 나타납니다.
하지만 TaskKil.exe를 이용하여 프로그램을 강제종료 시키면 약 5천번을 빠른 속도로 연속 재실행해도 에러가 없었습니다.