TrueCrypt 는?

2008. 9. 17. 11:51
출처 : http://www.usboffice.kr/zbxe/por_release/32909
I. TrueCrypt 는...


  파일 및 폴더를 함호화하여 저장하는 보안 프로그램입니다. 암호화된 컨테이너 이미지 파일을 만들어 이 컨테이너 파일을 가상의 드라이브로 마운트해서 파일 입출력이 가능하고 파일 입출력간에 실시간으로 파일이 암호화 또는 복호화 됩니다. USB 메모리에 소중한 자료를 보관한다면 꼭 사용해야 하는 Must Have 프로그램입니다. TrueCrypt Launcher 는 사용하기 불편한 TrueCrypt 를 보다 손쉽게 사용 할 수 있도록 해주는 프로그램입니다.

  • 라이센스        : Open Source
  • 최선 버전       : 6.0a
  • 파일 사이즈    : 3.6 MB
  • 한글지원        : 지원함
  • 구동환경        : Windows Vista/XP/2000
  • 특 징             : 가상 드라이브를 생성하여 파일을 암호화 및 복호화 ( on_the_fly )
  • 제약사항        : 관리자 계정 (Administrator account) 에서만 동작



II. 스크린샷 & 사용 설명

* [TrueCrypt] 프로그램의 기본 개념부터 확인하자~



[TrueCrypt] 에 대해서 간략하게 개념부터 소개하면 먼저, 이 TrueCrypt 는 암호화된 하나의 이미지파일(보안영역 이미지파일)을 만들어서 이 이미지 파일을 가상의 디스크로 생성시킨다. ( 마치 iso 파일을 가상 CD 에 넣는것 처럼.. )  이때 미리 설정한 비밀번호를 입력해야만 드라이브를 생성 시킬 수 있고,  가상 드라이브가 생성되면 일반 디스크처럼 자유롭게 파일을 읽고 쓰기를 할 수 있다.


작업 종료 후, 이 가상의 디스크를 종료하면 다시 이미지파일만 남고 다시 드라이브를 생성시키기 전까지는 이미지 파일안에 있는 데이타를 볼수 없다. - 프리웨어 / 한글화 / 프로그램 파일크기 2MB


 
* 스크린샷 & 사용법

 아래 첨부파일을 다운받아서 압축을 풀면 위와같이 나온다.App 폴더에는 원래 TrueCrypt 프로그램 파일이 들어있고~ Data 폴더에는 환경설정과 암호화된 이미지 파일이 들어간다. Other 폴더에는 TrueCrypt_Loader.exe 파일 오토잇 소스와 아이콘이 들어있다.

truecrypt_2.jpg

TrueCrypt 는 원래의 실행화일은  App 폴더안의 TrueCrypt.exe 이다. 이 실행파일을 실행해보면 아래와 같은 창이 뜨는데~ 사실 이렇게 써보면 꽤 불편하다. 그래서 여기서는 TrueCrypt.exe 를 통하여 실행하지 않고 별도로 만든 TrueCrypt_Loader.exe 파일을 실행하여 사용하는 방법을 소개한다.

 TrueCrypt 원래 실행이미지~ 그러나 불편하다. 그래서 본인은 Truecrypt.exe 를 실행하지 않는다.



* 1. 보안영역 이미지 만들기

TrueCrypt_Loader.exe 를 더블클릭하여 실행해보자.

위와 같은 화면이 뜨는데... 최초 실행했다면 보안영역 이미지가 없으므로 "만들기"를 누르자.


 그냥 인사말이니 "다음" 눌러주자.


보안영역 이미지가 없더라도 "파일선택"을 누르자


임의의 경로에 원하는 파일이름을 쓰자. 위에서는 Data 폴더안에 "USB OFFICE" 라는 이름을 입력했다.

여기서 쓰는 파일이름이 바로 보안영역 이미지 이름이다.확장자없이 써도된다.

 조금전 입력한 "USB OFFICE" 파일경로가 등록되어있다. "다음"눌러주자.


 이부분은 암호 알고리즘을 선택하는 화면인데~ 잘모르면 그냥 "다음" 눌러주면된다.


이제는 보안영역의 크기를 지정해야 한다. 위에서는 그냥 100 MB 만 입력했다.


이제는 보안영역 이미지를 사용할때 입력할 암호를 입력하는 화면이다. 가능하면 긴 암호를 입력하고 "다음"누르자.


 암호가 너무 짧거나 단순하면 경고화면이 뜬다~ 수정하거나 "아니요" 눌러서무시해도 된다.


 보안영역을 포맷하는 부분인데~ 위에서는 NTFS 로 선택했다. 그리고 마우스를 움직이면 위 하이라이트 된부분의 숫자가 임의로 변하는데 그냥  마우스를 적당히 움직인 후 "포맷"을 눌러주자.


 그럼~ 포맷이 진행되고 완료되었다는 메세지가 나온다.


 이제 "종료" 버튼을 눌러주면 된다. 기억하자~~ 방금 당신은 NTFS 포맷의 용량 100MB 짜리의 "USB OFFICE" 라는 보안영역 이미지를 만든것이다. 위에서 지정한 경로로 가보자. 아래처럼 USB OFFICE 라는 파일이 만들어졌다.



* 2. 보안영역 이미지 가상디스크로 마운트하기


이제 다시 "TrueCrypt 환경 설정" 창으로 돌아가서 위에서 만든 보안영역 이미지를 가상 드라이브로 마운트 해보자.

"선택"을 눌르고 위에서 만든 USB OFFICE 파일을 선택하자. (보안 영역이미지가 동일 드라이브내에 있어야함 )


아래에 3가지 옵션이 있다. "DRIVE" 는 가상드라이브의 레터가 되며~ 이동식 디스크를 체크하면 가상디스크가 이동식디스크로 인식되고 체크하지 않으면 일반 고정식 디스크로 마운트된다.  그리고 "Explorer 열기"를 체크하면 가상 드라이브 생성 후
해당 드라이브가 탐색기로 자동으로 열리도록 하는것이고 체크하지 않으면 가상드라이브 생성 후 아무것도 하지 않는다.


이제 "확인" 을 눌러주자. 그러면 아래에 비밀번호 입력창이 뜬다. 보안영역 이미지 만들때 입력했던 비밀번호를 입력하면된다. 비밀번호를 정확히 입력하고 "확인" 누르자.


아래 그림처럼 U: 가 탐색기로 열린다.  그리고 자세히 보면 U: 는 이동식 디스크로 되어있다. 위에서 "TrueCrypt 환경설정" 에서 설정한대로 실행된것이다.  이제 이 U: 에 보안을 요하는 파일을 넣자~  위에서 우리는 용량이 100MB 로 설정했으므로 그 이상은 넣을 수 없다.


방금 위에서 셋팅한 "TrueCrypt 환경 설정" 을 변경하려면 Data 폴더안에 settings.ini 파일을 수정하면 된다. Explorer 값을 "0" 으로 하면 보안영역 마운트후 탐색기가 열리지 않는다. Removable 값을 "0" 으로하면 고정식 디스크로 마운트된다. Imageletter 값을 다른 알파벳으로 변경하면 가상드라이브의 레터가 변경된다.


* 3. 가상디스크 제거하기


보안 영역을 생성할때도 그랬던것처럼 보안 영역을 제거할때도 역시 TrueCrypt_Loader.exe 파일을 더블 클릭하면된다.

truecrypt_1.jpg


보안영역이 가상드라이브로 마운트 된 상태에서 다시 한 번 TrueCrypt portable.exe 를 실행하면 아래와 같이 보안영역을 제거한다는 메세지가 나온다. "예" 를 누르면 보안영역이 마운트된 가상드라이브가 제거된다.



* 런처에 등록해서 런처 종료시 바로 가상 드라이브 종료하기

Truecrypt_loader.exe -q 옵션을 주면~ 묻지않고 바로 드라이브를 종료시킵니다. 그래서 런처에 등록해놓고 런처 종료 시 자동 종료 되도록 하면 편합니다.



위 에 장황하게 설명했지만 실제로는 TrueCrypt portable.exe 실행시키면 바로 비밀번호 입력창이 뜬다. 비밀번호 입력하면 자동으로 보안영역의 가상 드라이브가 생성되고~   한번더  더블클릭하면 가상 드라이브가 제거된다.  무지 간단한다. ^^;

주의할사항은 만약 자신이 비밀번호를 잊어버린다면~ 더이상 복구할 수 없다. 꼭 비밀번호를 잘 기억하자.


III. 포터블 버전 만드는법

암호화된 가상 드라이브를 생성 할 수 있는 프로그램인 TrueCrypt Loader file 을 올립니다. 먼저 아래의 절차에 따라서 프로그램을 완성하시기 바랍니다. (TrueCrypt 는 재배포 가능하나 수정이 불가능하여 요렇게....^^)

 
1. TrueCrypt 프로그램 파일을 다운 받고 설치 한다. (Extract mode)
2. loader 파일을 다운받아서 압축을 푼다.
3. 1번에서 추출된 파일을 2번과정에서 생성된 Program files\Truecrypt 폴더 안에 복사한다.
4. 한글화 하기위해서 위에 링크된 한글언어 파일을 다운로드 받고
Program files\Truecrypt 폴더에 복사한다.
5. TrueCrypt_Loader.exe 를 실행한다.




IV. Release Note
*2008. 7월 31일
   - 정식버전
   - Truecrypt 강제 종료되더라도 마운트된 드라이브 삭제되도록 수정
*2008. 7월 22일
   - 이미지 파일위치가 Data 폴더가 아니라도 동작이 되도록 수정
   - 베타버전
Posted by 나비:D
:
출처 : http://blog.powerumc.kr/article/2008/03/09/MSI-Deploy-Vista.aspx
Written By 엄준일(땡초)
비스타에서 설치 프로젝트 권한상승 하기
 
비스타가 출시되면서 화려해진 인터페이스 만큼 수많은 부분에서 이슈가 생겼습니다. 게임/시스템/개발 등의 대체로 전반적인 부분에서 사용자들의 따가운 질타를 받아왔습니다.
 
그 중에서 배포 프로젝트와 관련하여 알아보았던 설치 프로젝트의 권한 상승 문제는 Visual Studio 2008 의 설치 프로젝트의 권한 상승 기능을 이용하여 해결할 수 있었습니다. 하지만 Windows Installer 에 의한 MSI 설치 파일을 약간 다른 방법으로 해결해야 합니다.
 
 
Windows Installer 에 의한 MSI 설치 파일 권한 상승 방법
 
Windows Installer 는 msiexec 에 의해 설치파일(.MSI) 설치 환경을 제공해 줍니다. 때문에 근본적으로 MSI 설치 파일의 관리자 권한을 획득하기 위해서는…
 
1.     비스타의 UAC 기능을 제거하는 방법입니다.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System 의 EnableLUA 키값을 변경하여 UAC 기능을 허용/제거 할 수 있습니다. ( 1은 UAC On/ 0은
UAC Off)

2.     Msiexec.exe 를 관리자 권한으로 실행하는 것이 가장 손쉽게 관리자 권한을 얻는 방법일 것입니다. Msiexec.exe 에 의해 MSI 설치 파일이 호스팅 된다는 것을 생각해 본다면 아마도 당연한 방법일 것입니다.

3.     지난번에 말씀드렸던 Orca Tool 을 이용하여 MSI 파일의 스크립트를 조작하는 방법입니다. 하지만, 빌드 후에 매번 Orca Tool 로 스크립트를 조작하기엔 너무나도 번거롭고 불편합니다.
하지만, 이 툴을 이용하여 빌드 단계를 거치지 않고, 설치 프로젝트의 제목이나 설치 경로 등의 스크립트를 조작할 수 있기 때문에, 가끔씩 매우 유용하게 사용할 수 있습니다
.

 
하지만, 강제로 사용자의 UAC 기능을 제거하는 방법은 그다지 좋은 방법이 아니고. 또한, 나머지 방법들도 효율적이지 않습니다.
 
 
빌드 이벤트를 이용한 MSI 설치 파일 권한 상승 하기
 
우선 Windows Installer Development Kit 을 다운로드 받도록 합니다. 제법 용량이 크고, 설치도 오래 걸립니다. 이 Kit 의 WiRunSql.vbs 파일이 필요한데, 이 파일만 구할 수 있다면 굳이 Windows Installer Development Kit 전체를 받을 필요는 없을 것 같네요.
 
1.     WiRunSql.vbs 파일을 설치 프로젝트 폴더에 복사합니다.
2.     속성 창에 PostBuildEvent 을 찾아 다음의 스크립트를 삽입합니다.
( 참고 : 빌드 이벤트는 여기 아티클에서 다루었습니다 )



cscript //nologo "$(ProjectDir)WiRunSql.vbs" "$(BuiltOuputPath)" "UPDATE CustomAction SET CustomAction.Type=3073 WHERE CustomAction.Type=1025 AND CustomAction.Source='InstallUtil' AND CustomAction.Target='ManagedInstall'"
 
cscript //nologo "$(ProjectDir)WiRunSql.vbs" "$(BuiltOuputPath)" "UPDATE CustomAction SET CustomAction.Type=3585 WHERE CustomAction.Type=1537 AND CustomAction.Source='InstallUtil' AND CustomAction.Target='ManagedInstall'"

Orca Tool 로 MSI 프로젝트 내부를 한번이라도 보셨다면 저 희한한 문장이 무엇을 의미 하는지는 아실 것 같습니다.
 
위와 같이 간단하게 Windows Installer Development Kit 의 vbs 스크립트를 이용하여 MSI 설치 파일의 비스타 권한 상승 효과를 얻을 수 있습니다. 단, MSI 설치 파일 실행 시 관리자 권한 상승 창이 뜨는 것은 아니고, 실제 설치 중 ProgressBar 가 나타나게 되면, 권한 상승 창이 뜨게 됩니다.
 
 
너무 쉬었네… ㅠ.ㅠ
 
그 동안 너무 쉬었던 탓에, 블로그 글 쓰는 것이 너무너무 버겁습니다;; 이거 써놓고, 또 한 달을 띵가띵가 쉴 지 모르죠 ㅋ;;; 아무튼 이번 달에 Silverlight 2.0 Beta 출시를 시작으로 많은 MS 의 신제품이 출시가 되네요. 정말 기대가 많습니다.
Posted by 나비:D
:
 

win32/코드팁

windows 프로그램, 특히 Visual C++에 관련된 유용한 코드 팁을 담는 곳.

내용

  1. Link: Knowledge Base
  2. 다이알로그에 스킨 입히기
  3. 투명 윈도우 만들기
  4. 내 IP 얻는 방법
  5. 트레이 아이콘
  6. 패스 분리하는 방법
  7. FTP 관련
  8. Downloaded Program Files 폴더의 OCX 삭제방법
  9. 디버깅 용도의 실행시간 출력 코드
  10. Trace Tip
  11. 모니터 끄기
  12. 프로세스간 데이터 교환
  13. Log 함수
  14. Ctrl+Esc, Alt+Tab, and Alt+Esc를 막는 법
  15. About box에 URL 링크 걸기
  16. 특정 파일 오픈시 바로 프로그램 불러오기
  17. 드래그 앤 드롭으로 파일 오픈
  18. app가 트레이에만 띄우도록 하려면
  19. single thread 어플리케이션에 취소 버튼 구현
  20. PC시간 셋팅하기

1 Link: Knowledge Base

[WWW]Knowledge base : MFC 및 Visual C++ 프로그래밍에 대한 방대한 tip과 know-how를 담고 있다.

2 다이알로그에 스킨 입히기

void CDlgPictureDlg::OnPaint()  
{ 
    if (IsIconic()) 
    { 
        CPaintDC dc(this); // device context for painting 
 
        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); 
 
        // Center icon in client rectangle 
        int cxIcon = GetSystemMetrics(SM_CXICON); 
        int cyIcon = GetSystemMetrics(SM_CYICON); 
        CRect rect; 
        GetClientRect(&rect); 
        int x = (rect.Width() - cxIcon + 1) / 2; 
        int y = (rect.Height() - cyIcon + 1) / 2; 
 
        // Draw the icon 
        dc.DrawIcon(x, y, m_hIcon); 
    } 
    else 
    {     
        CWindowDC dc(this); 
        CDC memDC; 
        memDC.CreateCompatibleDC(&dc); 
        CBitmap bitmap; 
        BITMAP bm; 
        bitmap.LoadBitmap(IDB_BITMAP1);      <<-- 비트맵 이미지를 만들어야한다. 
        bitmap.GetBitmap(&bm); 
        CBitmap* pOld = memDC.SelectObject(&bitmap); 
        dc.BitBlt(5, 30, bm.bmWidth, bm.bmHeight, &memDC, 0, 0, SRCCOPY); 
        dc.SelectObject(pOld); 
        memDC.DeleteDC(); 
        CDialog::OnPaint(); 
    } 
  
} 

3 투명 윈도우 만들기

BOOL CWin1Dlg::OnInitDialog() 
{ 
    CDialog::OnInitDialog(); 
  
    SLWA pSetLayeredWindowAttributes = NULL;     // 함수포인터 선언, 초기화. 
    HINSTANCE hmodUSER32 = LoadLibrary("USER32.DLL");  // 인스턴스 얻음. 
    pSetLayeredWindowAttributes=(SLWA)GetProcAddress(hmodUSER32,"SetLayeredWindowAttributes");   //함수포인터 얻음. 
    HWND hwnd = this->m_hWnd;                              //다이얼로그의 핸들 얻음. 
 
    SetWindowLong(hwnd, GWL_EXSTYLE,GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); 
    pSetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA); 
 
    return TRUE;  // return TRUE  unless you set the focus to a control 
} 
 
// 해당 다이알로그의 상단에 추가 
#define WS_EX_LAYERED           0x00080000 
#define LWA_COLORKEY            0x00000001 
#define LWA_ALPHA               0x00000002 
#define ULW_COLORKEY            0x00000001 
#define ULW_ALPHA               0x00000002 
#define ULW_OPAQUE              0x00000004 
  
typedef BOOL(WINAPI *SLWA)(HWND, COLORREF, BYTE, DWORD); 

4 내 IP 얻는 방법

CString GetMyIPAddress() 
{ 
    char        chName[255]; 
    CString        sAddress; 
    PHOSTENT    pHostEntry; 
    IN_ADDR        inAddr; 
 
    if( gethostname( chName, 255 ) != 0 ) return ""; 
    else 
    { 
        if( ( pHostEntry = gethostbyname( chName ) ) == NULL ) return ""; 
        else 
        { 
            memcpy( &inAddr, pHostEntry->h_addr, 4 ); 
            sAddress.Format( "%s", inet_ntoa( inAddr ) ); 
            return sAddress; 
        } 
    } 
} 

5 트레이 아이콘

void CWin1Dlg::OnButton1()  
{ 
            //일단 트레이아이콘 생성 
 
    NOTIFYICONDATA nid;     
                     
    nid.cbSize = sizeof(nid);  
    nid.hWnd = m_hWnd;         
    nid.uID = IDR_MAINFRAME;   
    nid.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP; 
    nid.uCallbackMessage = WM_ICON_NOTIFY; 
    //이 부분이 중요 - 트레이 아이콘에서 이벤트 발생시 발생되는 메세지 정의하는 부분..  
    nid.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);  
    strcpy(nid.szTip,"Test"); 
    Shell_NotifyIcon(NIM_ADD,&nid);  
    SendMessage(WM_SETICON,(WPARAM)TRUE,(LPARAM)nid.hIcon); 
 
    //일단 아이콘 생성하기 전에 다이얼로그를 숨겨야 겠죠..^^ 
    ShowWindow(SW_HIDE); 
} 
 
void CWin1Dlg::OnButton2()  
{ 
    //다음 트레이아이콘 삭제 
    NOTIFYICONDATA nid; 
 
    nid.cbSize = sizeof(nid); 
    nid.hWnd = m_hWnd; 
    nid.uID = IDR_MAINFRAME; 
    Shell_NotifyIcon(NIM_DELETE,&nid); 
 
    //글구 이젠 다이얼로그를 나타나게.. 
    ShowWindow(SW_SHOW); 
} 
 
LRESULT CWin1Dlg::OnTrayNotification(WPARAM wParam, LPARAM lParam) 
{ 
    // 왼쪽 마우스 버튼이 더블 클릭되었을 경우 
   if (LOWORD(lParam) == WM_LBUTTONDBLCLK)  
   { 
                // 프로그램 정보 다이얼로그 박스 출력 
                SendMessage(WM_COMMAND, IDC_BUTTON2); 
   } 
   return 1; 
} 

6 패스 분리하는 방법

void _splitpath( const char *path, char *drive, char *dir, char *fname, char *ext ); 

를 쓰면 됩니다. 다음은 MSDN에 나와있는 예제입니다

#include  
#include  
 
void main( void ) 
{ 
   char path_buffer[_MAX_PATH]; 
   char drive[_MAX_DRIVE]; 
   char dir[_MAX_DIR]; 
   char fname[_MAX_FNAME]; 
   char ext[_MAX_EXT]; 
 
   _makepath( path_buffer, "c", "\\sample\\crt\\", "makepath", "c" ); 
  
   printf( "Path created with _makepath: %s\n\n", path_buffer ); 
  
   _splitpath( path_buffer, drive, dir, fname, ext ); 
  
   printf( "Path extracted with _splitpath:\n" ); 
   printf( "  Drive: %s\n", drive ); 
   printf( "  Dir: %s\n", dir ); 
   printf( "  Filename: %s\n", fname ); 
   printf( "  Ext: %s\n", ext ); 
} 

7 FTP 관련

#include"CFTPTrans.h" 
 
CFTPTrans::CFTPTrans() 
{ 
} 
 
CFTPTrans::~CFTPTrans() 
{ 
    DisConnect(); 
} 
 
BOOL CFTPTrans::Connect(char *serverName, int port, char *username, char *password) 
{ 
    hInternet = InternetOpen("File Transfer for FTP", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); 
    if(hInternet == NULL) 
    { 
        errCode = GetLastError(); 
        return FALSE; 
    } 
  
    hFtpSession = InternetConnect(hInternet, serverName, port, username, password, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); 
    if (NULL == hFtpSession) 
    { 
        InternetCloseHandle(hInternet); 
        errCode = GetLastError(); 
        return FALSE; 
    } 
    return TRUE; 
} 
 
CFTPTrans::DisConnect() 
{ 
    InternetCloseHandle(hFtpSession);  
    InternetCloseHandle(hInternet); 
} 
  
BOOL CFTPTrans::ChangeDirectroy(char *dirName) 
{ 
    if(!FtpSetCurrentDirectory(hFtpSession, dirName)) 
    { 
        errCode = GetLastError(); 
        return FALSE; 
    } 
    return TRUE; 
} 
  
BOOL CFTPTrans::PutFile(char *source, char *destination) 
{ 
    if(!FtpPutFile(hFtpSession, source, destination, FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_RELOAD, 0)) 
    { 
        errCode = GetLastError(); 
        return FALSE; 
    } 
    return TRUE; 
} 

8 Downloaded Program Files 폴더의 OCX 삭제방법

