Skip to content
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

IPC 메커니즘에 사용되는 각종 Windows리소스를 이용하는 authotkey


IPC 메커니즘에 사용되는 각종 Windows리소스
IPC
내용
메시지(Message)
SendMessage, PostMessage 등을 이용하여 해당 어플리케이션에게 메시지 구조체를 전달하여 메시지 핸들러를 실행시킬 수 있다.
공유메모리(Shared Memory)
공유메모리를 이용하여 데이터를 교환한다.
WM_COPYDATA
메시지와 공유메모리를 합쳐 놓은 형태로서메시지와 데이터 교환이 동시에 가능하다.
클립보드
클립보드에 임시 저장하였다가 전달한다.
Dynamic Data Exchange(DDE)
대화상자 또는 폼에서 컨트롤에서의 동적 데이터 교환 프로토콜
ATOM
시스템의 Atom 전역변수를 이용한 데이터 교환
Mailslots
메일슬롯 오브젝트를 이용한 단 방향 통신과 원격 시스템간의 프로세스 통신도 가능하다우편과 비유된다.
PIPE
MSNP (Microsoft Network Provider) 기반의 파이프 오브젝트를 이용한 단 방향 또는 양방향 통신과 원격 시스템간의 프로세스 통신도 가능하다전화와 비유된다.
RPC (Remote Procedure Call)
IPC의 확장으로 다른 프로세스의 함수 호출에 대한 규약을 담고 있다원격 시스템간의 프로세스 통신도 가능하다.
COM (Component Object Model)
DCOM (Distributed COM)
RPC의 확장으로 프로시저 호출에 관한 인터페이스 규약이다. COM 컴포넌트의 위치이름 등에 관계없이 사용할 수 있도록 정해져 있다.
윈도우 소켓
소켓 오브젝트를 이용한 통신현재 클라이언트/서버 구조에서 가장 많이 쓰이며, TCP/IP, UDP/IP 프로토콜 등을 통해서 통신을 한다.
 

메시지 (PostMessage, SendMessage)

