<윈도우 접기>

  다이얼로그 베이스에서 보여줘야 할 항목이 많을 때, 약간 지저분해 보이는 경향이 있는데, 단추를 하나 달아서, 안쓰는 항목은 접어 놓고 보면, 상당히 프로그램을 깔끔하게 할 수 있더군요....

 그전에 하셔야 할 작업은 다음과 같습니다.

1) 잘 안쓰이는 항목들은 Static Box를 하나 생성해서 모아 놓는다.

2) StaticBox의 이름을 ID_STATIC이 아닌 다른 이름을 붙인다.(여기서는 ID_STATIC_SEP라는 이름을 붙였습니다. )

3) 그 항목을 보여주거나, 안보여주는 기능을 추가하기 위해서 코맨드 버튼이나 메뉴항목을 추가한다.( 여기서는 IDC_BUTTON_TEST라는 이름입니다. )

 간단하죠..?? 이렇게 하시고 난 후에 윈도우에 다음과 같은 함수를 추가하시면 됩니다.

 코드의 내용은 아까 만든 차일드 윈도우의 영역을 기억해서, 버튼을 한 번 누르면 원래의 윈도우의 모습을, 두번 누르면 차일의 윈도우 영역을 제외한 부분만 보여주는 그런 일을 합니다.

( 아까 만든 버튼의 이벤트 핸들러 입니다. 착오 없으시길... )

void CXXXXDlg::OnExpand()
{
    // TODO: Add your control notification handler code here
    static BOOL bExpand = TRUE;
    Expanding(IDC_STATIC_SEP, bExpand);
    bExpand = !bExpand;
}

void CXXXXDlg::Expanding(int nResourceID, BOOL bExpand)
{
    static CRect rcLarge;
    static CRect rcSmall;
    CString strExpand;
    if (rcLarge.IsRectNull()) {
        CRect rcLandMark;
        CWnd *pWndLandMark = GetDlgItem(nResourceID);
        ASSERT(pWndLandMark);
        GetWindowRect(rcLarge);
        pWndLandMark -> GetWindowRect(rcLandMark);
        rcSmall = rcLarge;
        rcSmall.bottom = rcLandMark.top;
    }
 
    if (bExpand) {
        SetWindowPos(NULL, 0 , 0, rcLarge.Width(), rcLarge.Height(),
                SWP_NOMOVE | SWP_NOZORDER);
        strExpand = " << Test ";
    } else {
        SetWindowPos(NULL, 0, 0, rcSmall.Width(), rcSmall.Height(),
                SWP_NOMOVE | SWP_NOZORDER);
        strExpand = " Test >>";
    }

    SetDlgItemText(IDC_BUTTON_TEST, strExpand);
}

 

<상태바에 그림 출력>

상태바에 그림 출력하시려면Create함수에

CStatusbarCtrl& m_Status = m_wndStatusBar.GetStatusBarCtrl();

m_Status.SetIcon(페인인덱스,
     (HICON)LoadImage(AfxGetResourceHandle(),
     MAKEINTRESOURCE(그림아디),
     32,32,10));

해주시면 됩니다.

더 자세한 정보는  MSDN찾아 보세요..

 

<시스템 강제로 다운시키기>

1.요약

 윈도우즈 NT 나 윈도우즈 98 에서 시스템을 강제로 다운시키는 방법에 대해서 알아봅니다.

2.본문

 필요한 경우가 거의 없겠지만(바이러스가 아니고서야. ^^;;), 시스템을 강제로 다운시키고자 하는 경우가 있을 수 있습니다. 이런 경우 아래의 예제에서 처럼 어셈블리 코드를 사용하는 방법과 Thread 를 와장창 만들어서 thread 러쉬(?)를 통해서 다운시키는 방법이 있습니다. 아래의 예제를 참고 하세요.

3.예제

if(g_bIsWinNT) {
     SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
     while(1) {
          DWORD dwTid;
          HANDLE hThread=CreateThread(NULL,0,LockThread,NULL,0,&dwTid);
          SetThreadPriority(hThread,THREAD_PRIORITY_TIME_CRITICAL);
     }
} else {
     lockpoint:
     __asm {
          cli
          jmp lockpoint
     }
}

4.참고