typedef HRESULT (WINAPI *REMOVECONTROLBYNAME) 
( 
             LPCTSTR lpszFile, 
             LPCTSTR lpszCLSID, 
             LPCTSTR lpszTypeLibID, 
             BOOL bForceRemove, 
             DWORD dwIsDistUnit 
); 
 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    HMODULE                   hMod; 
    REMOVECONTROLBYNAME       pfn =  NULL; 
 
    hMod = LoadLibrary("OCCACHE.DLL"); 
    if (hMod == NULL) 
    {   // Error loading module -- fail as securely as possible 
        return 0; 
    } 
 
    HRESULT hr; 
    pfn = (REMOVECONTROLBYNAME)GetProcAddress(hMod, "RemoveControlByName"); 
    if (pfn)  
    { 
        hr = (*pfn)(_T("C:\\WINDOWS\\Downloaded Program Files\\teechart5.ocx"), 
            _T("{B6C10532-FB89-11D4-93C9-006008A7EED4}"), 0, TRUE, TRUE); 
    } 
    FreeLibrary(hMod); 
 
    return 0; 
} 

위에서 빨간 글자는 레지스트리에서 아래의 위치와 같습니다.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Code Store Database\Distribution Units 위 값의 하위키를 보면 숫자로된 키가 그것입니다.
파란색 글자는 빨간색 숫자로된 키 밑에 '\Contains\Files' 로 들어가서 오른쪽 값을 참고하면됩니다.

9 디버깅 용도의 실행시간 출력 코드

디버깅시 TRACE로 시간정보를 출력하고 싶을때 사용한다.

#include <sys/timeb.h> 
 
void printCurrentTime() 
{ 
    time_t ltime; 
    struct _timeb tstruct; 
    struct tm *gmt; 
 
    time(<ime); 
    gmt = localtime(<ime); 
    _ftime( &tstruct ); 
    TRACE2("called - %u(ms) %s", tstruct.millitm, asctime(gmt)); 
} 

예)

while(1) 
{ 
    Sleep(10); 
    printCurrentTime(); 
} 
 
<실행결과> 
... 
called - 92(ms) Tue Oct 01 17:44:28 2002 
called - 102(ms) Tue Oct 01 17:44:28 2002 
... 

10 Trace Tip

출처: http://www.codeproject.com/debug/tracetips.asp
디버그 창에서 trace 메시지를 더블클릭하면 에디터 커서를 해당 라인으로 위치시킨다.

TRACE(_T("%s(%i) : Please double click on me!\n"), __FILE__,__LINE__); 

11 모니터 끄기

PostMessage( GetDesktopWindow(),WM_SYSCOMMAND, SC_MONITORPOWER, 2 );  

12 프로세스간 데이터 교환

보통 서로 다른 프로세스끼리 데이터를 주고 받을 때는 shared memory나 clipboard를 이용하는데 간단한 데이터를 주고 받을 때는 사용하기에 부담스러운 면이 있다. 이때 간편하게 이용할 수 있는 프로세스간 데이터 교환 방법을 이용한다.

1. GlobalAddAtom()을 이용한 데이터 전송
예)

서버측: 
 
ATOM atom = GlobalAddAtom("This is test!");  
 
HWND hWnd = ::FindWindow(NULL, "AtomClinet");  
 
::PostMessage(hWnd, WM_MYMSG, 0, atom); // atom값을 전송  
 
클라언트측: 
void CATOMClientDlg::OnGetMsg(WPARAM wParam, LPARAM lParam)  
{  
    char vc_b[256];  
 
    ATOM atom = (ATOM)lParam;  
 
    GlobalGetAtomName(atom, vc_b, 256);  // vc_b를 사용  
}  

2. WM_COPYDATA를 이용한 데이터 교환

// source window에서..  
void CSourceWnd::Write(const void* lpBuf, UINT nCount)  
{  
       CWnd *pTraceWnd = CWnd::FindWindow(TRACEWND_CLASSNAME, NULL);  
 
       if (pTraceWnd) {  
              COPYDATASTRUCT cds;  
 
              cds.dwData = ID_COPYDATA_TRACEMSG /* Flag 용도 */;  
              cds.cbData = nCount;  
              cds.lpData = (void*)lpBuf;  
 
              pTraceWnd->SendMessage(WM_COPYDATA,  
                     (WPARAM)AfxGetApp()->m_pMainWnd->GetSafeHwnd(),  
                     (LPARAM)&cds);  
       } else {  
              AfxMessageBox("윈도우를 찾을 수 없습니다.");  
       }  
}  
 
//////////////////////////////////////////////////////  
// target windows에서..  
 
// 메세지 핸들링 설치  
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)  
       //{{AFX_MSG_MAP(CMainFrame)  
       ON_MESSAGE(WM_COPYDATA, OnTraceMsg)  
       .  
       .  
       .  
       //}}AFX_MSG_MAP  
 
END_MESSAGE_MAP()  
 
LRESULT CMainFrame::OnTraceMsg(WPARAM wParam, LPARAM lParam)  
{  
       COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)lParam;  
 
       // pcds 를 사용한다..  
 
       return 0;  
}  

3. Shared section을 이용한 global variable 공유

#pragma data_seg("Shared") 
volatile LONG g_lSharedData = 0;   // initialized data 
#pragma data_seg() 

또는,

__declspec(allocate("Shared")) int g_nSharedData1 = 0;  // initialized data 
__declspec(allocate("Shared")) int g_nSharedData2;      // uninitialized data 

이때 dll의 프로세스간 데이타 공유라면 dll의 def파일에 데이타 공유 섹션을 지정해 줘야 한다.

SECTIONS 
        .Shared Read Write Shared 

이렇게 하면 g_lSharedData은 이 dll을 로딩한 모든 프로세스에서 공유해서 사용할 수 있다

4. File Map을 이용한 메모리 공유

// 
// 파일을 매핑하는 함수 
 
LPVOID InitFileMapping( HANDLE *phFileMap, char *pszShareName ) 
{      
     LPVOID lpMapView; 
 
     // 파일맵 핸들을 생성한다 
     *phFileMap = CreateFileMapping( (HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 
                              0, sizeof(DWORD), pszShareName ); 
     if( INVALID_HANDLE_VALUE == *phFileMap ) 
          return NULL; 
 
     // 파일을 매핑한다 
     lpMapView = MapViewOfFile( *phFileMap, FILE_MAP_ALL_ACCESS, 0, 0, 0 ); 
     if( NULL == lpMapView ) 
          return NULL; 
 
     // 매핑한 데이터의 선두번지를 리턴 
     return lpMapView; 
} 
 
.... 
 
// 
// 파일맵 데이터를 쓰는 부분(1번 프로세스) 
pdwShare = (DWORD*)InitFileMapping( &hFileMap, dszFileMapName ); 
 
DWORD dwCount = 0; 
 
while( !( GetKeyState( VK_ESCAPE ) & 0x8000 ) ) 
{ 
     *pdwShare = ++dwCount; 
 
     printf( "Write - [%3d] %3d\n", dwCount, *pdwShare ); 
 
     Sleep( 500 ); 
} 
 
... 
 
// 
// 파일맵 데이터를 읽는 부분(2번 프로세스) 
DWORD dwCount = 0; 
 
while( !( GetKeyState( VK_ESCAPE ) & 0x8000 ) ) 
{ 
     printf( "Read - [%3d] %3d\n", ++dwCount, *pdwShare ); 
 
     Sleep( 500 ); 
} 
 
... 
 
// 파일매핑 해제 
UnmapViewOfFile( pdwShare ); 
CloseHandle( hFileMap ); 

13 Log 함수

윈도우 프로그래밍을 하다보면 가끔 dll 소스코드를 디버깅해야할 때가 있는데 이때 MSDEV의 디버거를 이용할수도 없기 때문에 디버깅하기가 까다롭다. 때론 printf대용인 MessageBox를 사용하기도 했는데 아무래도 썩 좋은 방법은 아니다. 이때 로그 함수를 만들어서 로그 파일을 생성하는 방법도 유용한 대안이다.
아래는 Nasser Remy Rowhani라는 외국 친구가 쓴 코드인데 써보니 쓸만한 코드라 생각되서 소개한다. 굳이 디버깅 용도뿐만 아니라 API를 후킹하는 응용에서도 유용하게 쓸 수 있다.

#define LogFile "d:\\Logs\\LOG.txt"                   // log파일이 생성되는 위치 
#define Append(text) AppendLog(text, strlen(text))    // log를 기록하는 함수 
 
HANDLE   hLogFile=0;                // log파일의 핸들 
BOOL     IsLogging=false;           // log파일이 제대로 open되었는지 체크하는 플래그 
 
HANDLE OpenLog(char *Filename);     
BOOL CloseLog(HANDLE h=hLogFile);    
DWORD AppendLog(char *str, DWORD uSize, HANDLE h=hLogFile); 
 
HANDLE OpenLog(char *Filename) 
{ 
        HANDLE hLogFile; 
 
        hLogFile = CreateFile( Filename, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS,0,0); 
        if(hLogFile!=INVALID_HANDLE_VALUE) 
                IsLogging = true;//SetFilePointer(hLogFile, 0,0, FILE_END);//*/ 
         
        return hLogFile; 
} 
 
BOOL CloseLog(HANDLE h) 
{ 
        IsLogging = false; 
        return CloseHandle(h); 
} 
 
DWORD AppendLog(char *str, DWORD uSize, HANDLE h) 
{ 
        DWORD written; 
        if(!IsLogging) return 0; 
 
        SetFilePointer( h, 0, 0, FILE_END ); 
        WriteFile(h, str, uSize, &written, 0); 
 
        return written; 
} 

용례:

hLogFile = OpenLog( LogFile );   // 처음엔 log파일을 open한다. 
   ... 
sprintf(buf, "Error Code: %d", dwErr); 
Append(buf); 
   ... 
CloseLog();                     // 프로그램에서 exit할 때 log를 close 

14 Ctrl+Esc, Alt+Tab, and Alt+Esc를 막는 법

출처: http://msdn.microsoft.com/msdnmag/issues/0700/win32

/************************************************************************ 
Module:  DisableLowLevelKeys.cpp 
Notices: Written 2000 Jeffrey Richter 
**************************************************************************/ 
 
#define _WIN32_WINNT 0x0400 
#include <Windows.h> 
 
LRESULT CALLBACK LowLevelKeyboardProc(int nCode,  
   WPARAM wParam, LPARAM lParam) { 
 
   BOOL fEatKeystroke = FALSE; 
 
   if (nCode == HC_ACTION) { 
      switch (wParam) { 
      case WM_KEYDOWN:  case WM_SYSKEYDOWN: 
      case WM_KEYUP:    case WM_SYSKEYUP:  
         PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam; 
         fEatKeystroke =  
            ((p->vkCode == VK_TAB) && ((p->flags & LLKHF_ALTDOWN) != 0)) || 
            ((p->vkCode == VK_ESCAPE) &&  
            ((p->flags & LLKHF_ALTDOWN) != 0)) || 
            ((p->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) &  
             0x8000) != 0)); 
         break; 
      } 
   } 
   return(fEatKeystroke ? 1 : CallNextHookEx(NULL, nCode, wParam,  
          lParam)); 
} 
 
int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) { 
 
   // Install the low-level keyboard & mouse hooks 
   HHOOK hhkLowLevelKybd  = SetWindowsHookEx(WH_KEYBOARD_LL,  
      LowLevelKeyboardProc, hinstExe, 0); 
 
   // Keep this app running until we're told to stop 
   MessageBox(NULL,  
      TEXT("Alt+Esc, Ctrl+Esc, and Alt+Tab are now disabled.\n") 
      TEXT("Click \"Ok\" to terminate this application and re-enable 
            these keys."), 
      TEXT("Disable Low-Level Keys"), MB_OK); 
   UnhookWindowsHookEx(hhkLowLevelKybd); 
 
   return(0); 
} 

15 About box에 URL 링크 걸기

About 대화상자에 웹으로 가는 링크를 넣는 방법. 이것은 액티브X 컨트롤을 이용하는 것이 가장 손쉬운 방법이다. IE에서 제공하는 오브젝트중 '마이크로소프트 웹브라우저'라는 것이 있는데 다음과 같이 하면 프로젝트에 포함된다.

  1. Project 선택
  2. Add to Project 선택
  3. 컴포넌트와 컨트롤 선택
  4. Registered ActiveX Controls 폴더를 선택
  5. Microsoft Web 브라우저라는 오브젝트 선택
  6. 컴포넌트 클래스 대화 상자에서 확인
  7. 대화상자 편집 모드에서 컨트롤 툴바에 등록된 웹브라우저 컨트롤을 대화상자에 드래그해 화면 디자인을 한다.
  8. Class Wizard를 사용해서 Member Variable 탭에서 IDC_WEBxxx에 대한 데이터 멤버를 지정한다. 이때 Add Member Variable 대화상자의 category 콤보를 컨트롤로 지정
  9. 이제는 OnInitDialog등 원하는 지점에서 m_Webxxx로 지정된 멤버 객체 데이터를 사용해 다음과 같이 작성한다.
     m_WebBrowser.Navigate("http://dasomnetwork.com/~leedw", NULL, NULL, NULL, NULL); 
     

위와 같이 해주면 대화상자가 기동 시에 동우의 홈페이지가 대화 상자에 나타날 것이다. 참고로 원격 사이트가 아닌 로컬에 html 문서를 미리 작성해서 가지고 있다가 URL 부분에 "file:///c:/My Documents/DHTML/ftv20/ftexample.html"라고 해주면 html이 나타난다.

16 특정 파일 오픈시 바로 프로그램 불러오기

ACDSee라는 그래픽 뷰어 프로그램같이 해당 그림 파일을 더블클릭하면 프로그램이 실행되면서 그 데이타를 불러오는 것과 같은 기능을 볼 수 있다. 이런 임의 파일의 싨행은 WinExec, ShellExecute API를 사용하면 구현할 수 있다. 여기서 원하는 것은 실행 파일 및 문서 파일들이므로 ShellExecute를 사용해야 한다.

HINSTANCE ShellExecute( 
    HWND hwnd,  
    LPCTSTR lpOperation, 
    LPCTSTR lpFile,  
    LPCTSTR lpParameters,  
    LPCTSTR lpDirectory, 
    INT nShowCmd 
);       

자세한 것은 MSDN 참조

17 드래그 앤 드롭으로 파일 오픈

윈도우 탐색기나 바탕 화면에서 드래그 앤 드롭으로 파일을 불러서 처리하고자 할 경우. 응용 프로그램에서의 드래그 앤 드롭의 처리는 InitInstance() 처리 부분에서 DragAcceptFiles라는 멤버 함수 또는 API를 수행하면 된다. 그러면 윈도우는 드래그 앤 드롭되는 것이 존재할 때 WM_DROP_FILES라는 메시지를 받는다.(이 메시지는 OnDropFiles(..)라는 멤버 함수가 처리한다.) 이 메시지에서 적당히 처리해주면 된다. 이것은 OLE와는 다른 것이니 주의한다.

18 app가 트레이에만 띄우도록 하려면

winamp같은 어플리케이션이 실행될 때 윈도우가 뜨더라도 상태바에는 뜨지 않고 트레이에만 생기는 모습을 보았을 것이다. 이것을 구현하려면 다음의 코드를 사용한다

void HideApplication(HWND hwnd) 
{ 
   ShowWindow(hwnd, SW_HIDE); 
   ShowOwnedPopups(hwnd, FALSE); 
    
   SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, 
                 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 
} 

CApp 클래스에 위와 같은 이름을 가진 멤버 함수가 존재하는데 그 함수를 사용해도 된다. 실제 응용 프로그램을 태스크 바에서 사라지도록 하는 것은 SetWindowPos API에 있다. 또한 Shell_NotifyIcon이란 API가 있는데 이것을 이용해봐도 될 것이다.

WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( 
    DWORD dwMessage,  
    PNOTIFYICONDATA pnid 
);       

주의해야할 것은 응용 프로그램은 메인 프레임을 가져야 하나 메인 프레임을 활성화시키지는 않도록 한다. 활성화만 안시키면 <Alt-Tab>에서 나타나지 않을 것이다. 대신 별도의 윈도우를 WS_POS으로 만들어 이를 트레이에 등록해야 한다. 즉, 트레이에 등록된 팝업 스타일의 윈도우가 모든 메시지를 가로채도록 하기 위함이다.

19 single thread 어플리케이션에 취소 버튼 구현

출처: http://www.fhcf.net/misc/id_ws/database/essays/fboyjoe/cancel/cancel.html

어플리케이션을 개발하다가 간혹 processor-intensive한 루틴을 작성해야할 때가 있다. 단일 쓰레드로 이를 구현했을 때는 하나의 루틴이 프로세서 자원을 다 잡아 먹기 때문에 윈도우즈 메시지를 처리할 수 없는 문제에 부딪힌다. 그래서 이때는 processor 자원을 많이 요구하는 연산 부분은 thread로 구현해서 돌려 놓고 원래의 thread는 윈도우 메시지를 처리하는 방법으로 구현을 한다. 그런데 별도의 쓰레드를 만들지 않고 단일 쓰레드에서 processor 자원 요구량이 많은 모듈과 윈도우즈 메시지를 동시에 처리할 수 있는 기법이 있어 소개한다.

결론을 미리 말하자면, processor-intensive한 연산 안에 message pump를 두어 메시지를 처리하도록 하며 유저가 언제든 취소를 원할 때 연산을 취소할 수 있도록 플래그를 검사하도록 하는 것이다. 플래그는 "취소"버튼을 눌렀을 경우에 셋팅되도록 한다. 핵심 부분을 코드로 살펴보면 다음과 같다.

void StartProcessing() 
{ 
   hCancelDialog = CreateDialog (inst, MAKEINTRESOURCE(IDD_DIALOG2),    
                               hwnd, CancelDialog); 
   ShowWindow (hCancelDialog, TRUE);          // 취소 버튼이 있는 대화 상자 출력 
   cancelled = 0;       // 취소 버튼이 눌렸는지에 대한 플래그 
    
   for ( ...) {       // 여기부터가 processor-intensive한 연산 부분이다. 
        .... 
    
        if(!MessagePump()) { // 연산 수행중에도 메시지를 처리할 수 있도록 한다. 
             return;       // WM_QUIT 메시지를 받으면 루틴을 빠져나간다. 
        }  
 
        if(cancelled) { 
            // 취소 플래그가 셋팅되면 연산을 중지한다. 
    
         .... 
    
   } 
   DestroyWindow(hCancelDialog); 
   EnableWindow(hwnd, TRUE); 
    
   .... 
 
BOOL MessagePump()        // 메시지 펌프 
{ 
   MSG msg; 
    
   while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {  // 메시지 큐에 메시지가 있는지 체크 
      if (!GetMessage(&msg, NULL, 0, 0)) {    // 메시지를 가져온다. 
         PostQuitMessage(0); 
         return FALSE; 
      } 
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
   } 
   return TRUE; 
} 

예제 프로젝트 : CancelTestApp.zip

20 PC시간 셋팅하기

void CSetDateDlg::OnOK()  
{ 
        // TODO: Add extra validation here 
 
        CTime time( 2005, 1, 10, 22, 17, 43 );  
        SYSTEMTIME sysTime; 
 
        time.GetAsSystemTime(sysTime); 
 
        if (getSystemType() == VER_PLATFORM_WIN32_NT) 
        { // NT 계열이면 시간을 바꿀 수 있는 특권을 가져와야 한다. 
                HANDLE hToken; 
                TOKEN_PRIVILEGES tp; 
                LUID luid; 
 
                OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); 
                LookupPrivilegeValue(NULL, "SeSystemtimePrivilege", &luid); 
 
                tp.PrivilegeCount = 1; 
                tp.Privileges[0].Luid = luid; 
                tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 
                AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL); 
        } 
 
        SetLocalTime(&sysTime); 
         
        CDialog::OnOK(); 
} 
 
void CSetDateDlg::getSystemType() 
{ 
        OSVERSIONINFO osv; 
 
        osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 
        GetVersionEx(&osv); 
 
        return osv.dwPlatformId; 
} 



출처: http://www.dasomnetwork.com/~leedw/mywiki/moin.cgi/win32_2f_c4_da_b5_e5_c6_c1?action=highlight&value=VisualC%2B%2B%C6%C1%C1%F6%B5%B5

Posted by 나비:D
:
 

윈도우 채팅 프로그램(서버)


이전에 Mutex 를 이용하여 만들었던 채팅 프로그램의 업그레이드(?)..

윈도우 모드로 서버 제작


[윈도우 채팅 서버 세팅]
















처음 프로젝트 생성시 Win32 Application 모드를 선택한다.  여기서 프로젝트 이름은 Server.  cpp 는 Main.cpp 생성


사실 API 나 MFC 를 조금이라도 안봤다면 여기 나오는 부분이 좀 이해가 안갈수도 있다. 여기선 그냥 이런 역활을 하는 구나 라고만 이해하면 될거 같다.













단축키 [CTRL] + [R]  /* Menu - Insert - Resource */ 를 누르게 되면 이와 같은 창이 나온다. Dialog 창을 선택하고 New 를 눌러준다.


















이런 화면이 나오게 된다. 왼쪽에 네모난게 Dialog .. 즉 대화상자이다. API 나 MFC 부분에서 좀더 자세히 공부를 하면 된다. 


서버는 하얀색창에서 접속된 클라이언트. 종료된 클라이언트를 출력하기 위함이니 단순하게 만들면 된다. ( 좀더 멋지게 꾸며도 상관없다.. )

OK 와 Cancel 을 선택한뒤 Delete 해주자.

Dialog 가 선택된 곳에서 단축키 [ALT] + [Enter]  /* 마우스 오른쪽 버튼 - Properties */ 를 눌러준다.







ID 는 이 대화상자의 이름이다. 여기선 IDD_CHATSERVER 로 설정.    Caption 은 대화상자에 위에 나오는 글씨이다. Server 로 설정(아무거나 해주면 된다)

Font 에서 원하는 글꼴을 설정해주어도 된다.. ( 항상 습관적으로 돋움 체를 선택하고 있다.. )


그리고 오른쪽을 보면  가로 2줄과 세로로 길게 아이콘이 정렬되어 있는 것이 있다. 거기에서 ListBox 란 아이콘을 선택한다. (여기선 오른쪽 위에서 5번째였다. 아닐수도...)

ListBox를 선택하고 Dialog 에 클릭을 하면 아래와 같이 된다.


















ListBox 역시 단축키 [ALT] + [Enter] 로 이름을 설정해주자.







ID : IDC_LISTCHAT 으로 설정


Dialog 와 ListBox의 크기를 간단한 드래그로 원하는 대로 조정해주면 된다.


* 서버 최종 모습













서버는 단순하니 요정도만..^^    저장을 누르게 되면 다른이름으로 저장 이 뜨면서 기본으로 Script1.rc 로 저장할건지 물어본다.

이때 프로젝트를 생성한 폴더가 맞는지 확인하고 프로젝트에 생성한 폴더에 그대로 저장을 하면 된다.









리소스는 이와 같이 저장을 하게 된다. 앞으로 다른 리소스를 만들게 되면 계속 저기에 저장이 될것이다. 이거는 API 때 자세히 공부하면 된다.

저장을 했다고 끝이 아니다. WorkSpace 창을보면 Source Files 에는 Main.cpp 가 있다. Header Files 폴더를 클릭하면 아무것도 나오질 않는다.










Header Files 에서 마우스 우클릭을 하고 Add Files to Folder 를 선택한다.










Insert Files into Project     을 보면 resource.h 파일이 생겼다. 이 헤더파일에 전에 만든 Dialog 정보가 들어있다.

resource.h 파일을 선택하고 다시 Add Files to Folder 를 선택해 Script1.rc(Resource Files 폴더)도 선택해주고 저장하면 된다.












단축키 [ALT] + [F7] /* Menu - Project - Settings */

C/C++ - Category : Code Generation                    설정

           Use run-time library : Multithreaded DLL    설정












단축키 [ALT] + [F7]  /* Menu - Project - Settings */

Link - Obect/library modules : WS2_32.lib 추가


이걸로 기본 세팅이 끝난거다..



[윈도우 채팅 서버 소스]


서버 의 기본골격은 도스 기반에서 만든 Mutex 방식과 비슷하다. 다만 윈도우 모드를 사용하면서 필요한 함수들이 있다.

이런 함수들은 API 나 MFC 에서 보면 공부하면 된다. 여기선 그냥 간단히 이해하면 될 것 같다.


#include <stdio.h>

#include <stdlib.h>

#include <process.h>

#include <windows.h>

#include "resource.h"                                    // Header Files 에 추가한 resource.h 를 선언해주어야 한다.


#define BUFSIZE 1024


SOCKET g_ServSocket  = NULL ;                      

SOCKET g_ClientSocket = NULL ;                     

SOCKET g_ClntSock[10] ;                                // 지금 서버에 접속하는 클라이언트는 최고 10개 까지 접속할수 있다. (늘려줘도 상관없다.)


HWND g_hwndList = NULL ;                              // 대화상자에서 만든 하얀색(ListBox)부분.


SOCKADDR_IN servAddr ;

SOCKADDR_IN clientAddr ;

HANDLE hThread1, hThread2 ;

HANDLE hMutex ;

DWORD dwThreadID1, dwThreadID2 ;


int clientAddrSize ;

int clntNum = 0 ;


BOOL CALLBACK DlgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) ;   // 메시지 처리함수