메시지를 이용하여 통신을 하는 방법은 다른 프로세스 (엄밀히 다른 프로세스의 스레드의 메시지 큐에 메시지를 저장하는 방법이다데이터는 wParam, lParam의 각각 4바이트씩 8바이트 값을 전달할 수 있다.
 
IPC
내용
SendMessage
특정 메시지 처리함수(핸들러)를 호출하고종료된 이후에 리턴 한다메시지 처리가 끝날 때까지 블록 된다.
PostMessage
특정 메시지를 메시지 큐에 넣고 바로 리턴 한다.
PostQuitMessage
WM_QUIT 메시지를 메시지 큐에 넣고 바로 리턴 한다.
PostThreadMessage
특정 메시지를 윈도우 핸들 없이 메시지 큐에 넣고 리턴 한다.
SendMessageTimeout
SendMessage에 타임아웃 시간을 추가한 것이다메시지를 받은 스레드가 시간 내에 처리를 못하면 리턴 한다.
SendMessageCallback
PostMessage와 같이 메시지를 보내고 바로 리턴 하지만상대편에서 처리가 모두 끝나면 지정한 콜백함수가 호출된다.
SendNotifyMessage
같은 스레드 내에서 사용할 경우, SendMessage와 동일하지만,다른 스레드에서 생성된 윈도우에게 메시지를 보낼 경우는 블록 되지 않고 바로 리턴 된다.
ReplyMessage
SendMessage를 이용하여 메시지를 받은 스레드에서 이 함수를 호출하면 호출한 스레드는 즉시 리턴 하고자신은 계속 수행이 가능하다.
InSendMessage
현재의 스레드가 SendMessage에 의해 전달된 메시지를 처리하고 있는지를 확인한다.
 
메시지는 Windows 운영체제의 기본 메커니즘이지만명령이나 신호와 같은 적은 양의 데이터를 보내는 용도에는 효과적이지만 일반적으로 사용되는 중대규모의 데이터를 보내기에는 부적절한 IPC이다.
 

메모리맵 파일(Memory Mapping File)

메시지 구조체는 대용량의 데이터를 보낼 수 없지만메모리맵 파일을 이용하면 대용량의 데이터 교환이 가능하다.
 
메모리맵 파일을 사용하는 순서
1.        파일 맵핑 오브젝트 생성 또는 열기(CreateFileMapping, OpenFileMapping)
2.        파일에 대한 뷰 생성  MapViewOfFile
3.        메모리 주소 참조
4.        파일에 대한 뷰 닫기  UnmapViewOfFile
5.        파일 맵핑 오브젝트 반환  CloseHandle
 
메모리맵 파일은 데이터를 쓰고 메시지를 보내고서버가 받기 전에는 또 다른 클라이언트가 데이터를 훼손하면 안되는 개발자로서는 고민해야 하는 부분이 좀 많은 것이 사실이다. Windows 운영체제는 메시지를 보냄과 동시에 데이터 전송까지 한 번의 API 함수 호출로 가능하도록 WM_COPYDATA 라는 메시지를 제공한다.
 

WM_COPYDATA

 
공유 메모리인 메모리맵 파일을 이용하면 많은 데이터를 전송할 수 있었지만메시지도 같이 보내야 했다.이를 보완하기 위한 방법으로 메시지 전달과 데이터 전송을 동시에 할 수 있는 WM_COPYDATA 메시지가 제공된다.
WM_COPYDATA 메시지를 보낼 때에는 wParam에 윈도우 핸들을, lParam COPYDATASTRUCT 구조체에 대한 포인터를 대입하여 다음과 같이 SendMessage로 보내야 한다메시지를 받는 서버측에서 응답하기 위해서는 역시 클라이언트의 윈도우 핸들을 알아야 하기 때문이다.
 
COPYDATASTRUCT copydata;
SendMessage(hWnd, WM_COPYDATA, m_hWnd, &copydata);
 
반드시 SendMessage 또는 SendMessageTimeOut 함수를 써야만 한다. PostMessage와 같이 비동기 방식으로 메시지를 전달하게 되면서버측에서 처리하기도 전에 다른 요청에 의해 데이터가 변형될 수 있기 때문이다.
 
typedef struct tagCOPYDATASTRUCT {
ULONG_PTR          dwData; // 전달될 숫자값보통 커맨드 값을 넘긴다.
DWORD                  cbData; // 전달할 데이터의 크기
PVOID                    lpData; // 전달할 데이터의 메모리 주소(포인터)
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
 
INFO info;
COPYDATASTRUCT copydata;
copydata.dwData = 0;
copydata.cbData = sizeof(info);
copydata.lpData = &info;
SendMessage(hWnd, WM_COPYDATA, m_hWnd, &copydata);
 

PIPE

 
메시지나 공유 메모리 형태는 서로 다른 프로세스간에 메모리 상에서 구조체 또는 데이터를 주고받았다파이프 역시 공유 메모리의 일종으로써 쌍방간의 버퍼를 통해서 데이터를 읽거나 쓸 수 있는 시스템을 말한다.기존의 것과 구별되는 특징은 Windows의 파일 시스템을 이용한다는 것이다이는 Windows 보안체계를 적용할 수 있어 신뢰성 높은 통신이 가능하다는 것과 직렬화된 파일로 데이터를 교환함으로써 네트워크 통신이 가능하다는 장점을 가지게 됨을 의미한다따라서, ReadFile WriteFile을 사용한다.
 
파이프에는 다음과 같이 두 가지로 구분된다.
1.        익명 파이프 (Anonymous Pipe)
2.        네임드 파이프 (Named Pipe)
 
익명 파이프는 네트워크에서는 사용할 수 없으며입출력 파이프가 별도로 존재하고각각의 파이프는 단 방향 통신을 맡는다이러한 특성으로 일반적으로 부모와 자식 프로세스간의 통신에 주로 사용된다.
 
네임드 파이프는 MSNP(Microsoft Network Provider)를 사용하여 TCP/IP, IPX와 같은 복잡한 프로토콜을 사용하지 않고도 비교적 간단하게 네트워크 통신을 구현할 수 있다익명 파이프와는 달리 하나의 파이프로 입력과 출력이 가능한 양방향 통신을 지원한다.
 
이름에서도 알 수 있듯이 익명 파이프는 문자열의 고유 이름이 없으나네임드 파이프는 마치 프로세스간에 커널 오브젝트가 문자열 고유이름으로 식별이 가능한 것과 유사하게 다음과 같은 형식의 파이프의 이름은 네트워크상에서 식별이 가능하다.
 
LPTSTR lpszPipename = \\.\pipe\MyNamedPipe;
 
내용
익명파이프(Anonymous Pipe)
네임드파이프(Named Pipe)
문자열 이름
통신방향
단방향
양방향
확장범위
로컬(Local)
네트워크(Network)
적용범위
부모자식 프로세스간 통신
클라이언트/서버, P2P 구조
데이터 단위
바이트메시지
바이트메시지
 
일반적으로 파이프를 생성한 프로세스를 파이프 서버서버에 접속하는 프로세스를 파이프 클라이언트라고 부른다하나의 프로세스가 데이터를 기록하면다른 프로세스가 그것을 읽는 형태가 된다.
 

네임드 파이프 커널 오브젝트 생성

HANDLE CreateNamedPipe(
LPCTSTR lpName,  // 파이프 이름
DWORD dwOpenMode,        // 파일프 열기모드
DWORD dwPipeMode,         // 파이프 모드
DWORD nMaxInstances,      // 최대 인스턴스 개수
DWORD nOutBufferSize,       // 출력 버퍼 크기
DWORD nInBufferSize,         // 입력 버퍼 크기
DWORD nDefaultTimeOut,    // 타임 아웃 시간
LPSECURITY_ATTRIBUTES lpSecurityAttributes          // 보안속성
);
성공하면 정상적인 파이프 핸들이 리턴 되며,
실패할 경우에는 GetLastError()를 이용하여 자세한 내용을 파악하면 된다.
 

익명 파이프 커널 오브젝트 생성

BOOL CreatePipe(
PHANDLE hReadPipe,          // 입력 파이프 핸들
PHANDLE hWritePipe,          // 출력 파이프 핸들
LPSECURITY_ATTRIUTES lpPipeAttributes,    // 보안 속성
DWORD nSize                      // 파이프 크기
);
 

서버의 네임드 파이프 생성이 완료되면, ConnectNamedPipe() 호출하여 클라이언트 접속을 기다린다소켓의 listen()  유사하다.

 
BOOL ConnectNamedPipe(
HANDLE hNamedPipe,                      // 네임드 파이프 핸들
LPOVERLAPPED lpOverlapped         // OVERLAPPED 구조체에 대한 포인터
);
 
클라이언트의 서버 접속이 완료되면위 함수가 리턴한다이 후, ReadFile WriteFile을 이용하여 서로 데이터를 교환한 후작업이 끝났으면, DisconnectNamedPipe()  CloseHandle() 을 사용하여 생성된 네임드 파이프를 해제하면 통신이 종료된다.
 

네임드 파이프 서버 작성 순서

1.        네임드 파이프 오브젝트를 생성한다 CreateNamedPipe()
2.        네임드 파이프에 클라이언트가 접속할 때까지 리슨한다 ConnectNamedPipe()
3.        네임드 파이프를 이용하여 데이터를 읽거나 쓴다 ReadFile(), WriteFile()
4.        사용이 끝났으면 네임드 파이프 접속을 종료한다 DisconnectNamedPipe()
5.        네임드 파이프 핸들을 반환한다 CloseHandle()
 

파이프를 이용한 IPC 클라이언트 예제

 
1.        네임드 파이프 오브젝트가 사용 가능할 때까지 대기한다 WaitNamedPipe()
2.        네임드 파이프를 이용하여 서버에 접속한다 CreateFile()
3.        네임드 파이프를 이용하여 데이터를 읽거나 쓴다 ReadFile(), WriteFile()
4.        사용이 끝났으면 네임드 파이프 핸들을 반환한다 CloseHandle()
 
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName,           //파이프 이름
DWORD nTimeOut                             // 타임 아웃 시간 설정(1/1000)
);
 
위 함수가 리턴하면 파이프 시스템을 이용해도 좋다는 뜻이다.
이미 생성되어 있는 파이프를 통해서 데이터를 주고받기 위해 CreateFile()을 사용하여 파이프 핸들을 얻어온다.
 
네임드 파이프 서버와 클라이언트 통신
파이프 클라이언트
 
파이프 서버
 
WaitNamedPipe()
←대기−
CreateNamedPipe()
생성
CreateFile()
−접속→
ConnectNamedPipe()
리슨
ReadFile(), WriteFile()
↔입출력↔
ReadFile(), WriteFile()
반환
CloseHandle()
 
DisconnectNamedPipe()
 
 
 
CloseHandle()
 
 
익명 파이프에 대해서는 설명하지 않았는데네임드 파이프가 단방향과 양방향을 모두 지원하기 때문에 사용빈도가 작기 때문이다단방향 전송에는 익명 파이프 외에 메일슬롯이 있다.
 

메일슬롯(Mailslots)

메일슬롯은 단방향 프로세스간 통신에 사용되며, WIN32 기반의 어플리케이션은 메일 슬롯 오브젝트에게 메시지를 전달할 수 있으며메일슬롯을 소유하는 프로세스는 그것을 꺼내어 볼 수 있다익명 파이프와 달리 메일슬롯은 단방향 통신으로서 네트워크 전송이 가능하다.
 
파이프와 마찬가지로 다음과 같이 문자열의 고유 이름을 부여할 수 있다.
\ComputerNamemailslot[path]name                    //특정 시스템의 메일슬롯
\DomainNamemailslot[path]name                        //도메인의 메일슬롯
\*mailslot[path]name                                          //주 도메인의 메일슬롯
Ex) \.mailslottexessues_comments
 
