<다른 프로젝트로부터 리소스 복사>

다른 프로젝트에 있는 리소스를 현재 프로젝트에 복사하고 싶습니다. 리소스를 드래그 앤 드롭이나 임포트로 넣을 수 있는지요.

물론 가능합니다. 다른 프로젝트로부터 리소스르 사용하는 방법에는 리소스를 임포트(Import)하는 방법과 다른 프로젝트의 리소스 스크립트 파일(.rc)에서 직접 리소스를 복사해 오는 두 가지 방법이 있습니다.

 리소스를 임포트하려면 임포트 메뉴에서 'Insert->Resource' 를 선택한 후 'Insert Resource'란 다이얼로그가 나오면 리소스 타입중에 하나를 더블클릭합니다. 그러면 새로운 리소스가 추가됩니다. 기존 프로젝트에서 비트맵(*.bmp, *.dib) 파일, 아이콘 파일(*.ico), 커서 파일(*.cur), 웨이브 파일(*.wav), 비주얼 베이직 폼 형태의 리소스를 복사하려면 'Import' 버튼을 누르면 간단히 추가할 수 있습니다.

 다른 프로젝트의 리소스 스크립트로부터 직접 복사하는 과정은 아주 간단합니다. 우선 'File->Open'을 선택하고, 원본이 되는 리소스 스크립트 파일(*.rc) 을 연후 윈도우 탐색기에서 복사하듯이 드래그 앤 드롭으로 프로젝트 창(Project Workspace)에 옮겨 주면 됩니다. 단 <Ctrl>키를 누르지 않고 옮기면, 이전 프로젝트의 리소스가 사라지는 것에 유의해야 합니다. Resource.h 파일의 ID관리는 디벨롭퍼 스튜디오가 내부적으로 해결해 주므로 신경쓰지 않아도 됩니다.

 

<바탕 화면에 그림 그리기>

바탕화면에 그림을 그리려 합니다. 그리고 일반 윈도우에 그리는 것과의 차이점도 알고 싶습니다.

우선 바탕화면도 하나의 윈도우이므로 바탕화면의 윈도우 핸들을 알아야 합니다. 즉 일반윈도우에 그리는 것과 전혀 다를게 없습니다 .바탕 호면에 직접 그림을 그리게 되면 화면에 떠 있는 모든 윈도우에 겹쳐져서 그려지므로 바탕 화면또는 일반 윈도우에만 그리고 싶다면 별도의 작업이 필요합니다.

 여기에서는 화면 왼쪽 위부터 빗금이 그어지는 예로 필요한 부분만 보이기 위해 윈도우를 만들지 않았습니다.

#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpszArgs, int nWinMode)
{
        HWND hwnd = GetDesktopWindow();
        // 바탕화면의 윈도우 핸들 얻기
        HDC hdc = GetWindowDC(hwnd);
        // 바탕화면의 DC 핸들 얻기
        // GetWindowDC를 쓴다.
        
        HPEN hPen;
        register int i;
        for(i=0; i<=255; i++) {
                hPen = CreatePen(PS_SOLID, 0, RGB(i, i, i));
                // 원하는 색으로 펜 만들기
                HGDIOBJ hOldPen = SelectObject(hdc, hPen);
                // 펜을 현재 DC에서 선택
                // 펜 핸들 보관
                MoveToEx(hdc, i*3, 0, NULL);
                LineTo(hdc, 0, i*3);
                // 선택된 펜으로 선을 긋는다.
                SelectObject(hdc, hOldPen);
                // 원래대로 펜 돌려놓기
                DeleteObject(hPen);
                // 필요없는 펜 제거
        }
        ReleaseDC(hwnd, hdc);
        // DC를 반납하고 그리기 작업을 마친다.
}

 

<캐럿의 위치를 알려면>

윈도우 메모장에서 현재 캐럿으로부터 블럭을 설정한 후 클립보드로 복사할 수 있게 캐럿위치를 구하는 함수를 알고 싶습니다.

GetSel()은 현재 에디터 내에서 캐럿의 위치(열과 행 또는 인덱스)를 구하는 함수로 에디터에 선택된 부분이 없더라도 이 함수를 쓸 수 있습니다. 이 경우는 선택 부분의 시작 위치와 끝위치가 같으므로 블럭을 설정하면 현재 캐럿의 위치를 인덱스로 삼아 라인 위치로 바꿔 줍니다. 이 경우 사용하는 함수가 LineFromChar()입니다. 그리고 몇 번째 컬럼에 있는가는 캐럿위치에서 LineIndex() 로 얻은 값을 빼는 식으로 두 함수를 조합하면 알아낼 수 있습니다.

 

<OnPrint()로 프린트 기능을>

화면에 그려진 사각형을 특정 버튼을 눌러 프린트로 출력하고 싶습니다.

사각형을 출력하는 정도라면 애플리케이션 위저드가 생성해 주는 프린트 소스로 충분합니다. 처음 애플리케이션을 만들 때 프린트 기능을 추가하면 CView에 OnPrint 함수가 생깁니다. 이 함수는 DC를 OnDraw() 로 넘겨주는데, 여기서 OnDraw() 는 주어진 DC를 화면이라 생각하고 그리게 됩니다. 그리고 버튼을 누르면 발생하는 버튼 클릭 이벤트에 SendMessage(WM_COMMAND, ID_FILE_PRINT); 또는 SendMessage(WM_COMMAND, ID_FILE_PRINT_PREVIEW);를 덧붙이면 파일에서 프린트로 보낼 수 있습니다.

 참고로 여러 페이지의 도큐먼트라든가 혹은 큰 화면을 여러 장으로 나눠 찍는 복잡한 프린트를 원한다면 OnPrint() 의 도움말을 참조하기 바랍니다.

 

