대화 상자 기반의 프로그램을 만들고, 에디트 컨트롤같은 텍스트 입력 컨트롤을 올려놓은 후 엔터(ENTER) 키를 누르면 대화 상자가 종료되는 현상이 발생한다. 이를 방지하기 위해서 엔터키 입력을 따로 처리해주어야 하는데, 이 경우 PreTranslateMessage() 함수를 오버로딩 해주어야 한다.

예를 들어 CTestDlg 클래스가 대화 상자를 나타내는 클래스라면 아래와 같이 코드를 추가한다.

BOOL CTestDlg::PreTranslateMessage(MSG* pMsg)
{
	if( pMsg->message == WM_KEYDOWN )
	{
		if( pMsg->wParam == VK_RETURN ) return TRUE;

		if( pMsg->wParam == VK_ESCAPE )
		{
			SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0);
			return TRUE;
		}
	}

	return CDialog::PreTranslateMessage(pMsg);
}

즉, 대화 상자 클래스에서 윈도우 메시지를 처리하기 전에 이미 WM_KEYDOWN 키 중에서 RETURN 키에 대해서는 아무 일도 하지 않도록 만들고, ESC 키에 대해서는 프로그램을 최소화하도록 하는 코드이다.


Posted by kkokkal
:
Visual C++ 2005를 사용할 때, 솔루션 파일(*.sln)과 프로젝트 파일(*.vcproj)을 하위 버전으로 변환하는 방법은 매우 간단한다.

두 파일을 텍스트 에디터에서 열어서 문자열을 바꾸기만 하면 되는데...

솔루션파일의 경우 맨 처음 시작이 다음과 같이 되어있는데,

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005

이를 아래와 같이 변환한다.

Microsoft Visual Studio Solution File, Format Version 8.00
# Visual Studio 2003

그리고, 프로젝트 파일에서는 네 번째 줄 정도에 다음과 같이 된 부분을

Version="8.00"

아래와 같이 변환한다.

Version="7.10"

위 과정을 반대로 수행하면, 2003 버전 파일을 2005 버전으로 바꿀 수 있다.

만약, 2003 버전의 프로젝트 파일을 Visual C++ 6.0 의 것으로 변환하고 싶다면 아래 링크에서 그 해답을 구할 수 있다.

http://www.codeproject.com/tools/prjconverter.asp
Posted by kkokkal
:
Visual Studio .NET 2005 를 사용하여 예전 프로그램들을 새로 컴파일하다 보면 유난히도 deprecated warning 이 많이 나오는 것을 발견할 때가 있다.

보안 관련 기능을 강화하였기 때문인데, 특정 함수가 deprecated 되었다는 것은 이 함수는 앞으로 사용하지 않을 것이므로, 이를 대체할 다른 함수를 사용하라는 의미로 해석하면 된다. 아마 warning 메시지에서 대체 함수 이름을 알려줄 것이다.

그러나, 너무 많은 deprecated 함수가 존재할 경우 모든 함수를 다 변경하는데 시간이 너무 오래 걸리고, 또한 그다지 보안이 심각하게 필요한 상황이 아니라면 이 warning 을 안나오게 하는 것이 더 편할 수도 있다.

솔루션 속성 창을 열어서

[구성 속성] -> [C/C++] -> [전처리기] 항목을 연다.

아마 WIN32;DEBUG; 등의 옵션이 설정되어 있을 것이다.

여기에 _CRT_SECURE_NO_DEPRECATE 항목을 추가하고 컴파일하면 deprecated 경고는 모두 사라질 것이다.

그러나, 만약 보안에 민감한 프로젝트라면 warning 메시지에서 알려주는 대로 함수를 새 함수들로 교체하는 것이 바람직할 것이다.

데브피아에서 참조하였습니다.
http://www.devpia.com/
Posted by kkokkal
:
영상 처리 프로그램을 작성하다보면, 영상 처리 결과 이미지를 새 창으로 띄우는 경우가 많다. 여러개의 영상에 대하여 테스트하다보면 프로그램 내에 너무 많은 창이 떠있어서 지저분해보이는 경우가 있다. 이 때, '모든 창 닫기' 같은 메뉴가 있으면 유용하다.