메일슬롯과 파이프는 언뜻 보기에 내용이 비슷하다.
네임드 파이프는 단순히 양방향 통신을 통해서 메시지를 교환하는 반면에,
메일슬롯은 동시에 여러 프로세스에게 메시지를 보낼 수 있다이를 브로드캐스트(Broadcast)라고 한다.
또 다른 차이점은 메일슬롯은 패킷 단위의 데이터그램 방식으로 메시지를 전송하기 때문에 수신자가 메시지를 받는 것에 대한 신뢰성이 없다따라서 이름처럼 우편을 상대편이 받는 것과 관계없이 우체통에 넣는 것으로 끝나는 것과 같다.
이와 반대로 파이프는 연결 지향형으로 데이터 전송에 대하여 신뢰할 수 있다일반적으로 전화에 비유
 

클립보드

클립보드는 Windows 표준 데이터 전송 방식이다.
클립보드는 Windows 세션에 의해 관리되는 시스템 차원에서의 서비스로서핸들이나 클래스 따위는 없고, WIN32 API 또는 CWnd의 멤버함수를 이용해서만 사용 가능하다.
 
API
내용
OpenClipboard()
클립보드를 열면다른 프로세스가 클립보드의 내용을 변형할 수 없다.
CloseClipboard()
클립보드의 사용이 끝났으면 닫는다.
SetClipboardData()
클립보드로 데이터를 기록한다.
GetClipboardData()
클립보드로부터 데이터를 얻어온다.
CountClipboardFormats()
현재 클립보드에 기록된 데이터의 개수를 얻는다.
 