void AddStringToList( HWND hWndList, char *szItem ) ;                                            // 화면에 글을 출력(printf 로 출력하는게 아니다...)

void OnClose( HWND hWnd ) ;                                                                              // 대화상자 종료

BOOL OnInitDialog( HWND hWnd, HWND hWndFocus, LPARAM lParam ) ;                          // 대화상자 초기화

unsigned int __stdcall ThreadMain( void *arg ) ;                                                     // 쓰레드 메인( 이전 도스에서 main 함수이다.)

void SendMsg( char *str, int len ) ;                                                                      // 모든 클라이언트에게 메시지를 전송한다.

DWORD WINAPI ClinetConn(void *arg);                                                                    // 메시지를 받으면 SendMsg 함수 호출. 종료 처리


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// WinMain :  윈도우 모드는 프로그램의 시작점이 main 이 아니라 WinMain 이다.

// hInstance  : 프로그램의 인스턴스 핸들이다. 인스턴스는 클래그사 메모리에 실제로 구현된 실체를 의미한다.

//                  자세한건 API 에서 공부하고 여기선 그냥 프로그램이 실행될때 이를 인스턴스라 생각하자.

// hInstance 는 많은 함수들이 요구하기 때문에 g_hlnst(전역) 에 값을 넣은후 g_hlnst를 사용한다. 여기선 pass ~ 나중에 공부..^^

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,

                                          LPSTR lpCmdLind, int nCmdShow )

{

     WSADATA wsaData ;

     WSAStartup( MAKEWORD( 2, 2 ), &wsaData ) ;                       

     // 쉽게 대화상자를 나오게 한다고 하자...

     DialogBox( hInstance, MAKEINTRESOURCE( IDD_CHATSERVER ), NULL, DlgProc ) ;        // DlgProc .. CALLBACK 메시지 처리함수이다.


     WSACleanup();

     return 0;

}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// 이전에 도스에서 main 역활을 하던 것이다. main 자체가 쓰레드화 되서 계속 실행되는 것이다.

// 이 함수는 OnInitDialog 함수에서 호출한다.(쓰레드 생성)    내용은 전에 main 과 동일하다.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned int __stdcall ThreadMain(void *arg)

