작은숲:위키노트/MFC 프로그래밍
캡션바가 없을 때 윈도우 이동
CWnd의 OnNcHitTest() 함수를 오버라이드하여, 캡션바가 없을 때 클라이언트 영역을 클릭하면 캡션바가 클릭된 것처럼 인식하게 한다. 이렇게 하면 캡션바가 없더라도 클라이언트 영역을 클릭해서 윈도우를 이동시킬 수 있다.
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);참고:
- http://www.bame.pe.kr/oldpf/prj/snowday/sd012.html
- http://kr.blog.yahoo.com/alripica/342011.html?p=1
- How to Open Control Panel Folders from the Command Prompt
- Description of Control Panel (.cpl) Files
- HOWTO: Start a Control Panel Applet in Windows 95 or Later
- INFO: Windows Rundll and Rundll32 Interface
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.h에 WINVER 매크로를 정의해주면 된다.
#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);
}
}참고:
- http://www.codeguru.com/forum/archive/index.php/t-339554.html
- http://www.codeproject.com/csharp/DesktopCaptureWithMouse.asp
- http://openproject.nazzim.net/opencapture.htm
- http://openproject.nazzim.net/zboard/view.php?id=anilove32&no=346
Thread 생성시 어떤 API를 사용해야 하는가?
크게 3가지 방법이란 다음과 같다.
- CreateThread() 를 사용
- _beginthread()/_beginthreadex() 를 사용
- AfxBeginThread() 를 사용
사실상 윈도우 운영체제에서 쓰레드를 생성하는 방법은 하나이다. CreateThread()를 사용하는 방법이 그것이다. 하지만 이렇게 많은 경우의 수가 생긴 이유는 순수한 Windows SDK만으로 프로그램을 작성하는 경우는 거의 없기 때문이다.
대부분의 프로그램은 CRT(C 런타임 라이브러리)를 사용하고 있고 좀 크다 싶은 프로그램은 MFC를 사용하고 있다. 이런 대형 라이브러리들은 훌륭한 체계를 갖추고 있고 그 중에는 쓰레드와 관련된 것도 있으므로 나름대로 쓰레드의 생성과 관련하여 해줘야 될 일들도 많이 있을 것이다.
순수한 Win32를 사용해서 작성되는 프로그램이라면 CreateThread()를 사용해야 할 것이며, CRT를 사용하는 경우라면(대부분의 경우) _beginthread()/_beginthreadex()를 사용해야 할 것이며, MFC를 사용하는 경우라면 AfxBeginThread()를 사용해야 한다.