일반적으로 창(Window) 메뉴의 서브 메뉴로 모든 문서 닫기(Close All), ID_WINDOW_CLOSEALL 이라는 ID로 메뉴 아이템을 만들고, 이에 해당하는 이벤트 처리기를 CxxxApp 클래스에 만든다. 그리고, CDocTemplate 클래스의 멤버 함수인 CloseAllDocuments 함수를 호출하면 되는데, 자세한 코드는 아래를 참고하라.

void CxxxApp::OnWindowCloseall()
{
POSITION pos;
CDocTemplate* pTemplate;

pos = GetFirstDocTemplatePosition();
while( pos != NULL )
{
pTemplate = GetNextDocTemplate(pos);
pTemplate->CloseAllDocuments(FALSE);
}
}

위 코드에 대해 간단히 설명하자면, App 클래스에 연결된 모든 도큐먼트 템플릿의 포인터를 받아와서 CloseAllDocuments 함수를 호출하고 있다. while 루프를 사용하는 이유는 여러 개의 도큐먼트 템플릿을 사용하는 프로그램을 염두에 둔 것이다.

참고로, CxxxApp 클래스의 InitInstance() 함수 내 AddDocTemplate 하는 부분에서 도큐먼트 클래스가 지역변수로 설정되어 사용되는데, 이를 멤버 변수로 바꾸면 편리하다. 예를 들어,

[CxxxApp.h 파일]
CDocTemplate* m_pDocTemplate;

[CxxxApp.cpp 파일]
m_pDocTemplate = new CMultiDocTemplate(IDR_XXX_TYPE,
RUNTIME_CLASS(CxxxDoc),
RUNTIME_CLASS(CChildFrame), // 사용자 지정 MDI 자식 프레임입니다.
RUNTIME_CLASS(CxxxView));
if( !m_pDocTemplate)
return FALSE;
AddDocTemplate(m_pDocTemplate);

형태로 사용한다면, 위 OnWindowCloseall 함수를 다음과 같이 간략하게 바꿀 수 있다.

void CxxxApp::OnWindowCloseall()
{
pDocTemplate->CloseAllDocuments(FALSE);
}

만약 도큐먼트 템플릿이 여러 개라면 각각의 도큐먼트 템플릿을 멤버 변수로 만들어서, 각각 CloseAllDocuments 함수를 호출해주어야 한다.

아래는 MSDN에서 설명하고 있는 CloseAllDocuments 함수에 대한 내용이다.

---------------------------------------------------------------------------------------

Call this member function to close all open documents.

virtual void CloseAllDocuments(
BOOL bEndSession
);

Parameters
bEndSession
Specifies whether or not the session is being ended. It is TRUE if the session is being ended; otherwise FALSE.

Remarks
This member function is typically used as part of the File Exit command. The default implementation of this function calls the CDocument::DeleteContents member function to delete the document's data and then closes the frame windows for all the views attached to the document.

Override this function if you want to require the user to perform special cleanup processing before the document is closed. For example, if the document represents a record in a database, you may want to override this function to close the database.

Posted by kkokkal
:

Visual Studio .NET 을 사용하여 프로그램을 만들어보면, 전체 프로그램의 전체 프레임은 윈도우 테마를 따르지만, 내부 컨트롤들은 여전히 구식 스타일을 그대로 사용한다. 간단한 작업만으로 모든 컨트롤들에게 윈도우 비주얼 스타일을 적용할 수 있기때문에, 5분만 투자하여 멋진 프로그램을 만들어보자.

지금부터 설명할 내용들은 이미 codeproject 또는 MSDN 사이트에서 이미 찾을 수 있는 내용들이지만, 아무래도 한글이 이해하는데 빠르지 않을까 싶다. 보다 많은 정보를 원한다면 아래 링크를 따라가보기 바란다.

http://www.codeproject.com/cpp/xpstylemfc.asp
http://www.codeproject.com/cpp/AddXpStyle.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwxp/html/xptheming.asp

이제부터 하나하나 설명하도록 하겠다. 여기서 설명은 다이얼로그 기반의 프로그램을 실제로 하나 만들어보면서 진행하도록 하겠다.

우선 XPStyle 이라는 제목의 프로젝트를 하나 생성하자. 당연히 'MFC 응용프로그램' 항목을 선택하고, MFC 응용 프로그램 마법사에서는 응용 프로그램의 종류를 '대화 상자 기반'을 선택하도록 하자. 아무래도 컨트롤 올리기가 편하니까... 그리고, 중요한 것은 아래 그림과 같은 고급 기능 탭이다. 그림을 보자.


