프로그래밍/C/C++2013. 8. 13. 14:13

내가 만드는 프로그램을 항상 하나만 실행하도록 하려면 어떻게 해야 하나 ? 그리고 두 번째 실행시에는 먼저 실행되어 있던 프로그램을 앞으로 띄우고 싶다.

만일 프로그램의 캡션이 고정되어 있다든가 사용되는 윈도우의 클래스 이름을 알고 있다면 FindWindow 혹은 FindWindowEx를 사용하면 아주 손쉽게 문제를 해결할 수 있다. 하지만 윈도우의 캡션이 계속적으로 변한다든지 윈도우 클래스 이름을 모르는 경우에는 이것으로 해결책이 되지 못한다. MFC를 사용할 경우가 대표적인데 윈도우 클래스의 이름을 MFC 내부에서 만들어 사용하기 때문에 윈도우 클래스를 직접 등록하여 사용하는 고난도의 기술을 사용하기 이전에는 윈도우 클래스의 이름을 알 수가 없다. 해결책에는 여러 가지 방법이 있겠지만 파일 맵핑 객체를 이용하도록 하겠다. 원래 파일 맵핑 객체의 목적은 여러 프로세스간에 데이터를 주고 받기 위해 사용되며 사용 방식 자체는 기존의 동적 메모리 할당 방식과 흡사하다.
간단히 절차를 설명하면 다음과 같다. 처음 뜨는 프로그램이 정해진 이름의 파일 맵핑 객체(여기서는 이름은 My TestMap)를 만들어 놓는다. 그리고 나서 자신의 메인 윈도우의 핸들을 그 객체에 기록해 놓는다. 이 때 다시 그 프로그램을 실행시키면 이 이름의 파일 맵핑 객체가 있는지 살펴보고 있으면 실행을 중지한다. 그리고 파일 맵핑 객체에 기록되어 있는 윈도우의 핸들값을 읽어서 앞으로 띄워 버린다. 이것이 가능한 이유는 파일 맵핑 객체는 시스템 전역 객체이기 때문이다. 즉 한 프로세스에서 만들어도 다른 프로세스에서 접근할 수 있다. 먼저 항상 하나만 떠 있게 하는 방법을 알아보고 다음으로 기존의 프로그램을 앞으로 띄우는 코드를 알아보도록 하겠다. 사실 아래의 코드를 이해하려면 파일 맵핑 객체에 대해 알아야 하는데 그것 자체에 대한 설명은 생략하기로 하겠다.

항상 하나만 떠 있게 하는 방법
다음 코드를 애플리케이션 클래스의 InitInstance의 선두에 넣는다. 파일 맵핑 객체의 크기를 4바이트로 잡은 이유는 윈도우 핸들이 UINT이고 그 크기가 4바이트이기 때문이다. CreateFileMapping API는 파일 맵핑 객체를 생성할 때 사용되는데, 이름과 크기를 줄 수 있다. 이미 존재하는 이름으로 파일 맵핑 객체를 생성하려고 하면 GetLast Error라는 API를 호출했을 때 ERROR_ALREADY_ EXISTS라는 에러 코드가 리턴된다. 이것으로 먼저 실행된 프로그램이 있는지 알아낼 수 있다.

HANDLE hMapping;

// MyTestMap이란 이름으로 4바이트의 영역을 잡는다
hMapping = CreateFileMapping( (HANDLE) 0xffffffff, NULL,
PAGE_READWRITE, 0, 4, “MyTestMap” );
if (hMapping)
{
if (GetLastError() == ERROR_ALREADY_EXISTS)
// 같은 이름의 파일 맵핑 객체가 존재
{
// 프로그램이 이미 실행중임을 알리는 메시지를 띄운다.
MessageBox( NULL, “이미 실행 중입니다.”, “경고”, MB_OK );
return FALSE;
}
}
위의 코드는 하나의 실행파일을 실행할 수 있는 방법이지만 기존에 실행되어 있는 프로그램을 앞으로 띄우는 일까지 할 수는 없다. 그렇게 하고 싶다면 InitInstance의 코드의 끝 부분에 메인 윈도우의 핸들을 파일 맵핑 객체에 저장하는 코드를 추가한다. 아래의 예는 SDI(Single Document Interface) 혹은 MDI(Multiple Document Interface)형식의 프로젝트인 경우의 예이다. 만일 다이얼로그 기반의 프로젝트라면 아래의 코드는 메인 다이얼로그 클래스의 WM_CREATE 메시지나 OnInitDialog 함수에 추가되어야 한다. CreateFileMapping으로 생성된 파일 맵핑 객체는 MapViewOfFile이란 API를 통해 메모리 포인터로 변환할 수 있다. 즉 할당된 파일 맵핑 객체를 접근할 수 있는 메모리 포인터를 사용해 내용을 읽고 쓰고 할 수 있다.

LPDWORD lpDword;

// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// ---------------------------------
// 이번에는 파일 맵핑 객체를 쓰기 모드로 오픈한다.
lpDword=(LPDWORD)MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 4);
// 메인 윈도우의 핸들을 저장한다.
*lpDword = (DWORD)m_pMainWnd->m_hWnd;
UnmapViewOfFile(lpDword);
// ---------------------------------
m_pMainWnd->ShowWindow(SW_SHOW);
그러면 앞서 살펴보았던 두 번째로 뜨는 프로그램인지 체크하는 코드 부분을 보강해 보자. 두 번째 뜨는 프로그램일 경우에 원래 먼저 떴던 프로그램의 메인 윈도우를 앞으로 내세우는 코드를 추가해 보자.

hMapping = CreateFileMapping( (HANDLE) 0xffffffff, NULL,
PAGE_READWRITE, 0, 32, “MyTestMap” );
if (hMapping)
{
if (GetLastError() == ERROR_ALREADY_EXISTS)
// 같은 이름의 파일 맵핑 객체가 존재
{
// 프로그램이 이미 실행 중임을 알리는 메시지를 띄운다.
MessageBox( NULL, “이미 실행 중입니다.”, “경고”, MB_OK );

// 기존 파일 맵핑 객체를 연다. 기존 파일 맵핑 객체의 선두에 이전 프로그램
// 의 메인 윈도우 핸들이 들어있다. 뒷부분의 코드를 보면 알 것이다.
LPDWORD lpDword;
HWND hWnd;
// 파일 맵핑 객체를 읽기 모드로 오픈한다.
hMapping = OpenFileMapping(FILE_MAP_READ, FALSE,
“MyTestMap”);
// 파일 맵핑 객체 핸들로부터 포인터를 얻어낸다.
lpDword = (LPDWORD)MapViewOfFile(hMapping,
FILE_MAP_READ, 0, 0, 4);
// 저장되어 있던 윈도우 핸들을 얻어낸다.
hWnd = (HWND)*lpDword;

// 윈도우를 전면으로 내세우기 전에 먼저 아이콘 상태에 있을지도 모르는
// 윈도우를 원래 크기로 되돌린다. IsIconic 함수를 호출하여 아이콘 상태
// 여부를 확인해도 될 것이다.
ShowWindow(hWnd, SW_SHOW);
// 윈도우를 전면으로 내세운다.
SetForegroundWindow(hWnd);
// 파일 맵핑 객체를 닫는다.
UnmapViewOfFile(lpDword);
return FALSE;
}
위의 코드에서 윈도우를 전면으로 내세우는데 SetFore groundWindow라는 함수를 사용한다는 것을 기억해 두기 바란다. 이 함수의 인자로는 반드시 메인 윈도우의 핸들을 지정해야 한다. 지금까지 살펴보았던 코드를 다시 전체적으로 정리하면 다음과 같다.

HANDLE hMapping;

hMapping = CreateFileMapping( (HANDLE) 0xffffffff, NULL,
PAGE_READWRITE, 0, 32, “MyTestMap” );
if (hMapping)
{
if (GetLastError() == ERROR_ALREADY_EXISTS)
// 같은 이름의 파일 맵핑 객체가 존재
{
// 프로그램이 이미 실행 중임을 알리는 메시지를 띄운다.
MessageBox( NULL, “이미 실행 중입니다.”, “경고”, MB_OK );

// 기존 파일 맵핑 객체를 연다. 기존 파일 맵핑 객체의 선두에 이전 프로그램의
// 메인 윈도우 핸들이 들어있다. 뒷부분의 코드를 보면 알 것이다.
LPDWORD lpDword;
HWND hWnd;
// 파일 맵핑 객체를 읽기 모드로 오픈한다.
hMapping = OpenFileMapping(FILE_MAP_READ, FALSE,
“MyTestMap”);
// 파일 맵핑 객체 핸들로부터 포인터를 얻어낸다.
lpDword = (LPDWORD)MapViewOfFile(hMapping,
FILE_MAP_READ, 0, 0, 4);
// 저장되어 있던 윈도우 핸들을 얻어낸다.
hWnd = (HWND)*lpDword;

// 윈도우를 전면으로 내세우기 전에 먼저 아이콘 상태에 있을지도 모르는
// 윈도우를 원래 크기로 되돌린다. IsIconic 함수를 호출하여 아이콘
// 상태 여부를 확인해도 될 것이다.
ShowWindow(hWnd, SW_SHOW);
// 윈도우를 전면으로 내세운다.
SetForegroundWindow(hWnd);
// 파일 맵핑 객체를 닫는다.
UnmapViewOfFile(lpDword);

return FALSE;
}
// 이 뒤부터 ------ 까지는 원래 InitInstance 코드
.....
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// --------------
// 다음을 추가한다.
LPDWORD lpDword;

lpDword = (LPDWORD)MapViewOfFile(hMapping, FILE_MAP_WRITE,
0, 0, 4);
*lpDword = (DWORD)m_pMainWnd->m_hWnd;
UnmapViewOfFile(lpDword);
// 이 뒤로는 다시 원래 InitInstance의 코드
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();



출처 : www.codeland.co.kr

[펌]http://snslab.kangwon.ac.kr/home/?bo_table=openclass&doc=bbs/gnuboard.php&wr_id=132

Posted by 컴투