Windows 95의 출현과 함께 문서의 개념이 중요성을 띠게 되었다. 이제는 실행파일이라는 개념이 좀더 복잡해 지고 단순히 구동한다는 의미를 떠나 아주 방대한 개념으로 자리 잡고 있다.
문서라고 하는 것은 시스템의 네임스페이스의 일부인 보다 일반적인 객체를 말하고자 하며, 이문서에 대하여 '열기(open)', '인쇄(print)', '탐색(explore)', '찾기(find)'를 하는 프로그램이 있다. 다시 말해서, 문서라는 것은 그것에 대해서 프로그램이 어떤 동사(Verb)를 실행할 수 있는 모든 아이템을 말한다.
지금의 프로그램 실행자의 모체였던 WinExec()에서 ShellExecuteEx()라는 함수로 그 진행이 옮겨가는 이유도 이해 따른다.
이 장에서는 다음과 같은 것들을 다룰 것이다.
1. WinExec()와 CreateProcess() 사이의 차이점
2. ShellEcecute()와 ShellExecuteEx()가 다른 함수보다 우수한점
3. 동사들(Verb), 문서들 그리고 정책들(Policy)
4. 훅킹을 사용하여 프로세스 실행을 내 마음대로
그리고 다음과 같은 몇가지 예제 코드를 살펴볼 것이다.
1. 디폴트 브라우져 감지법
2. 프로그램을 실행시키고 종료를 기다리는법
3. 어떤 파일에 대한 등록정보 대화상자를 나타내는법
4. 찾기 대화상자를 화면에 출력하는 법
5. 사용자가 특정 폴더를 액세스하지 못하게 하거나, 다른 특정 어플리케이션을 실행하지 못하게 막는법
=====================================================
1. WinExec()에서 CreateProcess()로...
UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow);
이 함수는 Windows 3.x에서는 외부 프로그램을 실행시키는 유일한 방법이었다. 그리고 가장 간단한 사용법을 가지기도 한다.
하지만 단점이라 불리울 수 있는 것이 일단 실행을 시켜 놓으면 실행이 되는지 에러가 났는지, 종료 되었는지 전혀 알수가 없다는 것이다.
다음은 CreateProcess()의 프로토 타입이다.
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 실행파일 모듈의 이름에 대한 포인터
LPTSTR lpCommandLine, // 커맨드 문자열에 대한 포인터
LPSECURITY_ATTRIBUTES lpPA, // 프로세스 보안 속성 포인터
LPSECURITY_ATTRIBUTES lpTA, // 스레드 보안속성 포인터
BOOL bInheritHandles, // 핸들 상속 여부 플래그
DWORD dwCreationFlags, // 생성 플래그
LPVOID lpEnvironment, // 환경 블록에 대한 포인터
LPCTSTR lpCurrentDirectory, // 현재 디렉토리
LPSTARTUPINFO lpStartupInfo, // STARTUPINFO 포인터
LPPROCESS_INFORMATION lpPI // PROCESS_INFORMATION 포인터
);
보시다 시피 여기에는 많은 파라미터들이 있다 하지만.. 대부분의 내용이 MSDN에 잘 문서화가 되어 있으므로 그렇게 어렵다거나 하지는 않다.
일단 가장 간단한 호출을 한번 살펴보자.
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
CreateProcess(NULL, szPrgName, NULL, NULL, TRUE, NORMAL_PRIORITY_CLSS, NULL, NULL, &si, &pi);
WinExec보다는 복잡하지만 여러가지 부가적인 정보를 줄 수도 있고 받을 수도 있으므로 상대적인 저비용이다.
만약 위의 프로그램을 실행시키고 종료하기를 기다린다고 하면..
BOOL b = CreateProcess(............);
if(!b)
return FALSE;
WaitForSingleObject(pi.hProcess, INFINITE);
return TRUE;
정말 간단하지 않는가? ^^;
위의 결과를 외견상으로 보면 WinExec의 문제점들의 CreateProcess() 함수로써 모두 해결했다는 생각을 할 수도 있겠지만 현재의 문서라는 개념은 좀더 일반화 되었다. 위에서 사용된 실행가능한 프로그램(exe, com ...etc)등은 문서의 한가지 유형일 뿐이다.
2. ShellExecute()와 ShellExecuteEx()가 다른 함수보다 우수한점
HINSTANCE ShellExecute(
HWND hwnd, // 부모 윈도우 핸들
LPCTSTR lpVerb, // 동사 혹은 작업
LPCTSTR lpFile, // 실행 대상 문서
LPCTSTR lpParameters, // 컴맨드 인자
LPCTSTR lpDirectory, // 현재 디렉토리
INT nShowCmd // 보여주기 옵션
);
일단 위의 함수를 살펴보면 CreateProcess() 함수보다 기능이 많이 떨어지는 것 처럼 보이지만 이 함수의 진정한 의미는 동사 연결이라는 점이다.
보통 우리가 말하는 [연결 프로그램]을 지칭한다.
위에서 [동사 혹은 작업] 부분의 LPCTSTR lpVerb 에 사용될 수 잇는 것은데 대한 나열이다.
===============================================
열기(open) - 프로그램/문서/폴더
탐색(explore) - 폴더
인쇄(print) - 문서
~~로 인쇄(printto) - 문서
찾기(find) - 폴더
------> 먼저 열기(open)에 대하여 살펴보자.
ShellExcute(hwnd, "open", "c:\\prog.exe", "/nologo", "d:\\", SW_SHOW);
ShellExcute(hwnd, "open", "c:\\file.txt", NULL, NULL, SW_SHOW);
만약 txt확장자를 가진 파일에 대한 연결이 존재하지 않으면 우리가 많이 보던 연결프로그램 대화상자가 나타날 것이다.
------> 탐색 작업
탐색 작업은 폴더에 대해서만 가능하고 open 인경우와 약간의 차이를 나타낸다.
open로 동사를 주면 pane이 하나로 된 창이 뜨고, explore로 주면 pane가 2개인 탐색기다 뜬다.
ShellExcute(hwnd, "expolre", "c:\\", NULL, NULL, SW_SHOW);
------> 인쇄 작업
인쇄작업은 문서를 인쇄하기 위한 것이지만, 지정된 문서를 인쇄할 수 있는 명령어 라인과 프로그램을 정확히 알나내기 위해 레지스트리에 저장된 정보에 의존한다.
ShellExcute(hwnd, "print", "c:\\file.txt", NULL, NULL, SW_SHOW);
이 함수는 텍스트 파일을 처리하기 위해 등록된 프로그램을 찾고, 그 프로그램에 인쇄를 위한 명령이 있는지 검사한다. 정상적이라면 이 프로그램은 notepad.exe가 될 것이고, 그 명령어 라인은 다음과 같다.
notepad.exe /p
디폴트 프린터로의 인쇄는 위에서 처럼 간단하게 사용이 가능하지만 만약 여러개의 프린터가 있거나 출력 포트를 설정하고 싶다면 printto를 사용해야 한다.
만약 printto가 문서에서 지원이 된다면 등록된 명령이 실행될것이고 그렇지 못하다면..
경고창이 뜨면서 디폴트 프린터로 출력할 것이지를 묻는다.
------> 찾기 작업
지정된 경로를 루트로 찾기 창이 생성된다.
예제는 생략한다. ^^;
그렇다면 파일을 열기 위해서 혹은 그 파일과 연결된 실행 프로그램은 어떻게 구할 수 있는가?
의외로 쉽게 구할 수 있다. SDK에서 API를 제공하니까 ^^;
HINSTANCE FindExecuteable(
LPCTSTR lpFile, // 알아볼 파일
LPCTSTR lpDir, // 경로
LPTSTR lpResult) // 찾은 결과값
하지만 위 함수에는 치명적인 결함이 있다.
1. 보통 연결 프로그램은 확장자를 기준으로 검색되지만 이 함수는 항상 파일이 존재하여야만 결과를 리턴한다.
2. 경로에 공백이 있어도 않된다.
3. 리턴하는 결과에도 공백이 있으면 잘린다.
한마디로 엉터리에 가까운 API라 말할 수 있다.
이렇듯 이 함수는 규칙없이 긴 이름을 가진 파일을 염두에 두지 않고 설계되었기 때문에 생기는 문제인데..
MS에서 알고 있지만 절대 고치지 않는다..
Windows 이후로 긴 이름의 파일을 지원하지만..
MS Windows에서 시스템 명령어 중에 8.3포멧을 지키지 않는 명령어는 아직까지도 존재하지 않는다.
이 문제를 바로잡기 위한 FindExecutableEx를 만들어보자.
HINSTANCE FindExecutableEx(... 인자는 동일하다 ...)
{
TCHAR drive[_MAX_DRIVE];
TCHAR dir[_MAX_DIR];
TCHAR dir1[_MAX_DIR];
TCHAR file[_MAX_FILE];
TCHAR ext[_MAX_EXT];
HINSTANCE hi = FindExecutable(file, dir, result);
result[lstrlen(result)] = 32;
_splitpath(result, deive, dir, file, ext);
LPTSTR p = strchr(dir, ':');
if(p != NULL)
{
--p;
dir[p-dir] = 0;
_splitpath(dir, NULL, dir1, file, ext);
p = strchr(ext, 32);
ext[p-ext] = 0;
_makepath(result, drive, dir1, file, ext);
}
return hi;
}
문서라고 하는 것은 시스템의 네임스페이스의 일부인 보다 일반적인 객체를 말하고자 하며, 이문서에 대하여 '열기(open)', '인쇄(print)', '탐색(explore)', '찾기(find)'를 하는 프로그램이 있다. 다시 말해서, 문서라는 것은 그것에 대해서 프로그램이 어떤 동사(Verb)를 실행할 수 있는 모든 아이템을 말한다.
지금의 프로그램 실행자의 모체였던 WinExec()에서 ShellExecuteEx()라는 함수로 그 진행이 옮겨가는 이유도 이해 따른다.
이 장에서는 다음과 같은 것들을 다룰 것이다.
1. WinExec()와 CreateProcess() 사이의 차이점
2. ShellEcecute()와 ShellExecuteEx()가 다른 함수보다 우수한점
3. 동사들(Verb), 문서들 그리고 정책들(Policy)
4. 훅킹을 사용하여 프로세스 실행을 내 마음대로
그리고 다음과 같은 몇가지 예제 코드를 살펴볼 것이다.
1. 디폴트 브라우져 감지법
2. 프로그램을 실행시키고 종료를 기다리는법
3. 어떤 파일에 대한 등록정보 대화상자를 나타내는법
4. 찾기 대화상자를 화면에 출력하는 법
5. 사용자가 특정 폴더를 액세스하지 못하게 하거나, 다른 특정 어플리케이션을 실행하지 못하게 막는법
=====================================================
1. WinExec()에서 CreateProcess()로...
UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow);
이 함수는 Windows 3.x에서는 외부 프로그램을 실행시키는 유일한 방법이었다. 그리고 가장 간단한 사용법을 가지기도 한다.
하지만 단점이라 불리울 수 있는 것이 일단 실행을 시켜 놓으면 실행이 되는지 에러가 났는지, 종료 되었는지 전혀 알수가 없다는 것이다.
다음은 CreateProcess()의 프로토 타입이다.
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 실행파일 모듈의 이름에 대한 포인터
LPTSTR lpCommandLine, // 커맨드 문자열에 대한 포인터
LPSECURITY_ATTRIBUTES lpPA, // 프로세스 보안 속성 포인터
LPSECURITY_ATTRIBUTES lpTA, // 스레드 보안속성 포인터
BOOL bInheritHandles, // 핸들 상속 여부 플래그
DWORD dwCreationFlags, // 생성 플래그
LPVOID lpEnvironment, // 환경 블록에 대한 포인터
LPCTSTR lpCurrentDirectory, // 현재 디렉토리
LPSTARTUPINFO lpStartupInfo, // STARTUPINFO 포인터
LPPROCESS_INFORMATION lpPI // PROCESS_INFORMATION 포인터
);
보시다 시피 여기에는 많은 파라미터들이 있다 하지만.. 대부분의 내용이 MSDN에 잘 문서화가 되어 있으므로 그렇게 어렵다거나 하지는 않다.
일단 가장 간단한 호출을 한번 살펴보자.
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
CreateProcess(NULL, szPrgName, NULL, NULL, TRUE, NORMAL_PRIORITY_CLSS, NULL, NULL, &si, &pi);
WinExec보다는 복잡하지만 여러가지 부가적인 정보를 줄 수도 있고 받을 수도 있으므로 상대적인 저비용이다.
만약 위의 프로그램을 실행시키고 종료하기를 기다린다고 하면..
BOOL b = CreateProcess(............);
if(!b)
return FALSE;
WaitForSingleObject(pi.hProcess, INFINITE);
return TRUE;
정말 간단하지 않는가? ^^;
위의 결과를 외견상으로 보면 WinExec의 문제점들의 CreateProcess() 함수로써 모두 해결했다는 생각을 할 수도 있겠지만 현재의 문서라는 개념은 좀더 일반화 되었다. 위에서 사용된 실행가능한 프로그램(exe, com ...etc)등은 문서의 한가지 유형일 뿐이다.
2. ShellExecute()와 ShellExecuteEx()가 다른 함수보다 우수한점
HINSTANCE ShellExecute(
HWND hwnd, // 부모 윈도우 핸들
LPCTSTR lpVerb, // 동사 혹은 작업
LPCTSTR lpFile, // 실행 대상 문서
LPCTSTR lpParameters, // 컴맨드 인자
LPCTSTR lpDirectory, // 현재 디렉토리
INT nShowCmd // 보여주기 옵션
);
일단 위의 함수를 살펴보면 CreateProcess() 함수보다 기능이 많이 떨어지는 것 처럼 보이지만 이 함수의 진정한 의미는 동사 연결이라는 점이다.
보통 우리가 말하는 [연결 프로그램]을 지칭한다.
위에서 [동사 혹은 작업] 부분의 LPCTSTR lpVerb 에 사용될 수 잇는 것은데 대한 나열이다.
===============================================
열기(open) - 프로그램/문서/폴더
탐색(explore) - 폴더
인쇄(print) - 문서
~~로 인쇄(printto) - 문서
찾기(find) - 폴더
------> 먼저 열기(open)에 대하여 살펴보자.
ShellExcute(hwnd, "open", "c:\\prog.exe", "/nologo", "d:\\", SW_SHOW);
ShellExcute(hwnd, "open", "c:\\file.txt", NULL, NULL, SW_SHOW);
만약 txt확장자를 가진 파일에 대한 연결이 존재하지 않으면 우리가 많이 보던 연결프로그램 대화상자가 나타날 것이다.
------> 탐색 작업
탐색 작업은 폴더에 대해서만 가능하고 open 인경우와 약간의 차이를 나타낸다.
open로 동사를 주면 pane이 하나로 된 창이 뜨고, explore로 주면 pane가 2개인 탐색기다 뜬다.
ShellExcute(hwnd, "expolre", "c:\\", NULL, NULL, SW_SHOW);
------> 인쇄 작업
인쇄작업은 문서를 인쇄하기 위한 것이지만, 지정된 문서를 인쇄할 수 있는 명령어 라인과 프로그램을 정확히 알나내기 위해 레지스트리에 저장된 정보에 의존한다.
ShellExcute(hwnd, "print", "c:\\file.txt", NULL, NULL, SW_SHOW);
이 함수는 텍스트 파일을 처리하기 위해 등록된 프로그램을 찾고, 그 프로그램에 인쇄를 위한 명령이 있는지 검사한다. 정상적이라면 이 프로그램은 notepad.exe가 될 것이고, 그 명령어 라인은 다음과 같다.
notepad.exe /p
디폴트 프린터로의 인쇄는 위에서 처럼 간단하게 사용이 가능하지만 만약 여러개의 프린터가 있거나 출력 포트를 설정하고 싶다면 printto를 사용해야 한다.
만약 printto가 문서에서 지원이 된다면 등록된 명령이 실행될것이고 그렇지 못하다면..
경고창이 뜨면서 디폴트 프린터로 출력할 것이지를 묻는다.
------> 찾기 작업
지정된 경로를 루트로 찾기 창이 생성된다.
예제는 생략한다. ^^;
그렇다면 파일을 열기 위해서 혹은 그 파일과 연결된 실행 프로그램은 어떻게 구할 수 있는가?
의외로 쉽게 구할 수 있다. SDK에서 API를 제공하니까 ^^;
HINSTANCE FindExecuteable(
LPCTSTR lpFile, // 알아볼 파일
LPCTSTR lpDir, // 경로
LPTSTR lpResult) // 찾은 결과값
하지만 위 함수에는 치명적인 결함이 있다.
1. 보통 연결 프로그램은 확장자를 기준으로 검색되지만 이 함수는 항상 파일이 존재하여야만 결과를 리턴한다.
2. 경로에 공백이 있어도 않된다.
3. 리턴하는 결과에도 공백이 있으면 잘린다.
한마디로 엉터리에 가까운 API라 말할 수 있다.
이렇듯 이 함수는 규칙없이 긴 이름을 가진 파일을 염두에 두지 않고 설계되었기 때문에 생기는 문제인데..
MS에서 알고 있지만 절대 고치지 않는다..
Windows 이후로 긴 이름의 파일을 지원하지만..
MS Windows에서 시스템 명령어 중에 8.3포멧을 지키지 않는 명령어는 아직까지도 존재하지 않는다.
이 문제를 바로잡기 위한 FindExecutableEx를 만들어보자.
HINSTANCE FindExecutableEx(... 인자는 동일하다 ...)
{
TCHAR drive[_MAX_DRIVE];
TCHAR dir[_MAX_DIR];
TCHAR dir1[_MAX_DIR];
TCHAR file[_MAX_FILE];
TCHAR ext[_MAX_EXT];
HINSTANCE hi = FindExecutable(file, dir, result);
result[lstrlen(result)] = 32;
_splitpath(result, deive, dir, file, ext);
LPTSTR p = strchr(dir, ':');
if(p != NULL)
{
--p;
dir[p-dir] = 0;
_splitpath(dir, NULL, dir1, file, ext);
p = strchr(ext, 32);
ext[p-ext] = 0;
_makepath(result, drive, dir1, file, ext);
}
return hi;
}