BO2K

 * cli : Clear Interrupt Flag
     --> 이 어셈블리 코드는 현재 시스템이 다운되는 것을 방지하기 위해서 만들어진
            Interrupt Flag 를 제거해버림으로써, 아무도 자신을 건드릴 수 없게 합니다.

           (The Intel Architecture Software Developer's Manual)  

 

<Win9x VS Win2000,WinNT 시스템 종료하기>

시스템을 관리하는 프로그램에서 자주 쓰이는 기능인데여...

좀 보기 쉽게 정리된 자료가 없는거 같아서 올려봅니다.....

먼저 Win9x Series... 

간단하니까...그냥 함수로 구현한 것만 적어 놓으께여...

 

void CUpsManagerApp::Win9XSystemReboot()
{
     ExitWindowsEx(EWX_REBOOT,0);
}

void CUpsManagerApp::Win9XSystemPowerOff()
{
     ExitWindowsEx(EWX_POWEROFF,0);
}

void CUpsManagerApp::Win9XSystemLogOff()
{
     ExitWindowsEx(EWX_LOGOFF, 0);
}

 

먼저 WinNT 및 Win2000 Series...

 요건 문제가 쩜 있습니다...

프리빌리지라는 넘을 설정해서 토큰(버스 토큰 아님 --;;;)이라는 넘을 설정줘야 합니다.

뭐하는 넘인지는 MSDN 찾아보면 잘 나오구여... 그냥 시스템 수준의 프로세스 액세스를 할려면... 이거 설정하구 해야한다구 알고 계시면 될겁니다.

글고 이 설정에 관한 방법은 자료실에 이미 계제가 되어 있습니다.

근데.. 또 글을 적어논 이유는 약간의 부연 설명을 달라구여...

지금의 설정대로 하면

 

ExitWindowsEx(EWX_SHUTDOWN | EWX_REBOOT,0);
ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF,0); 

위의 두개의 기능은 당연히 달라야 합니다....

근데... 둘다 리부팅이 됩니다. 원래 하나는 파워 오프되어야 하는데...

아무리 추적해 봐도 왜 그런지 잘 모르겠어여...

 

전 유피에스 관리 프로그램 지금 짜고 있는데...

바떼리 떨어지면... 시스템을 죽여야 하는데... 이넘이 다시 켜지면 곤란하지여..

 

그려서... ExitWindowsEx() 함수 대신에 ....

InitiateSystemShutdown() 함수를 써서 구현해봤심더...

엔티4.0하구 윈이천(Professional 버젼)은 잘 동작 하든디...

다른건 아직 테스트 못해봤습니다.

 

void CUpsManagerApp::WinNTSystemReboot()
{
     HANDLE hToken;
     TOKEN_PRIVILEGES tp;
     LUID luid;

     OpenProcessToken(GetCurrentProcess(),
          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,  
          &hToken);
     LookupPrivilegeValue(NULL,"SeShutdownPrivilege",&luid);

     tp.PrivilegeCount = 1;
     tp.Privileges[0].Luid = luid;
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  
     AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);

     InitiateSystemShutdown(NULL, NULL, 30, FALSE, TRUE);
     // ExitWindowsEx(EWX_SHUTDOWN | EWX_REBOOT,0);
}

void CUpsManagerApp::WinNTSystemPowerOff()
{
     HANDLE hToken;
     TOKEN_PRIVILEGES tp;
     LUID luid;

     OpenProcessToken(GetCurrentProcess(),
          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
          &hToken);
     LookupPrivilegeValue(NULL,"SeShutdownPrivilege",&luid);

     tp.PrivilegeCount = 1;
     tp.Privileges[0].Luid = luid;
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

     AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);

     InitiateSystemShutdown(NULL, NULL, 30, FALSE, FALSE);
     // ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF,0);
}

void CUpsManagerApp::WinNTSystemLogOff()
{
     ExitWindowsEx(EWX_LOGOFF, 0);
}

 

<실수 나눗셈 연산을 정수 연산으로 하기>

1.요약

 소수점이 있는 나눗셈 연산을 정수로 계산하여 계산 속도가 빠르고 정확한 숫자를 얻을 수 있는 방법에 대해서 알아보겠습니다.

2.본문

 정수로 계산은 소수점 계산보다 연산 속도가 빠릅니다. 그리고 정확한 숫자값을 얻기 위해서는 나누기 전에 왼쪽으로 16-bit shift하고, 동등한 16-bit의 2진 숫자를 주어야 합니다. 이것은 나누기 전에 100,000으로 곱셈을 한 효과가 있습니다. 이렇게 함으로써 소수점을 계산하지 않아도 됩니다.

3.예제

1) 10 / 85 = x;

  10을 100,000으로 곱하면, 1,000,000이 나온다.

  1,000,000 / 85 = 11764를 먼저 계산하고,

  결과값 11764를 100,000으로 나누면 0.11764가 나온다.

 

2)

int ratio = ((( dege - px2) < <  16) / (px1 - px2));
px1 = edge;
py1 = py2 + (((py1 - py2) * ratio) >> 16));  

3)

float ratio = (float)((edge - px2) / (px1 - px2));
px1 = edge;
py = py2 + (int)(((float)(py1 - py2) * ratio));  

[결과]

2)연산속도가 3)연산속도보다 약 2배정도 빠른결과가 나옵니다.

 

4.참고

Real-Time Strategy Game Programming using Ms DirectX  p.318  

 

<연결 프로그램 찾기 다이얼로그 띄우기>

[요약]

Explorer 에서 파일 아이콘을 더블클릭할 경우 파일의 확장명에 따라 연결되어 있는 프로그램이 동작하게 되어 있다. 하지만 연결되어 있는 프로그램이 없을 경우 "연결 프로그램 찾기" 다이얼로그가 화면에 나타난다.

[추가 정보]

 파일 아이콘에 연결된 프로그램이 없을 경우 연결 프로그램을 지정하도록 "연결 프로그램 찾기" 다이얼로그를 프로그램적으로 띄우는 방법은 ShellExecuteEx API 를 Call 할 때 "Openas" 를 파라미터로 사용하면 된다.

우선 ShellExecuteEx를 Call하기 전에 FindExecutable API를 사용하여 파일의 프로그램 연결여부를 우선 알아보도록 한다. 만약 연결된 응용프로그램이 있을 경우 SHELLEXECUTEINFO structure에 "Open" 파라미터를 사용하고, 그렇지 않을 경우 "Openas"를 사용하면 된다. 이 파라미터에 의해 ShellExecuteEx 가 "연결 프로그램 찾기" 다이얼로그를 화면에 나타내게 된다.

 다음은 위 내용에 대한 Sample code이다.