오른쪽 항목에서 맨 아래쪽 '공용 컨트롤 매니패스트' 가 선택이 되어있어야 한다. 기본적으로 선택이 되어있으므로 따로 건들이지만 않으면 된다. 이제 [마침] 버튼을 클릭하여 프로젝트를 생성한다.

아마 빈 대화상자를 편집할 수 있는 화면이 나타날 것이다. 왼쪽의 리소스 뷰의 [XPStyle.rc] 에서 마우스 오른쪽 버튼을 클릭하여 [리소스추가...] 를 선택하거나 또는 상단의 메뉴에서 [프로젝트]->[리소스추가...] 를 선택하여 다음과 같은 창이 나오게 하자.


오른쪽 버튼들 중에서 세번째 [사용자지정...] 버튼을 눌러서, 새 사용자 지정 리소스에 숫자 24를 입력하고 [확인] 버튼을 누른다.


그러면 위와 같이 RT_MANIFEST 라는 항목을 새로 편집하는 창이 나타나게 된다. 여기에 XML 문장을 입력해야한다. XML 문장을 완전히 새로 쓰는 것은 아니고, 솔루션 탐색기에서 XPStyle.manifest 파일에 적혀있는 내용을 그대로 복사해서 옮기면 된다. 아래 그림을 보자.


위 그림에서 오른편의 XML 문장을 전체 선택하여 copy 해서 아까 리소스 편집기로 다시 가서 paste 한다. 결과 그림은 다음과 같다.


마지막으로, 새로 추가하였던 리소스의 아이디(ID)를 IDR_RT_MANIFEST1 에서 숫자 1로 변경한다.


이로써 모든 작업이 끝났다. 이제 프로그램을 빌드하고 실행해보면 버튼과 같은 컨트롤에도 windows visual style 이 적용된 것을 확인할 수 있을 것이다.

아래 샘플 프로그램의 프로젝트 파일을 다운받아서 테스트해보기 바란다.



Posted by kkokkal
:
영상 처리 프로그래밍에 있어서 가장 많이 접하는 타입이 바로 2차원 배열이다. C/C++ 에서는 모든 자료형에 대하여 다차원 배열을 지원하고 있다. 그러나, 기본적인 2차원 배열을 사용하려면 그 크기를 미리 지정해야한다는 단점이 있다. 그러나, 다양한 크기의 영상을 배열에 넣기 위해서는 배열의 크기가 프로그램 동작 시간에 결정되게 마련이고, 그 때문에 new 연산자를 이용한 동적 배열 할당이 필요하다.

가장 일반적인 형태의 2차원 배열 동적 할당 방법은 다음과 같다.


위의 코드를 그림으로 설명하면 다음과 같다.


위의 코드에서 주의해야 할 점은 할당한 메모리 공간을 해제할 때, 그림상 하늘색으로 표현된 공간을 모두 해제해주어야 한다는 점이다. 즉, 가로로 그려진 하늘색 메모리 공간 w개와 세로로 그려진 메모리 공간 1개를 delete [] <변수이름> 형태로 삭제해주어야 한다는 점이다.

이제 이것과 동일한 역할을 하는, 새로운 2차원 배열 동적 할당 방법을 알아보자. 코드는 다음과 같다.


마찬가지로 이 코드에 대한 그림 설명을 보면 이해가 더 쉽게 되리라 믿는다.


이 새로운 방법에서는 new 연산을 2번만 수행한다는 장점이 있다. 그러므로, for 문을 돌면서 메모리를 할당하거나 해제하는 작업이 없다. pixels[1]~pixels[h-1] 까지는 모두 새로운 메모리를 할당하여 그 위치를 가리키는 것이 아니라, pixels[0] 이 할당한 큰 메모리 공간의 중간을 각각 가리키는 방식을 취한다.

이러한 Alloc2 방법은 배열의 크기가 아주 크지 않을 때에는 Alloc1 보다 어느정도의 속도 향상의 성능을 보여준다. 예를 들어, 256x256 크기의 배열을 10번 할당&해제할 때의 속도를 VC++ 6.0의 Profile 기능을 이용하여 그 시간을 체크해보았다. 실험에 사용한 컴퓨터 환경은 Pentium IV 2.8GHz(3.3GHz로 overclocking), 1GByte RAM, Windows XP sp2 이다.

Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
20.624 58.9 20.624 58.9 10 Alloc1(void) (dyna2d.obj)
14.391 41.1 14.391 41.1 10 Alloc2(void) (dyna2d.obj)
0.007 0.0 35.023 100.0 1 _main (dyna2d.obj)

