MFC에 대한 유용한 팁 모음
- Control
에디트 박스에서 엔터키 확인 방법
CListCtrl에 컬럼 넣기
에디트 컨트롤을 마음대로
컨트롤을 사용할 수 없게 처리하려면
컨트롤 크기를 뷰에 맞추기
윈도우 95/NT에서 아이템 개수 제한 여부
트리 컨트롤을 이용한 애플리케이션 만들기
체크 리스트박스를 템플릿에 올리기
리스트박스 깜박임 멈추기
입력 컨트롤에 텍스트를 추가하려면
입력 컨트롤에서 허용하는 문자 제한하기
줄 단위로 끊기는 CEditView를 만들려면
CEdit에서 엔터키 감지하기
CListCtrl에서 팝업 메뉴 구현
다이얼로그 에디트 박스에서 값 입력받기
CtrlList에서 컬럼 고정시키기
탭 콘트롤의 크기를 바꾸는 법
프로퍼티 시트의 탭에 아이콘 넣기
리스트박스 엔터처리
에디트박스에서 커서를 임의의 위치에
동적으로 컨트롤 크기 변경하기
콘트롤의 사이즈나 위치 변경시 깜박임 현상 줄이기
CAnimateCtrl 등에서 WM_LBUTTONDOWN 과 같은 마우스 메시지 처리
트리컨트롤의 글자색을 마음대로 바꾸기
버튼 콘트롤 캡션 바꾸기
버튼에서 메뉴명령을 실행하려면
에디트 박스에서 엔터키 확인 방법
CListCtrl에 컬럼 넣기
에디트 컨트롤을 마음대로
컨트롤을 사용할 수 없게 처리하려면
컨트롤 크기를 뷰에 맞추기
윈도우 95/NT에서 아이템 개수 제한 여부
트리 컨트롤을 이용한 애플리케이션 만들기
체크 리스트박스를 템플릿에 올리기
리스트박스 깜박임 멈추기
입력 컨트롤에 텍스트를 추가하려면
입력 컨트롤에서 허용하는 문자 제한하기
줄 단위로 끊기는 CEditView를 만들려면
CEdit에서 엔터키 감지하기
CListCtrl에서 팝업 메뉴 구현
다이얼로그 에디트 박스에서 값 입력받기
CtrlList에서 컬럼 고정시키기
탭 콘트롤의 크기를 바꾸는 법
프로퍼티 시트의 탭에 아이콘 넣기
리스트박스 엔터처리
에디트박스에서 커서를 임의의 위치에
동적으로 컨트롤 크기 변경하기
콘트롤의 사이즈나 위치 변경시 깜박임 현상 줄이기
CAnimateCtrl 등에서 WM_LBUTTONDOWN 과 같은 마우스 메시지 처리
트리컨트롤의 글자색을 마음대로 바꾸기
버튼 콘트롤 캡션 바꾸기
버튼에서 메뉴명령을 실행하려면
- Doc / View
Doc 배열을 View에서 사용하는 방법
도큐먼트/뷰 구조
도큐먼트 없는 애플리케이션 만들기
MDI 프로그램 시작시 뜨는 도큐먼트 없애기
도큐먼트와 멀티 뷰간의 통신
분할 윈도우에서 뷰 바꾸기
투명한 뷰 만들기
뷰 클래스 변수 제어
뷰 클래스에서 프레임 클래스
SDI에서 뷰 전환하기
뷰의 배경색 바꾸기
Doc 배열을 View에서 사용하는 방법
도큐먼트/뷰 구조
도큐먼트 없는 애플리케이션 만들기
MDI 프로그램 시작시 뜨는 도큐먼트 없애기
도큐먼트와 멀티 뷰간의 통신
분할 윈도우에서 뷰 바꾸기
투명한 뷰 만들기
뷰 클래스 변수 제어
뷰 클래스에서 프레임 클래스
SDI에서 뷰 전환하기
뷰의 배경색 바꾸기
- SDI / MDI
MDI 프로그램 시작 시 차일드 윈도우를 띄우지 않으려면
최대화된 SDI 윈도우를 실행하려면
MDI에서 Child LIST를 얻는 방법
MDI에서 Child View Handle 구하는 방법
MDI 프로그램 시작 시 차일드 윈도우를 띄우지 않으려면
최대화된 SDI 윈도우를 실행하려면
MDI에서 Child LIST를 얻는 방법
MDI에서 Child View Handle 구하는 방법
- Dialog
다이얼로그에서 뷰 포인터 액세스
윈도우 3.1의 다이얼로그 구현
모달 프로퍼티시트 다이얼로그에서 버튼 제거
다이얼로그 폰트를 변경하려면
프로퍼티 시트에 공통 다이얼로그 박스를
다이얼로그박스에 툴팁을 추가하려면
디렉토리 선택 다이얼로그 띄우기
다이얼로그에서 키 값 메시지 처리
API로 파일 오픈 대화 상자 띄우기
다이얼로그박스를 중앙에 오게 하려면
다이얼로그 박스없이 뷰 화면에 버튼 만들기
다이얼로그 박스 대신 프레임 생성하기 (다이얼로그 박스에서 뷰 사용하기)
대화상자에서 엔터 누르면 다음 컨트롤로
Dialog Box 생성자를 통해 데이터를 전달하는 방법
대화상자에 비트맵 올리기
다이얼로그 리소스대로 폼뷰 크기 설정하기
CFileDialog 인자 사용법
ESC키로부터 Dialog 사라짐을 방지
플로피디스켓 포맷 다이알로그 호출하기
다이얼로그 박스 동적으로 키우기
모달리스 다이얼로그에서 ESC키와 ENTER키 무시하기
윈도우 접기
연결 프로그램 찾기 다이얼로그 띄우기
Dialog의 Min/Max/Close Box를 Run Time Show/Hide
다이얼로그에서 뷰 포인터 액세스
윈도우 3.1의 다이얼로그 구현
모달 프로퍼티시트 다이얼로그에서 버튼 제거
다이얼로그 폰트를 변경하려면
프로퍼티 시트에 공통 다이얼로그 박스를
다이얼로그박스에 툴팁을 추가하려면
디렉토리 선택 다이얼로그 띄우기
다이얼로그에서 키 값 메시지 처리
API로 파일 오픈 대화 상자 띄우기
다이얼로그박스를 중앙에 오게 하려면
다이얼로그 박스없이 뷰 화면에 버튼 만들기
다이얼로그 박스 대신 프레임 생성하기 (다이얼로그 박스에서 뷰 사용하기)
대화상자에서 엔터 누르면 다음 컨트롤로
Dialog Box 생성자를 통해 데이터를 전달하는 방법
대화상자에 비트맵 올리기
다이얼로그 리소스대로 폼뷰 크기 설정하기
CFileDialog 인자 사용법
ESC키로부터 Dialog 사라짐을 방지
플로피디스켓 포맷 다이알로그 호출하기
다이얼로그 박스 동적으로 키우기
모달리스 다이얼로그에서 ESC키와 ENTER키 무시하기
윈도우 접기
연결 프로그램 찾기 다이얼로그 띄우기
Dialog의 Min/Max/Close Box를 Run Time Show/Hide
- Frame Window
창의 트래킹 크기 제한
윈도우를 중앙에 위치
캡션바에 애플리케이션 이름만 표시하려면
클라이언트 영역을 클릭해 윈도우를 이동하려면
메인프레임이 차지하는 행의 수를 찾으려면
캡션 외에 다른 곳을 클릭해 윈도우를 이동하려면
캡션바가 없는 윈도우의 이동
애플리케이션 위저드로 생성한 창의 기본 스타일을 변경하려면
방패모양 윈도우 만들기
투명한 윈도우를 만들려면
타이틀 바에 비트맵 입히기
틀이 없는 윈도우 (SDI에서 메뉴 없애기)
전체 화면 보기 옵션 만들기
창의 트래킹 크기 제한
윈도우를 중앙에 위치
캡션바에 애플리케이션 이름만 표시하려면
클라이언트 영역을 클릭해 윈도우를 이동하려면
메인프레임이 차지하는 행의 수를 찾으려면
캡션 외에 다른 곳을 클릭해 윈도우를 이동하려면
캡션바가 없는 윈도우의 이동
애플리케이션 위저드로 생성한 창의 기본 스타일을 변경하려면
방패모양 윈도우 만들기
투명한 윈도우를 만들려면
타이틀 바에 비트맵 입히기
틀이 없는 윈도우 (SDI에서 메뉴 없애기)
전체 화면 보기 옵션 만들기
- ToolBar / StatusBar
상태바 모양 변경
상태바에 작업진행 표시
툴바 숨기기/보이기 옵션 주기
상태바와 툴바의 포인터 얻기
2개의 툴바를 한줄에
컨트롤바(툴바, 다이얼로그바) 보이기/숨기기
상태바에 그림 출력
상태바 모양 변경
상태바에 작업진행 표시
툴바 숨기기/보이기 옵션 주기
상태바와 툴바의 포인터 얻기
2개의 툴바를 한줄에
컨트롤바(툴바, 다이얼로그바) 보이기/숨기기
상태바에 그림 출력
- Bitmap / Image
오버랩 이미지 표현하기
화면의 일부분을 비트맵으로
비트맵을 움직이게 하려면
BMP 파일에 투명색을 지정하는 법
바탕 화면에 그림 그리기
COLORREF 에서 r, g, b 를 정수형으로 뽑아보자
투명 256 비트맵
오버랩 이미지 표현하기
화면의 일부분을 비트맵으로
비트맵을 움직이게 하려면
BMP 파일에 투명색을 지정하는 법
바탕 화면에 그림 그리기
COLORREF 에서 r, g, b 를 정수형으로 뽑아보자
투명 256 비트맵
- Hardware Control
프린터 포트 제어방법
VC++에서 시리얼 포트로 데이터 비트 발생
디스크 섹터로의 쓰기
I/O 포트 제어법
<Ctrl-Alt-Del>로 프로그램을 종료하지 못하게
키보드로 마우스 커서 움직이기
여러가지 시스템 종료 기법
버튼으로 해당 윈도우 종료하기
CD, 플로피등의 디스크 삽입 자동 판단루틴
ALT+F4로 종료안되게 하려면
F10 키를 처리하기
드라이브 포맷하기
프린터 포트 제어방법
VC++에서 시리얼 포트로 데이터 비트 발생
디스크 섹터로의 쓰기
I/O 포트 제어법
<Ctrl-Alt-Del>로 프로그램을 종료하지 못하게
키보드로 마우스 커서 움직이기
여러가지 시스템 종료 기법
버튼으로 해당 윈도우 종료하기
CD, 플로피등의 디스크 삽입 자동 판단루틴
ALT+F4로 종료안되게 하려면
F10 키를 처리하기
드라이브 포맷하기
- Data Type (Converstion, Detect, Calculate)
CString 타입의 SQL 문장을 인자로 넘기려면
아스키 값을 Hex 값으로 바꾸는 함수
데이터 형변환 (ASCII data -> 16진수)
LRESULT와 CALLBACK의 데이터형에 관해
문자열을 16진수로 변환하는 방법
VC++ 복소수 쓰기
float형을 int로 빠르게 cast 하는 방법
문자열에서 코드종류 알아내기
조합/완성형 한글코드 판단 소스
조합<->확장완성형 변환 소스
CString 형을 char* 형으로 바꾸기
한글을 판정하는 방법
실수 나눗셈 연산을 정수 연산으로 하기
CString 타입의 SQL 문장을 인자로 넘기려면
아스키 값을 Hex 값으로 바꾸는 함수
데이터 형변환 (ASCII data -> 16진수)
LRESULT와 CALLBACK의 데이터형에 관해
문자열을 16진수로 변환하는 방법
VC++ 복소수 쓰기
float형을 int로 빠르게 cast 하는 방법
문자열에서 코드종류 알아내기
조합/완성형 한글코드 판단 소스
조합<->확장완성형 변환 소스
CString 형을 char* 형으로 바꾸기
한글을 판정하는 방법
실수 나눗셈 연산을 정수 연산으로 하기
- File / Directory / Folder
파일을 링크드 리스트 형식으로 저장하려면
텍스트 파일에서 한 줄씩 읽어와 출력하려면
여러 줄에서 한 줄씩 차례로 읽는 방법
여러개의 파일 한꺼번에 열기
하드에서 특정파일 찾기
파일 찾아보기 기능 구현
대용량 파일 읽기(빠르게...)
대용량 파일 빠르게 읽기 2
디렉토리 만들기(서브 폴더 포함)
응용 프로그램이 실행된 디렉토리를 찾으려면
현재 디렉토리의 정보를 알아내는 법
파일 등록정보 보여주기
제일 짧은, 파일 크기 알아내는 함수
파일을 링크드 리스트 형식으로 저장하려면
텍스트 파일에서 한 줄씩 읽어와 출력하려면
여러 줄에서 한 줄씩 차례로 읽는 방법
여러개의 파일 한꺼번에 열기
하드에서 특정파일 찾기
파일 찾아보기 기능 구현
대용량 파일 읽기(빠르게...)
대용량 파일 빠르게 읽기 2
디렉토리 만들기(서브 폴더 포함)
응용 프로그램이 실행된 디렉토리를 찾으려면
현재 디렉토리의 정보를 알아내는 법
파일 등록정보 보여주기
제일 짧은, 파일 크기 알아내는 함수
- Message
윈도우 프로그램 시작할 때 메시지
마지막 메시지를 얻으려면
윈도우에 전달된 마지막 메시지 얻기
수동으로 메시지 맵에 연결하기
모든 Top Level Windows 에게 메세지 보내기
친절한 메시지 WM_NULL
윈도우 프로그램 시작할 때 메시지
마지막 메시지를 얻으려면
윈도우에 전달된 마지막 메시지 얻기
수동으로 메시지 맵에 연결하기
모든 Top Level Windows 에게 메세지 보내기
친절한 메시지 WM_NULL
- Debug / Error Handling / Class Wizard
디버깅으로 실행 진행을 보려면
예외처리란?
릴리즈 모드에서 에러가 발생하는 경우
생성한 Class를 간단히 완전제거
릴리즈 버젼 실행시 런타임 에러 찾아내기
릴리즈 모드에서 브레이크 포인트 사용하기
Console Window 에 Trace 정보 보내기
프로파일링[profiling]
버그 잡기
리모트 디버깅 하기
듀얼 모니터 디버깅 하기
error LNK2001: unresolved external symbol _main
자신만의 type 을 Watch Window 에서 보는 방법
디버깅으로 실행 진행을 보려면
예외처리란?
릴리즈 모드에서 에러가 발생하는 경우
생성한 Class를 간단히 완전제거
릴리즈 버젼 실행시 런타임 에러 찾아내기
릴리즈 모드에서 브레이크 포인트 사용하기
Console Window 에 Trace 정보 보내기
프로파일링[profiling]
버그 잡기
리모트 디버깅 하기
듀얼 모니터 디버깅 하기
error LNK2001: unresolved external symbol _main
자신만의 type 을 Watch Window 에서 보는 방법
- System
응용 프로그램을 최소 크기 만들기
사용 가능한 시스템 메모리 용량
다른 프로그램을 실행할 땐 WinExec
캐럿의 위치를 알려면
다른 애플리케이션 제어 방법
프로그램 작성후 MRU 기능 삭제 및 변경
DC 핸들로 CDC 객체를 만들려면
기본 브라우저를 띄우려면
항상 최상위 창을 유지하려면
현재 작업중인 목록을 만들려면
베이스 클래스를 변경하려면 (CView -->CScrollView)
프로그램 시작시 About박스를 표시하려면
멤버 함수에서 다른 함수의 포인터 호출
핸들이란 무엇인가요
베이스 클래스 바꾸는 방법 (CDialog --> CPropertySheet)
프로그램 시작 시 한/영키의 변환
밀리초를 구현하는 방법
Create 함수와 OnCreate 함수의 차이점
동영상 반복 기능
비주얼 C++에서 엔터키 처리법
새창을 활성화시키기 않고 생성시키기
더블버퍼링 사용법
각 클래스의 포인터 얻기
작업표시줄에서 프로그램 숨기기
객체에 툴팁달기
단 한 개의 프로그램만 실행하기
바탕화면의 월페이퍼 변경하기
Visual C++의 유용한 단축키
클래스 이름 등록방법
fscanf()에서 쓸데 없는 값 읽지않고 버리기
Toggle 기능 구현하기
한글 윈도우에서 일본어 프로그램 빌드하기
#과 ##
일반적인 윈도 소멸 순서
해상도 변경하기
화면 지우기
byte alignment
makefile 을 .dsw 로 바꾸어 보자
new로 생성된 포인터를 안전하게 지우자. SafeDelete
레지스트리를 이용하여 파일명을 인자로 실행파일 실행하기(ShellExcecut가 아님)
매크로 사용하기
시스템 강제로 다운시키기
Win9x VS Win2000,WinNT 시스템 종료하기
트레이의 아이콘이 사라지지 않게
특정한 다이알로그 박스에 버튼을 누른 효과를 내기
프로그램내에서 한영전환을 하는 방법
프로그램 실행 시 자기 프로그램 패스 구하는 방법
프로그램의 중복 실행 방지
ActiveX를 dialog base처럼 만드는 법
인라인 어셈블러에 대한...
C++에서의 const 포인터에 대한 정리
CString으로 문자열 리소스를 사용하자
MFC의 역사
restart in NT
WIN32_LEAN_AND_MEAN
응용 프로그램을 최소 크기 만들기
사용 가능한 시스템 메모리 용량
다른 프로그램을 실행할 땐 WinExec
캐럿의 위치를 알려면
다른 애플리케이션 제어 방법
프로그램 작성후 MRU 기능 삭제 및 변경
DC 핸들로 CDC 객체를 만들려면
기본 브라우저를 띄우려면
항상 최상위 창을 유지하려면
현재 작업중인 목록을 만들려면
베이스 클래스를 변경하려면 (CView -->CScrollView)
프로그램 시작시 About박스를 표시하려면
멤버 함수에서 다른 함수의 포인터 호출
핸들이란 무엇인가요
베이스 클래스 바꾸는 방법 (CDialog --> CPropertySheet)
프로그램 시작 시 한/영키의 변환
밀리초를 구현하는 방법
Create 함수와 OnCreate 함수의 차이점
동영상 반복 기능
비주얼 C++에서 엔터키 처리법
새창을 활성화시키기 않고 생성시키기
더블버퍼링 사용법
각 클래스의 포인터 얻기
작업표시줄에서 프로그램 숨기기
객체에 툴팁달기
단 한 개의 프로그램만 실행하기
바탕화면의 월페이퍼 변경하기
Visual C++의 유용한 단축키
클래스 이름 등록방법
fscanf()에서 쓸데 없는 값 읽지않고 버리기
Toggle 기능 구현하기
한글 윈도우에서 일본어 프로그램 빌드하기
#과 ##
일반적인 윈도 소멸 순서
해상도 변경하기
화면 지우기
byte alignment
makefile 을 .dsw 로 바꾸어 보자
new로 생성된 포인터를 안전하게 지우자. SafeDelete
레지스트리를 이용하여 파일명을 인자로 실행파일 실행하기(ShellExcecut가 아님)
매크로 사용하기
시스템 강제로 다운시키기
Win9x VS Win2000,WinNT 시스템 종료하기
트레이의 아이콘이 사라지지 않게
특정한 다이알로그 박스에 버튼을 누른 효과를 내기
프로그램내에서 한영전환을 하는 방법
프로그램 실행 시 자기 프로그램 패스 구하는 방법
프로그램의 중복 실행 방지
ActiveX를 dialog base처럼 만드는 법
인라인 어셈블러에 대한...
C++에서의 const 포인터에 대한 정리
CString으로 문자열 리소스를 사용하자
MFC의 역사
restart in NT
WIN32_LEAN_AND_MEAN
GetWindowRect( &test );
int x,y,cx,cy;
x = test.left;
y = test.top;
cx = (test.right-test.left) + 30;
cy = (test.bottom-test.top) + 30;
MoveWindow(x,y,cx,cy, TRUE );
int x,y,cx,cy;
x = test.left;
y = test.top;
cx = (test.right-test.left) + 30;
cy = (test.bottom-test.top) + 30;
MoveWindow(x,y,cx,cy, TRUE );
위처럼 하면 다이얼로그 박스가 30 커집니다.
이 코드는 DOS 프로그램 또는 Win32 Console 모드 프로그램을 외부실행화일로 사용하고자 할때 문제가 되는 다음 세가지 항목을 해결하는 방법을 담고 있습니다.
1. 콘솔창(DOS창) 창 안띄우기.
2. 끝날때까지 기다리기(실행이 끝난것을 감지하기)
3. 출력되는 내용을 화일로 저장하기
(만일 이 부분을 사용하시기 원치 않으시면 STARTF_USESTDHANDLES 를 삭제해 주세요)
void CTttDlg::OnOK()
{
// TODO: Add extra validation here
PROCESS_INFORMATION pInfo;
STARTUPINFO sInfo;
DWORD exitCode;
HANDLE hOut = CreateFile("stdinout.txt",
GENERIC_WRITE, NULL, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
sInfo.cb = sizeof(STARTUPINFO);
sInfo.lpReserved = NULL;
sInfo.lpReserved2 = NULL;
sInfo.cbReserved2 = 0;
sInfo.lpDesktop = NULL;
sInfo.lpTitle = NULL;
sInfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
sInfo.dwX = 0;
sInfo.dwY = 0;
sInfo.dwFillAttribute = 0;
sInfo.wShowWindow = SW_HIDE;
sInfo.hStdOutput = hOut;
if (!CreateProcess(NULL, "command.com /c lha a tt",
NULL, NULL, TRUE, 0, NULL, "c:\",
&sInfo, &pInfo)) {
printf("ERROR: Cannot launch child processn");
exit(1);
}
// Give the process time to execute and finish
WaitForSingleObject(pInfo.hProcess, INFINITE);
if (GetExitCodeProcess(pInfo.hProcess, &exitCode)) {
switch(exitCode) {
case STILL_ACTIVE:
printf("Process is still activen");
break;
{
// TODO: Add extra validation here
PROCESS_INFORMATION pInfo;
STARTUPINFO sInfo;
DWORD exitCode;
HANDLE hOut = CreateFile("stdinout.txt",
GENERIC_WRITE, NULL, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
sInfo.cb = sizeof(STARTUPINFO);
sInfo.lpReserved = NULL;
sInfo.lpReserved2 = NULL;
sInfo.cbReserved2 = 0;
sInfo.lpDesktop = NULL;
sInfo.lpTitle = NULL;
sInfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
sInfo.dwX = 0;
sInfo.dwY = 0;
sInfo.dwFillAttribute = 0;
sInfo.wShowWindow = SW_HIDE;
sInfo.hStdOutput = hOut;
if (!CreateProcess(NULL, "command.com /c lha a tt",
NULL, NULL, TRUE, 0, NULL, "c:\",
&sInfo, &pInfo)) {
printf("ERROR: Cannot launch child processn");
exit(1);
}
// Give the process time to execute and finish
WaitForSingleObject(pInfo.hProcess, INFINITE);
if (GetExitCodeProcess(pInfo.hProcess, &exitCode)) {
switch(exitCode) {
case STILL_ACTIVE:
printf("Process is still activen");
break;
default:
printf("Exit code = %dn", exitCode);
break;
}
} else {
printf("GetExitCodeProcess() failedn");
}
CloseHandle(hOut);
}
printf("Exit code = %dn", exitCode);
break;
}
} else {
printf("GetExitCodeProcess() failedn");
}
CloseHandle(hOut);
}
위의 CreateProcess()를 호출하는 부분에서 "Command.com /c"를 호출하는 부분이 있는데 이렇게 해주지 않으면, 호출하는 프로그램이 DOS 프로그램인 경우 자동으로 창이 닫히지 않는 문제가 발생하기 때문입니다.
< 참고 >
HOWTO: Access Child Process Exit Code from 32-Bit Parent Proc.
Article ID: Q131775
common control이 4.7 이상이 되면서 NM_CUSTOMDRAW라는 메시지가 생겼습니다.
공통 컨트롤들은 각 아이템을 그리기 전에 이 메시지를 부모 윈도우에 보내줘서 직접 그릴 수 있는 기회를 줍니다.
가장 쉽게 사용할 수 있는 예가 트리 컨트롤에서 글자의 색을 바꾸는 것입니다.
(리스트 컨트롤이나 NM_CUSTOMDRAW를 보내는 다른 컨트롤들이 다 비슷한 방법을 사용하면 됩니다.)
일단 소스를 보죠. 제 경우에는 CTreeCtrl을 계승해서 새로운 트리 컨트롤을 만들고 notify 메시지가 reflect되는 걸 받아서 처리했습니다.
XXXTreeCtrl.h
class CXXXTreeCtrl
{
...
//}}AFX_MSG
afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
....
};
XXXTreeCtrl.cpp
...
BEGIN_MESSAGE_MAP(CXXXTreeCtrl, CBaseTree)
//{{AFX_MSG_MAP(CNewsTree)
...
//}}AFX_MSG_MAP
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
...
END_MESSAGE_MAP()
...
void CNewsTree::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) {
NMTVCUSTOMDRAW* pcd = (NMTVCUSTOMDRAW*)pNMHDR;
HTREEITEM hItem;
switch ( pcd->nmcd.dwDrawStage ) {
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT :
hItem = (HTREEITEM)pcd->nmcd.dwItemSpec;
if (hItem에 따른 적당한 조건) {
pcd->clrText = RGB(0, 0, 204);
pcd->clrTextBk = RGB(255, 255, 255);
} else {
...
}
*pResult = CDRF_DODEFAULT;// do not set *pResult = CDRF_SKIPDEFAULT
break;
}
}
class CXXXTreeCtrl
{
...
//}}AFX_MSG
afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
....
};
XXXTreeCtrl.cpp
...
BEGIN_MESSAGE_MAP(CXXXTreeCtrl, CBaseTree)
//{{AFX_MSG_MAP(CNewsTree)
...
//}}AFX_MSG_MAP
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
...
END_MESSAGE_MAP()
...
void CNewsTree::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) {
NMTVCUSTOMDRAW* pcd = (NMTVCUSTOMDRAW*)pNMHDR;
HTREEITEM hItem;
switch ( pcd->nmcd.dwDrawStage ) {
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT :
hItem = (HTREEITEM)pcd->nmcd.dwItemSpec;
if (hItem에 따른 적당한 조건) {
pcd->clrText = RGB(0, 0, 204);
pcd->clrTextBk = RGB(255, 255, 255);
} else {
...
}
*pResult = CDRF_DODEFAULT;// do not set *pResult = CDRF_SKIPDEFAULT
break;
}
}
1. CDDS_PREPAINT가 오면 진짜로 아이템 그리기 전에 알켜줘라는 플랙을 설정합니다.
2. CDDS_ITEMPREPAINT가 오면 해당 아이템의 HTREEITEM 핸들을 얻어서 원하는 색으로 설정해줍니다.
참고로 말씀드리자면...
1. 위의 예처럼 하면 배경색을 바꿀 수 있지만 fullrow select일 경우가 아니면 그다지입니다... -_-;
2. 그리고 pcd->iLevel 값은 각 트리의 차수를 표시합니다. 0이면 루트 노드와 그 형제노드, 1이면 0인 노드들의 자식 노드들... 등등.
3. 폰트를 바꾸는 것도 가능합니다. 아이콘을 안그리게 할 수도 있습니다. CDDS_ITEMPREPAINT의 결과를 바꾸면 됩니다. 자세한 내용은 MSDN을 보세요...
4. 참고로 글씨를 굵게 하는 정도만 필요하다면
CTreeCtrl::SetItemState(hItem, TVIS_BOLD, TVIS_BOLD); 으로도 가능합니다.
5. 그러나 OE5처럼 일부만 굵게하고 색도 두가지이상으로 하려면 NM_CUSTOMDRAW에서 다 그려줘야 할겁니다. 아마도...
전에 어느분이 질문답변란에 new로 생성한 포인터를 다른 함수에서 지우는데 지우고나서 NULL로 만들어주지 않아서, 그것을 delete로 지워도 되는지 판단할 수가 없는데 이미 지워진 것인지 판별해 내는 방법을 물으신적이 있습니다.
그때는 잘 몰랐는데, Win32에서 제공하는 Exception Handling을 활용하면 된다는것을 알았습니다. 그래서 만들어 본 것이, SafeDelete template입니다. SafeDelete는 인자로 넘어온 변수를 안전하게 delete해 주는 함수입니다.
우선 소스를 보시죠.
template < class _DataType >
BOOL SafeDelete(_DataType **pDT)
{
if (*pDT == NULL)
return FALSE;
__try
{
delete *pDT;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
{
*pDT = NULL;
return FALSE;
}
*pDT = NULL;
return TRUE;
}
BOOL SafeDelete(_DataType **pDT)
{
if (*pDT == NULL)
return FALSE;
__try
{
delete *pDT;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
{
*pDT = NULL;
return FALSE;
}
*pDT = NULL;
return TRUE;
}
이와 같습니다. 사용법은..
char *p;
SafeDelete< char >(&p);
SafeDelete< char >(&p);
이와 같습니다.
위에서 p가 할당되지 않았는데 SafeDelete로 지웠습니다.
그냥 delete하게되면 ACCESS_VIOLATION에러가 뜨겠지요.
p가 이미 NULL로 되어있다면 SafeDelete는 그냥 빠져 나오며, NULL이 아닌데 이미 delete로 지워진 포인터면 그 포인터를 NULL로 바꾸어주어 다음부터는 delete시도를 하지 않도록 해줍니다.
템플리트로 만들었으므로 임의의 타입에도 적용할 수 있습니다.
그리고 굳이 template을 쓰지 않아도....
#define SAFEDELETE(x) SafeDelete((void **) &(x))
BOOL SafeDelete(void **ptr)
{
if(*ptr == NULL)
return FALSE;
__try
{
delete *ptr;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
{
*ptr = NULL;
return FALSE;
}
*ptr = NULL;
return TRUE;
}
BOOL SafeDelete(void **ptr)
{
if(*ptr == NULL)
return FALSE;
__try
{
delete *ptr;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
{
*ptr = NULL;
return FALSE;
}
*ptr = NULL;
return TRUE;
}
사용 방법은 걍 SAFEDELETE(p); 해주시믄 됩니다.
그래픽을 처리하다 보면 RGB() 함수를 상당히 자주 사용하게 되는데...
여기서 가끔 빨간색, 녹색, 파란색을 개별적을 뽑아서 사용할 경우가 발생하더군여..
전에는 왼쪽 쉬프트...오른쪽 쉬프트...
또는 쉬프트 한번 할려면 비트연산을 하곤 했죠... 일례....
전에 사용하던 방법을 보여드립니다...
지금도 이거 비슷한 방법을 사용하시는 분들이 계신다면 뒤에 나오는 방법을 사용해 보세여.
COLORREF rgb = RGB(255, 100, 30);
long m = (long)rgb;
long m = (long)rgb;
(1) 젤 첨에 사용하던 방법입니다.
int r = m>>16;
int g = m<<8; g = g>>16;
int b = m<<16; b = b>>16;
int g = m<<8; g = g>>16;
int b = m<<16; b = b>>16;
(2) 얼마전 꺼정 사용하던 방법입니다.
int r = m&0xff000; r = r>>16;
int g = m&0x00ff00; g = g>>8;
int b = m&0x0000ff;
#if !defined(GetB)
#define GetB(rgb) ((BYTE) ((rgb) >> 16))
#endif
#if !defined(GetG)
#define GetG(rgb) ((BYTE) (((WORD) (rgb)) >> 8))
#endif
#if !defined(GetR)
#define GetR(rgb) ((BYTE) (rgb))
#endif
int g = m&0x00ff00; g = g>>8;
int b = m&0x0000ff;
#if !defined(GetB)
#define GetB(rgb) ((BYTE) ((rgb) >> 16))
#endif
#if !defined(GetG)
#define GetG(rgb) ((BYTE) (((WORD) (rgb)) >> 8))
#endif
#if !defined(GetR)
#define GetR(rgb) ((BYTE) (rgb))
#endif
그리고 wingdi.h를 보시면
GetRValue, GetGValue, GetBValue라는 매크로가 있습니다.
GetR, GetG, GetB와 같은 내용입니다.
당연히 MSDN에도 도움말이 나옵니다.
모든 윈도우의 컨트롤들은 하나의 작은 윈도우라고 보시면 됩니다. 따라서 해당 콘트롤들의 윈도우 객체를 확보하셔서 SetWindowText() 함수를 사용하시면 됩니다.
버튼 컨트롤 ID 가 IDC_MYBTN 라고 가정한다면,
pWnd = GetDlgItem(IDC_MYBTN);
if(pWnd) {
pWnd->SetWindowText("test button");
}
if(pWnd) {
pWnd->SetWindowText("test button");
}
이렇게 GetDlgItem() 함수를 사용하는 방법이 있고, 또 한가지는 Dialog 기반이니 Class Wizard 에서 버튼 컨트롤 멤버를
CButton m_MyBtn;
이라고 만들었을 경우
m_MyBtn.SetWindowText("test button");
와 같이 사용을 하셔도 됩니다.
모달리스 다이얼로그를 만들고 이 ESC,ENTER 키를 입력하면 다이얼로그가 사라지게 됩니다. 그래서 이 두키를 무시하도록 해야 다이얼로그가 사라지지 않습니다.
그래서 다음과 같이 처리해주면 되죠
위자드로 PreTranslateMessage()를 등록하고 밑의 코드를 넣어주면 됨.
BOOL C~~Dlg::PreTranslateMessage(MSG* pMsg)
{
…….
if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_ESCAPE)
return TRUE;
if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN)
return TRUE;
return CDialog::PreTranslateMessage(pMsg);
}
{
…….
if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_ESCAPE)
return TRUE;
if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN)
return TRUE;
return CDialog::PreTranslateMessage(pMsg);
}
물론 ESC와 ENTER를 무시하는 것은 좋습니다만...
modeless 다이얼로그에서는 반드시 OnOK와 OnCancel을 오버라이드 해줘야 합니다.
왜냐하면 CDialog에서는 기본적으로 두 함수에서 EndDialog를 호출하는데 이 함수는 modal 다이얼로그를 위한 함수입니다.
modeless 다이얼로그는 DestroyWindow를 호출해야 제대로 파괴됩니다. (뭐... 계속 그 다이얼로그를 써먹으시겠다면 ShowWindow(SW_HIDE)를 사용하면 되고요.)
그리고 ENTER키는 기본적으로 디폴트 버튼을 누르게 됩니다. 무시하는 것도 좋지만 디폴트 버튼을 적절한 것으로 바꿔주는 것도 좋은 방법입니다.
이건 조금 보충설명입니다..
생성하는 다이얼로그가 모달 다이얼로그라면 별 문제가 없겟지만 모덜리스라면 반드시 IDOK의 ID를 가진 버튼을 만들어줘야 합니다..
그래야 OnOk 함수에서 DestroyWindow를 호출해줄수 있고 PostNcDestroy 함수에서 자신의 객체를 제거해 줄수 있으니까요.. 안그러면 메모리가 줄줄 새겠죠?
하지만 IDOK버튼을 넣고싶지 않다.. 이 다이얼로그는 버튼이 하나도 없는 다이얼로그라던지 혹은 IDOK버튼이 들어갈 자리가 없다.. 하는 경우가 있을겁니다..
이럴땐 조금 편법을 써야 합니다.. 위에 말씀드렸다시피 IDOK 버튼은 있어야 합니다..
이럴땐 먼저 버튼을 만드시고 IDOK로 바꿔주신다음에 대충 적당한 위치에 넣어주신후 버튼컨트롤의 Visible 속성을 없애버리시면 됩니다.. Tab Stop 속성도 없애시구요.. 탭으로 선택이 되면 안돼니까요..
그리고 나서 ESC키를 눌렀다던지 타이틀바의 X버튼을 눌렀을때 메시지를 가로채서 OnOk 함수를 실행시키시면 됩니다.. 그리고 OnOk에서 윈도우 파괴 -> 자신 제거.. 이런식으로 하시면 됩니다..
윈도우에서 파일명을 더블클릭하면 레지스트리에 그 파일의 링크 실행 파일이 등록이 되어 있으면 그 파일명을 인자로 실행파일이 실행됩니다.
물론 ShellExecute로 실행해도 되지만 만일 Parent의 프로그램도 중단해야 할 경우 다음의 로직을 써보세요. 이는 주로 DLL 루틴에서 사용합니다.
__declspec(dllexport) long GetNFileInfo(char* filepath)
{
//레지스트리를 읽어서 실행파일을 읽어온다.
char fname[256];
bool flag = FALSE;
char kind[10];
int i;
int j;
i = j = 0;
kind[0] = '';
strcpy(fname, filepath);
//파일의 확장명 얻음
//아래 루틴이 아닌 더 좋은 함수가 Tips & Tricks에 있슴.
for(i=0; i<=(int)strlen(fname); i++) {
if(fname[i] == '.') {
flag = TRUE;
j=0;
}
if(flag) {
kind[j] = fname[i];
j++;
}
}
if(j=0)
return NO_EXTENDED_FILE; //#define에서 전달인자로 정의
//여기서부터 레지스트리에서 실행화일을 얻어온다.
TCHAR szname[64];
LONG lRes1, lRes2;
HKEY hRootKey;
HKEY hSubKey;
DWORD dwType;
DWORD dwBytes=64;
//레지스트리 오픈
lRes1 = RegOpenKeyEx(HKEY_CLASSES_ROOT, kind, 0, KEY_ALL_ACCESS,
&hRootKey);
if(lRes1 != ERROR_SUCCESS)
return NO_REGISTRY_FILE; //#define에서 전달인자로 정의
RegQueryValueEx(hRootKey, "", 0, &dwType, (LPBYTE)szname, &dwBytes);
RegCloseKey(hRootKey);
strcat(szname, "\shell\open\command");
lRes2 = RegOpenKeyEx(HKEY_CLASSES_ROOT, szname, 0, KEY_ALL_ACCESS,
hSubKey);
if(lRes2 != ERROR_SUCCESS)
return NO_EXECUTE_FILE; //#define에서 전달인자로 정의
dwBytes=64;
RegQueryValueEx(hSubKey, "", 0, &dwType, (LPBYTE)szname, &dwBytes);
RegCloseKey(hSubKey);
strcpy(fname, szname);
if(fname[strlen(fname)-2] == '%')
fname[strlen(fname)-3] = '';
strcat(fname, " ");
strcat(fname, filepath);
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
if(!CreateProcess(NULL, fname, NULL, NULL,
FALSE, CREATE_NEW_PROCESS_GROUP,NULL, NULL, &si, &pi))
return NO_EXECUTE; //#define에서 전달인자로 정의
//호출한 메인 프로그램의 실행을 일시중지
if(WAIT_FAILED == WaitForSingleObject(pi.hProcess, INFINITE))
return NO_END_EXECUTE; //#define에서 전달인자로 정의
return EXE_SUCESS; //#define에서 전달인자로 정의
}
{
//레지스트리를 읽어서 실행파일을 읽어온다.
char fname[256];
bool flag = FALSE;
char kind[10];
int i;
int j;
i = j = 0;
kind[0] = '';
strcpy(fname, filepath);
//파일의 확장명 얻음
//아래 루틴이 아닌 더 좋은 함수가 Tips & Tricks에 있슴.
for(i=0; i<=(int)strlen(fname); i++) {
if(fname[i] == '.') {
flag = TRUE;
j=0;
}
if(flag) {
kind[j] = fname[i];
j++;
}
}
if(j=0)
return NO_EXTENDED_FILE; //#define에서 전달인자로 정의
//여기서부터 레지스트리에서 실행화일을 얻어온다.
TCHAR szname[64];
LONG lRes1, lRes2;
HKEY hRootKey;
HKEY hSubKey;
DWORD dwType;
DWORD dwBytes=64;
//레지스트리 오픈
lRes1 = RegOpenKeyEx(HKEY_CLASSES_ROOT, kind, 0, KEY_ALL_ACCESS,
&hRootKey);
if(lRes1 != ERROR_SUCCESS)
return NO_REGISTRY_FILE; //#define에서 전달인자로 정의
RegQueryValueEx(hRootKey, "", 0, &dwType, (LPBYTE)szname, &dwBytes);
RegCloseKey(hRootKey);
strcat(szname, "\shell\open\command");
lRes2 = RegOpenKeyEx(HKEY_CLASSES_ROOT, szname, 0, KEY_ALL_ACCESS,
hSubKey);
if(lRes2 != ERROR_SUCCESS)
return NO_EXECUTE_FILE; //#define에서 전달인자로 정의
dwBytes=64;
RegQueryValueEx(hSubKey, "", 0, &dwType, (LPBYTE)szname, &dwBytes);
RegCloseKey(hSubKey);
strcpy(fname, szname);
if(fname[strlen(fname)-2] == '%')
fname[strlen(fname)-3] = '';
strcat(fname, " ");
strcat(fname, filepath);
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
if(!CreateProcess(NULL, fname, NULL, NULL,
FALSE, CREATE_NEW_PROCESS_GROUP,NULL, NULL, &si, &pi))
return NO_EXECUTE; //#define에서 전달인자로 정의
//호출한 메인 프로그램의 실행을 일시중지
if(WAIT_FAILED == WaitForSingleObject(pi.hProcess, INFINITE))
return NO_END_EXECUTE; //#define에서 전달인자로 정의
return EXE_SUCESS; //#define에서 전달인자로 정의
}
프로그램 하다 보면 가장 짜증날 때가 분명히 일어날 수 없는 일이라고 생각하고 있는 데 눈앞에서 버젓이 일어날 때 입니다. 버그죠. 프로그래머의 실수이든, 운영 체제의 실수이든(근데 프로그래머 실수일 가능성이 높죠. 운영 체젠 비싸게 돈주고 파는 거니까 오죽이나 잘 만들어 놓았겠습니까?) 버그 때문에 잠 설친 적이 하루 이틀이 아니죠. 그래서 제가 쓰는 디버깅 방법을 몇 자 적어 볼까 합니다.
우선, 모든 변수를 믿지 않죠. 분명 1,2 둘중에 하나다..라고 하더라도 믿지 않습니다. 그래서 switch 문을 쓸 땐 항상 default, if 문을 쓸 땐 else를 넣죠. 함수를 만들 경우, 전 우선 될 수 있으면 모든 함수의 리턴형을 불린으로 합니다.
CRgn 뭐 이런 걸로 리턴해야 한다면, 포인터 있으니까 포인터로 넘겨 버리죠. 그렇게 한 다음, 함수의 첫머리에서 하는 일은 항상 그 함수 내에서 쓸 값들(보통 파라미터가 되겠죠)의 유효성 검사 입니다. 말이 멋있는 데, 사실 별거 없구, if문으로 쭉 검사하는 거죠. 그 땐, 파일 첫머리에 #define _ENABLEERRORMESSAGE_ 이렇게 해주고, if문 안에서
if(어쩌구 저쩌구 해서 false를 리턴해야 한다면){
#ifdef _ENABLEERRORMESSAGE_
AfxMessageBox(_T("에러에요. ~~~가 잘못되었네요^^"));
#endif
#ifdef _DEBUG
TRACE2(_T("%s(%d) : 에러 발견!! 내용 - 어쩌구~~~"),__FILE__,__LINE__);
#endif
return false;
}
#ifdef _ENABLEERRORMESSAGE_
AfxMessageBox(_T("에러에요. ~~~가 잘못되었네요^^"));
#endif
#ifdef _DEBUG
TRACE2(_T("%s(%d) : 에러 발견!! 내용 - 어쩌구~~~"),__FILE__,__LINE__);
#endif
return false;
}
이렇게 해주죠. TRACE구문은 컴파일 모드에 따라 알아서 컴파일 되겠지만, 명확히 할려구 저렇게 하구요. 저렇게 하고, 테스트 끝나면
#define _ENABLEERRORMESSAGE_
부분을 주석처리하죠. TRACE구문을 저렇게 하면 저 구문이 디버그 아웃풋 창에 나올 때 그 줄을 더블클릭하거나 F4를 누르면 저 줄로 캐럿이 이동하더군요. 또 컴파일 끝나면, Control + F5보단, 좀 느리지만, F5를 눌르죠.
그렇게 해서 에러나면, 메세지 박스 뜨잖아요. '취소' '재시도' '무시' 뭐 이런거 나오는 데, 그 때 '재시도' 누르면 어디서 에러가 났는 지 그 부분으로 이동하죠.
그 때 어쩔 땐 어셈 코드같은 게 나오는 데, 그 땐 디버그 툴 바 중에 CallStack창 띄워서 함수가 실행된 순서를 거꾸로 찾아가죠. 어셈 코드가 아니면 보통 ASSERT구문에서 걸리게 되는 데 그걸 보고 어디가 잘못되었는 지 알 수 있죠. 가령
ASSERT(::IsWindow(hWnd));
에서 걸렸다면, 아직 윈도우가 만들어지지 않은 경우죠.
이런 건, PreCreateWindow같이, 윈도우를 만들기 전에 실행되는 함수에서
GetClientRect();
같이 윈도우의 크기를 얻어갈려면 발생하겠죠. 포인터를 잘못 쓸땐 그건 좀 어렵더라구요... mfc함수 중에도 불린형 값을 리턴하는 함수를 쓰면 그뒤 바로 리턴값을 확인한답니다. 한번은 윈2000에서
CRgn rgn;
ASSERT(rgn.CreateRectRgn(0,0,0,0));
ASSERT(rgn.CreateRectRgn(0,0,0,0));
한 적이 있었는 데, 윈98로 가니까 에러가 나더라구요. 이 부분에서 걸린 겁니다.
윈98은 좌표값을 모두 0으로 주면 영역이 만들어지지 않았던 겁니다. 어쩔 땐 나만의 ASSERT구문을 만들기도 하죠. 이런 식으로도 에러를 못찾으면 적당한 곳에 브레이크포인트 걸어 놓구, 쭉 가다가 에러나는 곳에서 GetLastError();구문을 써서 무엇이 잘못되었는 지 알아가기도 합니다. 예외 처리를 많이 해주면 처음엔 확실히 오래 걸리지만 확실히 디버깅하긴 편하더라구요.
일단 디버깅을 시작할 PC의 프로젝트 메뉴의 Setting 을 클릭합니다. 그 중 Debug tab 을 선택하면 Excutable for Debug Session 이 있는데, 이 란을 자신의 프로그램이 있는 디렉토리와 화일명을 쓰도록 합니다.
두번째 란인 Working Directory 는 비워 놓습니다.
세번째란은 Remote Executable path 로 상대방 pc ( 실행이 될 .. )에서 잡아놓은 내 가상 드라이브와 실행화일 명을 쓰도록 합니다. ( 일단 상대방에서 내가 디버깅을 실행할 디렉토리를 가상드라이브로 연결해야만 합니다. 그러기 위해선 당연히 네트워크상에서 그 디렉토리는 공유가 되어야만 하겠지요 )
그 다음은 build 메뉴안에 debugger remote connection 을 선택합니다. 아마 local, network 등의 여러 connection type 이 있을 겁니다. 그 중 자신이 원하는 네트워크를 선택한후 setting 으로 자신의 실행화일이 실행이 될 상대방의 네트워크 주소를 기입합니다.
상대방 pc 에서 msvcmon.exe 화일을 띄어 놓습니다.(자주쓰이니 바탕화면에 바로가는 아이콘으로...) 물론 그곳에서도 셋팅은 필요합니다. 연결이 될 상대방의 네트워크 주소를 기입해야 합니다.
그리고 connection 으로 대기상태에서 기다립니다. 그리고는 f5 키로 디버깅을 시작하면 잠시후 이것저것 dll 화일에 대해서 불만(?)을 토로할 수 (?) 있으나 무시하고 넘어갑니다. ( 두 컴의 소프트웨어 상태가 같은게 제일 편합니다. 같은 버전의 윈도우, 서비스 팩도 같이 깐 비주얼 스투디오.....)
아마 리모트가 되고있는 자신의 프로그램을 상대방의 pc 에서 보실수가 있을겁니다.
- 리모트 디버깅에 필요한 화일
리모트 디버깅에 필요한 화일은 디버깅할 컴퓨터에서는 VC++ 이 설치되어있기 때문에 추가로 필요한 화일이 없습니다. 타겟 컴퓨터에는 다음과 같은 화일들이 필요합니다.
MSVCMON.EXE
TLN0T.DLL
DM.DLL
MSDIS110.DLL
TLN0T.DLL
DM.DLL
MSDIS110.DLL
이상 4개의 화일은 Program FilesMicrosoft Visual StudioCommonMSDev98Bin
디렉토리에 있습니다.
MSVCP6O.DLL
MSVCRT.DLL
MSVCRT.DLL
이상 2개의 화일은 WindowsSystem 디렉토리에 있습니다.
이 화일들을 타겟컴퓨터의 WindowsSystem 디렉토리로 복사합니다.
- 윈도우즈 NT에 관한 추가사항
윈도우즈 NT 에서는 위의 화일들 이외에 PSAPI.DLL이란 화일도 System 디렉토리로 복사 해야합니다. MSVCRT.DLL 화일을 System32 디렉토리로 옮깁니다.
프로그램이 사용하는 DLL들도 타겟컴퓨터로 복사해야 됩니다. 릴리즈 버전 런타임 DLL은 운영체제와 같이 설치가 되었겠지만 디버그 버전 DLL들은 기본적으로는 설치가 안되었겠죠?
기본적으로 MSVCRTD.DLL(Visual C++ Runtime DLL) 이 필요하고 iostream과 관련된 함수를 사용하셨다면 MSVCIRTD.DLL이 추가됩니다.
MFC를 사용하신다면 다음 화일들도 추가됩니다.
MFC42D.DLL (core)
MFCO42D.DLL (OLE)
MFCD42D.DLL (database)
MFCN42D.DLL (network)
MFCO42D.DLL (OLE)
MFCD42D.DLL (database)
MFCN42D.DLL (network)
그외에 프로그램이 추가적으로 사용하는 DLL이 있다면 그것도 복사해야겠지요. 만일 테스트할 프로그램에서 정확히 어떤 DLL들을 사용하는지 모르시겠다면 로컬로 디버깅 할때 VC++ IDE(통합개발환경)의 Output Window에 나오는 메시지들을 참고하세요.
예를 들어 "Loaded symbols for 'C:WINDOWSSYSTEMMFCO42D.DLL" 이란 메시지가 있다면 프로그램이 MFCCO42D.DLL을 사용한다는 뜻이니까 타겟 컴퓨터에도 이 화일이 있어야 합니다.
이 방법은 일반적으로 프로그램을 배포할 때도 유용한 팁이므로 기억해 두시면 유용할 겁니다
- 타겟 컴퓨터의 리모트 디버깅 환경설정하기
1. MSVCMON.EXE를 실행합니다.
"Visual C++ Debug Monitor"란 제목을 가진 다이알로그가 나타나는데, 리스트박스에 TCP/IP란 항목만 있을겁니다. VC++ 4.2버전에는 Serial도 있었는데 언제 없어졌는지 VC++ 6에선 이 항목이 없더군요.
2. Settings 버튼을 누르면 "Win32 Network Settings"란 제목을 가진 다이알로그가 나타납니다.
3. "Target machine name or address"란 필드에 디버깅을 할 타겟 컴퓨터의 IP어드레스나 컴퓨터 명을 적어주고 OK 버튼을 누릅니다.
4. Connect 버튼을 누르면 "Connecting.."이란 제목을 가지는 다이알로그가 나오는데 대기 상태로 들어간 것이지요. 취소하시려면 Disconnect 버튼을 누르면 되겠고요.
여기까지가 타겟 컴퓨터의 셋팅 전부 입니다.
- 디버깅을 할 컴퓨터의 리모트 디버깅 환경설정하기
1. IDE의 Build 메뉴에서 Debugger Remote Connection 항목을 선택하면 "Remote Connection" 다이알로그가 나타납니다.
2. 여기 리스트 박스에는 항목이 두개가 있는데 "Network(TCP/IP)"항목을 선택하고 Settings 버튼을 누릅니다. "Target machine name or address" 필드에다 타겟 컴퓨터의 IP어드레스나 컴퓨터 명을 적어주고 OK버튼을 누릅니다.
3. "Remote Connection" 다이알로그에서도 OK버튼을 눌러 다이알로그를 닫습니다.
4. IDE의 Project의 Settings 항목을 선택하면 "Project Settings" 다이알로그가 나타는데 Debug 탭을 눌러줍니다.
5. "Category" 콤보박스 필드는 General로 하시고 "Executable for Debug Session" 필드에는 실행화일의 경로를 적어주시는데 디버깅하는 컴퓨터 입장에서 본 실행화일의 경로를 의미합니다.
예를 들어 타겟컴퓨터의 이름이 "TARGET"이고 실행화일이 "Shaerd"란 공유폴더에 들어있다면 "\TARGETSharedTest.exe"라고 적어주시고 타겟컴퓨터의 "Shared" 란 공유폴더가 "H"라는 네트워크 드라이브로 잡혀 있다면 "H:Test.exe"라고 적어주시면 되죠.
"Working Directory" 필드는 공란으로 비워두시고 "Remote Executable Path" 필드는 타겟컴퓨터 입장에서 본 실행화일의 경로를 적어줍니다. 예를 들면 "C:SharedTest.exe"와 같이 적어주시면 됩니다.
6. 실행 화일이외에 추가로 디버깅할 DLL이 있다면 "Category" 콤보박스 필드에서 "Additional DLLs"를 선택한 후 디버깅하는 컴퓨터 입장에서 본 DLL 화일의 경로를 입력하시면 됩니다. 추가로 디버깅할 DLL이 없다면 이 과정은 생략하시구요.
7. F5키를 눌러 디버깅을 시작합니다.
- 참고 사항
디버깅을 시작하면 "Find Local Module"이라는 다이알로그가 나타나고 프로그램이 사용하는 DLL 이름을 하나씩 대면서 이 DLL의 경로를 물어봅니다. "Try to locate other DLLs"란 체크박스의 표시를 풀어버리면 귀찮게 안할 겁니다. 그리고 두대의 시스템에 있는 DLL버전이나 언어형식이 일치하지 않으면 경고 메시지 박스가 나타나는데 무시하시거나 찜찜하시면 어느 한쪽으로 DLL을 복사해서 똑같이 만들어 주면 되겠죠.
리모트 디버깅을 하다보면 실행속도가 많이 떨어지는 것을 느낄 수 있는데 네트워크로 연결하다 보니 어쩔수 없는 부분이죠. 하지만 루프를 많이 돈다거나 계산결과를 기다리는 경우에는 IDE를 Minimize 상태로 해두면 조금의 속도 향상을 얻을 수 있습니다.
아래 방법을 쓰시기 전에(비디오카드 하나 더꼽기 전) 바이오스 셋업 항목에서 반드시 Init VGA sequence 인가? 하는 설정을 반드시 먼저 설정하고 카드를 꼽으세요. 어느 카드를 메인 카드로 쓰겠다는걸 뜻합니다. 그리고 비디오 드라이버는 둘다 최신으로 해주시구요.(안되다 되는걸 봐서요.) 비디오 카드끼리 궁합도 있으니...
괜히 이상한 카드로 인내심 테스트 하지 마시구요.
보통 프로그램(윈도우 기반)은 여기 까지만 해도 디버깅이 잘 되는되요... (보통 듀얼 쓸것도 없지만...^^) DirectX같이 풀스크린 잡아먹는 녀석은 디버깅이 힘들죠...^^
DirectDraw 에센셜에 보면 나와있는데, 우선,제어판의 DirectX실행하신다음 DirectDraw탭에서 Advanced에서 다중모니터 디버깅옵션을 체크해줍니다. 그 다음 중요한것은 개발환경은 Primary Device에 그리고 만드시는 애플리케이션을 Secondary Device에 띄우셔야 합니다...
Secondary에 띄우시는 방법은(아주 무식하게 하자면) (이것을 제대로 쓰시고자 하시면 FSWindow라는 예제에 비디오 카드를 고르게 해주는 루틴이 들어있으니 참고하세요)
DDRAW 예제에서 DDENUM이라는 실행하셔서 Secondary디바이스가 몇번째로 잡히는지 확인하신다음 DirectDrawEnumerateEX함수에서 내부적인 카운트를 설정하셔서 세번째 실행됐을때 그때의 GUID를 복사하신다음(반드시 복사를 하셔야합니다.그냥 어사인은 안됩니다. 나중에 해제되는것 같습니다) 그 GUID를 가지고 DirectDrawCreate에 첫번째 인자로 넣어주시면 됩니다.... (보통 널값을 넣습니다)
우선 메뉴와 특정 버튼이 같은 동작을 하게 하시려면 버튼컨트롤의 ID와 해당메뉴의 ID를 같게 해주면 되는데 이때 주의해야할 것은 메뉴를 처리하는 윈도우 클래스와 버튼의 부모 윈도우가 같아야 한다는 것입니다. 만일 메뉴는 메인 프레임 윈도우에서 처리하고 버튼은 메인 프레임의 차일드로 존재하는 뷰에서 처리가 된다는 바로 연결이 되지 않죠. 이런 경우에는 뷰에서 메인 프레임의 포인터를 구한 후에 처리함수를 즉 메뉴 처리함수를 호출하시면 됩니다.
그리고 기본적인 메뉴들. 그러니까 새문서, 인쇄, 미리보기 등은 CWinApp나 CMainFrame 클래스에서 처리가 되어집니다. 이러한 것을 확인하시려면 각 클래스의 시작부분에 존재하는 메세지 맵 부분을 보시면 확인 가능합니다.
예를 들면 다음과 같습니다.
BEGIN_MESSAGE_MAP(CSampleApp, CWinApp)
//{{AFX_MSG_MAP(CDEDITORApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
//}}AFX_MSG_MAP
// Standard file based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
//{{AFX_MSG_MAP(CDEDITORApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
//}}AFX_MSG_MAP
// Standard file based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
위를 보시면 File New, Open, Print Setup 등의 처리가 CWinApp 클래스에서 처리되어짐을 알 수 있습니다.
프로그램을 작성하고 있을 때[동일한 조작]을 몇 번이고 반복하는 경우가 있습니다. 이 와 같은 [동일한 조작]을 기록해놓고 원터치로 재 이용할 수 있습니다. 이것을 매크로라고 말합니다. VC++에서는 퀵 매크로 기능과 표준 매크로 기능이 있습니다.
* 퀵 매크로 기능은 소위 [키보드 매크로]라고 하는 것으로서 키보드에서의 실제의 조작을 기록하여 재현합니다. 퀵 매크로의 사용법은 간단합니다.
Ctrl + shift + r //매크로의 기록 개시 툴바가 생성됩니다.
//자기가 필요한 매크로를 작성한다.
//자기가 필요한 매크로를 작성한다.
예를 들면*
switch() {case : break ;default;} // 이런식으로 작성한후.
툴바의 [Stop Recording]버튼 // 퀵 매크로의 기록 종료
Ctrl + shift + p // 필요한 곳에서 퀵매크로 실행
switch() {case : break ; default;} // 매크로가 실행된다.
퀵 매크로는 VC++을 종료한뒤 다시 실행했을 때도 계속 이용할 수 있다.
* 표준 매크로 사용은 퀵 매크로 사용보다 복잡합니다.
표준 매크로의 내용은 기본적으로는 키보드로 직접 조작 하는 방법으로 기록 합니다. 그러나 그 내용은 VBScript!라는 간이 언어로 작성된 파일(매크로 파일)이 됩니다. 기록된 조작에는 이름(매크로명) 을 붙입니다. 또 매크로 파일도 이름을 붙여서 저장합니다.
매크로 파일의 내용은 VBScript!로 씌어 있으므로 나중에 자유롭게 수정할수 있습니다.
그럼 지금 부터 그 사용법에 대해서 알아 보겠습니다.
1.우선 매뉴중 Tool/Macro...를 선택합니다.
2.그럼 다이얼로그 박스가 뜨는데 그냥 [OK]버튼을 누르면 됩니다.
3.매크로 이름 과 매크로를 저장할 파일 입력 창이 뜨는데 예를 들어 매크로 이름을 switchStyle 이라하고 매크로 파일은 디폴드 파일인 Mymacros로 하고 [Record] 버튼을 클릭 합니다.
4.그러면 매크로에 대한 설명을 적을수 있는 다이얼로그가 뜹니다. 설명을 작성하고 [OK]버튼은 클릭 합니다.
5.사용하고자 하는 매크로를 작성합니다.
예) switch() { case : break;default;}
6. 매크로 정지 버튼을 클릭합니다.그러면 작성한 매크로에 대한 에 대한 코드가 표시됩니다.(VBScript!)
** 작성한 매크로는 그 차체로도 메뉴에서 실행할 수 있지만, 키나 버튼에 할당하여 실행할 수도 있습니다. 그 설정에 대해서 알아 보겠습니다.
위의 1,2단계를 합니다.
3.이번에는 [Record]가 아닌 [<<Option]버튼을 누릅니다. 그러면 다이얼로그박스가 길어지면서 4개의 버튼이 생성 됩니다.
각각의 버튼에 대해서 알아 보면
[New File...] 신규 매크로 파일을 작성할때 선택
[Loaded Files] 인스톨 파일 지정(로드 파일 지정)을 할 때는 이 버튼을 클릭한다.
[Toolbars] 버튼을 할당할때 사용.
[Keystrokes] 키를 할당할때 사용.
4.[Keystrokes]버튼을 클릭한다.
탭 다이얼로그 가 뜨는데 Keybord라는 탭이 활성화 되어 있을 것이다.
commands에서 키 작성을 할 매크로를 선택한후 Press New shortcut 라는 에디트 박스에 사용할 키를 넣으면 됩니다.
주의) 예를 들어 Alt + S 를 키로 설정하고 싶으면 키보드로 Alt + S이렇게 작성하는것이 아니라 Alt 키와 S 키를 한꺼번에 누르면 그 에디트 박스에 Alt + S라고 표시가 됩니다.
5.Assign버튼을 누르면 키 작성을 끝냅니다.
***이번에는 키 가 아닌 버튼을 만들어 사용하도록 설정 하겠습니다.
위의 3에서 4가지 버튼중 이번에는 Toolbars 버튼을 클릭합니다.
4.Commands라는 탭이 활성화된 탭 다이얼 로그가 뜹니다.
작성할 매크로명을 선택하고 VC++ 메인 화면의 툴바들이 위치한 곳으로 그래그를 합니다.
여러개의 아이콘이 있는 다이얼로그 박스가 화면에 뜨는데 사용할 아이콘을 선택하고 [OK]버튼을 누르면 메인 화면의 툴바에 선택한 아이콘이 툴바가 되어 표시가 됩니다.
그럼 이 툴바에 있는 아이콘만 누르면 매크로가 실행 됩니다.
- the end of this article -
출처 : 프로그래머의 일상
글쓴이 : 김건우 원글보기
메모 :
'개발 > 프로그래밍(일반)' 카테고리의 다른 글
Segmentation fault 가 발생하는 경우 (0) | 2012.04.27 |
---|---|
Linux 오디오 프로그래밍 (0) | 2011.10.26 |
[MFC] Setting the width of the dropdown list (0) | 2010.02.02 |
[MFC] ComboBox with Item Tooltips (0) | 2010.02.02 |
[스크랩] O_NONBLOCK 그리고 EAGAIN (0) | 2009.12.11 |