{

     char *servPort = "9000";

     char ServMsg[100];


     hMutex = CreateMutex( NULL, FALSE, NULL);                                                      // Mutex 생성
     if( hMutex == NULL)     AddStringToList(g_hwndList, "CreateMutex error");              // AddStringToList 함수는 대화상자에 출력해주는 함수


     g_ServSocket = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
     if( g_ServSocket == INVALID_SOCKET )     AddStringToList(g_hwndList, "socket error");
   
     memset( &servAddr, 0, sizeof( servAddr));
     servAddr.sin_family = AF_INET;
     servAddr.sin_addr.s_addr = htonl( INADDR_ANY );
     servAddr.sin_port = htons(atoi( servPort ));


     if(bind( g_ServSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)    AddStringToList(g_hwndList, "bind error");

     if( listen( g_ServSocket, 5) == SOCKET_ERROR )     AddStringToList(g_hwndList, "listen error" );
   
     AddStringToList(g_hwndList, "서버 대기중입니다.");
 
     while(1)
     {
          clientAddrSize = sizeof(clientAddr);
          g_ClientSocket = accept( g_ServSocket, (SOCKADDR*)&clientAddr, &clientAddrSize );
 
          WaitForSingleObject(hMutex, INFINITE);
          g_ClntSock[clntNum++] = g_ClientSocket;
          ReleaseMutex(hMutex);


          sprintf(ServMsg, "접속된 클라이언트 : ");
          strcat(ServMsg, inet_ntoa( clientAddr.sin_addr) );
          AddStringToList(g_hwndList, ServMsg);

          // 쓰레드 . ClinetConn . 

          hThread1 = (HANDLE)_beginthreadex( NULL, 0, (unsigned int(__stdcall *)(void*))ClinetConn,
                                                                  (void*)g_ClientSocket, 0, (unsigned*)&dwThreadID1);
     }
 
     closesocket(g_ServSocket);
     return 0 ;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// 메시지 처리함수란 메시지가 발생할때 프로그램의 반응을 처리하는 것이다. 

// uMsg 는 어떤 메시지인가..  wParam 과 lParam 은 메시지에 대한 부가정보이다.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
      switch(uMsg)
      {
           case WM_INITDIALOG: OnInitDialog( hWnd, hWnd, lParam );  break;         // 초기화
           case WM_CLOSE:  OnClose(hWnd); break;                                        // 대화상자 종료
      }
      return FALSE;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// 대화상자 종료. 이게 없으면 대화상자에서 오른쪽 맨위 X 표시를 눌러도 안닫힌다..;;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void OnClose(HWND hWnd)
{
      closesocket(g_ServSocket);                                
      EndDialog(hWnd, 0);
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// 대화상자(ListBox)에 글씨를 출력한다.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void AddStringToList(HWND g_hwndList, char *szItem)
{
     SendMessage(g_hwndList, LB_ADDSTRING, 0, (LPARAM)szItem);
     int nCount = (int)SendMessage(g_hwndList, LB_GETCOUNT, 0, 0);        // 칸을 밀리게 함.
 
     SendMessage(g_hwndList, LB_SETTOPINDEX, nCount-1, 0);
}

BOOL OnInitDialog( HWND hWnd, HWND hWndFocus, LPARAM lParam )
{
     //윈도우 핸들 구하기
     g_hwndList = GetDlgItem(hWnd, IDC_LISTCHAT);                               // 대화상자 핸들

     // 쓰레드 메인 함수.. 여기서 만들어 주고 있다.

     hThread2 = (HANDLE)_beginthreadex( NULL, 0, (unsigned int(__stdcall *)(void*))ThreadMain,
                            (void*)g_ServSocket, 0, (unsigned*)&dwThreadID2);
 
     return TRUE;
}


DWORD WINAPI ClinetConn(void *arg)
{
     SOCKET temp = (SOCKET)arg ;
     int strLen = 0;
     char message[BUFSIZE];
     char ServMsg[100];

     while((strLen = recv( temp, message, BUFSIZE, 0)) > 0)                          // recv  를 받게 되면
          SendMsg(message, strLen);                                                         // SendMsg 란 함수를 호출한다.
 
     WaitForSingleObject( hMutex, INFINITE);                                               // 임계영역
     for( int i = 0 ; i < clntNum; i++)
     {
          if( temp == g_ClntSock[i])                                                            // 종료되는 클라이언트
          {
               sprintf( ServMsg, "%d", i);
               strcat( ServMsg, " 번 클라이언트 종료");
               AddStringToList(g_hwndList, ServMsg);
     
               for( ; i < clntNum -1 ; i++)
                    g_ClntSock[i] = g_ClntSock[i+1];                                          

               break;
          }
     }
     clntNum--;                                                                                     // 전체 클라이언트 갯수를 줄여준다.

     ReleaseMutex(hMutex);
     closesocket(temp);
     return 0 ;
}


void SendMsg(char *str, int len)
{
     WaitForSingleObject( hMutex, INFINITE);

     for( int i = 0 ; i < clntNum ; i++)                                                          // Clientconn 함수에서 호출.  받은 메시지를
          send( g_ClntSock[i], str, len, 0 );                                                  // 전체 클라이언트에게 전송한다.

     ReleaseMutex(hMutex);
}

Posted by 나비:D
:

7.차일드 윈도우 컨트롤

본장에서는 버튼,체크버튼,라디오버튼,리스트박스,콤보박스,정적윈도우,스크롤 윈도우 등을 출력하는 방법에 대해서 설명합니다. 이런 윈도우들은 자신이 독립적으로 출력되는 것이아니라 어느 윈도우 내부에 자식윈도우로써 출력됩니다. 윈도우 프로그램에서 하나의 윈도우 밑에 여러 윈도우들이 등록되는 것이 일반적이고 이런 윈도우들도 자신만의 프로시저들이 있으며 이 프로시저들은 메시지를 받고 필요한 메시지를 부모윈도우에 전달합니다. 본장에서는 차일드 윈도우들의 메시지 프로시저를 가로체는 방법과 차일드윈도우로부터 전달된 메시지를 처리하는 방법에 대해서 설명합니다.


차일드(Child) 윈도우 만들기

“모든 윈도우들이 차일드(Child) 윈도우가 될 수있다!” 결국 “윈도우위에 윈도우를 윈도우 밑에 윈도우를 붙일수 있다” 이것은 윈도우 프로그램의 기본입니다. 차일드 윈도우는 정의를 하자면 WinMain에서 만들어진 기본윈도우 안에 새로운 윈도우를 만들 때 이것이 차일드 윈도우인것입니다. 본장에서는 모든 차일드윈도우를 설명하기 보다. 차일드 윈도우로서만 생을 마감해야할 운명을 가진 윈도우에 대해서 설명합니다. 버튼,체크버튼,라디오 버튼등은 자신 혼자서 메인 윈도우로 설정된다는 것이 무의미 합니다. 물론 버튼 하나만 윈도우로 출력할수 있습니다. 그렇지만 버튼이라는 윈도우는 어떤 메인윈도우에서 특정 항목을 실행하기위해서 부수적으로 존재하는 윈도우라고 볼수있지 이것을 주메인 윈도우로 설정한다는 것은 조금 문제가 있다는 것입니다. 본장에서 설명하는 차일드윈도우란 그 성격이 메인 윈도우로 존재하기 보다 메인윈도우의 자식윈도우로 존재하는 것이 적당한 성격의 윈도우를 이야기 합니다.

차일드윈도우를 만들때도 윈도우를 만드는 함수 CreateWindow를 사용합니다. 메인 윈도우에서는 9번째인자 즉 HMENU hMenu를 메뉴 설정 핸들로 사용하였으나 차일드윈도우에서는 이인자를 자신의 윈도우 ID로 사용합니다. 다음은 CreateWindow를 이용하여 버튼을 만든 예제입니다.

                CreateWindow("button","버튼",

                        WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON ,                                           10,10,100,40,hwnd,(HMENU)1,hinst,NULL);

CreateWindow두번째 인자 즉 DWORD dwStyle 의 값에는 일반적인 윈도우 스타일 WS 계열과 함께 차일드 윈도우의 스타일을 결합시킵니다.

위의 예에서는 WS_CHILD|WS_VISIBLE 와 함께 BS_BUSHBUTTON이라는 버튼스타일중의 하나를 첨부시켰습니다. 또한 HWND hWndParent 에는 부모 윈도우 핸들을 설정시켰습니다. 결국 차일드 윈도우를 만들고자 하면 다음과 같은 형태가 됩니다.

   CreateWindow(차일드윈도우이름,차일드윈도우에 출력할 텍스트,

               차일드윈도우들이 가지고 있는 스타일과 일반윈도우 스타일결합

               x,y,cx,cy,부모윈도우핸들,자기 아이디번호, 인스턴스,윈도우를만들

                        때 필요한 데이터)

여기에서 “차일드윈도우 이름”이란 윈도우 시스템에서 설정한 이름입니다. 버튼윈도우는 "button"이며 에디터 박스는 "edit" 리스트 박스는 "listbox" 콤보박스는 "combobox" 스크롤 바는 “scrollbar” 로 설정해야 합니다. 차일드윈도우의 스타일은 각각의 윈도우에 따라 다릅니다. button윈도우들은 BS_ 계열의 스타일을 가지고 있고  에디터 박스는 ES_ 계열의 스타일을 가지고 있습니다. 이부분에 대해서는 차일드윈도우를 만들어가면서 하나하나 설명해 가겠습니다.

차일드 윈도우에서 이벤트가 발생되면 이것은 부모 윈도우 전달됩니다. 전달 될 때 스크롤 윈도우를 제외한 대부분의 차일드 윈도우들이 WM_COMMAND로 전달됩니다. 전장에서 WM_COMMAND로 메뉴 ID들이 전달된다고 하였습니다. 그렇다면 차일드윈도우들이 전달하는 WM_COMMAND와 메뉴 ID에서 전달하는 WM_COMMAND가 구별될 필요성이 있는데 이것은 어떻게 구별될까요? 표1이 차일드 윈도우 들이 전달하는 WM_COMMAND와 메뉴에서 전달되는 WM_COMMAND의 차이를 보여줍니다.

                 (표1 ) 차일드윈도우와 메뉴의 차이

 

 LOWORD(wParam)

 HIWORD(wParam)

 lParam

 차일드윈도우

  차일드 윈도우 ID

 이벤트 메시지

 윈도우핸들

  메뉴 ID

 메뉴 ID

  0

 0

차일드 윈도우에서는 LOWORD(wParam)에 자신의 ID를 전달합니다. 즉 CreateWindow에서 9번째 인자 HMENU hMenu 에 설정한 값을 전달합니다. 메뉴에서는 HIWORD(wParam)에 아무값도 없으나 차일드 윈도우에서는 이벤트 메시지값이 전달됩니다. 예를 들어 버튼윈도우에서 마우스로 버튼을 클릭했다면 이때 발생되는 이벤트가 BN_CLICKED 인데 이값이 HIWORD(wParam)에 전달된다는 것입니다. lParam에는 메시지를 보낸 차일드 윈도우의 윈도우 핸들이 전달됩니다. 따라서 차일드윈도우에서 보내는 WM_COMMAND와 메뉴에서 보내는 ID가 분명히 서로 다릅니다. 따라서 차일드 윈도우에 설정된 ID와 메뉴에서 설정된 ID가 중복되더라도 분별은 할수 있습니다. 그러나 일반적으로 메뉴에서 설정된 ID번호를 중복해서 차일드 윈도우에 사용하지은 않습니다. 이것은 좋지 않는 형태입니다. 중복해서 사용한다면 WM_COMMAND에서 차일드윈도우인지 메뉴인지 확인을 하기위해서 또한번 검사를 해보아야 하기 때문입니다.


버튼 윈도우

버튼윈도우는 매우 많은 종류가 있습니다. 표2는 버튼윈도우에서 설정하는 BS_계열 스타일에 의해서 변화되는 버튼의 종류를 나열한것입니다.

표2에 설정한 스타일 외에서 버튼의 속성을 변경하기위한 스타일 들이 있습니다. 사용자가 직접 윈도우를 그릴수 있는 BS_OWNERDRAW 체크버튼이나 라디오버튼에서  문자가 버튼 좌측에 출력되는 BS_LEFTTEXT  스타일들이 이것입니다.



       (표2)_ 다양한 버튼 윈도우 스타일



 버튼 윈도우

  추가되는 BS_계열 스타일

 일반적인 버튼

 BS_PUSHBUTTON

 윈도우 기본 버튼

 BS_DEFPUSHBUTTON

 체크 박스

 BS_CHECKBOX

 라디오 버튼

 BS_RADIOBUTTON

 3가지 상태 체크박스

 BS_3STATE

 그룹 박스

 BS_GROUPBOX

 자동적으로 전환되는 체크박스

 BS_AUTOCHECKBOX

 자동적으로 전환되는 라디오버튼

 BS_AUTORADIOBUTTON

예를 들어서 체크박스를 만들고자 한다면 다음과 같이 할수 있습니다.


                CreateWindow("button","체크 박스",

                        WS_CHILD|WS_VISIBLE|BS_CHECKBOX,                                               10,50,100,40,hwnd,(HMENU)3,hinst,NULL);

보통 차일드윈도우들은 윈도우 스타일에서 WS_CHILD와 WS_VISIBLE 두 개를 기본적으로 사용하고 그리고 차일드윈도우의 스타일을 첨부합니다.


버튼 윈도우 메시지

버튼 윈도우에서 어떤 이벤트가 발생되었을 경우 BN_계열의 메시지를 보낸다고 설명하였습니다.  표3은 버튼윈도우들이 보내는 메시지 리스트입니다.

       (표3) 버튼 윈도우 이벤트 메시지

 이벤트 메시지

 내용

BN_CLICKED

 버튼을 좌측 마우스버튼으로 클릭하였음

BN_DBLCLK

 버튼을 좌측 마우스버튼으로 더블 클릭하였음

BN_DISABLE

 버튼이 비활성화 되었음

BN_DOUBLECLICKED

 라디오버튼이나 BS_OWNERDRAW 스타일 버튼에서 좌측 마우스 버튼을 더블클릭하였음

BN_HILITE

 사용자가 버튼을 선택하였을 때 발생하는 메시지

BN_UNHILITE

 사용자가 버튼에서 다른 윈도우로 전환되었을 때 메시지

BN_SETFOCUS

 버튼에 포커스가 주어졌을 때 메시지

BN_KILLFOCUS

 버튼에서 포커스가 사라졌을 때 메시지

BN_PAINT

 버튼이 다시 그려질 때 발생하는 메시지

예를 들어서 ID가 1번인 버튼윈도우를 만들었다면 다음과 같은 방법으로 이벤트에 해당하는 프로그램을 할수 있습니다.


case WM_COMMAND:

       if(LOWORD(wParam)==1) //1번으로 설정된 버튼윈도우이면

       {

          switch(HIWORD(wParam))

          {

               case BN_CLICKED:

                       //버튼을 클릭했을 때 수행 내용 작성

                       break;

               case BN_SETFOCUS:

                       //버튼에 포커스가 주어졌을대 메시지 작성

                       break;

                       :

          }

       }

       

위의 내용에서 보면 버튼 몇 개정도 만들어 놓고 메뉴 몇 개 정도 만들면 WM_COMAND메시지에서 switch문이 여러개가 아주 복잡하게 연결될것입니다.

이럴경우에는 WM_COMMAND안에 모든 내용을 수행하는 것은 무리입니다. 결국 함수 포인터를 사용하는 것이 편리합니다. 예를 들어 버튼 3개에 대한 각각의 프로세싱을 처리한다면

//버튼 1번 함수

void Button1(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) //버튼 2번함수

void Button2(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

//버튼 3번함수

void Btton3p(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam


void (*Button[3])(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM                                lParam)

       ={Button1,Button2,Button3};

와 같이 설정한후에

case WM_COMMAND:

               Button[LOWORD(wParam)(hwnd,HIWORD(wParam),wParam,                       lParam);

               break;

라고 하여 각각의 프로세스를 함수로 나누어서 설정하는 것이 좋습니다.

본내용에 대해서 이해가 안가시는 부분은 C언어의 포인터 부분의 함수포인터를 다시 보시기 바랍니다.


체크버튼,라디오버튼 상태 바꾸기

체크 버튼이나 라디오 버튼을 클릭하면 한번은 선택되었다가 다시 누르면 선택이 해제 됩니다. 이렇게 버튼의 체크상태를 변경할 때 사용하는 방법은 차일드 윈도우에  BM_SETCHECK 라는 메시지를 보내는 것입니다.

일반적으로 체크버튼 상태를 기록하는  BOOL 형의 변수를 설정한다음 초기에는 0으로 설정합니다.

         static BOOL radioflag=FALSE;

이함수는 값이 변경되지 말아야 하기 때문에 static로 설정합니다. 이렇게 한후 BN_CLICKED라는 메시지가 오면 이플러그를 0이면 1로 1이면 0으로 전환시킵니다. 그리고 이값을 wParam값으로 버튼윈도우에 보내면 됩니다.

        case BN_CLICKED:

                radioflag=1-radioflag;

                //버튼 윈도우에 메시지르 보낸다.

                SendMessage((HWND)lParam,BM_SETCHECK,

                        (WPARAM)radioflag,(LPARAM)NULL);

                break;



버튼 윈도우 예제 ButtonEx

ButtonEx는 버튼과 체크박스,라디오버튼,그룹박스등을 화면에 출력하고 이 차일드로부터 이벤트를 받아서 출력하는 예제입니다.


(프로그램 소스)

//버튼 윈도우 예제

//ButtonEx.c

#include <windows.h>


HINSTANCE hinst;


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

     {

     static char szAppName[] = "ButtonEx" ;

     HWND        hwnd ;

     MSG         msg ;

     WNDCLASSEX  wndclass ;


     wndclass.cbSize        = sizeof (wndclass) ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

     wndclass.lpszMenuName  = NULL ;

     wndclass.lpszClassName = szAppName ;

     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;


     RegisterClassEx (&wndclass) ;

        hinst=hInstance;

     hwnd = CreateWindow (szAppName,    

                           "버튼윈도우 예제:ButtonEx",  

                    WS_OVERLAPPEDWINDOW, 

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    NULL,                

                    NULL,                

                    hInstance,           

                           NULL) ;                    


     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;


     while (GetMessage (&msg, NULL, 0, 0))

          {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

          }

     return msg.wParam ;

}

//버튼에 이벤트에 해당하는 함수

void Button(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

       switch(iMsg)

       {

               case BN_CLICKED:

               MessageBox(hwnd,"버튼 이 클릭되었습니다.","버튼메시지",MB_OK );

               break;

       }


}

//체크 버튼 이벤트에 해당하는 함수

void CheckButton(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

 static BOOL checkflag=FALSE;

       switch(iMsg)

       {

       case BN_CLICKED:

       checkflag=1-checkflag;

       SendMessage((HWND)lParam,BM_SETCHECK,(WPARAM)checkflag,(LPARAM)NULL);

               break;

       }


}

//라디오 버튼에 해당하는 이벤트 함수

void RadioButton(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

        static BOOL radioflag=FALSE;

       switch(iMsg)

       {

       case BN_CLICKED:

               radioflag=1-radioflag;

               //버튼 윈도우에 메시지르 보낸다.

               SendMessage((HWND)lParam,BM_SETCHECK,(WPARAM)radioflag,(LPARAM)NULL);

               break;

       }


}

//버튼윈도우와 맞게 각 함수의 배열을 만든다.

void (*ButtonProcess[6])(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

={Button,Button,CheckButton,RadioButton,Button,Button};


LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

     HDC         hdc ;

     PAINTSTRUCT ps ;

     switch (iMsg)

          {

          case WM_CREATE :

                           //차일드 윈도우를 만든다.

                       CreateWindow("button","버튼",

                       WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON ,

                       10,10,100,40,hwnd,(HMENU)1,hinst,NULL);

                       CreateWindow("button","OK",

                       WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,                        120,10,100,40,hwnd,(HMENU)2,hinst,NULL);

                       CreateWindow("button","체크 박스",

                       WS_CHILD|WS_VISIBLE|BS_CHECKBOX,

                       10,50,100,40,hwnd,(HMENU)3,hinst,NULL);

                       CreateWindow("button","라디오버튼",

                       WS_CHILD|WS_VISIBLE|BS_RADIOBUTTON,                          120,50,100,40,hwnd,(HMENU)4,hinst,NULL);

                       CreateWindow("button","3D State",

                       WS_CHILD|WS_VISIBLE|BS_3STATE,                                 10,100,100,40,hwnd,(HMENU)5,hinst,NULL);

                       CreateWindow("button","Group Box",

                       WS_CHILD|WS_VISIBLE|BS_GROUPBOX,

                       120,100,100,40,hwnd,(HMENU)5,hinst,NULL);

               return 0 ;

          case WM_PAINT :

                  hdc = BeginPaint (hwnd, &ps) ;

                  EndPaint (hwnd, &ps) ;

               return 0 ;

                 case WM_COMMAND:

               //함수 배열 번호와 LOWORD(wParam)-1과 같은 함수 실행

               ButtonProcess[LOWORD(wParam)-1]

                               (hwnd,HIWORD(wParam),wParam,lParam);

                       break;                                                 

          case WM_DESTROY :

               PostQuitMessage (0) ;

               return 0 ;

          }


     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

(프로그램 소스끝)










               (그림 1) ButtonEx 출력 결과



차일드 윈도우 프로시저 가로채기

버튼이나 스크롤바, 리스트박스,에디터박스 등은 CreateWindow에 의해서 위도우가 만들어지면 자신의 기능을 담당하는 기본적인 프로시저들이 함께 등록됩니다. 버튼의 경우 자신을 클릭했으면 버튼이 들어가고 그리고 부모 윈도우에 WM_COMMAND와 HIWORD(lParam) BN_CLICKED라는 메시지를 보내는 기능을 합니다. 즉 버튼이 하는 일반적인 기능을 가진 프로시저가 내장되어 있다는 뜻입니다. 에디터 박스와 리스트박스,콤보 박스, 스크롤바도 각각의 기능에 맞는 프로시저들이 있습니다. 에디터 박스는 글자를 입력하면 화면에 출력하는 기능 및 여러 기능키에의해서 커서가움직이는 기능들이 있으면 리스트박스는 데이터를 한라인씩 기록하고 선택하였을 때 특정 선택값을 알리며 그값을 리턴하는 기능들이 있습니다. 따라서 차일드 윈도우는 단지 CreateWindow에 의해서 도시만 하고 부모윈도우는 모든 내용을 차일드 윈도우에 전담시킨다음 WM_COMMAND를 받아서 이벤트에 해당되는 내용만 처리하면 됩니다.

그러나 때로는 차일드 윈도우 내부로 들어가서 차일드 윈도우에서 받는 메시지를 가로채고 싶을 경우가 있습니다. 예를 든다면 에디터 박스 차일드 윈도우로 들어가서 WM_KEYDOWN를 가로채고 여기에서 특정 키늘 누르면 에디터박스에서 포커스가 사라지게 한다든지 Tab키를 누르면 다음 차일드윈도우로 포커스가 이동하게 한다는지 하는 것은 차일드 윈도우 프로시저에서 처리 해야 합니다.

이렇게 프로시저를 새로운 프로시저를 바꿀때는 GetWindowLong를 사용합니다.

예를 들어 하나의 버튼을 만들고 이버튼의 프로시저를 바꾸고자 한다면 다음과 같이 할수 있습니다.

 HWND butwnd;

 WNDPROC butProc;


       butwnd=CreateWindow("button","버튼     ",

                       WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON ,

                       10,10+i*40,100,40,hwnd,(HMENU)1,hinst,NULL);

       butProc=(WNDPROC) SetWindowLong (butwnd, GWL_WNDPROC,

                                                 (LONG) ButtonProc) ;


LRESULT CALLBACK ButtonProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

       switch(iMsg)

       {

       이곳에서 버튼에 해당하는 내용을 기록한다.

               :

       }

       //처리를 하지 못한 것은 기본적인 프로시저로 넘긴다.

     return CallWindowProc (butProc, hwnd, iMsg, wParam, lParam) ;


}

SetWindowLong의 두 번재 인자를 GWL_WNDPROC 로 설정하고 ButtonProc를 등록하면 CALLBACK함수인 ButtonProc가 실행됩니다. SetWindowLong 함수에서 새로운 프로시저 함수를 등록하면 이전에 설정된 프로시저 함수가 리턴됩니다. 이함수를 butProc에 받아둡니다. 이이유는 새로 설정된 프로시저 함수에서 처리하지 못한 것들은 기본적으로 내장된 프로시저가 처리하게 하기 위함입니다. 새로운 프로시저를 등록하였을 때 기본적으로 내장된 프로시저를 보관해두지 않는다면 버튼이 눌려졌을 때 화면에서 버튼이 들어간 모양과 버튼이 윈도우에서 버튼에 포커스가 잡혔을 때 버튼위에 가느다란 라인을 그린다든지 하는 잡단한 기능들을 일일이 다해주어야 합니다. 이렇게 하는 것을 방지하기 위해서 SetWindowLong에서 리턴된 이전 프로세스를 보관한후에 필요한 처리만 ButtonProc의 switch(iMsg)에서 처리하고 나머지 처리는 CallWindowProc함수에 의해서 이전에 보관해둔 butProc에 넘겨주는 방식을 이용합니다. 실제 메인윈도우와 연결견 WndProc 또한 필요한 내용만 자신이 처리하고 나머지는 DefWindowProc 에 의해서 시스템에게 모든 권한을 넘겨주는것과 같은 방식입니다.



차일드 윈도우 색상 바꾸기

ButtonEx에서 체크버튼이나 그룹박스 라디오 버튼등은 기본적인 배경색이 회색입니다. 그림1에서의 출력결과를 보듯이 메인윈도우의 배경색은 흰색이며 체크버튼과 그룹박스, 라디오 버튼 등은 회색이기 때문에 보기에 매우 좋지 않습니다. 차일드 윈도우의 색상을 바꾸고자할 경우에는 특정 메시지를 받고 배경으로 설정할 브러쉬를 리턴하면 됩니다. 예를 들어 그림 1에서 체크박스나 그룹박스 등의 색을 흰색으로 설정한다면 WM_CTLCOLORSTATIC 메시를 받고 흰색 브러쉬를 리턴하면 됩니다.

          case WM_CTLCOLORSTATIC:

               return (LRESULT) CreateSolidBrush(RGB(255,255,255));


CreateSolidBrush는 HBRUSH를 리턴함으로 위와 같이 단축해서 간단하게 표현할수 있습니다.

이외에 차일드 윈도우의 색상을 바꾸고자 한다면 표4와 같은 메시지를 받았을 때 배경색상에 맞는 브러쉬를 리턴하면됩니다.

             (표4) 차일드윈도우 색상 정의 메시지

 메시지

 차일드 윈도우

WM_CTLCOLORSTATIC

  정적 외곽 박스형 차일드윈도우

WM_CTLCOLORMSGBOX

  메시지 박스

WM_CTLCOLORBTN

  버튼

WM_CTLCOLORDLG

 대화상자

WM_CTLCOLORLISTBOX

 리스트 박스

WM_CTLCOLOREDIT

 에디터 박스

WM_CTLCOLORSCROLLBAR

  스크롤 바

위 메시지와 함께 wParam에는 차일드 윈도우의 DC가 lParam에는 차일드 윈도우 핸들이 넘어옵니다.

때로 는 wParam에 의해서 넘겨받은 DC를 이용하여 차일드윈도우에 새로운 내용을 출력할수도 있습니다. 그러나 이부분에서 다소 주의를 요합니다. 잘못하면 차일드윈도우의 형태가 깨질수도 있기 때문입니다.



사용자 그리기

버튼을 만들 때 스타일에 BS_OWNERDRAW를 설정하면 기존에 사용하는 버튼이 아닌 사용자가 버튼을 그릴수 있습니다. BS_OWNERDRAW를 설정하고 버튼윈도우를 만들면 메인윈도우에는 WM_DRAWITEM 메시지가 발생되고 wParam에는 버튼 번호가 lParam에는 버튼의 정보가 들어 옵니다. 이정보는 DRAWITEMSTRUCT 형으로 들어옵니다. 다음은 DRAWITEMSTRUCT 구조체의 형태입니다.

typedef struct tagDRAWITEMSTRUCT {

    UINT   CtlType;

    UINT   CtlID;

    UINT   itemID;

    UINT   itemAction;

    UINT   itemState;

    HWND   hwndItem;

    HDC    hDC;

    RECT   rcItem;

    DWORD  itemData;

} DRAWITEMSTRUCT;


CtlType 은 현재 그리고자 하는 차일드윈도우가 어떤것인가를 알려줍니다. 이 변수에 저장되는 값들은 다음과 같습니다.

ODT_BUTTON : 버튼

ODT_COMBOBOX : 콤보박스

ODT_LISTBOX: 리스트 박스

ODT_MENU: 메뉴

ODT_LISTVIEW: 리스트 뷰

ODT_STATIC: 정적 윈도우

ODT_TAB: 탭컨트롤


CtlID 는 차일드 윈도우일 경우 이윈도우의 ID를 의미합니다. 만약 메뉴에서 사용자 그리기를 한다면 itemID에 ID값이 넘어오고 CtlID에는 0이 설정됩니다.

itemState는 현재 차일드 윈도우나 메뉴의 상태를 알려줍니다. 이값은 다음과 같습니다.


 ODS_CHECKED  : 차일드윈도우나 메뉴가 체크 되어 있을경우

 ODS_DISABLED : 차일드 윈도우나 메뉴가 비활성화 되어있을경우

 ODS_FOCUS :  차일드 윈도우에 포커스가 맞추어져 있을경우

 ODS_GRAYED :  메뉴가 비활성화 되어 있을경우

 ODS_SELECTED : 차일드 윈도우와 메뉴가 선택 되었을경우

 ODS_COMBOBOXEDIT : 콤보 박스에 데이터가 입력될 경우

 ODS_DEFAULT : 기본적인 상태일경우


hwndItem 은 차일드윈도우의 윈도우 핸들러 이며 hDC 는 차일드 윈도우의 DC

rcItem는 새로 그려줄 윈도우 영역이며 itemData 는 콤보박스나 리스트박스일 경우 현재 설정된 데이터의 값들이 넘어옵니다.

WM_DRAWITEM 메시지가 발생되면 이메시지에와 함께 들어오는 lParam을 DRAWITEMSTRUCT 포인터로 받아서 이구조체를 이용하여 사용자가 직접 그릴수 있습니다. 보통 일반적인 다른 차일드 윈도우들은 사용자가 직접 그리지 않습니다. 그러나 버튼은 사용자가 직접 그리는 경우가 많습니다. 어플리케이션 프로그램에서 비트맵 버튼등을 많이 보셨을 것입니다. 비트맵 버튼은 사용자 그리기 스타일을 첨부하여 버튼을 사용자가 직접 그린것입니다. 비트맵버튼을 출력하는 방법을 먼저 예로 보여 드리겠습니다.


//DRAWITEMSTRUCT형의 drawbut를 설정한다.

 LPDRAWITEMSTRUCT drawbut;

         //들어간 버튼 나온 버튼 의 비트맵을 저장할 변수 설정

         static HBITMAP hBitD,hBitU;

            //비트맵을 로드한다.

        hBitD=LoadBitmap(hinst,"BUTTOND");

        hBitU=LoadBitmap(hinst,"BUTTONU");


 case WM_DRAWITEM:

         //lParam으로 부터 DRAWITEMSTRUCT핸들을 얻는다.

         drawbut=(LPDRAWITEMSTRUCT)lParam;

         //비트맵 출력을 위한 메모리 dc를 설정한다.

         MemDC=CreateCompatibleDC(drawbut->hDC);

         //현재 버튼으리 눌려 진것이면

         if(drawbut->itemState & ODS_SELECTED)

         {

               SelectObject(MemDC,hBitD);

               //눌려진 그림을 그린다.

                 BitBlt(drawbut->hDC,0,0,69,35,MemDC,0,0,SRCCOPY);

         }

         else

         {

                 //기본적 버튼을 그린다.

                 SelectObject(MemDC,hBitU);

                 BitBlt(drawbut->hDC,0,0,69,35,MemDC,0,0,SRCCOPY);

               //현재 윈도우가에 포커스가 주어졌을때 박스를 그린다.

       if(drawbut->itemState & ODS_FOCUS)

       FrameRect(drawbut->hDC,&rect,CreateSolidBrush(RGB(255,255,0)));

         }

         DeleteDC(MemDC);

          return 0 ;

비트맵 버튼을 DC에 로드하는 것은 설명하였습니다. 버튼을 그릴경우에 버튼이 눌려?봉뻑㎰? 버튼이 나와있을 경우 그리고 현재 버튼윈도우에 포커스가 맞추어 졌을경우에 해당되는 것을 모두 설정해 주어야 합니다. 버튼이 눌려진 것은 itemState와 ODS_SELECTED를 AND 연산을 이용하여 확인할수 있으며 현재 버튼에 포커스가 주어진 것을 확인할경우에는 ODS_FOCUS를 AND 연산을 하여 알수 있습니다. 보통 버튼에 포커스가 주어지면 보튼 내부에 작은 박스가 그려집니다. 박스 라인을 그리기 위해서 보통 많이 사용하는 것이 FrameRect입니다. 위의 예제는 이함수를 이용하여 버튼내부에 박스를 노란색으로 그려준 형태입니다.



차일드 윈도우의 활성 비활성 포커스설정

차일드 윈도우들을 비활성화 시켜서 사용하지 못하게 할 경우 또한 다시 활성화 시켜서 사용을 가능하게 할 경우 이때에는 EnableWindow 함수를 사용합니다. EnbaleWindow함수형태는 다음과 같습니다.


BOOL EnableWindow(

HWND hWnd, // 윈도우 핸들

BOOL bEnable // 활성일경우에는 TRUE비활성일경우에는 FALSE

);


프로그램을 제작할 경우 특정 버튼이 어떤 조건에서만 활성화 되게 하는 경우가 많습니다. 예를 든다면 음성을 출력하는 간단한 프로그램에서 Play버튼이 눌려져서 현재 음성이 출력될경우에는  STOP와 PAUSE버튼이 활성화 되어 있게 해야 하고 Play버튼은 다시 비활성으로 바뀌어야 합니다. 이럴경우에는 EnableWindow함수를 이용하여 차일드윈도우를 비활성 또는 활성화 하게 할수 있습니다.

특정 차일드 윈도우에 포커스를 주어지고자 할 경우 이때는 SetFocus 라는 함수를 사용하면 됩니다. SetFocus의 형태는 다음과 같습니다.


HWND SetFocus(

HWND hWnd // 윈도우 핸들

);


일반 프로그램에서 Tab키를 누르거나 Enter키를 누르게 되면 차일드 윈도우들에서 포커스가 순서적으로 이동됩니다. 이럴경우에는 SetFocus함수를 사용합니다.



차일드 윈도우예제 ButtonEx2

ButtonEx2는 그룹박스의 배경색을 흰색으로 설정하고 3개의 기본 버튼가 사용자 그리기 버튼을 만든다음 각버는의 프로시저를 새로 등록한 예제입니다. ButtonEx2는 Enter키를 누르게 되면 버튼 4개로 돌아가면서 포커스가 맞추어 집니다. 또한 F1키를 누르게 되면 첫 번째 버튼이 비활성 되고 Tab키를 누르면 비활성화 된 버튼이 다시 활성화 됩니다.


(프로그램 소스)

//버튼 윈도우 예제

//ButtonEx2.c

#include <windows.h>


HINSTANCE hinst;


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

LRESULT CALLBACK ButtonProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

     {

     static char szAppName[] = "ButtonEx2" ;

     HWND        hwnd ;

     MSG         msg ;

     WNDCLASSEX  wndclass ;


     wndclass.cbSize        = sizeof (wndclass) ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

     wndclass.lpszMenuName  = NULL ;

     wndclass.lpszClassName = szAppName ;

     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;


     RegisterClassEx (&wndclass) ;

        hinst=hInstance;

     hwnd = CreateWindow (szAppName,    

                           "버튼윈도우 예제:ButtonEx2",  

                    WS_OVERLAPPEDWINDOW, 

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    NULL,                

                    NULL,                

                    hInstance,           

                           NULL) ;                    


     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;


     while (GetMessage (&msg, NULL, 0, 0))

          {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

          }

     return msg.wParam ;

}

//버튼 윈도우 핸들

HWND butwnd[4];

//버튼에서 받은 기본적인 프로시저를 저장하는 변수

WNDPROC butProc[4] ;

//버튼 윈도우 핸들 배열에서 현재 선택된 윈도우 핸들 번호

int butpos=0;


LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

     HDC         hdc ,MemDC;

     PAINTSTRUCT ps ;

        static HBRUSH hBrush;

        //들어간 버튼 나온 버튼 의 비트맵을 저장할 변수 설정

        static HBITMAP hBitD,hBitU;

       //DRAWITEMSTRUCT형의 drawbut를 설정한다.

        LPDRAWITEMSTRUCT drawbut;

        RECT rect={5,5,64,30};

        int i,id;

     switch (iMsg)

          {

          case WM_CREATE :

           //비트맵을 로드한다.

           hBitD=LoadBitmap(hinst,"BUTTOND");

       hBitU=LoadBitmap(hinst,"BUTTONU");

           //차일드 윈도우를 만든다.

           for(i=0;i<3;i++)

               {

               id=i+1;

               butwnd[i]=CreateWindow("button","버튼",

                       WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON ,

                               10,10+i*40,100,40,hwnd,(HMENU)id,hinst,NULL);

               //프로시저 설정

               butProc[i]=(WNDPROC) SetWindowLong (butwnd[i],

                               GWL_WNDPROC,(LONG) ButtonProc) ;

                               }

               id=i+1;

               //4번째 버튼은 사용자 그리기로

               butwnd[i]=CreateWindow("button","",WS_CHILD|

                               WS_VISIBLE|BS_OWNERDRAW,                                        10,10+i*40,69,35,hwnd,(HMENU)id,hinst,NULL);

               //프로시저 설정

               butProc[i]=(WNDPROC) SetWindowLong (butwnd[i],                             GWL_WNDPROC,(LONG) ButtonProc) ;

               //첫번째 윈도우에 포커스를 맞춘다.

               SetFocus(butwnd[butpos]);

               CreateWindow("button","Group Box",

                               WS_CHILD|WS_VISIBLE|BS_GROUPBOX,

                               120,100,100,40,hwnd,(HMENU)7,hinst,NULL);

               //흰색 브러쉬를 만든다. WM_CTLCOLORSTATIC에서 사용

               hBrush=CreateSolidBrush (RGB(255,255,255));

               return 0 ;

          case WM_PAINT :

                  hdc = BeginPaint (hwnd, &ps) ;

                  EndPaint (hwnd, &ps) ;

               return 0 ;

                 case WM_CTLCOLORSTATIC:

                 //흰색 브러쉬를 리턴하여 그룹박스의 배경색을 흰색으로 한다.

               return (LRESULT) hBrush;

          case WM_DESTROY :

               PostQuitMessage (0) ;

                          break;

                 case WM_DRAWITEM:

                 //lParam으로 부터 DRAWITEMSTRUCT핸들을 얻는다.

                 drawbut=(LPDRAWITEMSTRUCT)lParam;

                 //비트맵 출력을 위한 메모리 dc를 설정한다.

                 MemDC=CreateCompatibleDC(drawbut->hDC);

                 //현재 버튼으리 눌려 진것이면

                 if(drawbut->itemState & ODS_SELECTED)

                 {

                       SelectObject(MemDC,hBitD);

                       //눌려진 그림을 그린다.

                         BitBlt(drawbut->hDC,0,0,69,35,MemDC,0,0,SRCCOPY);

                 }

                 else

                 {

                         //기본적 버튼을 그린다.

                         SelectObject(MemDC,hBitU);

                         BitBlt(drawbut->hDC,0,0,69,35,MemDC,0,0,SRCCOPY);

                       //현재 윈도우가에 포커스가 주어졌을때 박스를 그린다.

                       if(drawbut->itemState & ODS_FOCUS)

       FrameRect(drawbut->hDC,&rect,CreateSolidBrush(RGB(255,255,0)));

                 }

                 DeleteDC(MemDC);

               return 0 ;

          }


     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}


LRESULT CALLBACK ButtonProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

       switch(iMsg)

       {

       case WM_KEYDOWN:

                       //리턴 키를 누르면

                       if(wParam==VK_RETURN)

                       {

                               //버튼 윈도우 포스를 증가시키고

                               butpos++;

                               //4일경우에는 0으로 전환

                               if(butpos==4)

                                       butpos=0;

                               //해당버튼윈도우에 포커스를 맞춘다.

                               SetFocus(butwnd[butpos]);

                               return 0;

                       }

                       //F1 키이면 첫번째 버튼을 비활성화 시키ㄱ고

                       else if(wParam==VK_F1)

                               EnableWindow(butwnd[0],FALSE);

                       //TAB키이면 첫번째 윈도우를 활성화 시킨다.

                       else if(wParam==VK_TAB)

                               EnableWindow(butwnd[0],TRUE);

                       break;

       }


    return CallWindowProc (butProc[butpos], hwnd, iMsg, wParam, lParam) ;


}

(프로그램 소스끝)











         (그림 2)ButtonEx2 출력 결과



정적 윈도우

정적윈도우란 이것은 단지 하나의 빈 판으로써 키보드 입력이나 또는 마우스 이벤트를 받지않고 어떤 부모 윈도우에  WM_COMMAND를 보내지 않습니다. 그냥 간단하게 윈도우가 설정될뿐입니다. 정적윈도우를 만들경우에는 CreateWindow의 첫 번째 인자에 "static"라고 설정하면 만들 수 있습니다. 이윈도우에 어떤 메시지를 전달하고자 할 경우 이때는 부모 윈도우에서 정적윈도우에 메시지를 보내는 방법을 이용하면 됩니다. 예를 들어서 정적윈도우에 마우스 버튼과 키보드 입력메시지를 전달하고자 할 경우 다음과 같은 방법을 이용합니다.

         //정적윈도우 영역 좌표

         static RECT procRect={0,0,400,400};


          case WM_CREATE :

                //정적 윈도우를 만든다.

                staticwnd=CreateWindow("static",NULL,

                WS_CHILD | WS_VISIBLE | WS_THICKFRAME |WS_VSCROLL,                                 0,0,400,400,hwnd,(HMENU)3,

                        ((LPCREATESTRUCT)lParam)->hInstance,NULL);

                //프로시저 설정

                staticProc=(WNDPROC) SetWindowLong (staticwnd, GWL_WNDPROC,

                                                 (LONG) StaticProc) ;

               break;


          case WM_KEYDOWN:

                    //키입력을 정적 윈도우에 보낸다

                    SendMessage(staticwnd,iMsg,wParam,lParam);

                   return 0;

        case WM_LBUTTONDOWN:

                //좌측 마우스 버튼을 클릭하면 위치를 point에

               //저장하고

                point.x=LOWORD(lParam);

                point.y=HIWORD(lParam);

                //point가 정적윈도우 영역에 있으면

                //메시지를 보낸다.

                if(PtInRect(&procRect,point))

                SendMessage(staticwnd,iMsg,wParam,lParam);

                return 0;


WM_CREATE에서 정적윈도우를 만든다음 이 윈도우 프로시적을 StaticProc로 등록합니다. WM_KEYDOWN 메시지가 발생되면 바로 정적윈도우로 메시지를 전송하고 WM_LBUTTONDOWN메시지가 발생되면 lParam으로부터 마우스 위치를 point에 저장하고 PtInRect함수를 사용하여 이 point가 정적윈도우 영역에 있는가를 확인합니다. PtInRect함수는 RECT와 POINT를 받아서 POINT변수가 RECT안에 있으면 TRUE값을 그렇지 않을경우에는 FALSE값을 리턴합니다.

정적윈도우를 사용하는 경우는 여러부분에 있으나 주로 많이 사용되는곳이 CD-TITLE를 만들경우입니다. CD-TITLE에서는 보통 박스 형태의 화면에 여러개의 프레임들이 교차되어 나타납니다. 이럴 경우 정적윈도우를 이용하여 여러개의 프레임을 만든다음 이것을 교차시키는 형태를 취합니다. 이방법은 윈도우의 전환에서 속도가 빠르기 때문에 자주 이용됩니다.


에디터 박스

에디터 박스란 문자를 입력하는 기능을 가진 윈도우입니다.

에디터 박스는 CreateWindow의 첫 번째 인자를 "edit"로 하고  ES_ 계열 스타일을 변경함으로써 만들 수 있습니다. 표5는 에디터 박스에서 사용되는 ES_ 계열의 리스트입니다.







 에디터 박스 스타일

   내용

ES_AUTOHSCROLL

 문자를 입력하면 수평으로 자동적으로 스크롤 된다.

ES_AUTOVSCROLL

 문자를 입력할 때 여러 줄 입력시 자동적으로

 수직 스크롤이 일어난다.

ES_CENTER

 문자가 중앙을 깃점으로 정렬된다.

ES_LEFT

 문자가 좌측을 깃점으로 정렬된다.

ES_RIGHT

 문자가 우측을 깃점으로 정렬된다.

ES_MULTILINE

 여러줄을 입력할수 있도록 한다.

ES_PASSWORD

 비밀번호 형태로 입력이 된다. 이 스타일을 설정

 하면 문자를 입력하여도 ‘*’ 코드만 화면에 출력된다.

ES_READONLY

 읽기전용, 입력이 되지 않는다.

ES_WANTRETURN

 에디터 박스에서 Enter키를 받는다.

       (표5) 에디터 박스 스타일



예를 들어서 에디터 박스가 여러줄을 입력받으면서 자동으로 수평수직 스크롤이 되면서 스크로 바가 붙은 형으로 윈도우를 만들고자 한다면 다음과 같이 할수 있습니다.


        editwnd=CreateWindow("edit",NULL,

                        WS_CHILD |WS_VISIBLE| WS_HSCROLL |WS_VSCROLL

                        |WS_BORDER| ES_LEFT|ES_MULTILINE|

                         ES_AUTOHSCROLL | ES_AUTOVSCROLL,

                        10,10,200,300,hwnd,(HMENU)1,hinst,NULL);


에디터 박스에서 어떤 이벤트가 발생되었을 때 WM_COMMAND가 발생되면서 HIWORD(wParam)에 저장되는 이벤트는 표6과 같습니다.

     (표6) 에디터 박스 이벤트 메시지 리스트

  이벤트 메시지

   내용

EN_CHANGE

 에디터 박스에서 데이터가 수정되었을경우

EN_ERRSPACE

 에디터 박스에서 더 이상 데이터를 입력할수 없을경우

EN_HSCROLL

 수평으로 스크롤이 일어날경우

EN_IMECHANGE

 한영 전환이 일어났을경우

EN_KILLFOCUS

 포커스가 사라졌을경우

EN_SETFOCUS

 현재 포커스가 설정되었을 경우

EN_CHANGE는 에디터박스에서 문자를 입력하는 순간에 발생됩니다. 에디터박스는 윈도우 95에서는 한계용량을 가지고 있습니다. 만일 이용량을 초과할경우에는 EN_ERRSPACE메시지가 발생됩니다. 특이 메시지로서 EN_IMECHANGE메시지 인데 이것은 한영전환이 일어 났을 때 발생되는 메시지입니다. IME에 대한 방법은 키보드 컨트롤 부분에서 설명하였습니다. 에디터박스에서는 단지 현재 한영전환이 되었다는 것만 알려줄뿐 현재가 어떤 상태인지는 알려주지 않습니다. IMM 함수를 이용해서 현재 전환 된 모드를 알수 있을것입니다.


에디터 박스에서 데이터를 얻고자 할경우네는 GetWindowText를 데이터를 넣고자 할경우에는  SetWindowText함수를 이용하시면 됩니다. 이함수의 형태는 다음과 같습니다.


int GetWindowText(

HWND hWnd, // 윈도우 핸들

LPTSTR lpString, // 문자열 버퍼

int nMaxCount // 문자열 버퍼의 최대크기

);


BOOL SetWindowText(

HWND hWnd, // 윈도우 핸들

LPCTSTR lpString //문자열 버퍼

);

SetWindowText나 GetWindowText는 에디터 박스에서만 사용하는 함수가 아니라 모든 윈도우에 통용됩니다. 윈도우 타이틀바에 문자열을 설정하고자 할경우에도 이함수를 이용할수 있습니다. 쉽게 보면 윈도우에 메인 문자열을 설정할 때 사용하는 함수라고 보시면 됩니다.


정적 윈도우와 에디터 박스 예제 ExEdit

ExEdit는 정적윈도우를 하나 만들고 이 이 윈도우에서 키보드 입력 및 마우스 가 입력되었을 때 메시지박스를 출력하고 버튼을 클릭하면 에디터박스에서 입력한 데이터가 출력됩니다.


(프로그램 소스)

//C언어 배우기 위한 코딩

//ExEdit.c

#include <windows.h>


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

LRESULT CALLBACK StaticProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);

HINSTANCE hinst;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

     {

     static char szAppName[] = "ExEdit" ;

     HWND        hwnd ;

     MSG         msg ;

     WNDCLASSEX  wndclass ;


     wndclass.cbSize        = sizeof (wndclass) ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH) ;

     wndclass.lpszMenuName  = NULL ;

     wndclass.lpszClassName = szAppName ;

     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;


     RegisterClassEx (&wndclass) ;

       hinst=hInstance;

     hwnd = CreateWindow (szAppName,    

                           "에디터 박스 예제:ExEdit",  

                    WS_OVERLAPPEDWINDOW, 

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    NULL,                

                    NULL,                

                    hInstance,           

                           NULL) ;                    


     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;


     while (GetMessage (&msg, NULL, 0, 0))

          {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

          }

     return msg.wParam ;

     }


WNDPROC staticProc;

HWND staticwnd;


LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

     {

     HDC         hdc ;

     PAINTSTRUCT ps ;

        static HBRUSH hBrush;

        POINT point;

        //정적윈도우 영역 좌표

        static RECT procRect={0,0,400,400};

        static HWND editwnd;

        char temp[80];


     switch (iMsg)

          {

          case WM_CREATE :

               //정적 윈도우를 만든다.

               staticwnd=CreateWindow("static",NULL,

                       WS_CHILD | WS_VISIBLE |

                        WS_THICKFRAME |WS_VSCROLL,

                       0,0,400,400,hwnd,(HMENU)3,

               ((LPCREATESTRUCT)lParam)->hInstance,NULL);

               //프로시저 설정

               staticProc=(WNDPROC) SetWindowLong

                               (staticwnd, GWL_WNDPROC,

                                                 (LONG) StaticProc) ;

               //에디터 박스를 만든다.

               editwnd=CreateWindow("edit",NULL,

                       WS_CHILD |WS_VISIBLE|

                        WS_HSCROLL|WS_VSCROLL|WS_BORDER|

                       ES_LEFT|ES_MULTILINE|

                       ES_AUTOHSCROLL | ES_AUTOVSCROLL,

                       10,10,200,300,hwnd,(HMENU)1,hinst,NULL);

               CreateWindow("button","입력데이터 보기",

                       WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON ,

                       220,10,150,40,hwnd,(HMENU)2,hinst,NULL);

                       hBrush=CreateSolidBrush (RGB(255,255,0));

               return 0 ;

         case WM_CTLCOLORSTATIC:

                 //흰색 브러쉬를 리턴하여 그룹박스의 배경색을 흰색으로 한다.

                  return (LRESULT) CreateSolidBrush(RGB(255,255,255));

         case WM_COMMAND:

               switch(LOWORD(wParam))

               {

                       case 2:

                       //버튼을 클릭하면  입력한 데이터를 출력한다.

                       if(HIWORD(wParam)==BN_CLICKED)

                       {

                       //에디터 박스에서 데이터를 얻는다.

                       GetWindowText(editwnd,temp,80);

                       MessageBox(hwnd,temp,"edit 데이터",MB_OK );

                       }

                       break;

               }

               break;


          case WM_PAINT :

                  hdc = BeginPaint (hwnd, &ps) ;

                  EndPaint (hwnd, &ps) ;

               return 0 ;

                 case WM_CTLCOLOREDIT:

               return (LRESULT) hBrush;

         case WM_KEYDOWN:

                   //키입력을 정적 윈도우에 보낸다

                       SendMessage(staticwnd,iMsg,wParam,lParam);

                       return 0;

         case WM_LBUTTONDOWN:

               //좌측 마우스 버튼을 클릭하면 위치를 point에

                   //저장하고

               point.x=LOWORD(lParam);

               point.y=HIWORD(lParam);

               //point가 정적윈도우 영역에 있으면

               //메시지를 보낸다.

               if(PtInRect(&procRect,point))

               SendMessage(staticwnd,iMsg,wParam,lParam);

               return 0;


          case WM_DESTROY :

               PostQuitMessage (0) ;

               return 0 ;


          }


     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

     }



//정적윈도우 프로시저

LRESULT CALLBACK StaticProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

       switch(iMsg)

       {

       // 부모 윈도우로 부터 키와 마우스 버튼 메시지를 받아서

       // 메시지 박스를 실행시킨다.

         case  WM_KEYDOWN:

                       MessageBox(hwnd,"키눌림","키눌림",MB_OK);

                       break;

         case WM_LBUTTONDOWN:

                       MessageBox(hwnd,"마우스눌림","마우스눌림",MB_OK);

                       break;

       }

    return CallWindowProc (staticProc, hwnd, iMsg, wParam, lParam) ;

}


(프로그램 소스끝)

























   


               (그림 3)ExEdit 출력 결과




리스트 박스와 콤보박스

리스트 박스와 콤보박스는 그림4와 같은 형태로써 문자열 리스트를 저장하는 차일드 윈도우입니다.








         (그림 4) 리스트 박스와 콤보박스 형태


리스트박스와 콤보 박스 또한 CreateWindow 함수를 이용하여 만들며 윈도우 스타일에 각각의 스타일을 첨부할수 있습니다. 표7은 리스트박스에 대한 스타일 이며 표8은 콤보박스에 대한 스타일입니다.





   (표 7) 리스트 박스 스타일

윈도우 스타일

 내용

LBS_EXTENDEDSEL

 다중 선택을 할 경우 Shift 키와 마우스를 이용할수 있게 한다.

LBS_MULTIPLESEL

 여러개의 아이템을 선택할수 있도록한다.

LBS_OWNERDRAWFIXED

 사용자 그리기, 리스트 박스 크기가 고정된  형태

LBS_OWNERDRAWVARIABLE

 사용자 그리기 ,리스트박스 크기가 변함

LBS_SORT

 데이터가 가나다 순으로 정렬됨

LBS_STANDARD

 표준 리스트 박스


   (표 8) 콤보 박스 스타일

윈도우 스타일

 내용

CBS_AUTOHSCROLL

 콤보박스에서 데이터를 입력할 때 자동적으로 스크롤이 이루어진다.

CBS_DROPDOWN 

 콤보박스 좌측 다운 버튼을 클릭하면 리스트가 보이는 형태

CBS_OWNERDRAWFIXED

 사용자 그리기, 콤보 박스 크기가 고정된  형태

CBS_OWNERDRAWVARIABLE

 사용자 그리기 ,콤보박스 크기가 변함

CBS_SORT

 데이터가 가나다 순으로 정렬됨

CBS_SIMPLE

 리스트박스가 항시보이는 형태


표준 형태의 리스트박스를 사용하고자 한다면 LBS_STANDARD 단 한 개를 사용하면 됩니다. 그외 특정 옵션이 있을경우에는 그옵션을 삽입시키면 됩니다. CBS_DROPDOWN이란 콤보박스가 처음에는 에디터 형태의 창만 있다가 다운 버튼을 클릭하면 리스트가 보이는 형태이면 이와 대조적으로 항시 보이는 형태는 CBS_SIMPLE입니다. 다음은 콤보박스와 리스트박스를 만드는 예입니다.


        listwnd=CreateWindow("listbox",NULL,

                WS_CHILD |WS_VISIBLE| LBS_STANDARD,

                0,0,100,100,

                hwnd,(HMENU)1,

                ((LPCREATESTRUCT)lParam)->hInstance,NULL);

        combownd=CreateWindow("combobox",NULL,

                WS_CHILD |WS_VISIBLE| CBS_DROPDOWN |CBS_AUTOHSCROLL ,

                150,0,100,100,

                hwnd,(HMENU)2,((LPCREATESTRUCT)lParam)->hInstance,NULL);




리스트 박스와 콤보박스  이벤트

리스트박스에서 어떤 이벤트가 발생되었을때는 LBN_ 계열의 메시지가 발생되면 콤보박스에서 특정 이벤트가 발생되었을 경우에는 CBN_ 계열의 메시지가 발생됩니다. 표9은 리스트박스 이벤트 메시지 이며 표10는 콤보박스 이벤트 메시지입니다.

 메시지

 내용

LBN_DBLCLK

 항목을 더블클릭했을경우

LBN_ERRSPACE

 메모리가 부족할경우

LBN_KILLFOCUS

 포커스 사라질경우

LBN_SETFOCUS

 현재 리스트에 포커스가  맞추어 졌을때

LBN_SELCANCEL

 리스트박스에서 CANCEL이벤트가 발생되었을때

LBN_SELCHANGE

 리스트박스에서 선택항목이 바뀌었을경우

       (표 9) 리스트 박스 이벤트


        (표 10) 콤보 박스 이벤트

 메시지

 내용

CBN_DBLCLK

 항목을 더블클릭했을경우

CBN_ERRSPACE

 메모리가 부족할경우

CBN_KILLFOCUS

 포커스 사라질경우

CBN_SETFOCUS

 현재 리스트에 포커스가  맞추어 졌을때

CBN_DROPDOWN

 콤보박스에서 다운 버튼을 눌렀을경우

CBN_SELCHANGE

 콤보박스에서 선택항목이 바뀌었을경우



리스트 박스와 콤보박스  명령 메시지

리스트박스에 데이터를 넣거나 또는 콤보박스에 데이터를 넣거나 삭제하거나 현재 선택된 리스트번호를 알고자 할경우에 리스트 박스 윈도우나 콤보 박스 윈도우에 메시지를 전송함으로써 알수가 있습니다. 이때 사용하는 함수가 SendMessage함수인데 이함수에 의해서 명령이 발생되면 넘겨준 lParam이나 또는 리턴값으로 결과정보를 넘겨줍니다. 표11은 리스트박스 명령 메시지이며 표12는 콤보박스 명령 메시지입니다.







   메시지

 내용

 결과값

 LB_ADDSTRING

 새로운 데이터를 입력한다.

lParam에 입력할 데이터 문자열을 넘겨준다.

 LB_DELETESTRING

 선택된 데이터를 삭제한다.

wParam에 삭제할 데이터 번호를 지정

 LB_INSERTSTRING

 데이터를 삽입한다.

wParam에 삽입할위치 lParam에 상입할 문자열

 LB_DIR

 파일 리스트를 보여준다.

wParam 파일속성

lParam에 검색할 파일명 (*.* 포함)

 LB_FINDSTRING

 리스트에서 특정 문자열을 찾는다.

wParam에 시작번호

lParam에 찾는문자열을

찾았을 경우 찾은 번호는 리턴값에 설정됨

 LB_GETCURSEL

 현재 리스트박스의 커서

 위치를 얻는다.

현재 선택된 리스트 번호를 리턴값에 설정함

 LB_GETTEXT

 특정위치의 문자열을 얻는다.

wParam에 특정위치번호 lParam 에는 해당번호에 있는 문자열이 리턴된다.

LB_GETTEXTLEN

 특정 위치의 문자열의 길이를 얻는다.

wParam에는 특정위치번호 리턴값에 길이가 설정된다.

   (표 11) 리스트 박스 명령 메시지



















   메시지

 내용

 결과값

 CB_ADDSTRING

 새로운 데이터를 입력한다.

lParam에 입력할 데이터 문자열을 넘겨준다.

 CB_DELETESTRING

 선택된 데이터를 삭제한다.

wParam에 삭제할 데이터 번호를 지정

 CB_INSERTSTRING

 데이터를 삽입한다.

wParam에 삽입할위치 lParam에 상입할 문자열

 CB_DIR

 파일 리스트를 보여준다.

wParam 파일속성

lParam에 검색할 파일명 (*.* 포함)

 CB_FINDSTRING

 리스트에서 특정 문자열을 찾는다.

wParam에 시작번호

lParam에 찾는문자열을

찾았을 경우 찾은 번호는 리턴값에 설정됨

 CB_GETCURSEL

 현재 콤보박스의 커서

 위치를 얻는다.

현재 선택된 리스트 번호를 리턴값에 설정함

 CB_GETLBTEXT

 특정위치의 문자열을 얻는다.

wParam에 특정위치번호 lParam 에는 해당번호에 있는 문자열이 리턴된다.

CB_GETLBTEXTLEN

 특정 위치의 문자열의 길이를 얻는다.

wParam에는 특정위치번호 리턴값에 길이가 설정된다.

   (표 12) 콤보박스 설정 메시지



예를 든다면 리스트박스에서 세로운 데이터를 넣고자 한다면  LB_ADDSTRING을 이용하여 다음과 같이 할수 있습니다.

SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터1");


위와 같이 하면 “데이터1”이라는 값이 리스트 박스에 삽입되게 됩니다.

마찬가지고 콤보박스는 다음과 같이 합니다.


        SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터1");


표11과 12을 참조하면 리스트 박스와 콤보박스에 대한 많은 것들을 컨트롤 할수 있을것입니다. 이외에도 많은 LB_ 계열의 명령 메시지와 CB_ 계열의 명령 메시지가 있습니다. 이것들은 도움말을 참조하시기 바랍니다.

하나의 예를 더들어 보겟습니다. 리스트 박스에서 특정 항목을 선택하였을 때 선택된 값을 알아내는 방법입니다.


          case WM_COMMAND:

                  //리스트 박스이고  이벤트가 LBN_SELCHANGE

          if(LOWORD(wParam)==1 && HIWORD(wParam)==LBN_SELCHANGE)

                           {

                           //선택된 리스트 번호를 num에 얻고

                           num=SendMessage(listwnd,LB_GETCURSEL,0,0);

                           //num의 데이터를 temp에 얻는다.

                           SendMessage(listwnd,LB_GETTEXT,

                                (WPARAM)num,(LPARAM)temp);

                                MessageBox(hwnd,temp,"리스트박스",MB_OK );

                           }



리스트 박스와 콤보박스 컨트롤 예제 ExListBox

ExListBox는 리스트박스와 콤보박스에 데이터를 넣은다음 선택된 데이터가 무엇인지 알아보는 예제입니다.

(프로그램 소스)


//리스트 박스와 콤보박스

//ExListBox.c

#include <windows.h>


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

     {

     static char szAppName[] = "ExListBox.c" ;

     HWND        hwnd ;

     MSG         msg ;

     WNDCLASSEX  wndclass ;


     wndclass.cbSize        = sizeof (wndclass) ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

     wndclass.lpszMenuName  = NULL ;

     wndclass.lpszClassName = szAppName ;

     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;


     RegisterClassEx (&wndclass) ;


     hwnd = CreateWindow (szAppName,    

                           "리스트박스와 콤보박스 예제:ExListBox",  

                    WS_OVERLAPPEDWINDOW, 

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    NULL,                

                    NULL,                

                    hInstance,           

                           NULL) ;                    


     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;


     while (GetMessage (&msg, NULL, 0, 0))

          {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

          }

     return msg.wParam ;

     }


LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

     {

     HDC         hdc ;

     PAINTSTRUCT ps ;

        static HWND listwnd,combownd;

        char temp[80];

        int num;


     switch (iMsg)

          {

          case WM_CREATE :

                 //리스트 박스 만들기

               listwnd=CreateWindow("listbox",NULL,

                       WS_CHILD |WS_VISIBLE| LBS_STANDARD,

                               0,0,100,100,hwnd,(HMENU)1,

                       ((LPCREATESTRUCT)lParam)->hInstance,NULL);

               //콤보 박스 만들기

               combownd=CreateWindow("combobox",NULL,

                       WS_CHILD |WS_VISIBLE| CBS_DROPDOWN

                        |CBS_AUTOHSCROLL ,

                               150,0,100,100,hwnd,(HMENU)2,

                       ((LPCREATESTRUCT)lParam)->hInstance,NULL);

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터1");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터2");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터3");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터4");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터5");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터6");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터7");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터8");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터9");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터10");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터11");

               SendMessage(listwnd,LB_ADDSTRING,0,(LPARAM)"데이터12");


       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터1");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터2");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터3");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터4");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터5");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터6");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터7");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터8");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터9");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터10");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터11");

       SendMessage(combownd,CB_ADDSTRING,0,(LPARAM)"데이터12");

                               return 0 ;


          case WM_PAINT :

                  hdc = BeginPaint (hwnd, &ps) ;

                  EndPaint (hwnd, &ps) ;

               return 0 ;

          case WM_DESTROY :

               PostQuitMessage (0) ;

               return 0 ;

                 case WM_COMMAND:

                         //리스트 박스이고  이벤트가 LBN_SELCHANGE

   if(LOWORD(wParam)==1 && HIWORD(wParam)==LBN_SELCHANGE)

   {

                          //선택된 리스트 번호를 num에 얻고

                          num=SendMessage(listwnd,LB_GETCURSEL,0,0);

                  //num의 데이터를 temp에 얻는다.

   SendMessage(listwnd,LB_GETTEXT,(WPARAM)num,(LPARAM)temp);

               MessageBox(hwnd,temp,"리스트박스",MB_OK );

   }

   //콤보박스도 같은 방법이다

  if(LOWORD(wParam)==2 && HIWORD(wParam)==CBN_SELCHANGE)

   {

          num=SendMessage(listwnd,CB_GETCURSEL,0,0);

   SendMessage(combownd,CB_GETLBTEXT,(WPARAM)num,(LPARAM)temp);

                       MessageBox(hwnd,temp,"콤보박스",MB_OK );

   }

                          break;

          }


     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

     }


(프로그램 소스끝)










            (그림 5)ExListBox출력 결과

스크롤바 윈도우 예제 ScrBarEx

ScrBarEx는 스크롤 윈도우를 만들고 이것에 대한 이벤트를 받는 예제입니다. 스크롤 윈도우에서 발생되는 이벤트는 WM_COMMAND가 아닌  WM_VSCROLL이나 WM_HSCROLL로 발생됩니다. 스크롤 이벤트를 컨트롤 하는 방법은 5장에서 설명한 스크롤 바가 붙은 윈도우와 거히 비슷합니다. 다만 차이점은 스크롤 윈도우를 만드는것과 WM_VSCROLL과 WM_HSCROLL이 발생될 때 LOWORD(wParam)에 스크롤 윈도우 ID가 설정된다는 것입니다.


(프로그램 소스)

//스크롤 윈도우 예제

//ScrBarEx.c

#include <windows.h>


HINSTANCE hinst;


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

     {

     static char szAppName[] = "ScrBarEx.c" ;

     HWND        hwnd ;

     MSG         msg ;

     WNDCLASSEX  wndclass ;


     wndclass.cbSize        = sizeof (wndclass) ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

     wndclass.lpszMenuName  = NULL ;

     wndclass.lpszClassName = szAppName ;

     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;


     RegisterClassEx (&wndclass) ;

        hinst=hInstance;

     hwnd = CreateWindow (szAppName,    

                           "스크롤 윈도우:ScrBarEx",  

                    WS_OVERLAPPEDWINDOW, 

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    CW_USEDEFAULT,       

                    NULL,                

                    NULL,                

                    hInstance,           

                           NULL) ;                    


     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;


     while (GetMessage (&msg, NULL, 0, 0))

          {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

          }

     return msg.wParam ;

     }



LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

     HDC         hdc ;

     PAINTSTRUCT ps ;

        HWND scwnd;

        static int pos=0;

        static HBRUSH hBrush;

        int totalline=100;

        char temp[80];

     switch (iMsg)

          {

          case WM_CREATE :

               scwnd=CreateWindow("scrollbar","버튼",

               WS_CHILD|WS_VISIBLE|SBS_VERT ,

               10,10,30,200,hwnd,(HMENU)1,hinst,NULL);

                SetScrollRange(scwnd,SB_CTL,0,totalline,FALSE);

                 SetScrollPos(scwnd,SB_CTL,pos,TRUE);

               hBrush=CreateSolidBrush (RGB(0,255,0));

               return 0 ;

          case WM_PAINT :

                  hdc = BeginPaint (hwnd, &ps) ;

                 wsprintf(temp,"현재 위치: %d",pos);

               TextOut(hdc,50,50,temp,strlen(temp));

                EndPaint (hwnd, &ps) ;

               return 0 ;

         case WM_VSCROLL:

           switch(LOWORD(wParam))

               {

                       case SB_TOP:

                               pos=0;

                               break;

                       case SB_BOTTOM:

                               pos=totalline-1;

                               break;

                       case SB_LINEUP:

                               pos-=1;

                               if(pos<0)

                               pos=0;

                               break;

                       case SB_LINEDOWN:

                               pos+=1;

                               if(pos==totalline)

                                       pos=totalline-1;

                               break;

                       case SB_PAGEUP:

                               pos-=10;

                               if(pos<0)

                               pos=0;

                               break;

                       case SB_PAGEDOWN:

                               pos+=10;

                               if(pos>=totalline)

                               pos=totalline-1;

                               break;

                       case SB_THUMBTRACK:

                               pos=HIWORD(wParam);

                               break;

               }

               SetScrollPos((HWND)lParam,SB_CTL,pos,TRUE);

               InvalidateRgn(hwnd,NULL,FALSE);

               break;

               //스크롤 버튼 배경색 바꾸기

  case WM_CTLCOLORSCROLLBAR:

               return (LRESULT) hBrush;

  case WM_DESTROY :

               PostQuitMessage (0) ;

               return 0 ;

          }


     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

(프로그램 소스끝)




 

       (그림 6)ScrBarEx  출력 결과

 

 

 

 

 

<자료출저 : 삼육대학교 이상엽 박사>

Posted by 나비:D
:

1. 버튼

 case WM_CREATE:

  CreateWindow(TEXT("button"), TEXT("click"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,20,20,100,25, hWnd

                        (HMENU)0, g_Inst, NULL);

  return 0;

 case WM_COMMAND:

  switch(LOWORD(wParam))

   case 0:

    MessageBox(hWnd, Text("clicked"), "TEXT(button)", MB_OK);

    break;

  return 0;

버튼 컨트롤 사용 방식이다.. 윈도우 생성시 버튼을 만든 것이고, 버튼이 클릭되었을때 메시지박스를 띄우는 것이다.

CreateWindow(만들윈도우클래스, 윈도우제목, 스타일, 좌표,좌표,좌표,좌표, 부모윈도우, 아이디값, 윈도우인스턴스핸들, MDI사용될 구조체);

 스타일을 살펴보면 차일드라는 속성이 사용되었다. 이것은 메인 윈도우가 만들어지면 그안에 포함되는 컨트롤인 된다는 것이다.

 비지블은 보여준다, 푸쉬버튼 누른다. 라는 의미이다.

 자식윈도우로 지정을 했으면 부모의 핸들을 부모윈도우 자리에 넣어주면 되고 아이디 값을 그 버튼을 눌렀을때 wParam으로 넘어가는 값을 말한다.. 커맨드에선 하이워드의 wParam을 조사하여 그에 해당하는 코드를 넣어주면 된다.



2. 체크 박스

 생성방식은  위와 동일 하다..

static HWND c1;    // 체크박스 핸들저장,,, (체크 확인여부위해서)

static bool draw = false;  // 그림을 그릴것인가에 대한 변수

RECT rt;

GetClientRect(hWnd, &rt);   //현제 윈도우 클라이언트 영역을 얻어온다


case WM_CRATE:

 c1 = CreateWindow("button", "checkbox", WS_CHILD | WS_VISIBLE | BS_CHECKBOX, 20, 20, 160, 25, hWnd, (HMENU)1, g_hInst, NULL) ;   //수동체크박스 생성, 자동으로 하려면    BS_AUTOCHECKBOX로 변경한면 된다

break;


case WM_COMMAND:

 switch(LOWORD(wParam))

  case 1 :

   if(SendMessage(c1, BM_GETCHECK, 0, 0) == BST_UNCHECKED)  //센드메시지는 전에 보았다.. 찾아봐라
   {                          //체크 확인 메시지            // 체크 되있지 않다면
    draw = TRUE;
    SendMessage(c1, BM_SETCHECK, BST_CHECKED, 0); // 체크박스로 다시 보내 체크 하도록 요구한다
   }                          // 변경               // 체크상태
   else
   {
    draw = false;
    SendMessage(c1, BM_SETCHECK, BST_UNCHECKED, 0);
   }                                                   //비체크상태
   InvalidateRect(hWnd, &rt, true);   //이것도 전에 보았다.
   break;

인자는 동일하다,,,, 굳이 설명할 필요가 없다 체크 메시지와 체크 상수만 알아보겠다.

 BM_GETCHECK  체크상태를 조사.. 리턴값으로 돌려진다
 BM_SETCHECK  체크상태 변경... wParam에 변경할 상태를 지정
 BST_UNCHECKED  체크안됨
 BST_CHECKED  체크됨
 BST_INDETERMINATE  아무것도 아닌상태


3. 라디오 버튼

체크박스 생성방법과 동일하다 다만, 생성할 때 3번째 인자..

즉, 속성을 BS_RADIOBUTTON 이나 BS_AUTORADIOBUTTON으로 변경해봐라...

일반과 오토의 차이점은 체크상태를 스스로 변경하는가, 그렇제 않는가의 차이이다..


4. 에디트

 문자열을 입력받을때 사용하는 컨트롤이다...

#define ID_EDIT 100


 CHAR str[128];

 HWND hEdit;     //에디트 컨트롤을 담을 핸들


case WM_CREATE:

 hEdit = CreateWindow("edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 10, 10, 200, 25, hWnd, (HMENU)ID_EDIT, g_hInst, NULL);

 return 0;


case WM_COMMAND:

 switch(LOWORD(wParam))

  case ID_EDIT:

   switch(HIWORD(wParam))   //하이워드에서 문자열 변경상태를 파악한다

    case EN_CHANGE    // 변경되었다면

     GetWindowText(hEdit, str, 128);  // 에디트 핸들에 써진것을 가져와서

     SetWindowText(hWnd,str);    // 윈도우 캡션바 이름을 바꾼다

별로 특별한 것은 없다.  스타일과 메시지에 대해서만 간단히 알아보겠다

 ES_AUTO(H),(V)SCROLL  H는 수평, V는 수직 스크롤바가 생긴다
 ES_MULTILINE  여러 줄을 편집
 ES_READONLY  읽기전용으로 만든다


 EN_CHANGE  문자열이 변경되었을때
 EN_(H), (V)SCROLL  H는 수평바 클릭시, V는 수직바 클릭시
 EN_(KILL), (SET)FOCUS  KILL은 포커스를 잃었을때, SET은 포커스를 얻었을때
 EN_UPDATE  문자열이 변경되기 직전....


5. 리스트 박스

 #define ID_LISTBOX 100

 CHAR str[128];

 HWND hListbox;     //에디트 컨트롤을 담을 핸들

 int i;


case WM_CREATE:

 hListbox = CreateWindow("listbox", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | LBS_NOTIFY, 10, 10, 200, 250, hWnd, (HMENU)ID_LISTBOX, GetModuleHandle(0), NULL);
   SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)"aaa");   //문자열 추가, 엘파람 으로 문자가 넘어간다.
   SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)"BBB");  //문자열 추가

 return 0;

case WM_COMMAND:

 switch(LOWORD(wParam))

  case ID_LISTBOX:

   switch(HIWORD(wParam))   //하이워드에서 리스트 선택  상태를 파악한다

    case LBN_SELCHANGE    // 변경되었다면

     i = SendMessage(hListbox, LB_GETCURSEL, 0, 0);   //현제 몇번째 셀인지 조사한다      

    //구현하고 싶은 내용

사용 방식이 거의 동일하다... 역시 속성과 메시지만 알아보겠다..

 LBS_MULTIPLESEL  여러개의 항목을 선택할 수 있다
 LBS_NOTIFY  목록중 하나를 선택했을때 부모 윈도우로 메시지를 날린다
 LBS_SORT  항목을 정렬한다
 LB_(ADD), (DELETE) STRING  add는 문자열 추가, delete는 문자열 삭제
 LB_GETCURSEL  현재 선택된 항목의 번호를 얻어온다
   



6. 콤보박스

 리스트박스와 거의 동일하다... 바뀌는 부분말 말하겠다..

 listbox 부분을 combobox로 바꾸어준다... 속성을 CBS_DROPDOWN 으로 바꾸어 준다. 센드 메시지 안을 LB(리스트박스)에서 CB(콤보박스)로 바꾸어 준다.. 이러면 생성이 된다..

 

 case WM_COMMAND:

 switch(LOWORD(wParam))

  case ID_LISTBOX:

   switch(HIWORD(wParam))   //하이워드에서 리스트 선택  상태를 파악한다

    case CBN_SELCHANGE:    // 변경되었다면

     i = SendMessage(hListbox, CB_GETCURSEL, 0, 0);   //현제 몇번째 셀인지 조사한다      

    //구현하고 싶은 내용

    case CBN_EDITCHANGE:

     //구현하고 싶은 내용


난 간단히 하기 위해서 메시지 박스로 확인을 해봤다..


7. 스크롤 바

지금까지 컨트롤과는 조금 다르다..일단 생성은 동일하다

listbox 부분을 scrollbar로 바꾸어준다... 속성을 SBS_HORZ 으로 바꾸어 준다.  ID값 변경은 센스다.

생성한 후 다음 코드를 입력 한다.

 case WM_CREATE:

  //생성코드


  SetScrollRange(hScroll, SB_CTL, 0, 255, TRUE);  // 스크롤바 범위 셋팅

                                                                     //첫번째 스크롤바핸들, 두번째 별도의 컨트롤 지정하겠다는 의미

                                                                  //세번째 최소값,  네번째 최대값, 다섯번째 화면 재 출력여부

  SetScrollPos(hScroll, SB_CTL, 0, TRUE);   //스크롤바 스크롤의 초기 위치 지정

                                                              //나머진 동일하도 3번째가 초기 위치가 된다.

  return 0;

case WM_HSCROLL:
 if ((HWND)lParam == hRed) TempPos = Red;
  switch (LOWORD(wParam)) {
   case SB_LINELEFT:
    TempPos=max(0,TempPos-1);
    break;
   case SB_LINERIGHT:
    TempPos=min(255,TempPos+1);
    break;
   case SB_PAGELEFT:
    TempPos=max(0,TempPos-10);
    break;
   case SB_PAGERIGHT:
    TempPos=min(255,TempPos+10);
     break;
   case SB_THUMBTRACK:
    TempPos=HIWORD(wParam);
     break;
}
if ((HWND)lParam == hRed) Red=TempPos;

SetScrollPos((HWND)lParam,SB_CTL,TempPos,TRUE);
InvalidateRect(hWnd,NULL,FALSE);
return 0;


8. 스태틱

 간단한 컨트롤이다.... 에디트랑 비슷하며.... 글을 쓰는것이 아니라 저장된 글을 그냥 출력만 시키는 컨트롤이다..

CteateWindow("static", "넣고싶은텍스트", WS_CHILD | WS_VISIBLE, 20, 20, 120, 25, hWnd, (HMENU)-1, g_hInst, NULL);


Posted by 나비:D
:

● 컨트롤을 윈도우에 그려질려면 CreateWindow() 함수를 호출 해야한다.


CreateWindow() 함수


CreateWindow( "만들 컨트롤", "컨트롤에 나타날 문자", 컨트롤 속성, 시작할 X좌표, 시작할 Y좌표,

           , 컨트롤 폭, 컨트롤 높이, 나타낼 윈도우 핸들, (HMENU) 컨트롤 ID

           , 컨트롤을 만드는 인스턴스의 핸들 , 사용자 정의 데이터);


ex)

※ 버튼 생성

HWND hButton;


hButton = CreateWindow("button","Click",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON

             , 20, 20, 100, 25, hWnd, (HMENU)0, g_hInst, NULL);


※ 에디트박스 생성

HWND hEdit;


hEdit = CreateWindow("eidt", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER

          | ES_AUTOHSCROLL, 10, 10, 200, 25, hWnd, (HMENU)ID_EDIT, g_hInst, NULL);


※ 리스트 박스 생성

HWND hList;


hList = CreateWindow("listbox", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER

          | LBS_NOTIFY, 10, 10, 100, 200, hWnd, (HMENU)ID_LISTBOX, g_hInst, NULL);


※ 컴보 박스 생성

HWND hCombo;


hCombo = CreateWindow("combobox", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOW

               , 10, 10, 100, 200, hWnd, (HMENU)ID_COMBOBOX, g_hInst, NULL);


※ 스크롤 바 생성

HWND hScroll;


hScroll = CreateWindow("scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ

             , 10, 10, 200, 20, hWnd, (HMENU)ID_SCROLL, g_hInst, NULL);


※ 스태틱 생성

HWND hStatic;


hStatic  = CreateWindow("static", "Only Text", WS_CHILD | WS_VISIBLE

              , 10, 10, 100, 24, hWnd, (HMENU)-1, g_hInst, NULL);


Posted by 나비:D
:

[출처] 다양한 형변환|작성자 삼돌이



Environment: Compiled using VC6.0 Sp3 and tested using Win95/98 WinNT4.0 and Win 2000
Here are a few data Conversions with small examples :-



########################################################################

PART ONE :- DECIMAL CONVERSIONS
########################################################################

Decimal To Hexa :-
Use _itoa( ) function and set radix to 16.

char hexstring[10];
int number = 30;
itoa( number, hexstring, 16);



In hexstring is 1e.

Hexa To Decimal :-
a)You can use strtol function and you can specify base.

char * hexstring= "ABCDEF";
char * p;
int number = strtol(hexstring, &p,16);



b) Or you can use this too

bool HexToDecimal (char* HexNumber, int& Number)
{
char* pStopString;
Number = strtol (HexNumber, &pStopString, 16);
return (bool)(Number != LONG_MAX);
}



Decimal to Time :-

char *DecToTime(float fTime, char *szTime)
{
int nHrs, nMin, nSec;
fTime *= 3600;
nHrs = (int)fTime / 3600;
nMin = (int)(fTime - nHrs * 3600) / 60;
nSec = (int)(fTime - nHrs * 3600 - nMin * 60);
wsprintf(szTime, "%02d.%02d.%02d Hrs.Min.Sec.", nHrs, nMin, nSec);
return szTime;
}



########################################################################

PART TWO :- STRING CONVERSIONS

########################################################################
String to Hexa :-

sscanf(string, %04X, &your_word16);
// where string = your string and 04 = length of your string and X = hex


Hex to CString :-
CString Str;
unsigned char Write_Buff[1];
Write_Buff[0] = 0x01;
Str.Format("0x0%x",Write_Buff[0]);


COleVariant to CString :-
CString strTemp;
COleVariant Var;
Var = "FirstName";
strTemp = Var.bstrVal;
AfxMessageBox(strTemp);


CString to Char Pointer :-
a) CString MyString = "ABCDEF";
char * szMyString = (char *) (LPCTSTR) MyString;



b) char *pBuffer = new char[1024];
CString strBuf = "Test";
pBuffer = strBuf.GetBuffer(sizeof(pBuffer));



Char Pointer to CString :-
char * mystring = "12345";
CString string = mystring;


Double to CString including the fractional part :-
CString strValue,strInt, strDecimal;
int decimal,sign;
double dValue = 4.125;
strValue = _fcvt(dValue,6,&decimal,&sign);

// Now decimal contains 1 because there is only one digit before the .

strInt = strValue.Left(decimal); // strInt contains 4
strDecimal = strValue.Mid(decimal); // strDecimal contains 125

CString strFinalVal;
strFinalVal.Format("%s.%s",strInt,strDecimal); // strFinalVal contains 4.125



Double To CString :-
CString strValue;
int decimal,sign;

double dValue = 123456789101112;
strValue = _ecvt(dValue,15,&decimal,&sign);


CString To Double :-
strValue = "121110987654321";
dValue = atof(strValue);


CString to LPCSTR :-
CString str1 = _T("My String");
int nLen = str1.GetLength();
LPCSTR lpszBuf = str1.GetBuffer(nLen);
// here do something with lpszBuf...........
str1.ReleaseBuffer();


CString to LPSTR :-
CString str = _T("My String");
int nLen = str.GetLength();
LPTSTR lpszBuf = str.GetBuffer(nLen);
// here do something with lpszBuf...........
str.ReleaseBuffer();

CString to WCHAR* :-
CString str = "A string here" ;
LPWSTR lpszW = new WCHAR[255];

LPTSTR lpStr = str.GetBuffer( str.GetLength() );
int nLen = MultiByteToWideChar(CP_ACP, 0,lpStr, -1, NULL, NULL);
MultiByteToWideChar(CP_ACP, 0, lpStr, -1, lpszW, nLen);
AFunctionUsesWCHAR( lpszW );
delete[] lpszW;



LPTSTR to LPWSTR :-
int nLen = MultiByteToWideChar(CP_ACP, 0, lptStr, -1, NULL, NULL);
MultiByteToWideChar(CP_ACP, 0, lptStr, -1, lpwStr, nLen);



string to LPCTSTR :-

std::string strTemp;

LPCTSTR lpName = strTemp.c_str();


string to BSTR :-
string ss = "Girish";
BSTR _bstr_home = A2BSTR(ss.c_str());


CString to BSTR :-
CString str = "whatever" ;
BSTR resultsString = str.AllocSysString();


_bstr_t to CString :-

#include
#include
_bstr_t bsText("Hai Bayram");
CString strName;
W2A(bsText, strName.GetBuffer(256), 256);
strName.ReleaseBuffer();
AfxMessageBox(strName);

char szFileName[256];
GetModuleFileName(NULL,szFileName,256);
AfxMessageBox(szFileName);



########################################################################

PART THREE :- CHARACTER ARRAYS

########################################################################

Char array to integer
char MyArray[20];
int nValue;

nValue = atoi(MyArray);



Char array to float
char MyArray[20];
float fValue;

fValue = atof(MyArray);


Char Pointer to double :-
char *str = " -343.23 ";
double dVal;
dVal = atof( str );


Char Pointer to integer :-
char *str = " -343.23 ";
int iVal;
iVal = atoi( str );



Char Pointer to long :-
char *str = "99999";
long lVal;
lVal = atol( str );


Char* to BSTR :-
char * p = "whatever";
_bstr_t bstr = p;

Float to WORD and Vice Versa :-
float fVar;
WORD wVar;
fVar = 247.346;
wVar = (WORD)fVar; //Converting from float to WORD. The value in wVar would be 247
wVar = 247;
fVar = (float)fVar; //Converting from WORD to float. The value in fVar would be 247.0 

Posted by 나비:D
:

C++ MFC Tip

2008. 7. 28. 16:16

출처 : Tong - navy9370님의 MFC통


1. DC얻기

  CClientDC dc(this);



2. Client 영역 구하기

  GetClientRect(&rect);

  WM_SIZE 메시지발생후 cx,cy 사용



3. 문자열 사각형안에 그리기

  pDC->DrawText(문자열,사각형,Style);

  Style: DT_BOTTOM - 문자열을 사각형 맨아래줄에배열 반드시 DT_SINGLELINE과 함께사용

        DT_CENTER - 문자열을 가로중앙에 배치

        DT_VCENTER - 문자열을 세로중앙에 배치

        DT_LEFT,RIGHT - 문자열을 좌,우로 배치

        DT_SINGLELINE - 문자열을 한줄로만 쓴다



4. Brush 사용법

  CBrush brushname(RGB(red,green,blue)); //브러쉬 생성

  CBrush *oldBrush=pDC->SelectObject(&brushname); //이전Brush 저장, 새로운 Brush 선택

  pDC->SelectObject(oldBrush); //원래의 브러쉬로 반환