그러나, 배열의 크기를 키우면 그다지 속도 차이가 나지 않음을 발견하였다. 1024x1024 배열을 10번 할당하고 해제할 때의 속도를 보자.

Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
297.449 54.1 297.449 54.1 10 Alloc1(void) (dyna2d.obj)
252.632 45.9 252.632 45.9 10 Alloc2(void) (dyna2d.obj)
0.007 0.0 550.087 100.0 1 _main (dyna2d.obj)

사실, 2003년도에 이 Alloc2 방법이 Alloc1 방법보다 매우 빠르다고 생각했었는데, 이번 실험에서 이렇게 결과가 나온 것은 상당히 당혹스러웠다. 그러나, 이번 실험의 하나하나를 다 따져보았으나 잘못된 부분은 발견할 수 없었다. 결국, 속도상에서의 merit 는 그다지 없는 것이 아닌가 하는 결론을 내려야만 했다.

첨부파일은 VC++ 6.0 에서 profile 을 보기 위해 만든 프로젝트와 VC .NET에서 GetTickCount 함수를 이용하여 배열을 할당 & 해제하는데 걸리는 시간을 재는 윈도우용 프로그램의 프로젝트 압축파일이다. 참고하시길...


ps. VC .NET 으로 만든 프로그램으로 256x256 배열을 1000번 할당 & 해제할 경우, 은근히 속도 차이를 나타내는 것을 발견하였다. 흠... 작은 배열일 때는 과연 Alloc2 가 유리한 것인가? ㅡㅡ;

012345
Posted by kkokkal
:


view 클래스에서 WM_ERASEBKGND 메시지 핸들러를 추가하여 다음과 같이 코드를 입력한다.

BOOL CImageShopView::OnEraseBkgnd(CDC* pDC)
{
	CBrush br;
	br.CreateHatchBrush(HS_DIAGCROSS, RGB(200, 200, 200));
	FillOutsideRect(pDC, &br);

	return TRUE; // Erased
}


한가지 주의할 것은, 함수 반환값을 항상 TRUE로 지정해주어야 한다.

참고로, FillOutsideRect 함수에 대한 설명은 다음과 같다.

Call FillOutsideRect to fill the area of the view that appears outside of the scrolling area. Use FillOutsideRect in your scroll view’s OnEraseBkgnd handler function to prevent excessive background repainting.




Posted by kkokkal
:
예를 들어, 다이얼로그 박스가 떠있고, 내부적으로 연산량이 많은 작업을 하고 있다고 치자.
다이얼로그에서 cancle 버튼을 클릭하면 내부적인 연산을 취소하고 싶다.
그러나, 일반적으로 이 경우, 윈도우즈는 버튼이 클릭되었다는 메시지 처리를 바로 수행하지
못하고, 단지 메시지 큐에만 넣게된다. 바로 single-thread를 사용하기 때문이다.

이러한 문제를 해결하기 위해서는 multi-thread 를 사용하는 것이 가장 적절한 해법이겠으나,
다소 복잡할 수 있다.

여기서는 single thread를 사용하면서, 주기적으로 메시지 큐를 검사하는 방법을 설명한다.

다음 함수를 보자.

void ProcessMessages()
{
CWinApp* pApp = AfxGetApp();
MSG msg;

while( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) )
pApp->PumpMessage();
}

만약 프로그램 내부에서 복잡한 연산을 수행하는 루틴에 위의 함수를 주기적으로 호출한다면,
multi-thread 를 쓰는 경우보다는 약간 어색할 수 있지만, 그래도 윈도우 메시지를 처리할 수 있다.

Posted by kkokkal
:
App 클래스의 InitInstance() 함수 중간 부분에 다음과 같은 부분을 찾는다.

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

이는 command line 인자에 대한 처리를 위한 것인데, 다음과 같이 한줄을 추가한다.

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
Posted by kkokkal
:
프로그램을 작성할 때 항상 생각하는 것 중의 하나가 "어떻게 하면 프로그램 동작 속도를 빠르게 할 것인가?" 이다. 이를 위해서 다양한 형태로 프로그램을 작성하고, 그 시간을 측정해본다. 요즘의 컴퓨팅 환경에서는 왠만한 루틴은 GetTickCount 같은 함수로는 시간을 측정할 수 없을 만큼 빠르게 동작한다. 그래서 멀티미디어 타이머라는 것을 사용하기도 한다. 그러나, Visual C++ 6.0 의 Profile 기능을 사용하면 간단하게 0.001 msec 단위까지 연산 속도를 측정할 수 있다.