// Get the name of the file to be openend.
CFileDialog dlg(TRUE);
if (dlg.DoModal() == IDOK ) {
     CString strFile = dlg.GetPathName();

     // See if there is an association for this file.
     char strExecutable[FILENAME_MAX];
     int result = (int)FindExecutable((LPCTSTR)strFile, NULL,
          (LPTSTR)&strExecutable);
     if (result == 31) {        // No Associated .EXE file
          AfxMessageBox("There is no association for the specified file type.
               The 'Open With...' dialog box will now be displayed.");
          SHELLEXECUTEINFO sei = {sizeof(sei), 0, m_hWnd, "Openas",
               (LPCSTR)strFile, NULL, NULL, SW_SHOWNORMAL,
               AfxGetApp()->m_hInstance};

          // Invoke the "Open With..." dialog box before opening the selected file.
               ShellExecuteEx(&sei);
     
     } else if (result >= 32 ) {               // Found an Associated .EXE file
          AfxMessageBox("The file will now be opened with the associated program...");
          SHELLEXECUTEINFO sei = {sizeof(sei), 0, m_hWnd, "Open",
               (LPCSTR)strFile, NULL, NULL, SW_SHOWNORMAL,
               AfxGetApp()->m_hInstance};

          // Open the selected file.
         ShellExecuteEx(&sei);

     } else {          // Error condition.
         AfxMessageBox("Some other problem has caused 'FindExecutable' to fail.");
     }
}

 

<Window에서 콘솔 얻기(API)>

보통 윈도우 Application은 화려한 GUI를 자랑하며 Application이 만들어 집니다.

그러나 서버에서 실행되는 프로그램인 경우하나의 콘솔 화면에서 여러 GUI를 띄어 작업을 하게 되지요.

보통은 하나의 Application에 하나의 콘솔에 여러개의 윈도우를 생성해 프로그램 됩니다. 물론 콘솔도 하나의 윈도우지만요.

그럼 여기서 간단히 윈도우 Application에서 콘솔을 띄어 Text Edit에 있는 문자를 콘솔에 찍는 API 함수를 소개합니다.

비교적 간단하고 여러군데서 사용가능하니 많은 참조 바랍니다.

1. 윈도우에서 콘솔 띄우기

    BOOL AllocConsole() 

2. 생성된 콘솔에 문자열 보내기

    CString str;
    str = "문자열"

    DWORD dwWrite;
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    WriteFile(hOut, str, str.GetLength(), &dwWrite, NULL);

3. 생성된 콘솔없애기

    BOOL FreeConsole()

 

함수의 자세한 사용은 MSDN에서 참고하세요.

 

<제어판에서 큰글꼴로 설정했을경우에도 일정한 크기의 글꼴 지정>

 

CDC* pDC = GetDC();
int nSize1 = 100;         // 작은 글꼴에서 보여지는 글꼴 크기
int nSize2 = MulDiv(nSize1, 96, pDC->GetDeviceCaps(LOGPIXELSY));
                                // nSize1의 크기를 현재 디스플레이의 인치당 픽셀수로 재계산한다.

m_Font.CreatePointFont(nSize2, "굴림체", pDC);
                                // 단순히 폰트명과 폰트크기로 생성할때

m_Button.SetFont(&m_Font);

이렇게 해서 앞에 올린 코드를 대신하면 제어판에서 설정한 큰글꼴, 작은글꼴의 값에 상관없이 일관된 크기의 폰트를 지정할 수 있습니다. 큰글꼴로 설정하면 글자가 잘리는 현상을 막을 수 있죠.

 

<투명 256 비트맵>

256 칼라의 비트맵을 화면에 뿌려줍니다. 물론 투명색을 지정하는 방식이구요.

그럼 소스 나갑니다.

 

int CCISBitmap::Width()
{
    BITMAP bm;
    GetBitmap(&bm);
    return bm.bmWidth;
}

int CCISBitmap::Height()
{
    BITMAP bm;
    GetBitmap(&bm);

    return bm.bmHeight;
}

void CCISBitmap::DrawTransparent(HDC hDC, int x, int y, COLORREF crColour)
{
    COLORREF crOldBack = SetBkColor(hDC,m_crWhite);
    COLORREF crOldText = SetTextColor(hDC,m_crBlack);
    CDC dcImage, dcTrans;
 
    // Create two memory dcs for the image and the mask
    
    dcImage.m_hDC = CreateCompatibleDC(hDC);
    dcTrans.m_hDC = CreateCompatibleDC(hDC);
    
    // Select the image into the appropriate dc
    CBitmap* pOldBitmapImage = dcImage.SelectObject(this);
 
    // Create the mask bitmap
    CBitmap bitmapTrans;
    int nWidth = Width();
    int nHeight = Height();
    bitmapTrans.CreateBitmap(nWidth, nHeight, 1, 1, NULL);

    // Select the mask bitmap into the appropriate dc
    CBitmap* pOldBitmapTrans = dcTrans.SelectObject(&bitmapTrans);
 
    // Build mask based on transparent colour
    dcImage.SetBkColor(crColour);
    dcTrans.BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCCOPY);
 
    // Do the work - True Mask method - cool if not actual display
    BitBlt(hDC, x, y, nWidth, nHeight, dcImage.m_hDC , 0, 0, SRCINVERT);
    BitBlt(hDC, x, y, nWidth, nHeight, dcTrans.m_hDC , 0, 0, SRCAND);
    BitBlt(hDC, x, y, nWidth, nHeight, dcImage.m_hDC , 0, 0, SRCINVERT);
 
    // Restore settings
    dcImage.SelectObject(pOldBitmapImage);
    dcTrans.SelectObject(pOldBitmapTrans);
    pOldBitmapImage->DeleteObject();
    pOldBitmapTrans->DeleteObject();
    SetBkColor(hDC,crOldBack);
    SetTextColor(hDC,crOldText);
}

입니다.

음.. 그런데 이게 좀 느려요. --;.

투명색 없이 출력하는 루틴은 아래껍니다.

 