클립보드의 장점은 별도의 작업 없이도 그림음성 등이 지원되는 포맷을 간단히 전송이 가능하다는 것이며,모든 어플리케이션이 클립보드에 쉽게 접근할 수 있다는 점이다클립보드의 발전은 OLE로 이어지며이는COM의 발전으로 이어져 오늘날까지와 있다.
 

DDE

WIN32 가 제공하는 IPC 중에서 대화상자 및 컨트롤에서 이미 소개된 것이 WIN32 동적 데이터 교환(Dynamic Data Exchange)이다. DDE 프로토콜은 메시지와 함수에 대한 규약으로서 메시지와 글로벌 아톰 테이블(Global Atom Table)을 사용하여 메시지와 데이터를 동시에 전달할 수 있다다음은 DDE에 사용되는 메시지들이다.
 
WM_DDE_POKE, WM_DDE_EXECUTE, WM_DDE_DATA,
WM_DDE_ADVISE, WM_DDE_UNADVISE,
WM_DDE_REQUEST, WM_DDE_INITIATE
 

아톰 테이블(Atoms)

아톰 테이블은 시스템에 의해 할당된 변수들의 테이블이다글로벌 테이블과 로컬 테이블이 있으며시스템 내의 모든 어플리케이션은 글로벌 테이블을 이용하여 데이터를 전송할 수 있다.
 

IPC 선택