프로파일 기능을 사용하기 위해서는 프로젝트 셋팅에서 프로파일 기능을 활성화(Enable) 시켜주어야 한다. VC++ 6.0 메뉴에서 [Project]->[Settings...]을 선택하면 아래와 같은 대화 상자가 나타난다. 여기서 Link 탭을 선택하고, [Enable profiling] 항목이 선택되도록 한다.


그림에서는 릴리즈 모드에 대하여 [Enable profiling]을 체크하였는데, 이는 릴리즈 모드에서 시간을 재는 것이 합리적(?)이기 때문이다. 아무래도 디버그 모드에서는 함수의 수행 시간에 디버그 정보 처리에 걸리는 시간이 포함되기 때문에 정확한 시간 측정이 불가능하다.

이제 프로그램을 빌드(F7)하고, 에러가 없이 exe 파일이 생성되도록 한다. 에러가 있다면 당연히 에러를 잡아라. 그리고, 메뉴에서 [Build]->[Profile...] 을 선택한다.


그림과 같이 첫번째 항목인 [Function Timing]을 선택하면, 바로 프로그램이 실행되면서 프로그램 수행 시간을 측정하게 된다.

실제 테스트를 해보자. 가급적 프로파일 기능을 사용하려면 콘솔형(도스형) 프로젝트를 만드는 것이 좋다.(아무래도 깔끔하니까...) 다음과 같은 간단한 도스형 프로그램을 만들어보았다.

-------------------------------------------------------------------------------
#include <stdio.h>
#include <math.h>

double Power1(double p, int n)
{
register int i;
double s;

s = 1;
for( i = 0 ; i < n ; i++ )
s = s * p;

return s;
}

double Power2(double p, int n)
{
return pow(p, n);
}

int main(void)
{
for( int i = 0 ; i < 10000 ; i++ )
{
Power1(1.2, 10);
Power2(1.2, 10);
}

return 0;
}
-------------------------------------------------------------------------------

대충 보면 알 수 있듯이, 1.2의 10승에 대한 연산을 직접 for 문을 사용하여 계산하는 경우와 math 라이브러리에서 제공하는 pow 함수를 사용하여 계산할 때의 시간을 비교하기 위한 프로그램이다. 이 프로그램에 대하여 프로파일을 해보면 출력창(Output window)에 다음과 같은 메시지가 나타난다.

-------------------------------------------------------------------------------
Profile: Function timing, sorted by time
Date: Wed Apr 06 11:12:23 2005


Program Statistics
------------------
Command line at 2005 Apr 06 11:12: "D:\업로드폴더\SpeedChk\Release\SpeedChk"
Total time: 16.673 millisecond
Time outside of functions: 11.598 millisecond
Call depth: 2
Total functions: 4
Total hits: 20001
Function coverage: 75.0%
Overhead Calculated 6
Overhead Average 6

Module Statistics for speedchk.exe
----------------------------------
Time in module: 5.075 millisecond
Percent of time in module: 100.0%
Functions in module: 4
Hits in module: 20001
Module function coverage: 75.0%

Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
3.203 63.1 3.203 63.1 10000 Power2(double,int) (speedchk.obj)
0.988 19.5 5.075 100.0 1 _main (speedchk.obj)
0.884 17.4 0.884 17.4 10000 Power1(double,int) (speedchk.obj)
-------------------------------------------------------------------------------

위에 내용을 잘 읽어보기 바라고, 맨 아래 부분 Func Time 부분을 보자. Power2 함수가 전체 3.203 msec 걸린 반면에 Power1 함수는 0.884 msec 밖에 걸리지 않았다. 즉, 1.2의 10승을 계산할 때 직접 for 문을 사용하여 계산하는 것이 더 빠르다는 것을 알 수 있다.

이처럼 프로파일 기능은 특정 루틴의 연산 시간을 편하게 측정할 수 있다. 그러나, 아쉽게도 Visual C++ .NET 버전부터는 이런 Profile 기능이 빠져있다. 돈을 주고 따로 구입해야 한다는 소문이 있는데, 잘 모르겠다. ^^; 아는 분은 답글을 남겨주시길...
01
Posted by kkokkal
: