본문으로 이동

미디어위키 1.45 안정화가 거의 끝났습니다. 다만 Flow 확장 기능 관련 이슈가 있어서 대체하는 작업을 수행할 계획입니다.

  1. 큰숲백과:청사진에서 위키 발전의 대략적인 방향성을 제시했습니다. 의견이 있으신 분은 큰숲백과토론:청사진에서 의견을 남겨주시면 좋겠습니다.
  2. 기능상의 오류로 지원하지 않고 있는 기능에 대해서는 큰숲백과토론:이슈 트래커에 요약했습니다. 참고하시기 바랍니다.
  3. 데이터베이스 덤프 받고싶으신 분은 큰숲백과 가입 후에 사용자토론:Bigforest에 의견 남겨주시면 ftp 주소, 계정, 비밀번호를 특수:EmailUser를 통해서 공개할 예정입니다.

작은숲:위키노트/MFC 프로그래밍

큰숲백과, 나무를 보지 말고 큰 숲을 보라.

캡션바가 없을 때 윈도우 이동

CWndOnNcHitTest() 함수를 오버라이드하여, 캡션바가 없을 때 클라이언트 영역을 클릭하면 캡션바가 클릭된 것처럼 인식하게 한다. 이렇게 하면 캡션바가 없더라도 클라이언트 영역을 클릭해서 윈도우를 이동시킬 수 있다.

UINT CTestDlg::OnNcHitTest(CPoint point)
{    UINT nHitTest = CDialog::OnNcHitTest(point);
    return (nHitTest == HTCLIENT) ? HTCAPTION : nHitTest;
}

클라이언트 영역을 클릭해서 윈도우 이동

OnLButtonDown() 함수를 오버라이드해서 클라이언트 영역이 클릭되더라도 캡션바가 클릭된 것처럼 다이얼로그에 메시지를 보낸다.

void CTestDlg::OnLButtonDown(UINT nFlags, CPoint point)
{    CDialog::OnLButtonDown(nFlags, point);
    PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
}

투명한 Bitmap 그리기

우선 Bitmap을 Resource에 IDB_BITMAP 이름으로 추가하고, 아래 함수를 만든다. 이때 Bitmap에서 투명하게 보일 부분은 흰색(RGB(255, 255, 255))이다.

void CTestDlg::DrawBitmap(void)
{    CPaintDC dc(this);
    CDC MemDc;
    MemDc.CreateCompatibleDC(&dc);
    CBitmap hBitmap;
    hBitmap.LoadBitmap(IDB_BITMAP);
    CBitmap *pOldBitmap = (CBitmap *) MemDc.SelectObject(&hBitmap);
    BITMAP tBitmap;
    hBitmap.GetBitmap(&tBitmap);
    dc.TransparentBlt(0, 0, tBitmap.bmWidth, tBitmap.bmHeight, &MemDc,
        0, 0, tBitmap.bmWidth, tBitmap.bmHeight, RGB(255, 255, 255));
    MemDc.SelectObject(pOldBitmap);
}

투명한 다이얼로그 창 만들기

먼저 창 스타일에 WS_EX_TRANSPARENT 스타일을 추가한다. 그런 다음 OnEraseBkgnd() 함수를 아래와 같이 오버라이드해준다.

BOOL CKeyViewerDlg::OnInitDialog()
{    ...
    ModifyStyleEx(0, WS_EX_TRANSPARENT);
    return TRUE;
}...
BOOL CKeyViewerDlg::OnEraseBkgnd(CDC* pDC)
{    //return CDialog::OnEraseBkgnd(pDC);
    return TRUE;
}

폴더 선택창 띄우기

BROWSEINFO bi;
LPITEMIDLIST pidl;
TCHAR szBuff[MAX_PATH] = {0}, szDisp[MAX_PATH] = {0}, szPath[MAX_PATH] = {0};
lstrcpy(szBuff, "Select a target folder that driver files will be copied");
::ZeroMemory( &bi, sizeof(BROWSEINFO) );
bi.hwndOwner = this->GetSafeHwnd();
bi.pidlRoot = NULL;
bi.lpszTitle = static_cast<LPSTR>(szBuff);
bi.pszDisplayName = static_cast<LPSTR>(szDisp);
// BIF_USENEWUI 는 '새 폴더 만들기' 기능을 위한 플래그.
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
bi.lParam = (LPARAM) this;
if ( (pidl = SHBrowseForFolder(&bi)) )
{    SHGetPathFromIDList(pidl, szPath);
    FileListFromDisk(szPath);
}else
    return;