5. Pen사용법

  CPen pen(Pen Style,RGB(red,green,blue)); //브러쉬생성

//Style: PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_GEOMETRIC,PS_COSMETRIC - 펜종류

        PS_ENDCAP_ROUND,PS_ENDCAP_SQUARE - 펜끝을 둥글게,각지게 설정

  CPen *oldPen=pDC->SelectObject(&pen); //이전Pen저장, 새로운 Pen설정

  pDC->SelectObject(oldPen); //펜반환



6. 화면다시그리기

  View Class에서 - Invalidate(TRUE) : 화면을 지우고다시그린다

                    Invalidate(FALSE) : 화면을 덮어씌운다

  UpdateAllViews(NULL);  // Doc Class에서 View 의 OnDraw 호출

  RedrawWindow();



7. 메시지,함수 수동으로 넣기 (EX)버튼클릭함수넣기

  헤더파일의 AFX_MSG_MAP 부분에 함수를 정의

  EX) afx_msg void funcName();

  .cpp파일의 AFX_MSG 부분에 메시지를 추가한다

  EX) ON_BN_CLICKED(ID_NAME,funcName)...

  ID 등록:  View 메뉴의 Resource Symbol 에 들어가서 메뉴 ID 를 등록해준다..

  .cpp파일의 맨아래에서 함수를 정의한다

  EX) void CClass::funcName() { ... }



8. 마우스커서 바꾸기

  리소스탭에서 커서를 그리고 저장한뒤 ID값은 준다음

  SetCapture(); //커서의입력을 클라이언트영역을 벗어나더라도 받아낸다

  SetCursor(AfxGetApp()->LoadCursor(nIDResource));

  //APP클래스의 LoadCursor View의 SetCursor 사용

  ReleaseCapture(); //SetCursor()상태를 해제한다



9. 색상표 사용하기

  CColorDialog dlg;

  if(dlg.DoModal()==IDOK) //Dialog 를 띄운후 OK버튼을누르면 실행할부분

  MemberFunc: GetColor() //선택된 색상을 받아온다 return 형은 COLORREF 형



10. 팝업메뉴 만들기

  CMenu menu; //메뉴 객체생성

  CMenu *pmenu; //메뉴 포인터생성

  menu.LoadMenu(IDR_MAINFRAME); //메뉴를 불러온다

  pmenu=menu.GetSubMenu(3); //메뉴의 3번째 메뉴를 가져온다

  menu.CheckMenuItem(ID_MENU,m_kind==ID_MENU ? MF_CHECKED : MF_UNCHECKED);

  //메뉴 체크하기 (메뉴 ID, ID 체크조건)

  pmenu->TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this)  //(TMP_Style,x좌표,y좌표,hWnd) 메뉴 띄우기



  *주의사항*

    [안내]태그제한으로등록되지않습니다-OnContextMenu(CWnd* pWnd, CPoint point)  //여기서 point 는 스크린 기준이고,

    OnRButtonDown(UINT nFlags, CPoint point)  //여기서 point 는 클라이언트 기준이다!



11. 클라이언트 포인터를 스크린 포인터로 변경

  ClientToScreen(&point);



12. 그림판기능

         if(m_flag==FALSE)  return;   //m_falg=그리기 기능 참,거짓설정  그리기 아니면 빠져나간다

        CClientDC dc(this);

        CPen myPen(PS_SOLID,m_width,m_color);

        CPen *pOldPen=dc.SelectObject(&myPen);

        switch(m_shape)

        {

        case ID_FREELINE: //자유선그리기

                dc.MoveTo(m_oldpt.x,m_oldpt.y); //지난포인터부터

                dc.LineTo(point.x,point.y); //새포인터까지 그린다

                break;

        case ID_RECT: //사각형그리기

                dc.SetROP2(R2_NOTXORPEN);

                dc.Rectangle(m_spt.x,m_spt.y,m_oldpt.x,m_oldpt.y);  //지워지는 효과

                dc.Rectangle(m_spt.x,m_spt.y,point.x,point.y); //그려지는 효과

                break;

        case ID_ELLIPSE: //원그리기

                dc.SetROP2(R2_NOTXORPEN);

                dc.Ellipse(m_spt.x,m_spt.y,m_oldpt.x,m_oldpt.y);  //지워지는 효과

                dc.Ellipse(m_spt.x,m_spt.y,point.x,point.y); //그려지는 효과

                break;

        case ID_LINE: //선그리기

                dc.SetROP2(R2_NOTXORPEN);

                dc.MoveTo(m_spt.x,m_spt.y); //시작점부터

                dc.LineTo(m_oldpt.x,m_oldpt.y); //지난점까지 그은선을 지운다

                dc.MoveTo(m_spt.x,m_spt.y); //시작점부터

                dc.LineTo(point.x,point.y); //새로운점까지 그린다

                break;

        }

        m_oldpt=point;  //바로이전값 보관

        dc.SelectObject(pOldPen); //펜 반환


13. MessageBox

  AfxMessageBox() -> 전역함수를 이용하영 메세지 박스를 출력한다.   //어디서든지 사용할수 잇다

  int CWnd::MessageBox("메세지","창제목","아이콘|버튼(상수값)");   //View클래스에서 사용한다

  아이콘 상수값  MB_IC[안내]태그제한으로등록되지않습니다-xxONERROR, MB_ICONWARNING, MB_ICONQUESTION,MB_ICONINFOMATION

                MB_SYSTEMMODAL //시스템모달 대화창 닫기전에 다른작업 못함

                MB_APPLMODAL //응용모달

  버튼 상수값    MB_OK, MB_OKCANCEL, MB_YESNO



14. OS 컨트롤

        ExitWindowEx(EWX_SHUTDOWN,NULL); //Shut Down

        ExitWindowsEx(EWX_FORCE,0); //강제종료

        ExitWindowsEx(EWX_LOGOFF,0); //로그오프

        ExitWindowsEx(EWX_POWEROFF,0); //Shut Down -> Turn Off

        ExitWindowsEx(EWX_REBOOT); //Shut Down -> Reboot



15. DialogBox 메시지 교환

        UpdateData(FALSE); // 컨트롤에 멤버변수의 내용을 표시해준다

        UpdateData(TRUE);  // 컨트롤 내용을 다이얼로그 클래스의 멤버변수로 저장



16. 자료변환

        atoi,itoa - int <=> ASCII(char) 변환

        str.Format(" %d %d",x,y); // int형을 문자열로 변환

        atol,ltoa - ASCII <=> long 변환

        atof - ACSII => float 변환

        fcvt,gcvt  - 실수를 text로 변환

        LPtoDP, DPtoLP - 장치좌표 <=> 논리좌표 변환



17. CEdit Class 사용하기

  CEdit e_str.SetSel(int StartChae, int EndChar); //처음문자부터 마지막까지 블록 지정

  CEdit e_str.GetSel(int SChar,int EChar); //블럭 지정한 처음문자와 마지막문자 받기

  CString str=m_str.Mid(SChar,EChar-SChar); //블럭지정한 부분을 가져온다


18. 컨트롤과 자료교환

  SetDlgItemText(컨트롤 ID,문자열) //컨트롤에 문자열을 넣는다

  GetDlgItemText(컨트롤 ID,문자열) //컨트롤의 내용을 문자열에 넣는다

  GetDlgItem(컨트롤 ID); //컨트롤의 주소를 가져온다