void CCISBitmap::DrawBitmap(HDC hDC, int x, int y)
{
    CDC MemDC;
    MemDC.m_hDC = CreateCompatibleDC (hDC);
 
    int nWidth = Width();
    int nHeight = Height();
 
    CBitmap *pBitmap = (CBitmap *)MemDC.SelectObject (this);
    BitBlt (hDC,x,y,nWidth,nHeight,MemDC.m_hDC,0,0,SRCCOPY);
    MemDC.SelectObject (pBitmap);
    pBitmap->DeleteObject();
    pBitmap = NULL;
}

그리고 헤더는 이렇게 해주세요.

public:
    void DrawBitmap(HDC hDC, int x, int y);
    CCISBitmap();
    virtual ~CCISBitmap();
 
    // Functions
    int Height();
    int Width();    
    virtual void DrawTransparent(HDC hDC, int x, int y, COLORREF crColour);    
    
private:
    COLORREF    m_crBlack;
    COLORREF    m_crWhite;

그리고요.. 또.. 생성자에 COLORREF 를 초기화 해주심 되구요

이건 CBitmap 을 상속받았습니다.

 

<트레이의 아이콘이 사라지지 않게>

IE가 죽을 때 트레이의 아이콘이 모두 사라지는 경우가 있습니다.

특히 트레이에 숨어버리는 프로그램이 그렇게 되면 참으로 난감해지죠.

IE 4이상에서는 그런 일을 막기 위해서 죽은 후 태스크바가 만들어질 때 모든 top-level 윈도우에 메시지를 보내줍니다. 그 메시지를 받으면 트레이에 새로 아이콘을 넣어주면 됩니다.

MSDN에 있는 내용이지만 모르시는 분들이 많더군요.

소스를 보면 다음과 같습니다.

 

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
     static UINT s_uTaskbarRestart;

     switch(uMessage) {
     case WM_CREATE:
          s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
          break;

     default:
          if(uMessage == s_uTaskbarRestart)
               AddTaskbarIcons();
          break;
    }
    
    return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

 

RegisterWindowMessage(TEXT("TaskbarCreated")); 는 TaskbarCreated라는 이름의 메시지를 시스템에 대해서 유일하게 만들어주는 함수입니다. 이미 이 이름으로 메시지가 등록되어 있을 경우에는 그 값을 돌려줍니다.

따라서 프로그램이 시작할 때 한번 실행해서 그 값을 저장해줘야 합니다.

그리고 해당 메시지가 오면 그 때 트레이에 아이콘을 새로 넣으면 되겠죠.

 

MFC의 경우는 메시지의 값이 상수로 정해져 있지 않기 때문에 메시지 맵에 붙이는 건 안될 것 같습니다. CWnd::WindowProc이나 PretranslateMessage에서 메시지 값을 확인해서 처리해주면 되겠죠.

 

- MFC의 경우

  윈도우는 tray icon에 있는 아이콘들을 보여줄때. "TaskbarCreated" 라는 윈도우 메세지를 보여줍니다...

 따라서 우리는 이 윈도우 메세지를 등록해서 사용하면 됩니다.

UINT g_uShellRestart;
g_uShellRestart = RegisterWindowMessage(__Text("TaskbarCreated"));

이렇게 해서.. 

Message Map 에서..

 

ON_REGISTERED_MESSAGE(g_uShellRestart, OnTrayShow)

 

이런식으로 메세지 핸들러를 설치하시고..

 