<다른 애플리케이션 제어 방법>

비주얼 C++/MFC 4.0 으로 작성한 프로그램을 제가 짠 프로그램에서 실행하고, 필요에 따라서는 제어하고 싶습니다. 말하자면 IPC(InterProcess Communication)가 되도록 하는 거죠. 마치 윈도우 매니저가 특정 프로그램을 죽일 수 있는 것처럼 제 프로그램에서 다른 프로그램을 손쉽게 제어하고 싶습니다.

CreateProcess 도움말을 보면 알겠지만 세부 사항을 조목조목 지정해 프로세스를 시작할 수 있으므로 다른 애플리케이션을 제어하는데 문제가 없을 것입니다. 물론 제어할 필요가 없는 항목은 디폴트로 주면 됩니다.

 참고로 이 함수의 10번째 인수인 lpProcessInformation에는 이 프로세스의 시작 정보가 들어있는데, 여기에 시작 프로세스 핸들과 메인 쓰레드 핸들이 담겨져 리턴됩니다. 참고로 다음 SuspendThread()와 ResumeThread()를 사용하면 메인 쓰레드를 잠깐 멈출 수도 있고, TerminateThread()로 프로세스의 모든 쓰레드를 종료하는 등 다른 애플리케이션을 쉽게 제어할 수 있습니다.

 

<컨트롤을 사용할 수 없게 처리하려면>

폼뷰에 있는 라디오 버튼을 선택할 경우 화면의 다른 컨트롤 중 일부를 완전히 그레이(gray)로 처리하고 싶습니다. 즉 에디트 컨트롤 입력이나 라디오 버튼을 체크하지 못하게 하는 식으로 컨트롤에 접근할 수 없도록 하고자 합니다.

어떤 라디오 버튼을 선택했을 경우에 특정 컨트롤을 쓸 수 없게 만드는 일은 생각보다 쉽습니다. DDX(Dialog Data eXchange) 메카니즘을 이용해서 컨트롤과 맵핑되는 변수를 이용하거나 혹은 GetDlgItem()를 사용해 컨트롤의 포인터를 얻은 후 멤버 함수 EnableWindow(BOOL)로 해결하면 됩니다.

 

<컨트롤 크기를 뷰에 맞추기>

윈도우 95의 '찾기'처럼 폼뷰를 만들었는데 윈도우 크기를 변경할 경우 함께 내부의 컨트롤 크기도 변하게 하고 싶습니다.

만약 윈도우 95에서만 쓴다면 WM_SIZE 에서 처리해주면 되지만, 플러스팩을 설치했다거나 윈도우 NT 4.0에서 사용하는 경우는 곤란합니다. 왜냐 하면 윈도우 크기를 조절할 때 윈도우는 동적으로 계속 그려지므로 WM_SIZE에서 처리해 주면 크기조절이 끝나고 나서야 컨트롤을 다시 그리므로 좋은 방법이라 할 수 없죠. 그러므로 컨트롤을 다시 그려주는 부분이 필요합니다.

 한 번은 꼭 호출되는 OnInitDialog()에서 바로 처리하는 것보다 따로 분리해서 작성한 후 호출하는 것이 좋습니다. 우선 필요한 컨트롤은 모두 DDX를 이용해 컨트롤 변수로 만들어 줍니다 (API에서는 GetDlgItem()등의 함수 이용).

void ResizeControlToParent(void) {
     CRect rect;
     GetClientRect(&rect);
     m_List1.MoveWindow(rect.left+10, rect.top+10,
          (rect.right-rect.left)/2, (rect.bottom-rect.top)/3, TRUE);

     m_Edit1.MoveWindow(...........................)
     ...
     return;
}

이렇게 만든 함수를 OnInitDialog()와 OnSize() 또는 OnSizing()에서 불러 주면 되는 거죠.

참고로 윈도우가 최소화될 경우만 유념하면 됩니다. 가령 윈도우 크기를 얻어서 컨트롤을 그릴 때 'rect.right-50' 식으로 '50'을 정수 처리하면 최소화될 때 값이 음수값이 되므로 'Assertion Failure' 또는 '이 프로그램이 잘못된 연산을 수행하여...'라는 메시지를 만나게 됩니다.

 

<응용 프로그램이 실행된 디렉토리를 찾으려면>

비주얼 C++로 프로그래밍을 하던 중 응용 프로그램이 실행된 경로를 알아야 할 필요가 생겼습니다.

프레임웍은 응용 프로그램의 초기화 과정에서 GetModuleFilesName을 호출하고 도움말 파일에 대한 경로를 CWinApp::m_pszHelpFilePath에 저장합니다.

 응용 프로그램이 실행된 디렉토리를 찾기 위해 이 데이터 멤버를 사용하는 방법은 다음과 같습니다. 여기서 _splitpath가 실행루틴이며, _MAX_DIR과 _MAX_DRIVE는 헤더파일인 STDLIB.H에 정의돼 있습니다.

void GetLaunchDir(CString& strLaunched)
{
    TCHAR szDir[_MAX_DIR];
    TCHAR szDrive[_MAX_DRIVE];
    _splitpath(AfxGetApp()->m_pszHelpFilePath,
            szDrive, szDir, NULL, NULL);
    strLaunched.Format(_T("%s%s"), szDrive, szDir);
}

 

- the end of this article -