본문 바로가기
C & C++/C & C++

[Tip] 외부 프로그램 실행 및 종료

by izen8 2011. 5. 4.
반응형
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
#include <windows.h>
#include <stdlib.h>
#include <Tlhelp32.h>


 DWORD dwSize = 250;
 HANDLE hSnapShot;
 PROCESSENTRY32 pEntry;
 BOOL bCrrent=FALSE;

 hSnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPALL,NULL);
 pEntry.dwSize =sizeof(pEntry);
 // 실행중인 프로세스들의 첫번재 정보를 가져온다.
 Process32First (hSnapShot,&pEntry);
 // Tool이 실행중인지 확인
 while(1)
 {
  // 다음번 프로세스의 정보를 가져온다.
  BOOL hRes=Process32Next (hSnapShot,&pEntry);
  if(hRes==FALSE)
   break;       
  if(!strncmp(pEntry.szExeFile,"SpecialChar.exe",15))
  {
   bCrrent = TRUE;
  }
  if(bCrrent)
  {
   HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pEntry.th32ProcessID);
   if(hProcess)
   {
    if(TerminateProcess(hProcess, 0))
    {
     unsigned long nCode; //프로세스 종료 상태
     GetExitCodeProcess(hProcess, &nCode);
    }
    CloseHandle(hProcess);
   }
   break;
  }
 }

//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
#include <windows.h>
#include <stdlib.h>
#include <Tlhelp32.h>



BOOL CPopSchedulingDlg::CloseProcess(CString sExeName)
{
 sExeName.MakeUpper();
 HANDLE hSnapshot = CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS, 0 );
 if ( (int)hSnapshot != -1 )
 {
  PROCESSENTRY32 pe32 ;
  pe32.dwSize=sizeof(PROCESSENTRY32);
  BOOL bContinue ;
  CString strProcessName;
  if ( Process32First ( hSnapshot, &pe32 ) )
  {
   do
   {
    strProcessName = pe32.szExeFile; //strProcessName이 프로세스 이름;
    strProcessName.MakeUpper();
    if( ( strProcessName.Find(sExeName,0) != -1 ) )
    {
     HANDLE      hProcess = OpenProcess( PROCESS_ALL_ACCESS, 0, pe32.th32ProcessID );
     if( hProcess )
     {
      DWORD       dwExitCode;
      GetExitCodeProcess( hProcess, &dwExitCode);
      TerminateProcess( hProcess, dwExitCode);
      CloseHandle(hProcess);
      CloseHandle( hSnapshot );
      return TRUE;
     }
 
     CloseHandle( hSnapshot );
     return FALSE;
    }
    bContinue = Process32Next ( hSnapshot, &pe32 );
   } while ( bContinue );
  }
  CloseHandle( hSnapshot );
 }
 return FALSE;
}

//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

0. 확장자가 현재 PC에 연결된 프로그램이 있는지 확인하려면
 
    DWORD Size = MAX_PATH;
    char Buffer[MAX_PATH+1];
    HRESULT _result = AssocQueryString(0, ASSOCSTR_EXECUTABLE, ".htm", "Open", Buffer, &Size);
    if ( _result == S_OK )
    {
       // PC에 연결된 프로그램이 있다.
     }
     else
     {
       // PC에 연결된 프로그램 없다.
      }
 
      ====> 닭질 포인트 : SIZE를 나름대로 값을 줘야 하는데, 마음 편하게 Size의 값과
Buffer 크기 값을 같거나 Buffer 크기가 하나 더 크게해라...  SIZE를 0로 했드니만, S_OK는
나온는데 연결된 프로그램의 패스값이 Buffer에 들어와야 하는데... 하나도 안들어오드라
~
      ====> open 대신 openas, print 등이 가능하며, 그런 명령이 DDE에서 지원안되면
S_OK가 나오지 않고 에러가 나온다.
1. MFC에서 특정 어플리케이션을 실행하여 종료가 끝날때 까지 기다리는 프로그램


#include <shellapi.h>