LRESULT CMyDlg::OnTrayShow(WPARAM wParam, LPARAM lParam)
{
    m_Tray.ShowTray();        // TrayIcon을 다시 보여줍니다. ShowTray 는 임시.

 

이런식으로 해주시면 됩니다..

 테스트를 해보시고 싶으시다면..Ctrl+Alt+Del 을 눌러서..Explorer 를 죽이세요.. 

그러면 tray icon들이 사라질텐데..

이 메세지를 등록한 프로그램들은 ..여전히 남아있는 모습을 볼 수 있습니다.

 

<특정한 다이알로그 박스에 버튼을 누른 효과를 내기>

일단 다이알로그 박스의 핸들을 얻어야겠지요?

HWND hWnd;

hWnd = ::FindWindow("클래스 명", "타이틀 명");

  보통 다이알로그박스는 클래스명이 "#32770" 이죠,

  따라서  ::FindWindow("#32770", NULL); 이렇게하면 타이틀에 관계없이 첫번째 다이알로그 박스를 찾아 핸들을 넘겨주죠 그담엔 아래와 같이 해보길...

 

  ::PostMessage(hWnd, WM_COMMAND, IDOK, 0L);   //"OK" 버튼 누름
  ::PostMessage(hWnd, WM_COMMAND, IDNO, 0L);   //"NO" 버튼 누름
  ::PostMessage(hWnd, WM_COMMAND, IDCANCEL, 0L);   //"CANCEL" 버튼 누름

 

<프로그램내에서 한영전환을 하는 방법>

해당 헤더파일 : imm.h  해당 라이브러리 파일 : imm32.lib

한글모드와 영문모드를 전환하는 함수는 다음 두가지입니다.

 

1. 한글모드로 전환하는 방법

void CHanengClass::SetHanMode(HWND hWnd)
{
    HIMC hIMC = ImmGetContext(hWnd);
    DWORD dwConv, dwSent;
    DWORD dwTemp;

    ImmGetConversionStatus(hIMC,&dwConv,&dwSent);
 
    dwTemp = dwConv & ~IME_CMODE_LANGUAGE;
    dwTemp |= IME_CMODE_NATIVE;
    dwConv = dwTemp;
    ImmSetConversionStatus(hIMC,dwConv,dwSent);
    ImmReleaseContext(hWnd,hIMC);
}

 

2. 영문모드로 전환하는 방법

void CHanengClass::SetEngMode(HWND hWnd)
{
    HIMC hIMC = ImmGetContext(hWnd);
    DWORD dwConv, dwSent;
    DWORD dwTemp;
 
    ImmGetConversionStatus(hIMC,&dwConv,&dwSent);
 
    dwTemp = dwConv & ~IME_CMODE_LANGUAGE;
    dwTemp |= IME_CMODE_ALPHANUMERIC;
    dwConv = dwTemp;
    ImmSetConversionStatus(hIMC,dwConv,dwSent);

    ImmReleaseContext(hWnd,hIMC);
}

 

<Dialog의 Min/Max/Close Box를 Run Time Show/Hide>

// 아래의 세 메세지 핸들러는
// Dialog Client Area에 만든 각 Button을 위한것입니다.
// 이 각 Button들을 클릭하면 Title Bar상의 해당 Box가 Show/Hide되죠.

void CTestDlg::OnBtnMin()
{
     // TODO: Add your control notification handler code here
     static BOOL bEnable = TRUE;
     bEnable = bEnable ? FALSE : TRUE;    
     if(bEnable)
           ModifyStyle(0,WS_MINIMIZEBOX);
     else
           ModifyStyle(WS_MINIMIZEBOX,0);

     PostMessage(WM_NCACTIVATE,TRUE);
}

void CTestDlg::OnBtnMax()
{
     // TODO: Add your control notification handler code here
     static BOOL bEnable = TRUE;
     bEnable = bEnable ? FALSE : TRUE;    
     if(bEnable)
         ModifyStyle(0,WS_MAXIMIZEBOX   );
     else
         ModifyStyle(WS_MAXIMIZEBOX   ,0);
  
     PostMessage(WM_NCACTIVATE,TRUE);
}

void CTestDlg::OnBtnClose()
{
      // TODO: Add your control notification handler code here
      static BOOL bEnable = TRUE;
      CMenu* pSysMenu = GetSystemMenu(FALSE);
      if (pSysMenu != NULL) {
           bEnable = bEnable ? FALSE : TRUE;
           if(bEnable)
                pSysMenu->EnableMenuItem(SC_CLOSE,  MF_ENABLED | MF_BYCOMMAND);
           else
                pSysMenu->EnableMenuItem(SC_CLOSE, MF_DISABLED | MF_BYCOMMAND);
      }
}

 

<error LNK2001: unresolved external symbol _main>

error LNK2001: unresolved external symbol _main

이 에러가 발생하는 경우는 크게 두가지가 있습니다. 

 

1. API로 프로그래밍을 처음 하시는 분들이 흔히 하는 실수 같습니다.

 윈도우에서 프로그램은 크게 두가지로 분류할 수 있습니다.

도스처럼 console 창에 텍스트만을 출력하는 console 프로그램과 흔히 쓰는 GUI를 가진 프로그램이 있죠.  

Visual C++에서 프로젝트를 만들 때

Win32 Application : WinMain 함수에서 시작하게 되는 GUI 프로그램

Win32 Console Application : main 함수에서 시작하는 console 프로그램

이 두가지가 있습니다.

 제목에 적은 것 같은 에러가 나는 분들은 대부분 GUI 프로그램을 만드시려고 하고 WinMain은 만드셨는데, 프로젝트를 만들 때 console application을 선택하신겁니다. 반대로 WinMain이 없다고 에러가 나오면 GUI 프로젝트를 선택하고서 main 함수를 작성하신 것이겠죠. 

자, 그럼 어떻게 해결하느냐. 설정을 바꿔주면 됩니다. 프로젝트 세팅(Alt + F7)에서 Link탭을 누르면 제일 아래에 에디트 박스가 보이죠? 잘 보면 /subsystem:console 이라고 써있을 겁니다. 이걸 /subsystem:windows로 바꿔주세요. (또는 상황에 따라서 그 반대로 해줄 수도 있겠죠) 이 옵션의 역할은 링커에게 있어서 이 프로그램이 어떤 함수로부터 시작해야 하는지를 알려주는 겁니다. 이 옵션이 console이라면 main을, windows라면 WinMain을 찾아서 그 함수에서부터 프로그램이 시작하게 하는거죠.

 

2. ATL에서 컨트롤을 만들 때 이 에러가 날 수 있습니다. 

디버그에서는 잘 컴파일이 되던게 릴리즈로 빌드하면 저 에러가 날 수 있습니다. ATL에서는 생성되는 코드의 크기를 최소화 하기 위해서 CRT (C RunTime) 함수의 일부를 제한하고 있기 때문이죠(정확히 말하자면 start up 코드를 넣지 않습니다). 그런데 프로젝트에서 특정 C 함수를 사용하면 ATL에서 제한한 CRT의 기능이 필요해지고 따라서 에러가 나는겁니다.

 

이 해결책도 간단합니다.

a. 프로젝트 세팅에서 predefined keyword를 찾아서 _ATL_MIN_CRT를 지워주세요.

b. 초기화가 필요한 CRT 함수를 사용하지 마세요. -_-; MSDN에 따르면 strcmp대신 lstrcmp를 사용하는 식으로 피해갈 수 있다고 합니다.

 

자세한건 MSDN에서 _ATL_MIN_CRT를 찾아보세요. (색인에서 입력하면 나옵니다.)

 

<친절한 메시지 WM_NULL>

1.요약

특정 Thread 나 Window 에 아무런 영향을 끼치지 않으면서 단순히 그 Thread 나 Window 가 살아있는지를 확인하고 싶을때 '친절한 메세지' 를 사용하시면 됩니다.  

2.본문

 MSDN 의 '색인' 에는 나와있지 않지만(MSDN 에서 특이하다 싶은것들은 일반적으로 '색인'에 없죠. ^^), 윈도우 메세지 종류에 WM_NULL 이라는 메세지가 존재합니다. MSDN 이나 다른 책에서 WM_NULL 메세지를 benign message 라고 소개하고 있습니다.

WinUser.H 파일에 아래와 같이 정의되어 있습니다.

 #define WM_NULL                         0x0000

 이 메세지는 메세지 큐에 날라가긴 하지만, 어느 윈도우에서도 이 메세지를 처리하지 않기 때문에 그냥 메세지 큐에 들어가기만 하는 메세지입니다. 그럼 윈도우는 왜 이런 메세지를 만들어 놓은 걸까요? Debugging Application 에서 보면 이 메세지를 정지해있는 혹은 메세지큐가 idle 상태에 있는 thread 를 깨우기 위해서 ( 마치 술 먹기 전에 밥먹는 것처럼..) 사용하고 있습니다. 또한 해당 윈도우가 정상적으로 메세지를 받을 수 있는 상태인지 단순히 확인만 하고 싶을때 ( 이런 경우가 종종있는데, 어떤 메세지를 보내야 할지 난감할때가 있습니다.

3.예제

 

- the end of this article -