모든 WIN32 기반의 어플리케이션은 적어도 최소한의 클립보드를 제공받을 수 있으며게다가 COM, DDE등은 프로세스간의 통신을 위한 규약을 제공한다특정 어플리케이션에 대하여 알지 못한다 하더라도 클립보드, COM, DDE 등의 기능은 지원함으로 해서 응용 프로그램이 다른 응용 프로그램과 데이터를 공유할 수 있다그렇다면 이에 대한 답변은 특정 IPC 메커니즘을 사용함으로써 효과를 증진시킬 수 있느냐에 대한 답이 우선일 것이다.
1.        로컬 시스템에서만 사용할 것인지
2.        네트워크 시스템에서 사용할 것인지
3.        교환 데이터의 크기는 어느 정도인지
4.        서로 다른 운영체제에서의 프로세스간 통신인지
5.        실행 속도가 매우 중요한 프로그램간의 통신인지.
6.        프로그램의 인터페이스가 GUI인지 CUI인지.
 
 
IPC는 그 내용의 영역이 너무 넓다.
현재의 프로그래밍에서 통신이라는 영역은 소프트웨어 및 네트워크에서 가장 중요한 요소로 인식되고 있기 때문이다. IPC의 발전 과정을 두 가지로 분류해 보면 다음과 같다.
 
1.        프로시저 호출에 대한 발전 - RPC, COM, DCOM, COM++
2.        데이터 전송에 대한 발전  Socket, XML, SOAP
 
이러한 것들이 프로세스간의 데이터 교환 및 프로시저 호출에 관한 기술로서 IPC라고 볼 수 있을 것이다.
 
 
 
 
 
출처 : Visual C++.Net Programming Bible(삼양출판사)


출처: 데브피아


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;
}

//
1. 디폴트 브라우저 감지하기
void GetDefaultBrowser(LPTSTR szBrowerName)
{
HFILE h = lcreate("dummy.htm", 0);
_lclose(h);

FindExecutable("dummy.htm", NULL, szBrowserName);
DeleteFile("dummy.htm");



2. URL로 연결
ShellExecute(NULL, NULL, "http://lop.com", NULL, NULL, SW_SHOW);

3. e-mail 보내기
ShellExecute(NULL, NULL, "mailto:crowback@a.co.kr", NULL, NULL, SW_SHOW);

4. 문서 인쇄
ShellExecute(NULL, "print", 문서명, NULL, NULL, SW_SHOW);

5. 파일과 폴더 찾기
ShellExecute(NULL, "find", 문서명, NULL, NULL, SW_SHOW);

자자...위에는 간단한 예문을 몇가지 들어본 것이다.
그럼 본격적으로 다음 단계를 진행해보자.

ShellExcute() vs CreateProcess()

여기서의 요점은 어느것이 더 좋은가가 아니라 어느것이 프로세스를 생성할 경우 더 유용한가를 논할 것이다.
고려해야할 첫번째 사항으로 ShellExecute()는 내부적으로 CreateProcess를 호출한다는 것과, 따라서 ShellExecute는 CreateProcess를 위한 더 작고 사용하기 간단한 wrapper이다. 다시 말해서, 문서들을 열고 인쇄하기에는 ShellExecute가 훨씬 더 융통성이 있으며, 문서에 대해 할 수 있는 다른 작업도 마찬가지이다.

ShellExecute()를 사용해서 프로그램을 실행하면 좋은이유

ShellExecute를 사용하는 쪽에 무게를 두는 이유는 MS의 지침이 MS의 새로운 LOGO에 대한 요구사항으로 방대한 안을 내놓았는데, Win98, WinNT 혹은 그이상의 운영체제에 사용되는 MS 어플리케이션 로고를 붙이려면 몇가지 ShellExecute를 사용해서 외부 어플리케이션을 실행시키도록 권장하고 있다. 그렇게 하면 시스템 관리자가 채택한 제한 정책이 모두 확실하고도 주의 깊게 검사될것이기 때문이다. 시스템 관리자는 어플리케이션이 Windows에서 시작될 수 있는지, 또는 없는지를 결정하게 한다. ShellExecute는 이 블랙리스트를 고려한 것이지만 CreateProcess는 그렇지가 않다.

ShellExecuteEx()로의 확장
정책적인 지원에도 불구하고 ShellExecute()는 여러가지 제한점을 많이 가지고 있다. 그 치명적인 단점의 하나가 새로 생성된 프로세스를 반환하지 않는다는 것이다. 다시 말해 프로그램이 실행되어서 종료됫는지, 아직도 실행중인지, 내 프로그램이 방금 실행한 프로그램을 기다릴 수 있는 방법이 없다는 것이다. 하지만 쉘버전 4.0에서 새로운 함수가 소개되었다. 바로 ShellExecuteEx() 이다.
이 함수는 프로토타입처럼 간결하고 많은 플래그를 지원하고 무엇보다도 PIDL을 지원할 수 있도록 ShellExecute()를 확장하여 놓았다는 점이다.

BOOL ShellExecuteEx(LPSHELLEXECUTEINFO lpExexInfo);

LPSHELLEXECUTEINFO 구조는 다음과 같다.
typedef struct _SHELLEXECUTEINFOA
{
DWORD cbSize;
ULONG fMask;
HWND hwnd;
LPCSTR lpVerb;
LPCSTR lpFile;
LPCSTR lpParameters;
LPCSTR lpDirectory;
int nShow;
HINSTANCE hInstApp;
// Optional fields
LPVOID lpIDList;
LPCSTR lpClass;
HKEY hkeyClass;
DWORD dwHotKey;
union {
HANDLE hIcon;
HANDLE hMonitor;
};
HANDLE hProcess;
} SHELLEXECUTEINFO, FAR *LPSHELLEXECUTEINFO;

이 구조를 사용하기 전에 이 구조체를 0으로 체구고 cbSize값에다가 실제 길이를 넣어두면 된다.

SHELLEXECUTEINFO si;
ZeroMemory(&si, sizeof(SHELLEXECUTEINFO));
si.cbSize(sizeof(SHELLEXECUTEINFO));

다음에서 ShellExecuteEx()를 실행하는 가장 간단한 방법을 살펴보자.

SHELLEXECUTEINFO si;
ZeroMemory(&si, sizeof(SHELLEXECUTEINFO));
si.cbSize(sizeof(SHELLEXECUTEINFO));

si.lpFile = __TEXT("explorer.exe");
si.nShow = SW_SHOW;
si.lpVerb = __TEXT("open");
ShellExecuteEx(&si);

다음에서는 부가적인 기능으로 PIDL을 이용하여 호출하는 것을 살펴보자.

LPITEMIDLIST pidl;
SHGetSpecialFolderLocation(NULL, CSIDL_PRINTER, &pidl);

SHELLEXECUTEINFO si;
ZeroMemory(&si, sizeof(SHELLEXECUTEINFO));
si.cbSize(sizeof(SHELLEXECUTEINFO));

si.nShow = SW_SHOW;
si.lpIDList = pidl;
si.fMask = SEE_MASK_INVOKEIDLIST;
si.lpVerb = __TEXT("open");
ShellExecuteEx(&si);

만약 fMask에 SEE_MASK_NOCLOSEPROCESS를 포함하였다면
hProcesss 멤버를 통하여 새로생긴 프로세스의 핸들을 반환 받을 수 있다.

WaitForSingoeObject(si.hProcess, INFINITE);

자 여기까지 기본적인 ShellExccute()와 그 확장 ShellExecuteEx()의 대략적인 용법과 사용예에 대하여 살펴보았다.

여기서 좀더 색다를 점을 강조해 보면 ShellExecuteEx()는 정적인 동사 뿐만 아니라 동적인 동사도 불러낼 수 있다는 점이다. 
이것이 움직이는 방법은 다음과 같다. 만일 ShellEcecuteEx()가 정적 동사 목록에서 찾는 동사를 찾을 수 없으면 주어지 ㄴ파일에 대한 컨텍스트 메뉴를 뒤진다. 이런 탐색 과정은 IContextMenu 인터페이스에 대한 포인터를 낳는다. 그리고나서, 이 인터페이스에 노축될 함수를 통해 동적인 동사를 불러낸다.

그 결과로, 파일의 [등록정보] 대화살자를 쉽게 화면에 출력할 수 있게 된다. 이 대화상자는 파일 위에서 오른쪽 마우스로 등록정보를 클릭한것과 같은 대화상자를 출력한다.

void ShowFileProperties(LPCTSTR szPathName)
{
SHELLECECUTEINFO sei;
ZeroMemory(&sei, sixeof(SHELLECECUTEINFO));
sei.cbSize = sixeof(SHELLECECUTEINFO);

sei.lpFile = szPathName;
sei.nShow = SW_SHOW;
sei.fMask = SEE_MASK_INVOKEIDLIST;
sei.lpVerb = __T("properties");
ShellExecuteEx(&sei);

로그인 후 댓글쓰기가 가능합니다.

?

http://urin79.com

우린친구블로그

sketchbook5, 스케치북5

sketchbook5, 스케치북5

나눔글꼴 설치 안내


이 PC에는 나눔글꼴이 설치되어 있지 않습니다.

이 사이트를 나눔글꼴로 보기 위해서는
나눔글꼴을 설치해야 합니다.

설치 취소