void KShellExecute(HWND handle, LPCTSTR exe, LPCTSTR param, LPCTSTR dir)
{
    DWORD ExitCode;
    SHELLEXECUTEINFO SEInfo;

    memset( &SEInfo, 0, sizeof(SEInfo));
    SEInfo.cbSize = sizeof(SHELLEXECUTEINFO);

    SEInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
    SEInfo.hwnd = handle;
    SEInfo.lpFile = exe;
    SEInfo.lpParameters = param;
    SEInfo.lpDirectory = dir;
    SEInfo.nShow = SW_HIDE; // SW_SHOWNORMAL;


    if (ShellExecuteEx(&SEInfo)==TRUE)
    {
        do
        {
            GetExitCodeProcess(SEInfo.hProcess, &ExitCode);
            Sleep(500);
        } while (ExitCode); // wait until the command is finished
    }
}
또는
BOOL ExecuteProgram( String FileName, String Params, INT Flag )
{
  SHELLEXECUTEINFO execinfo;
 
  // 실행을 위해 구조체 세트
  ZeroMemory( &execinfo, sizeof(execinfo) );
  execinfo.cbSize = sizeof(execinfo);
  execinfo.lpVerb = "open";
  execinfo.lpFile = FileName.c_str();
  execinfo.lpParameters = Params.c_str();
  execinfo.fMask = SEE_MASK_FLAG_NO_UI SEE_MASK_NOCLOSEPROCESS;
  execinfo.nShow = SW_SHOWDEFAULT;
 
  // 프로그램을 실행한다.
  int r = (int)ShellExecuteEx( &execinfo );
  if ( r == 0 ) return ( false );
 
  // 만약 Sync 플랙이 세트되었으면,
  // 실행이 종료될 때까지 기다린다.
  if ( Flag == 1 )
  {
    DWORD ec;
    do
    {
      GetExitCodeProcess( execinfo.hProcess, &ec );
      Application->ProcessMessages();
    }
    while ( ec == STILL_ACTIVE );
  }
 
  return ( true );
}
 
2. 연결 프로그램 찾기 다이얼로그 띄우기


1.요약

Explorer 에서 파일 아이콘을 더블클릭할 경우 파일의 확장명에 따라 연결되어 있는 프로그램이 동작하게 되어 있다.
하지만 연결되어 있는 프로그램이 없을 경우 "연결 프로그램 찾기" 다이얼로그가 화면에 나타나게 할 수 있습니다.

2.본문

파일 아이콘에 연결된 프로그램이 없을 경우 연결 프로그램을 지정하도록 "연결 프로그램 찾기" 다이얼로그를
프로그램적으로 띄우는 방법은 ShellExecuteEx API 를 Call 할 때 "Openas" 를 파라미터로 사용하면 된다.

우선 ShellExecuteEx를 Call하기 전에 FindExecutable API를 사용하여 파일의 프로그램 연결여부를 우선 알아보도록 한다.
만약 연결된 응용프로그램이 있을경우 SHELLEXECUTEINFO structure에 "Open" 파라미터를 사용하고,
그렇지 않을 경우 "Openas"를 사용하면 된다.
이 파라미터에 의해 ShellExecuteEx 가 "연결 프로그램 찾기" 다이얼로그를 화면에 나타내게 된다.

3.예제

// 열릴 파일의 이름을 얻는다.
CFileDialog dlg(TRUE); 
if (dlg.DoModal() == IDOK )
{
    CString strFile = dlg.GetPathName(); // 연결된 프로그램이 있는지 확인한다. char strExecutable[FILENAME_MAX];
int result = (int)FindExecutable((LPCTSTR)strFile, NULL, (LPTSTR)&strExecutable);
if (result == 31)

{ // 만약 연결된 프로그램이 없다면
AfxMessageBox("연결된 파일이 없습니다. 연결된 프로그램을 찾는 다이얼로그가보일 것입니다.");
SHELLEXECUTEINFO sei = {sizeof(sei), 0, m_hWnd, "Openas",
        LPCSTR)strFile, NULL, NULL,  SW_SHOWNORMAL, AfxGetApp()->m_hInstance}; // 다이얼로그 띄우기
ShellExecuteEx(&sei);     }
else if (result >= 32 )
{ // 연결된 프로그램을 찾았습니다.
AfxMessageBox("연결된 프로그램을 찾았습니다."); SHELLEXECUTEINFO sei = {sizeof(sei), 0, m_hWnd, "Open", (LPCSTR)strFile, NULL,
NULL,
SW_SHOWNORMAL, AfxGetApp()->m_hInstance}; // 파일 실행 ShellExecuteEx(&sei);
}
else { // 에러 발생
AfxMessageBox("예기치 못한 에러가 발생하였습니다."); }
}
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

다른 프로그램 제어하기

 [ 외부 프로그램의 실행과 종료여부를 판단 ]

자신의 어플리케이션이 아닌 다른 어플리케이션의 종료여부를 판단해야할 경우에 사용합니다. 예를 들어, winzip내의 인스톨를 실행시키면, 압축된 프로그램이 바로 인스톨이 되는 경우를 보셨을껍니다. 이때, 그 인스톨되는 프로그램이 종료되었을 때, winzip도 비로소 종료가 됩니다.

1. 어플리케이션 클래스의 사전 준비

외부 어플리케이션의 정보를 관리하기위한 정보구조체를 선언압니다.이때, 어플리케이션 클래스에 Protected로 작성해 둡니다.

class CALApp : public CWinApp
{

protected:

PROCESS_INFORMATION pinfo;

};
 

이때, pinfo의 값은 쓰레기값을 갖고 있으므로, 이에대한 초기화를 해 줍니다.
// CALApp 생성자
CALApp::CALApp()
{

// TODO

pinf.hProcess=NULL;
pinf.hThread=NULL;
pinf.dwProcessId=0L;
pinf.dwThreadId=0L;

}
 

2. 외부 어플리케이션을 정보를 저장한뒤 실행시킵니다.

// 다른 프로그램을 기동하는 루틴

int CALApp::ExecuteOutsideEditor()
{

STARTUPINFO sinfo;

if(IsOutsideEditorRunning())

return -2;

memset(&sinfo, 0x00, sizeof(sinfo));

if(CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo))

return -1;

else

return 0;

}
 

즉, 외부프로그램을 실행하면서, 그에대한 종료여부를 판단하기위해 정보구조체를 저장하는것이죠..
szCmdLine은 실행시킬 외부프로그램의 이름을 저장하는 스트링변수이다. 예를 들어, 아래와 같은 형식의 스트링을 저장시킨다고 보면됩니다.

CString szCmdLine;
szCmdLine.Format("c:\\windows\\notepad.exe c:\\help.txt");

즉, 위의 내용은 "윈도우에 있는 메모장에 루트디렉토리에 있는 헬프파일을 여는 외부프로그램을 실행시키는 의미"가 되는 것이 되는것입니다.(노파심에서 말씀드리는데, 프로그램상에서는 \를 쓰기 위해서는 \\두 번 써야한다는건 아시겠죠?)

if(IsOutsideEditorRunning())
return -2;

위의 명령문은 외부 프로그램이 이미 실행상태인지를 의미합니다. 이는 외부 프로그램을 관리하는 정보변수가  pinfo하나밖에 없기 때문에 한번에 하나의 외부프로그램을 실행할 수 있기 때문입니다. 만약에 복수의 외부 프로그램을 실행하고 싶으시다면, 이 정보구조체변수를 배열과 같이 복수개로 만들어 놓으면 됩니다. 이때는 카운터가 필요하겠죠 ?

memset은 STARTUPINFO 구조 체형 변수 pinfo를 프로세스의 실행마다 초기화합니다. memset(&sinfo, 0x00, sizeof(sinfo))은, sinfo의 모든 멤버 변수에 0을 대입함을 의미합니다.

CreateProcess 함수는 여러 가지 인수를 갖지만,  szCmdLine만 신경을 쓰시면 됩니다.

 3. 외부 어플리케이션이 아직 살아 있을까를 조사하는 루틴

// 기동한 프로그램이 아직 살아 있을까 탐색하는 루틴
int CALApp::IsOutsideEditorRunning()
{

unsigned long l;

if(pinfo.hProcess)
{

GetExitCodeProcess(pinfo.hProcess, &l);
if(l==STILL_ACTIVE)

return -1;       //동작중은 실행하지 않는다.

}

return 0;

}

pinfo는 위에서 실행한 외부프로그램의 현재상태가 저장되어 있습니다. 즉, pinfo.hProcess값이 NULL 인가 아닌가를 판단합니다. 그 값이NULL인경우는, 현재 실행중이나 판정하기 위한 정보가 없음을 의미하며,  NULL이 아닌 경우는 판정하기위한 정보가 생성되었음을 의미하고, pinf.hProcess로부터, GetExitProcess Win32API 함수를 개입시켜, 외부 프로그램의 상태를 나타냅니다.

이렇게 얻은 값을 조사해 줌으로써, 외부 프로그램의 상태를 조사할 수 있습니다. 만일 동작중이면, STILL_ACTIVE를 의미하는것입니다.

4. 현재 정보를 체크합니다.

위와 같은 순서로 하면, 대부분 필요한 정보를 얻을 수 있습니다. 그러나 가장 핵심이 되는 부분이 빠졌죠 ? 바로 pinfo의 프로세스상태를 체크해주는 루틴이 필요합니다. 이것은 주기적으로 체크를 해야하기 때문에 WM_TIMER 메세지에 의해 실행되는 CWnd::OnTimer를 사용하면 간단히 해결됩니다.

이때, 타이머처리는 어플리케이션 클래스에 둘 수 없으므로, CMainFrame등과 같은 다른 클래스에 삽입합니다.

void CMainFrame::OnTimer(UINT nIDEvent)
{

// pApp는 어플리케이션 클래스에의 포인터를 의미합니다. if(pApp->IsOutsideEditorRunning()==0)
{

// 만일 다른기능을 추가시, 아래 부분을 수정합니다.

AfxMessageBox("실행한 프로그램이 종료했습니다."); 

CFrameWnd::OnTimer(nIDEvent);
KillTimer(nIDEvent);
return;

}

CFrameWnd::OnTimer(nIDEvent);

}

여기서는 단순히 메시지 박스를 출력하도록 했지만, 이 부분을 수정해서, 다양한 기능을 추가할 수 있습니다.


//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
반응형

댓글