ZeroMemory(szPath, MAX_PATH);
SHGetPathFromIDList(pidl, szPath);

파일 선택창 띄우기

// OFN_ALLOWMULTISELECT는 멀티 셀렉트시 반드시 필요한 항목이다.
CFileDialog dlg(true, "opendlg", NULL, OFN_FILEMUSTEXIST | 
    OFN_ALLOWMULTISELECT, "Text File (*.txt) | *.txt|", NULL);  
// dlg.m_ofn.lpstrInitialDir = (LPSTR) ("C:\\Temp\\Text");
// 파일열기를 시도할 기본 폴더 지정하기
char buffer[4096] = {0}; //버퍼
dlg.m_ofn.lpstrFile = buffer; //버퍼 포인터
// 이 부분을 쓰지 않으면 선택하는 파일 갯수에 제한이 있다.
dlg.m_ofn.nMaxFile = 4096; //버퍼 크기
if (dlg.DoModal() != IDOK) return;
        
POSITION pos;
CString temp;
pos = dlg.GetStartPosition();
m_arr_directoryOdfAllFile.RemoveAll();
int i = 0;
while (true)
{    if (pos == NULL)
        break;
    temp = dlg.GetNextPathName(pos);
    m_arr_directoryOdfAllFile.InsertAt(i,temp);
    i++;
    // path는 빼고 파일명만 얻기 위해
}m_totalFileNumber.Format("%d",m_arr_directoryOdfAllFile.GetSize());
UpdateData(FALSE);

MFC 프로그래밍에서 자주 사용되는 변수 접두어

접두어 원래말 의미
cb Count of Bytes 바이트 수
dw double word 부호없는 long형 정수
h handle 윈도우, 비트맵, 파일 등의 핸들
sz Null Terminated NULL 종료 문자열
w Word 부호없는 정수형
i Integer 정수형
b Bool 논리형

제어판 항목 실행시키기

아래는 제어판의 화면 설정 창의 띄우기 위한 코드이다.

//char szWinDir[MAX_PATH];
char szSystemDir[MAX_PATH];
char szCmd[MAX_PATH];
// 윈도우즈가 설치된 경로를 얻는다.
//::GetWindowsDirectory(szWinDir, MAX_PATH);
::GetSystemDirectory(szSystemDir, MAX_PATH);
// 디스플레이 설정 창을 띄우기 위한 명령행 문자열을 조합한다.
//wsprintf(szCmd, "%s\\system32\\control.exe desk.cpl,,1", szWinDir);
wsprintf(szCmd, "%s\\control.exe desk.cpl,,1", szSystemDir);
// 명령행을 실행한다.
WinExec(szCmd, SW_SHOW);

참고:

MSN처럼 트레이 아이콘화 시키기 (트레이창으로 이동하는 모습)

메인 프로그램의 종료 버튼을 눌렀을때 MSN처럼 트레이 윈도우로 내려가는 모습이 보이도록 해주는 소스.

static void MoveToTray(HWND hWnd)
{    RECT rectFrom;
    RECT rectTo;
    ::GetWindowRect(hWnd, &rectFrom);
    
    // 윈도우 작업 표시줄 영역을 제외한 윈도우 전체 영역을 얻은 후, 짐작스레 트레이 창의 영역 구함.
    // 이 방법 말고도 트레이창의 영역을 구하는 방법이 있으면 그것을 사용하면 됨.
    ::SystemParametersInfo (SPI_GETWORKAREA, 0, &rectTo, 0);    
    rectTo.left = rectTo.right  - 70;
    rectTo.top  = rectTo.bottom - 10;
    
    // 트레이아이콘 방향으로 최소화 시키는 효과 내기 위해!
    ::DrawAnimatedRects(hWnd, IDANI_CAPTION, &rectFrom, &rectTo);
    
    ::ShowWindow(hWnd, SW_HIDE);
}void CSampleDlg::OnSysCommand(UINT nID, LPARAM lParam)
{    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else if(nID == SC_CLOSE) 
    {
        MoveToTray(GetSafeHwnd());
        return;
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }
}

참고: http://www.devpia.com/Forum/BoardView.aspx?no=6896&forumname=vc_lec

IME 설정창 띄우기

char temp[100];
// 현재 키보드 레이아웃의 이름을 얻어온다.
GetKeyboardLayoutName(temp);
// 키보드 레이아웃의 핸들을 얻는다.
HKL hKl = LoadKeyboardLayout(temp, KLF_ACTIVATE);
// IME 설정창을 띄운다.
ImmConfigureIME(hKl, ((CDialog*)pTemp)->m_hWnd, IME_CONFIG_GENERAL, NULL);