19. 상태바조작

  CMainFrame 생성자 위에

  static UINT indicators[] = //이안에 새로운 ID를 넣고 그 ID의 갱신핸들러를 만든다음 코딩

  pCmdUI->SetText("표시할내용“);



20. 수동으로 Bitmap 컨트롤 사용하기

  CStatic bitmap; //bitmap 컨트롤변수

  bitmap.SetBitmap(CBitmap m_bitmap); //컨트롤에 비트맵지정

  GetDlgItem(IDC_BITMAP)->ShowWindow(SW_SHOW,HIDE);  // 그림을 보이거나 숨긴다.

  

21. 응용프로그램 실행하기

  WinExec("프로그램경로“,SW_SHOW,HIDE); //응용프로그램실행,경로는 \\로 구분한다



22. Bitmap 사용하기

  CBitmap bitmap.LoadBitmap(IDC_BITMAP); //비트맵객체에 비트맵지정

  CDC memDC; //그림그릴 메모리DC생성

  MemDC.CreateCompatibleDC(pDC); //화면 DC와 메모리 DC 호환 생성

  CBitmap *pOldBitmap=MemDC.SelectObject(&m_bitmap); //메모리에 그림을그린다.

  pDC->BitBlt(int x, int y,int Width, int Height, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop);

//BitBlt(그림x좌표,그림y좌표,그림넓이,그림높이,그림그려진메모리DC,그림시작x좌표,그림시작y좌표,스타일);

  pDC->StretchBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop )

//StretchBlt(그림x좌표,그림y좌표,그림넓이,그림높이,그림그려진메모리DC,그림x좌표,그림y좌표,메모리그림넓이,메모리그림높이,스타일);

MemDC.SelectObject(pOldBitmap); // 메모리DC반환



23. Font 바꾸기

  CFontDialog dlg; //폰트다이얼로그 생성

  LOGFONT m_logFont; //폰트받을변수선언

  if(dlg.DoModal()==IDOK) //폰트다이얼로그표시

  {dlg.GetCurrentFont(&m_logFont)} //선택된 폰트받기

  OnDraw()

   CFont newFont,*pOldFont; //폰트 객체 만들기

   newFont.CreateFontIndirect(&m_logFont); //폰트 생성

   pOldFont=(CFont *)pDC->SelectObject(&newFont); //폰트 선택

   OnCreate()

   CClientDC dc(this); //DC 생성

   CFont *pFont=dc.GetCurrentFont();        //클라이언트 영역의 폰트를

   pFont->GetLogFont(&m_logFont); //로그폰트 멤버값으로 지정



24. Font 만들기

         LOGFONT logfont; //폰트를 만든다

        logfont.lfHeight=50;               //문자열 높이

        logfont.lfWidth=0;                 //너비

        logfont.lfEscapement=0;            //문자열기울기

        logfont.lfOrientation=0;             //문자개별각도

        logfont.lfWeight=FW_NORMAL;     //굵기

        logfont.lfItalic=TRUE;             //이탤릭

        logfont.lfUnderline=TRUE;  //밑줄

        logfont.lfStrikeOut=FALSE; //취소선

        logfont.lfCharSet=HANGUL_CHARSET; //필수

        logfont.lfOutPrecision=OUT_DEFAULT_PRECIS;               

        logfont.lfClipPrecision=CLIP_DEFAULT_PRECIS;      //가변폭폰트 고정폭폰트

        logfont.lfPitchAndFamily=DEFAULT_PITCH|FF_SWISS; //글꼴이름

        strcpy(logfont.lfFaceName,"궁서체");

        CClientDC dc(this);

        CFont newFont; //폰트객체생성

        newFont.CreateFontIndirect(&logfont); //폰트지정

        CFont *pOldFont=dc.SelectObject(&newFont); //폰트선택

        dc.TextOut(100,100,m_text);

        dc.SelectObject(pOldFont); //폰트반환



25. Font 만들기 2

  CFont newFont;

  newFont.CreateFont( int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, BYTE nQuality, BYTE nPitchAndFamily, LPCTSTR lpszFacename );

 CFont *pOldFont=dc.SelectObject(&newFont);



26. ComboBox 사용하기

  CComboBox combo; //콤보박스 선언

  combo.Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

  //Style - WS_CHILD|WS_VISIBLE

  int n=combo.GetCurSel(); //선택된 아이템의 index를 가져온다

  combo.AddString("문자열“); //문자열을 추가한다

  combo.GetLBText(n,str); //n번째 아이템을 str에 저장



27. Spin 사용하기

  Spin은 바로앞의 Tab Order에 따라 붙는다

  m_spinr.SetRange(1900,3000); //스핀 범위 지정

  m_spinr.SetPos(m_nYear); //스핀 위치 지정



28. CTime사용하기

  CTime time; //시간객체생성

  time=CTime::GetCurrentTime(); //현재시간을 저장

  time.GetYear(),time.GetMonth();,time.GetDay(),time.GetHour(),time.GetMinute(),time.GetSecond()



29. CListBox 메소드

  AddString("문자열");             //리스트에 문자열 추가

  DeleteString(index);             //리스트에서 항목 삭제

  GetCount()                     //전체 항목 갯수를 얻는다.

  GetSelcount()                   //선택된 항목 갯수 리턴

  GetSel()                       //선택된 것인지 아닌지를 리턴한다 -> 양수 = TRUE , 음수 => FALSE

  GetText(int index,문자열변수)     //index 번째 문자열을 문자열 변수에 넣는다

  FindStringExact(문자열)          //지정 문자열의 index 값 리턴 -> 없으면 리턴값 LB_ERR 반환

  FindString("a")                 //"a"로 시작하는 항목을 모두 찾는다.

  ResetCountent()                 //모든 내용을 지운다.



30. 파일입출력

 프로젝트생성시 Step4 => Advanced => 저장파일확장자지정

 .h 파일에       DECLARE_SERIAL(CSawon) //이 클래스를 저장,로드가능한 클래스로 쓰겟다는 선언

 .cpp 파일에     IMPLEMENT_SERIAL(CSawon,CObject,1) //이거를 해야 저장이 가능하다

void CFileioDoc::Serialize(CArchive& ar)

        if (ar.IsStoring())  //저장하기

        {ar<

        else    //열기

        {ar>>m_shape; //불러올걸 쓴다. 읽을때도순서대로읽어야한다}



31. MicroSoft FlexGrid 사용하기!

        CMSFlexGrid m_Grid; //FlexGrid 컨트롤 변수

        CString strTitle[]={"고객코드","고객성명","고객포인트","신장","몸무게","고객등급","BMT지수","판정결과"};

        // Grid 의 제목에 넣을문자배열

        int Width[]={900,900,1100,800,800,900,1000,900};

        // Grid 의 열넓이 지정할 배열

        m_Grid.SetRows(m_cnt+2); //전체행수 지정

        m_Grid.SetCols(8); //전체열수 지정

        m_Grid.Clear(); //지우기

        m_Grid.SetFixedCols(0); //고정열은 없다.

        m_Grid.SetRow(0); // 행선택

        for(int i=0;i<=7;i++)

        {

                m_Grid.SetColWidth(i,Width[i]); //열 넓이 설정

                m_Grid.SetCol(i); //열 선택

                m_Grid.SetText(strTitle[i]); // 선택된행, 선택된열에 Text 를 넣는다

        }



32. 4대 Class간 참조

//각각 헤더파일 include

#include "MainFrm.h" //메인프레임 헤더파일

#include "ClassDoc.h"   //Doc클래스 헤더파일

#include "ClassView.h" //View를 include 할때는 반드시 Doc 헤더파일이 위에잇어야한다

#include "Class.h" //APP Class 의 헤더파일



void CClassView::OnMenuView() //뷰클래스

        CClassApp *pApp=(CClassApp *)AfxGetApp();   //View -> App
        CMainFrame *pMain=(CMainFrame *)AfxGetMainWnd();  //View -> MainFrm

        CClassDoc *pDoc=(CClassDoc *)pMain->GetActiveDocument(); //View -> MainFrm -> Doc

        CClassDoc *pDoc=(CClassDoc *)GetDocument();                     //View -> Doc



 //MainFrame 클래스

        CClassView *pView=(CClassView *)GetActiveView();  //MainFrm -> View

        CClassDoc *pDoc=(CClassDoc *)GetActiveDocument();  //MainFrm -> Doc

        CClassApp *pApp=(CClassApp *)AfxGetApp(); //MainFrm -> App



//Doc 클래스

        CClassApp *pApp=(CClassApp *)AfxGetApp(); //Doc -> App

        CMainFrame *pMain=(CMainFrame *)AfxGetMainWnd(); //Doc -> MainFrm

        CClassView *pView=(CClassView *)pMain->GetActiveView(); // Doc -> MainFrm -> View

        CClassView *pView=(CClassView *)m_viewList.GetHead();      // Doc -> View



//App 클래스

        CMainFrame *pMain=(CMainFrame *)AfxGetMainWnd(); //App -> MainFrm

        CClassView *pView=(CClassView *)pMain->GetActiveView(); //App -> MainFrm -> View

        CClassDoc *pDoc=(CClassDoc *)pMain->GetActiveDocument(); //App -> MainFrm -> Doc



33. ToolBar 추가하기

  CMainFrame 으로 가서 멤버변수 추가

        CToolBar m_wndToolBar1;

  OnCreate 로 가서 다음 내용을 추가해준다 (위의 toolbar 부분을 복사하고 이름만 바꾸면 된다.3군데..)

  if (!m_wndToolBar1.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP

                | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||

                !m_wndToolBar1.LoadToolBar(IDR_TOOLBAR1))

        {

                TRACE0("Failed to create toolbar\n");

                return -1;      // fail to create

        }



  그 함수내에서 //TODO 아래에 내용추가..역시..복사해서 이름만 바꾸면 된다.

        m_wndToolBar1.EnableDocking(CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM);

        //DockControlBar(&m_wndToolBar1);   <= 이부분 대신..

        이거를 넣는다..

        CRect toolRect; //툴바 영역을 얻을 사각형

        this->RecalcLayout(); //현상태의 Client 영역을 구해서 저장한다

        m_wndToolBar.GetWindowRect(&toolRect); //툴바영역을 저장한다

        toolRect.left+=1; //사각형의 왼쪽을 1Pixel 줄인다

        DockControlBar(&m_wndToolBar1,AFX_IDW_DOCKBAR_TOP,&toolRect); //ToolRect에 툴바를 붙인다

        return 0;



34. ToolBar에 ComboBox붙이기

        CComboBox m_combo; //객체생성

        ID 등록 => view 메뉴 => resource symbol => new => ID_COMBO

  oncreate 에 내용 추가 (콤보를 만들고 표시하는 내용)

        m_wndToolBar.SetButtonInfo(10,IDC_COMBO,TBBS_SEPARATOR,150); 

        //툴바의 10번째버튼을 편집한다

        CRect itemRect; //콤보를넣을 사각형을 만든다

        m_wndToolBar.GetItemRect(10,&itemRect); //툴바의 10번째 버튼을 사각형에 넣는다
        itemRect.left+=5; //앞여백

        itemRect.right+=5; //뒤여백

        itemRect.bottom+=100; //콤보가열릴 공간확보

        m_combo.Create(WS_CHILD|WS_VISIBLE|CBS_DROPDOWN,itemRect,&m_wndToolBar,IDC_COMBO);

        //콤보박스를 툴바에 붙여준다

        m_combo.AddString("이름"); //내용추가

        m_combo.SetCurSel(0); //셀 선택



35.  Toolbar에 수동으로넣은 ComboBox 사용하기

  afx_msg void [안내]태그제한으로등록되지않습니다-xxOnSelectCombo(); //원형

  ON_CBN_SELCHANGE(IDC_COMBO,[안내]태그제한으로등록되지않습니다-xxOnSelectCombo) //메세지맵에 추가

        CMainFrame *pMain=(CMainFrame *)GetParent(); //메인프레임 주소참조

        CComboBox *pCom=(CComboBox *)(pMain->m_wndToolBar.GetDlgItem(IDC_COMBO));

        //콤보박스의 주소를 가져온다, 접근할 때 메인프레임 -> 툴바 -> 콤보박스 의 순서로 가야한다

        int n=pCom->GetCurSel(); //현재선택된 셀의 인덱스를 가져온다

        if(n==CB_ERR) return; //선택된셀이 없으면 중지한다

        CString str;

        pMain->m_combo.GetLBText(n,str); //선택된셀의 Text를 가져온다



36. UPDATE_COMMAND 사용하기

        pCmdUI->Enable(TRUE); //버튼 활성화

        pCmdUI->SetText((bAdd)?"취소":"신규"); //버튼의 text 설정

        pCmdUI->SetCheck(TRUE); //버튼 체크



37. 프로그램정보저장

  CWinApp::GetProfileString(섹션명,항목명,기본값); // 함수를 사용한다. (문자열)

  CWinApp::GetProfileInt(섹션명,항목명,기본값);  //불러올때사용 (숫자) 

  CWinApp::WriteProfileString(섹션명,항목명,값); //저장할때 사용 (문자열)

  CWinApp::WriteProfileInt(섹션명,항목명,값); //저장할때 사용 (숫자)

  //불러올때 사용할함수

  void CMainFrame::ActivateFrame(int nCmdShow)  //프로그램 실행후 프레임생성될때 실행

  //저장할 때 WM_DESTROY 메시지 사용



38. 컨트롤바 표시하기

        CMainFrame *pMain=(CMainFrame *)GetParent(); //MainFrame 주소가져오기

        pMain->ShowControlBar(&pMain->m_wndToolBar,bTool1,FALSE); //툴바를 bTool2 에따라 보이고 감춘다



39. Window 창크기,위치정보 저장하기

MainFrame 의 WM_DESTROY 에

        WINDOWPLACEMENT w;

        this->GetWindowPlacement(&w); //윈도우의 정보를 저장한다.

        CString strRect;

        strRect.Format("%04d,%04d,%04d,%04d", //04d 는 4자리 확보하고 남은건 0으로 채워라

                w.rcNormalPosition.left,w.rcNormalPosition.top,

                w.rcNormalPosition.right,w.rcNormalPosition.bottom); //윈도우의 위치,크기 확보..

        

        BOOL bMax,bMin; //윈도우의 상태를 저장하기위한 변수

        //w.falg 는 이전상태의 정보를 가지고 잇다!!

        if(w.showCmd==SW_SHOWMINIMIZED)           //최소화 상태

        {

                bMin=TRUE;

                if(w.flags==0) //falg 값이 0 이면 이전 상태가 보통상태이다!!

                        bMax=FALSE;

                else    //이전상태가 최대화 상태

                        bMax=TRUE;

        }

        else                            

        {

                if(w.showCmd==SW_SHOWMAXIMIZED) //최대화상태

                {

                        bMax=TRUE;

                        bMin=FALSE;

                }

                else  //보통 상태

                {

                        bMax=FALSE;

                        bMin=FALSE;

                }

        }

        AfxGetApp()->WriteProfileString("WinStatus","Rect",strRect);

        AfxGetApp()->WriteProfileInt("WinStatus","Max",bMax);

        AfxGetApp()->WriteProfileInt("WinStatus","Min",bMin);



//읽어올차례..

ActivateFrame 함수로 가서

        WINDOWPLACEMENT w;  //윈도우의 상태를 저장하는 구조체..

        BOOL bMax,bMin;               //최대,최소상태를 저장할 변수

        CString strRect; //창크기를 받아올 변수

        strRect=AfxGetApp()->GetProfileString("WinStatus","Rect","0000,0000,0500,0700");

        bMin=AfxGetApp()->GetProfileInt("WinStatus","Min",FALSE);

        bMax=AfxGetApp()->GetProfileInt("WinStatus","Max",FALSE);

        int a=atoi(strRect.Left(4)); //문자열을 int 로 바꿔준다.

        int b=atoi(strRect.Mid(5,4));     //atoi 아스키 값을 int형으로 바꿔준다..

        int c=atoi(strRect.Mid(10,4));

        int d=atoi(strRect.Mid(15,4));

        w.rcNormalPosition=CRect(a,b,c,d);

        if(bMin)

        {

                w.showCmd=SW_SHOWMINIMIZED;

                if(bMax)

                {

                        w.flags=WPF_RESTORETOMAXIMIZED  ;

                }

                else

                {

                        w.flags=0;

                }

        }

        else

        {

                if(bMax)

                {

                        w.showCmd=SW_SHOWMAXIMIZED;

                }

                else

                {

                        w.showCmd=SW_SHOWNORMAL;

                }

        }

        this->SetWindowPlacement(&w); //설정된 값으로 윈도우를 그리게 한다..

        

        //CFrameWnd::ActivateFrame(nCmdShow); //이건 반드시 주석처리한다..



40. progress Bar 쓰기



        m_progress.SetRange(m_first,m_last); //Progress 범위설정하기

        m_progress.SetStep(m_step); //Progress Step설정하기

        //m_progress.StepIt(); //스텝만큼 움직이기

        //또는 다음을 사용한다

        for(int a=m_first;a<=m_last;a+=m_step) //a가 처음부터 끝까지

        {

                m_progress.SetPos(a); // 위치를 a에 맞춘다

                Sleep(50); //천천히 움직이게한다

        }



41. 파일대화상자 FileDialog 사용하기

void CConDlg1::OnFileopen()  //파일열기 버튼

{

        CFileDialog *fdlg; //파일대화상자 객체 생성 // 포인터로 만든다..

        static char BASED_CODE szFilter[] = "Animate Video Files (*.avi)|*.avi|All Files (*.*)|*.*||";

        //필터를 만들어 준다..이건 할줄 모름..

        fdlg =new CFileDialog(TRUE, ".avi", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,szFilter);

        //대화상자 만들기..이렇게 해야댄다..

        if(fdlg->DoModal()==IDOK) //이제..대화상자를 띠우고..    

        {                               //OK 누르면 실행될 부분..

                m_filename=fdlg->GetPathName();        //대화상자에서 경로를 받아서 저장.

                UpdateData(FALSE);    

        }

}

선생님이 해준거 //파일 다이얼로그 만들기

CFileDialog fdlg(TRUE,"avi",".avi",OFN_OEVRWRITEPROMPT,"Vidoe Files(*.avi)|*.avi|All Files(*.*)|*.*||");



42. Animate Control 사용하기

        m_animate.Open(m_filename); //파일을 연다

        m_animate.Play(0,-1,1);  //(처음프레임,마지막프레임,반복횟수)

        m_animate.Stop(); //정지시키기

        m_ani.SetAutoStart(TRUE); //자동으로 시작한다

43. Control 의 Style 바꿔주기

        Control.ModyfyStyle(제거할스타일,추가할스타일); //스타일은 MSDN내용 참조



44. 시스템 날자바꾸기 버튼

//SetSystemTime(),GetSystemTime() //GMT 표준시를 가져온다.

//GetLocalTime(),SetLocalTime()  //현재 지역시간을 가져온다.



        SYSTEMTIME st;

        GetLocalTime(&st); //현재 시간, 날자를 넣는다.

        st.wYear=m_date2.GetYear();

        st.wMonth=m_date2.GetMonth();

        st.wDay=m_date2.GetDay();

        SetSystemTime(&st);



45. 시스템 시간 바꾸기 버튼

        UpdateData(TRUE);

        SYSTEMTIME st;

        GetLocalTime(&st);

        st.wHour=m_time.GetHour();

        st.wMinute=m_time.GetMinute();

        st.wSecond=m_time.GetSecond();

        SetLocalTime(&st);



46.시스템의 드라이브 문자 얻기



        char temp[50];

        GetLogicalDriveStrings(sizeof(temp),temp);

        CString str,str1;

        int n=0;

        while(*(temp+n)!=NULL)

        {

                str=temp+n;

                str1+= " "+str.Left(2);

                n+=4;

        }



47. 현재 작업경로 얻기

        char temp[MAX_PATH]; //MAX_PATH 는 경로길이의 최대를 define 해놓은것.

        GetCurrentDirectory(sizeof(temp),temp);  // 현작업하는 경로를 얻어온다.(경로 길이,문자형);



48. Tree Control 사용하기

        HTREEITEM hmov,hmus; //핸들을받을 변수 이게 잇어야 하위 디렉토리 생성가능

        hmov=m_tree.InsertItem("영화",TVI_ROOT,TVI_LAST); //,TVI_ROOT,TVI_LAST는 default

        hm1=m_tree.InsertItem("외화",hmov);  //hmov 아래 “외화”트리 생성

        CImageList m_image; //그림을 사용하기 위한 클래스다!! 알아두자..

        m_tree.SetImageList(&m_image,TVSIL_NORMAL); //Tree View Style Image List => TVSIL

        hmov=m_tree.InsertItem("영화",0,1,TVI_ROOT,TVI_LAST); //,TVI_ROOT,TVI_LAST는 default

        hmus=m_tree.InsertItem("가요",1,2); //("문자열",처음그림번호,선택시그림)

        hm1=m_tree.InsertItem("외화",2,3,hmov); //그림 번호는 default 로 0이 들어간다..



49. List Control 사용하기

        m_list.ModifyStyle(LVS_TYPEMASK, LVS_ICON); //리스트를 큰아이콘형태로 보인다

        m_list.ModifyStyle(LVS_TYPEMASK, LVS_SMALLICON);  //리스트를 작은아이콘형태로 보인다

        m_list.ModifyStyle(LVS_TYPEMASK, LVS_LIST); //리스트를 리스트형태로 보인다

        m_list.ModifyStyle(LVS_TYPEMASK, LVS_REPORT); //리스트를 자세히형태로 보인다



        CImageList m_treeimage; //이미지리스트

        CImageList m_small, m_large;

        m_large.Create(IDB_LARGE,32,0,RGB(255,255,255)); //이거는 클래스에서 추가해준거다

        m_small.Create(IDB_SMALL,16,0,RGB(255,255,255)); (bmp ID값,

        m_list.SetImageList(&m_large,LVSIL_NORMAL);

        m_list.SetImageList(&m_small,LVSIL_SMALL);

        CString name[]={"홍길동","진달래","한국남","개나리"};

        CString tel[]={"400-3759","304-7714","505-9058","700-9898"};

        CString born[]={"1980-1-1","1981-12-20","1980-05-15","1981-08-31"};

        CString sex[]={"남자","여자","남자","여자"};

        

        m_list.InsertColumn(0,"이름",LVCFMT_LEFT,70);

        m_list.InsertColumn(1,"전화번호",LVCFMT_LEFT,80);

        m_list.InsertColumn(2,"생일",LVCFMT_LEFT,90);

        m_list.InsertColumn(3,"성별",LVCFMT_LEFT,50);

        LVITEM it; //리스트 구조체

        char temp[100];

        for(int a=0;a<4;a++)

        {       

                int n=(sex[a]=="남자")?0:1;

                m_list.InsertItem(a,name[a],n); //insert item 은 행을 만들고..

                it.mask=LVIF_TEXT|LVIF_IMAGE; //마스크 설정

                it.iItem=a;

                it.iSubItem=1; //열 설정

                strcpy(temp,tel[a]); //이거 모하는거냐..

                it.pszText=temp;

                m_list.SetItem(&it);                      // setitem 열에 정보를 넣는다.



                it.iSubItem=2; //열 설정

                strcpy(temp,born[a]); //이거 모하는거냐..

                it.pszText=temp;

                m_list.SetItem(&it);                      // setitem 열에 정보를 넣는다.



                it.iSubItem=3; //열 설정

                strcpy(temp,sex[a]); //이거 모하는거냐..

                it.pszText=temp;

                m_list.SetItem(&it);                      // setitem 열에 정보를 넣는다.




50. Bitmap Button 사용하기

  CBitmapButton 을 사용한다! CButton 에서 상속 받는클래스임..

        m_button1.Create(NULL,

                WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,CRect(310,20,370,50),

                this,IDC_MYBUTTON); //버튼만들기

        m_button1.LoadBitmaps(IDB_UP,IDB_DOWN,IDB_FOCUS,IDB_DISABLE); //버튼의 그림설정

        m_button1.SizeToContent(); //버튼을 그림 크기로 맞춰 준다!!



 그냥 버튼을 비트맵버튼으로 바꾸기 -> 버튼을 만든다 속성에서 OWNERDRA 속성에 체크!!

        m_button2.LoadBitmaps(IDB_UP,IDB_DOWN,IDB_FOCUS,IDB_DISABLE); //버튼의 그림설정

        m_button2.SizeToContent(); //버튼을 그림 크기로 맞춰 준다!!



51. 중복없는 난수발생하기

        int su; //발생된 난수저장

        int a,b;

        BOOL bDasi; //숫자가중복될경우 다시하기위한 변수

        for(a=0;a<9;a++)  //난수 9개 발생

        {

                bDasi=TRUE;

                while(bDasi)

                {

                        bDasi=FALSE;

                        su=rand()%10; //난수발생

                        for(b=0;b

                        {

                                if(temp[b]==su)  //중복이면

                                {

                                        bDasi=TRUE; //중복이 잇으면 다시while 문을 실행한다

                                        break;

                                }//if

                        }//for

                }//while

                temp[a]=su; //중복이 아니면 대입한다



52. 메뉴 범위로 사용하기

  ON_COMMAND_RANGE(ID_LEVEL3,ID_LEVEL9,OnLevel); //범위메세지 발생

  //메뉴 ID의 값이 연속된 숫자일 경우 범위로 지정해서 사용할수잇다



53. 한,영 전환함수

void CCustView::SetHangul(BOOL bCheck) //T:한글 F:영문 이건 외우자..

{

        HIMC hm=ImmGetContext(this->GetSafeHwnd()); //뷰클래스의 윈도우 핸들포인터를 얻는다.

        if(bCheck)

        {

                ::ImmSetConversionStatus(hm,1,0); //1은 한글 0은 영문

        }

        else

        {

                ::ImmSetConversionStatus(hm,0,0); //영문으로 바꿔준다

        }

        ::ImmReleaseContext(this->GetSafeHwnd(),hm); //장치를 풀어준다

}

#include "imm.h" //헤더 반드시 추가하고

imm32.lib (라이브러리 파일)를 반드시 링크해주어야 한다!

**** 라이브러리 추가하기

프로젝트메뉴 -> 셋팅 -> 링크탭



54. DLL함수정의하기

임포트함수 :  extern "C"  __declspec(dllimport)   리터형  함수명(매개변수,...) ;

  - 메인프로그램에서 DLL에 있는 함수를 호출할때 사용한다.



엑스포트함수 :  extern "C"  __declspec(dllexport)   리터형  함수명(매개변수,...)

                      {

                             내용;

                      }

 

Posted by 나비:D
:

자 ! 이렇게 외우자 !! 아니, 외우지 말고 이렇게 이해하자!!!

  • 연산자 우선순위는 일단 15층짜리 건물로 생각을 하자. 보통 예전에는 아파트 15층 건물이 많지 않았는가? 쉽게 연상이 될 것이다.
  • 연산 순위가 낮은 연산자가 높은 층을 차지한다. 아무래도 1층과 가까운 층이 출구로 나오기 편하지 않겠는가? 그러므로 순위가 가장 낮은 컴마 연산자는 15층, 가장 높은 괄호 연산 등은 1층이다.
  • 각 층에 각각 연산자가 산다고 생각하자. 당연히 우선 순위가 중요한 것이 낮은 층에 위치시키고, 조금 낮은 순위를 높은 층에 위치시키자.
  • 연산자를 ‘외운다’ 고 생각하지 말고, 이제 한 ‘아파트’에 있는 ‘이웃’이라고 생각하자. 연산자의 특성을 생각하면서 ‘이웃’의 순서를 상기하자.
  • 연산자들은 각 역할 별로 고유한 특징이 있다. 그 특징을 바탕으로 연산자에게 좀더 ‘이웃’다운 성격을 부여하고 그 특징을 상기해 보자. 훨씬 이해하기 쉬울 것이다.

 

C 연산자 도시에는 많은 연산자들이 살고 있었다. C 연산자는 각각 그 나름대로의 개성과 고유한 역할, 성품이 있었고 연산자 도시에서 가지고 있는 사회적 지위, 재산과 평판을 가지고 있었다. 각각 연산자들은 자신의 역할을 담당하고 있었고, 그 역할에 따라 숫자 문자 논리값들은 각각 적절히 계산이 되어 결과가 나왔다. 연산자는 ‘연산’이라는 직업을 가지고 있었고, 그렇게 연산을 통해 생계를 꾸려갈 수 있었다.

 

어느 날 모든 연산자들은 연산자 타워라는 한 건물에서 살도록 프로그래머에게서 명령을 받았다. 제아무리 뛰어난 연산자라도 프로그래머의 명령은 절대적이었기 때문에 복종할 수밖에 없었다. 제각각 살던 그들은 이제 한 건물에서 살아야만 했다.

 

연산자 타워는 15층짜리 건물이었는데, 높은 층일수록 넓고 살기 좋았지만 한 층 한 층 올라갈수록 호되게 가격이 올라갔고, 1층 입구를 통해 바깥으로 나가 자신의 직장으로 출근하기도 어려웠다 게다가 높은 층에 걸맞은 사회적 지위가 없다면 눈치가 보여서 입주하기도 어려운 ‘불문율’이란 것이 있었다. 결국 누가 어느 층에서 살지를 의논하기 시작하였다.

 

15층은 너무나 좋아서 모든 연산자들이 탐을 낼만한 층이었다. 하지만 가격이 너무 비싸 선뜻 구입할 연산자가 없었다. 문득 컴마(,) 씨가 일어나 자신이 15층에 살겠다고 말하였다. 모든 연산자는 찬성하였다. 이미 컴마 씨는 다양한 분야에서 경험을 수많은 경험을 쌓은 베테랑이었고, 넉넉한 성품으로 유명하여 사회적으로도 명망이 높은 연산자였다.

컴마 씨의 재산 또한 어마어마하고, 최근 일선 업무에서 물러나 후진 양성에 힘쓰기로 하여 잦은 출퇴근을 할 이유가 없었으므로 결국 15층은 컴마 씨의 소유가 되었다.

 

14층은 연산 도시의 대부인 ‘할당 연산자’ 이퀄(=) 씨가 소유하기로 했다. 다른 연산자들도 동의하였다. 이퀄 씨 또한 이미 오래전부터 다양한 분야에서 활약을 펼친 존재였기 때문에 컴마 씨에 버금가는 사회적 지위와 재산을 누리고 있었다. 게다가 이퀄 씨의 할당 연산이 없이는 제아무리 복잡한 연산도 무용지물에 불과했다. 이퀄 씨 또한 후학 양성에 매진하였는데 그들의 직계 제자들은 다름아닌 += -= *= /= %= <<= >>= &= |= ^= 들이다. 이들은 종래 볼 수 없던 참신한 연산으로 연산업계의 새로운 화두로 떠오르고 있다.

 

13층은 의외로 삼항 연산자 씨가 소유하겠다고 말해 이목을 집중시켰다. 독특한 개성의 소유자인 삼항 연산자 씨는 젊을 때부터 C 연산업에서 주목받는 존재였다. 지금은 프리랜서 선언을 하고 조용히 자신의 창작 활동에 전념하고 싶은 터였다. 그에게 있어 13층은 자신의 거처로 더할 나위 없이 적당한 장소였다.

 

12층과 11층은 각각 논리 연산 오어(||) 씨 부부, 논리 연산 앤드(&&) 씨 부부가 맡기로 하였다. 각각 10층과 8층 비트 연산 오어(|) 씨, 비트 연산 앤드(&)씨의 형이기도 한 논리 오어 씨, 논리 앤드 씨는 맞벌이 부부였다. 두 부부 다 논리 연산에서 중요한 업무를 관장하고 있어 사회적 지위도 높았고 재산도 상당하였다.

원래 오어 씨가 11층을 먼저 선택하였다. 하지만 앤드 씨가 조금이라도 출근의 편의를 위해 11층에서 살기를 간곡히 원했고, 평소 앤드 씨와 친분이 깊은 데다 앤드 씨 부부의 바쁘디 바쁜 스케줄을 이미 잘 알고 있는 오어 씨는 ‘허허’하고 웃으며 선선히 11층을 양보해 주었다.

 

이미 언급하였듯 10층과 8층에는 논리 연산 오어 씨와 앤드 씨의 동생인 비트 연산 오어 군(|)과 앤드(&) 군이 살기로 하였다. 둘 다 각자의 형 못지않은 실력과 재능을 갖고 있어 비트 연산계의 젊은 인재로 촉망받는 두 연산자였다. 다소 파격적인 일이었지만 그들의 촉망받는 장래성과 논리 연산 오어 씨와 앤드 씨의 얼굴을 보아 다들 그 두 동생의 소유를 인정하는 분위기였다.

 

하지만 논리 연산 오어(||)씨와 앤드(&&)씨 부부는 내심 탐탁지 않다. 이제는 그 두 동생들도 알맞은 짝을 찾아 결혼하여 자신들처럼 아이도 낳고, 좀 더 행복하게 살기를 원했지만 아직 두 동생은 아직 결혼할 마음이 없는데다, 두 사람 다 9층에 있는 비트 연산 배타적 오어(^) 양에게 마음을 빼앗겨 있는 상황이기 때문이다.

 

논리 연산 오어 씨와 논리 연산 앤드 씨는 배타적 오어 양을 볼 때마다 속이 부글부글 끓어 올랐다. 자신들이 끔찍이 아끼는 동생들이 결혼할 생각도 하지 않고 오어 양에게만 매달려 있는 꼴이 맘에 들지 않아 사실 오어 양이 9층은 물론 연산자 타워에 입주하는 것조차도 반대하고 싶었기 때문이다. 게다가 자신들의 동생 사이 층에서 살겠다고 한 것은 대체 무슨 속내인지 알 수가 없었다.

하지만 그런 배타적 오어 양에게 무조건 몰아붙일 수만은 없었다. 배타적 오어 양은 깐깐하고 도도하기는 했지만 독특한 개성과 장기를 살려 이미 수많은 비트 연산 업무를 수월하게 진행한 그녀의 능력은 사실 그들 동생들을 능가할 정도이기 때문이었다.

 

그렇게 10층에는 비트 연산 오어 (|)군, 9층에는 비트 연산 배타적 오어(^)양, 8층에는 비트 연산 앤드 군(&)이 살기로 결정되었다.

 

7층에는 이퀄 씨의 아들이자 쌍둥이 같다(==) 씨와 다르다(!=) 씨가 같이 살기로 하였다. 생김새는 엇비슷하지만 (살펴보면 제법 다른 구석이 있다) 성격은 전혀 다른 두 연산자는 이퀄씨의 성격을 닮아 정직하고 우직한 성격이다. 아버지의 예전 업무(수학에서 =, ≠)를 물려 받아 C 연산 업무를 성실하게 또 하루하루 바쁘게 살고 있다.

 

6층에는 같다 씨와 다르다 씨보다 더욱 분주한 스케쥴을 보내는 관계 연산의 사총사가 입주하기로했다. 지방에서 지내다가 일약 C 연산자 업계의 다크호스로 떠오른 네 연산자 크거나 같다(>=), 작거나 같다(<=), 크다(>), 작다(<) 네 연산자는 의리로 똘똘 뭉친 친구들이었다. 같다 씨와 다르다 씨와 하는 일은 같지만, 수많은 연산 업무에서 더욱 많은 호출을 받는 탓에 항상 시간이 모자란 그들은 좀 더 출근에 유리한 6층에서 살기로 하였다.

 

5층에서 살기로 한 연산자는 말썽꾸러기 비트 이동 연산자(<<, >>) 두 남매였다. 두 남매는 매우 비사교적이었다. 솔직히 바쁘기로 따지만 관계 연산자나 논리 연산자들이 더욱 바쁘고 비트 이동 연산자는 매일 집에 틀어박혀 얼굴도 비추지 않고 무엇을 하는지 도통 알 수가 없었다.

그리고 비트 이동 연산자는 곱하기(*) 씨와 나누기(/) 씨에게 걸핏하면 트집을 잡았다. 지위가 좀 더 높은 까닭에 관계 연산자나 논리 연산자가 높은 층수에서 살았기에 망정이지.... 아무튼 비트 이동 연산자는 말썽꾸러기였다. 더 낮은 층도, 더 높은 층도 그들에게는 어울리지 않았다.

 

4층에는 너무나 유명한 더하기 씨(+), 빼기 씨(-) 부부, 3층에는 곱하기 씨(*), 나누기 씨(/) 부부, 그리고 그들의 자식인 몫 구하기(%)가 살기로 하였다. 이들 부부는 14층의 이퀄(=)씨 만큼이나 유명한 삶을 살았지만, 재물에는 초연하였다. 그들은 좋은 집을 원하지 않았다. 그리고 여전히 연산업에서 핵심적인 업무를 수행하기 때문에 이동이 편한 3층과 4층을 각각 원했고, 전통적으로 곱하기 씨와 나누기 씨 가족이 좀 더 우선순위가 주어졌기 때문에 곱하기 나누기 부부가 3층을 맡고 더하기 빼기 부부가 4층을 맡기로 했다.

 

2층에는 혼자만의 삶을 살기로 결정한 각 연산 분야의 많은 단항 연산자들이 모여 살기로 결정했다. 그들은 따로 또 같이 자신들이 원하는 삶을 살기로 결정하고 서로 간섭하지 않는다는 조건 하에 2층에서 뭉치기로 결정하였다. 그들은 반전(!), 비트 보수(~), 전치 증가(++) 전치 감소(--), 플러스 기호(+), 마이너스 기호(-), 주소 연산(&), 역참조 연산(*), 캐스팅 연산, sizeof 연산이었다.

 

1층에는 프로그래머의 잦은 호출로 24시간 항시 대기하는 연산자와 여러 도구들을 놓아두기로 하고 연산자들의 층 배분 토론은 끝이 났다. 이들은 [ ], 포인터 참조(->), 점 연산자(.) ... 프로그램 언어만 찾을 수 있는 연산들이었다.

그리고 괄호 연산자가 특이하게 그 도구들과 같이 살아가기로 했다. 다른 연산자는 그러려니 하고 생각하였다. 대관절 괄호 연산자는 괴짜인지, 도인인지 그 정체를 알 수 없었다. 그의 말 한 마디에 연산 전체가 좌지우지됨에도 불구하고, 재물도 지위도 그 어떤 것도 원하지 않았기 때문이다. 한편 괄호 연산의 강한 카리스마에 홀려 2층에서 1층으로 살기로 결심한 두 연산자가 있었으니 바로 전치 증가, 전치 감소 연산의 쌍둥이 형제 후치 증가(++), 후치 감소(--)이다. 이들은 항상 알 수 없는 행동을 하며 가끔씩 프로그래머조차 혼란에 빠뜨리는 기행을 저지르곤 한다.

Posted by 나비:D
:

BLOG main image
by 나비:D

공지사항

카테고리

분류 전체보기 (278)
Programming? (0)
---------------------------.. (0)
나비의삽질 (5)
Application (177)
SQL (51)
Web (27)
etc. (14)
Omnia (0)
---------------------------.. (0)

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

달력

«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Total :
Today : Yesterday :