참고: http://www.devpia.com/Forum/BoardView.aspx?no=6745&forumname=vc_lec

Dialog Based 프로그램에서 가속키 기능 넣기

SDI나 MDI의 경우 가속키 기능이 기본적으로 들어가 있지만, Dialog Based 프로젝트를 만들면 가속키 기능이 들어있지 않다. 이런 경우 가속키를 리소스로부터 읽어서 가속키 테이블을 프로그램에 설정해준 다음, PreTranslateMessage()를 재정의한 후 기본 메세지가 처리되기 전에 미리 가속키 Translator에 메세지 처리를 한번 수행하기만 하면 된다.

// in SamlpeDlg.h
class CSampleDlg : public CDialog
{    ...
protected:
    HACCEL m_hAccelTable;
    ...
}// in SampleDlg.cpp
BOOL CSampleDlg::OnInitDialog()
{    ...
    m_hAccelTable
        = ::LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
    ...
}BOOL CSampleDlg::PreTranslateMessage(MSG* pMsg)
{    if (m_hAccelTable != NULL)
    {
        if (TranslateAccelerator(m_hWnd, m_hAccelTable, pMsg))
            return TRUE;
    }
    return CDialog::PreTranslateMessage(pMsg);
}

IDC_HAND을 인식하지 못할 때

커서 타입 중 IDC_HAND를 인식하지 못하는 경우가 생길 수가 있다. 이것은 아래와 같이 정의되어 있기 때문이다.

#if(WINVER >= 0x0500)
#define IDC_HAND MAKEINTRESOURCE(32649)
#endif /* WINVER >= 0x0500 */

따라서, 아래와 같이 stdafx.hWINVER 매크로를 정의해주면 된다.

#define WINVER 0x0500

마우스 커서 캡처하기

hdcScreen = GetDC(hwndScreen);
hdcCapt = CreateCompatibleDC(hdcScreen);
hcursor = GetCursor();
DrawIconEx(hdcCapt, CursorPos.x, CursorPos.y, hcursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);

다른 Application의 마우스 커서 캡처하기

GetCursor() returns a handle to the last cursor that appeared above the calling thread. So, if somebody clicks something in your program that causes it to go into a wait state for a second, then the mouse is moved outside of your window, the last "retained" cursor is the hourglass. Often, it's the window sizing cursor since that's the last cursor you see when you drag your mouse outside the frame of a window. To get around this, you need to use AttachThreadInput() to get the thread ID that currently owns the cursor (if it's not your app) and then use GetCursor().

// Get the window that the cursor is currently over...
hCursor = WindowFromPoint(ptCursor);
// Get the thread ID of the application that currently owns it...
dwCursorId = GetWindowThreadProcessId(hCursor, NULL);
// Get my thread ID...
dwCurrentId = GetCurrentThreadId();
// If they're not the same (some other app owns the cursor), then attach its input to ours...
if (dwCursorId != dwCurrentId)
{    if (AttachThreadInput(dwCurrentId, dwCursorId, TRUE))
    {
        // Now, we can get the actual cursor...
        hCursor = GetCursor();
        // Make sure to detach when we're through...
        AttachThreadInput(dwCurrentId, dwCursorId, FALSE);
    }
}

참고:

Thread 생성시 어떤 API를 사용해야 하는가?

크게 3가지 방법이란 다음과 같다.

  1. CreateThread() 를 사용
  2. _beginthread()/_beginthreadex() 를 사용
  3. AfxBeginThread() 를 사용

사실상 윈도우 운영체제에서 쓰레드를 생성하는 방법은 하나이다. CreateThread()를 사용하는 방법이 그것이다. 하지만 이렇게 많은 경우의 수가 생긴 이유는 순수한 Windows SDK만으로 프로그램을 작성하는 경우는 거의 없기 때문이다. 대부분의 프로그램은 CRT(C 런타임 라이브러리)를 사용하고 있고 좀 크다 싶은 프로그램은 MFC를 사용하고 있다. 이런 대형 라이브러리들은 훌륭한 체계를 갖추고 있고 그 중에는 쓰레드와 관련된 것도 있으므로 나름대로 쓰레드의 생성과 관련하여 해줘야 될 일들도 많이 있을 것이다. 순수한 Win32를 사용해서 작성되는 프로그램이라면 CreateThread()를 사용해야 할 것이며, CRT를 사용하는 경우라면(대부분의 경우) _beginthread()/_beginthreadex()를 사용해야 할 것이며, MFC를 사용하는 경우라면 AfxBeginThread()를 사용해야 한다.

같이 보기

참고 자료