Visual-C++ 을 이용한 실무 프로그래밍


이 글은 제가 통신 상에서 얻었던 글입니다. 원본은 MS WORD 7.0으로 되어 있었으며, 하이텔, 천리안의 자료실에 있는 내용과 동일합니다. Visual-C++ 에 대한 이야기는 항상 프로그래머들 사이에 커다란 화제거리가 되고,컴퓨터 를 이용하는 많은 사용자들은 나름대로 보다 나은 컴퓨터 활용법을 찾다가 서점에서 쉽게 구할 수 있는 Visual-C++ 에 대한 책을 사보고는 공부를 시작하다가 이내 포기하는 경우를 많이 보았다. 이는 C 언어 자체를 혼자 공부하기에 여러가지 난관이 있으며, 또한 컴퓨터 에 대한 경험이 적은 경우에 누구나 겪는 혼돈스러움이다.

여기서, C 언어나 C++ 을 다루지는 않는다, 오로지 Windows 자체 OS 에 대한 프로그래밍 지식과 Visual-C++ 을 이용한 프로그래밍과 관련된 내용에 대해서만 언급을 할 것이다.

본인은 컴퓨터 프로그래밍을 도스이전의 8비트 시절부터 해왔기에 나름대로의 전문적인 지식보다는 프로그래머로서의 감각과 문제와 친해지는 방법에 대해 보다 많은 내용을 투자 하고 싶다.

일을 하다 보면, 항상 주위 사람들에게 듣는 질문이 있다, 어떻게 하면 프로그램을 잘 만 들 수 있는가에 대한 질문이 그것이다. 모든 일에는 왕도가 없다. 많은 일을 하고 많은 경 험을 가지는 것이 바로 그것이다. 또한, 이미 프로그래머로서의 길을 가는 사람들에게 하고 싶은 말은, 휴식에 대한 충분한 고려를 잊지 말라는 것이다. 이는 지친 심신으로 하는 일 은 단지 기능인으로 밖에 자리매김을 할 수 없기 때문이다. 바쁘고 힘들수록 보다 충분한 휴식을 구하여 자신에게 가능한 창조력에 대해 배려를 잊어서는 안된다는 것이다.

본 문서는 Visual-C++ 5.0 기반과 32 비트 API 함수를 사용하였으며, 각 단원에 맞는 기 본 지식부터 이해할 수 있는 부분에 대한 세심한 설명에 대해 최선을 다했다.

아무쪼록, 많은 것을 얻어 스스로 무엇이든지 할 수 있는 능력있는 많은 전문 인력이 보 다 많은 창조적인 작업에 시간 투자가 가능토록 기본적인 개념에 대해서는 자세히 다루고 예제로써 기본 틀을 보여 보다 쉬운 이해를 돕도록 하였다



1. 파일 다루기

2. DB 프로그램

ㄱ) CRecordset 다루기

ㄴ) ODBC 프로그래밍

3. Socket 프로그래밍

ㄱ) 서버와 통신하기

ㄴ) 선로상태 확인하기

4. Windows 에서 Graphic 다루기

ㄱ) CDC 객체 알기

ㄴ) 화면에 원하는 그림 그리기

5. 기타 프로그래밍에 도움이 되는 팁 모음.

1.

파일 다루기

컴퓨터를 사용한다는 것의 의미는 첫째, 자료를 저장한다는 것과 둘째, 자료를 처리 (전달,가공)한다는 것으로 표현된다. 자료를 입력하는 것은 사람이 하지만, 저장과 가공 은 컴퓨터가 처리를 해주는 것이다. 여기서 컴퓨터가 처리해 준다는 것 또한 프로그래머 가 이미 지시를 해 두어 알맞은 시기에 자료를 저장토록 프로그램을 만들어 둔 것이다.

저장, 읽기는 "File 을 사용" 하는 것에서 시작한다. 전통 C 언어에서부터 지금까지 여전히 파일관리만이 자료 저장의 완벽한 방법이다. 여기서는 MFC 의 CFile Class 를 이 용하여 원하는 자료를 쓰고, 읽는 방법에 대해 논의한다.


1) CFile Class 에서 꼭 알아야 할 Member Function

m_hFile : 고전 C 에서 사용하던 File-Handle 이다.

이는 CFile 의 객체를 ANSI-C 함수와 호환 가능토록 지원된다

Abort : 에러 또는 오동작시 바로 파일을 닫고 빠져나간다.

Open : 파일을 연다.

Close : 파일을 닫는다

Seek : 파일내의 원하는 위치로 이동한다.

SeekToBegin : 파일의 맨 앞으로 위치이동을 한다

SeekToEnd : 파일의 맨끝으로 이동한다.

GetLength : 파일의 길이를 가져온다

SetLength : 파일의 길이를 설정한다.

GetPosition : 파일의 위치를 알아낸다

GetStatus : 현재 CFile 객체의 상태를 알아낸다

GetFileName : 현재 CFile 객체의 파일이름을 알아낸다

GetFilePath : 현재 CFile 객체의 완전한 경로를 알아낸다

SetFilePath : 현재 CFile 객체의 완전한 경로를 지정한다

이상은 기본적인 파일 조작을 위한 CFile 의 Member 함수 이다. 물론, ANSI-C 로 파일관리 프로그래밍을 해 보신 독자라면 금방 함수 이름만으로 이해가 갈 것 이다. 꽤 많은 함수들이 기본적으로 지원됨을 느낄 수 있을 것이다. 또한 Windows 자체의 MainFrame 이 16 비트(Win31)부터 지원되는 악조건의 OS 이기 때문에 커 다란 파일(64KB이상)을 관리하는 또다른 함수가 있다.

Read : 버퍼로 현재의 파일 위치부터 원하는 길이만큼 읽는다.

ReadHuge : 64 KB 의 한계보다 큰 내용을 읽어서 버퍼에 저장한다.

Write : 버퍼의 내용을 현재의 파일 위치에 쓴다.

WriteHuge : 64 KB 보다 큰 버퍼의 내용을 한번에 파일 위치에 쓴다.

Flush : 캐시등에 저장되어 있는 저장할 자료를 모두 파일에 쓴다.

이상이 CFile 에 대한 꼭 필요한 내용의 파일 관리 함수들이다.

자료의 보관, 활용을 위한다면 이상의 함수만 알아도 개인 자료처리 프로그래밍 을 충분히 할 수 있다.




2) CFile Class 함수를 응용한 프로그래밍 예제

//

// CFile 객체 생성하기

//

CFile *pFile = new CFile();

//

// CFile 객체를 사용하여 원하는 파일 Open 하기

//

pFile->Open( "c:\\a.txt", CFile::modeCreate:CFile::modeWrite | CFile::typeBinary);

여기서 Option 은 | (Bit-Or) 연산자로 여러가지를 지정할 수 있다. 여기서는

파일을 새로 만들고, 쓰기전용이고, 2 진 파일의 Option 을 지정했다

//

// 파일에 원하는 내용을 쓰기.

//

CString szData = "This is file test ";

pFile->Seek( 0L, CFile::end ); // 파일의 맨끝으로 이동

//

// szData 의 내용을 a.txt 파일에 쓴다.

//

pFile->Write( szData, szData.GetLength() );

//

// 이제는 파일을 Close 시켜야 한다.

//

pFile->Close();

//

// 하나더, new 연산자로 CFile class 를 생성했으니

// 확실하게 delete 시키자.

//

delete pFile;

이로써 파일에 내가 원하는 내용("this is file test") 를 써 넣었다.

나머지 문제들이 생기는가? 추가적인 부분들은 뒤에서 더욱 깊이 있게 논의하자

2. DB 프로그래밍

기존의 전문 DB 관리 프로그램 툴은 이미 많은 명성을 얻고 많은 사용자를 확보하고 있다. 여기에 Visual-C++ 은 많은 노력을 하여 2.0 버전부터 ODBC 관련 클래스를 지원하 였으나, Visual-C 자체의 많은 장점( 빠른 속도, 작은 실행코드)에도 불구하고, 제작의 어려움이 많았으나, 5.0 버전부터는 보다 쉬운 인터페이스를 제공하여 많은 사용가능성 을 보였다.

이에 현재 버전에서 제공하는 CRecordset 의 사용법을 보고 적용가능한 업무를 찾아 보길 바란다. 또한 ODBC 함수에 대해 간단히 논의하여 ODBC 를 이용한 응용가능성도 함 께 보여 보다 많은 응용 분야를 찾는데 도움이 되고자 한다.

1) CRecordset Class

MFC 에서 제공하는 DB 관리 Class 로써 기존의 복잡하고 수작업이 많았던 DB 작 업을 간단히 해결해 준다. 여기에는 Filter 와 Sort 기능을 있으며, 기타 DB 관리 를 위한 기능이 함께 포함되어 있다

여기서 간단하게 예를 들어 여러분의 이해를 돕는다.

1. ClassWizard 를 이용하여 CRecordset Object 를 하나 만든다.

ClassName

CDB_recordset

BaseClass

CRecordset


2. ClassWizard 가 만든 DB_recordset.cpp 파일의 Source 코드를 보자

CString CDB_recordset::GetDefaultDBName()

{

return _T("D:\\학사 관리1.mdb");

}

CString CDB_recordset::GetDefaultSQL()

{

return _T("[과목]");

}

위의 코드는 d:\학사관리1.mdb 의 [과목] Table 과 matching 되었음을 알 수 있다

3. 다른 클래스에서 CDB_recordset 의 클래스를 이용하여

과목에 관한 DB table 을 관리해보자

#include "DB_Recordset.h"

CDB_recordset *pDB = new CDB_recordset( NULL );

pDB->Open();

하면 pDB 객체는 [과목]에 관한 Record 와 바로 대응되어 각 Column 의 값을

참조할 수 있다.

2) CRecordset 사용에 필수적인 Member 함수 보기

m_strFilter : Select 문을 사용토록 해준다.

m_strSort : ORDER BY 문과 같은 기능을 제공한다.

Open : Table 을 Open 한다

Close : Table 을 Close 한다

IsOpen : 현재 table 이 Open 되어 있는지 여부를 확인한다.

IsBOF : 현재 가리키는 Record 가 첫번째인가 확인한다.

IsEOF : 현재 가리키는 Record 가 마지막인가 확인한다.

Move : 원하는 레코드 번호로 이동한다

MoveFirst : 맨 처음 Record 로 이동한다.

MoveLast : 맨 마지막 Record 로 이동한다

MoveNext : 다음 Record 로 이동한다.

MovePrev : 이전 Reocrd 로 이동한다.

이상의 함수를 사용하면 DB-Table 내의 원하는 자료를 추출하여 찾아낼 수 있다

** m_strFilter 와 m_strSort 를 적용하려면

** 우선 CRecordset 을 Close 시킨뒤 m_strFilter, m_strSort 를 적용하고

** 다시 Open 을 해야한다.

** 이미 Open 된 CRecordset 의 m_strFilter 와 m_strSort 를 바꾸면

** 실행중 에러를 내고 프로그램이 멈춘다.

간단한 예제 )

//

// m_pSet 이 이미 Open 되어 있는지 확인한다.

//

if ( m_pSet->IsOpen())

m_pSet->Close();

//

// WHERE score > 90 절을 적용한다

//

m_pSet->m_strFilter = " score >= 90 ";

//

// ORDER BY socre 절을 적용시킨다.

//

m_pSet->m_strSort = " score ";

m_pSet->Open();

//

// 다시 Open 시킨뒤의 결과는

// score 가 90 이상이고 점수로 정렬되어 CRecordset 으로

// 변환된 후 m_pSet 객체에 삽입되어 진다.

//


3) ODBC 프로그래밍

1. CRecordset 의 한계

간단한 개인 DB 를 관리하는 데에는 3~5 개 이하의 Table 을 쓰기에 CRecordset 으로 관리가 가능하다. 그러나 대형 Project 구성에서는 DB-Table 이 10 개 이상이 되면, CRecordset 의 사용에 불편함이 많이 따른다.

1) 다른 Table 과 함께 연계된 Select 문을 쓸 경우.

2) 사용자의 SQL 문을 입력받아 Table 에 적용할 때.

이러한 경우 외에도 1 개의 Table 과 대응되는 CRecordset 은 불편함이 많다. 여기서 기존의 ODBC Function 을 사용해 야 할 경우이다. 여기서는 ODBC API 의 간단한 사용 예를 아래에서 보자

2. 간단한 예제

//

// ODBC 프로그래밍에 필요한 Handle 설정

//

SQLHDBC m_hdbc; // ODBC Handle -> ODBC Driver 핸들

SQLHSTMT m_hstmt; // hstmt Handle -> SQL 문장의 핸들

int nCol;

int nResult;

//

// ODBC 와 m_hstmt 를 연결시킨다.

//

if ((nResult = SQLAllocHandle(SQL_HANDLE_STMT,m_hdbc, &m_hstmt)) !=

SQL_SUCCESS)

{

DisplayError(nResult, SQL_HANDLE_DBC, m_hdbc);

}

//

// Select * from 학사관리 SQL 문을 실행시킨다.

//

nReturn = SQLExecDirect(m_hstmt, (unsigned char*)"Selct * from 학사관리", SQL_NTS);

if (nReturn != SQL_SUCCESS)

{

DisplayError(nReturn, SQL_HANDLE_STMT, m_hstmt);

if (nReturn != SQL_SUCCESS_WITH_INFO)

{

// SQL 문장에 에러가 발생하면

// SQL Handle 을 닫는다.

SQLCloseCursor(m_hstmt);

return;

}

}

//

// 최대 컬럼수를 받아온다.

//

nReturn = SQLNumResultCols(m_hstmt, &nCols);

if (nCols >= MAX_COL) {

nCols = MAX_COL;

wsprintf(szDispBuffer, COLTRUNC_WARNG, MAX_COL);

MessageBox( szDispBuffer, TRUNCERR, MB_OK | MB_ICONINFORMATION);

}

else if (nCols == 0) {

// 컬럼이 0(Zero) 이면 SQL Handle 을 닫고, 복귀한다.

SQLCloseCursor(m_hstmt);

return;

}

//

// 결과값으로 돌아온 각 컬럼값을 배열에 대응시켜

// 하나씩 값을 읽은뒤 배열에 하나씩 대입시킨다.

//

for( int nCount=0; nCount<nCols; nCount++)

SQLBindCol(m_hstmt, (UWORD)(nCount+1), SQL_C_CHAR, rgbData[nCount],

MAXDATALEN, &dwDataLen[nCount]);

for( int nRows = 0; (nReturn = SQLFetch(m_hstmt))==SQL_SUCCESS ||

nReturn==SQL_SUCCESS_WITH_INFO;)

{

if (nReturn != SQL_SUCCESS)

DisplayError(nReturn, SQL_HANDLE_STMT, m_hstmt);

for(nCount=0, szDispBuffer[0]='\0'; nCount< nCols; nCount++) {

if ( dwDataLen[nCount]==SQL_NULL_DATA )

// 값이 없으므로, 그냥 통과

else

//

// 결과값을 배열에 대입시킨다.

//

원하는 배열 = rgbData[nCount]);

}

}

}

if (nReturn == SQL_ERROR)

DisplayError(nReturn, SQL_HANDLE_STMT, m_hstmt);

// 결과값에 대한 DB-Set 을 Close 시킨다.

SQLCloseCursor(m_hstmt);

// Bind 된 결과값을 Unbind 시킨다.

SQLFreeStmt(m_hstmt, SQL_UNBIND);

위의 소스가 바로 ODBC 를 연결시켜 값을 받는데 필수적인 부분이다.

원하는 SQL 문을 SQLExecDirect() 함수를 사용하여 유효화 시키고,

SQLBindCol() 함수를 사용하여 각 값을 컬럼에 대응시킨다.

3. Socket 프로그래밍

예전의 개인용 PC 가 이제는 간단한 연결(전화, LAN)로 모든 PC 와 자료를 공유할 수

있으며, 대형 Host 의 서비스를 받을 수 있다. 앞으로 계속 발전할 Netware 의 개발에 필

수적인 요소인 Socket 프로그래밍에 대해 간단히 이야기를 하고, MFC 에서 제공하는

CSocket Class 에 대한 간단한 설명과 사용법에 대한 기본 예제를 보겠다.

통신의 의미는 두대의 컴퓨터가 대화를 하는 것이다. 여기서 한쪽은 요청을 하고, 다 른 한쪽은 그에 대한 응답을 한다. 이는 사람의 대화와 같이 주거니 받거니 서로간의 의사소통을 하는 것이다. 여기서 서버와 클라이언트 개념이 생기는데 클라이언트가 자료 를 요청하면 서버는 이에 대한 자료를 송출해준다. 여기서 서버는 자료를 송출해 주는 개념이고, 클라이언트는 자료를 요구하는 개념이다.

1) CSocket Class Member 함수 설명

Close : Socket 접속을 닫고 통신을 마친다.

Connect : Socket 을 접속한다

Receive : Socket 의 자료를 받는다.

ReceiveFrom : Datagram Socket 의 자료를 받는다

Send : Socket 에 자료를 보낸다

SendTo : Datagram Socket 의 자료를 보낸다

ShutDown : Send, Receive 호출을 무시한다.

Overridable 처리 함수

OnAccept : Listen 중에 Connect 호출이 오면 연결하며 호출된다

OnClose : Socket 이 Close 되면 호출된다.

OnConnect : Socket 이 연결(Connect) 되면 호출된다

OnOutOfBandData : 긴급 메시지를 포함한 자료가 올 때 호출된다.

OnReceive : 자료가 Receive 되면 호출된다.

OnSend : 자료가 Send 되면 호출된다.

위의 함수가 다른 컴퓨터와 연결하여 통신하는데 필요한 가장 기본적인 멤버 함수이 다. 여기서는 간단한 연결 예제로 Socket 프로그램이 얼마나 간단한지 알아보자.


2) 간단한 Socket 프로그래밍 예제

//

// Socket 객체를 생성한다.

//

CSocket* m_pSocket = new CSocket;

if ( !m_pSocket->Create() )

{

delete m_pSocket;

m_pSocket = NULL;

AfxMessageBox( "소켓 생성에 실패했습니다" );

return FALSE;

}

//

// Socket을 Host 와 연결한다.

//

CString lpszAddr = "164.124.101.2"; // IP Address 를 직접 입력한 다.

int nPort = 80; // 이미 지정된 Port 를 써준다.

while (!m_pSocket->Connect( lpszAddr, nPort ))

{

// 연결이 안되었으면

// 프로그램을 종료하는 루틴이 필요하다.

CString _str;

_str.Format("Retry Connect?\nServer:[%s], Port:[%x] ",lpszAddr, nPort);

if (AfxMessageBox( _str, MB_YESNO) == IDNO)

{

delete m_pSocket;

m_pSocket = NULL;

return FALSE;

}

}

//

// OnReceive 의 Overriding 함수처리를 한다. --- 중략 --

//

// 통신을 마치고 Socket 을 제거할 때..

if (m_pSocket != NULL)

{

BYTE Buffer[_ARCH_BUF_+1];

m_pSocket->ShutDown();

while(m_pSocket->Receive(Buffer,_ARCH_BUF_) > 0);

delete m_pSocket;

m_pSocket = NULL;

}

중간 처리부분에 대해서는 추후에 보다 자세히 논의하자.

여기서는 Socket 을 생성하고, 삭제하는 것에 대해 가장 기본적인 모습을 보였다. OnReceive Overriding 함수처리나, Send 메시지에 대한 처리는 기본적인 실행 형 태를 정확히 기획하여 자료를 주고 받으면 된다.

4. Windows 에서 Graphic 다루기

윈도우에서는 도스상에서 또는 Unix 상에서 말하는 Text 라는 개념은 없다. 이는 처 음 윈도우용 프로그램을 대하는 사람들에게 대단히 혼란스러운 문제가 되는데, 윈도우 는 기본적으로 GUI 기반의 OS로서 화면에 보여지는 모든 것들은 전부 그림으로 보면 된다. 이것은 일반 컴퓨터 사용자들에게는 이해하기 어려운 문제일 것이다. 따라서, 간단한 에디터를 만드는 것조차 기능이 조금 추가되도록 하려면, 화면 전체를 프로그 래머가 직접 디자인해야 하는 것이다.

자, 그럼 여기서는 화면 디자인에 필요한 것이 무엇이고, 프로그래머가 사용할 수 있는 도구는 어떤 것인지 알아보고, 그것들을 사용하는 간단한 방법에 대한 설명을 하 겠다.

1) Windows 의 화면 Design 에 대한 이야기

윈도우 프로그래밍에서 사용가능한 Design 도구는 Font, Pen, Brush, BITMAP 이 있 고 이를 총괄 관리하는 CDC class 가 있다. CDC class 에는 Font 를 이용한 화면에 글 씨 그리기, 글씨의 두께(Pen)지정하기, 배경색(Brush)지정하기, 이미 그려진 그림(BMP 파일)을 이용하기등에 관련된 함수를 모두 가지고 있다.

윈도우에서는 일정한 자원을 서로 나누어 쓰는데, 이러한 상황을 기존의 소스로 보 면 이해가 훨씬 쉽다.

//

// 화면에 그림(Font, Line, Box, Bitmap 등등) 을 그리기 위해 화면 Device

// 를 얻어온다.

//

CDC *pDC = GetDC();

//

// 새로운 Font 객체를 만든다.

//

CFont *pTitleFont = new CFont();

//

// pTitleFont 를 생성하여 지정한다.

//

pTitleFont->CreateFont( 12, 12, 0, 0, FW_BOLD, 0, 0, 0, 0, 0, 0, 0, FIXED_PITCH, "굴림체

" );

//

// 현재의 화면 Device 에 새로 만든 pTitleFont 를 지정한다.

//

CFont* pOldFont = pDC->SelectObject( pTitleFont );

// -중략- 여기서 화면에 글을 그리는 작업을 한다.

//

// 원하는 작업을 모두 마친 뒤에는 원래의 상태로 돌려 놓는다.

pDC->SelectObject( pOldFont );

ReleaseDC( pDC );

여기서 자세히 보고 이해해야 할 부분은 바로 다음과 같다

CFont* pOldFont = pDC->SelectObject( pTitleFont );


SelectObject 함수의 Return 값은 기존에 Windows 가 사용하던 어떤 Font 가 돌려진 다. 이를 잘보관해 두었다가 원하는 작업을 모두 마친 후에는 기존의 객체를 다시 셋 팅해준다. 이는 윈도우 자체의 OS 개념인데, 각 프로그램이 모두 하나의 CPU 를 사용 하는 것처럼 기타 자원(Resource) 도 하나를 서로 나누어서(Time Split) 사용하는 것 이다.

2) 비트맵 Resource 를 화면에 뿌리기.

DevStudio 에서 비트맵 파일을 하나 만들어서 둔다. 이 비트맵 리소스를 불러서 실 행중에 화면에 뿌리는 작업을 해보자. 여기서 또하나의 새로운 개념은 똑 같은 형식의 화면 Device 를 만들어서 복사하는 작업이다. 이는 도화지 한장에 비트맵 그림을 그린 뒤 화면에 다시 복사하는 것과 같다. 이는 비트맵을 메모리에 올리는 작업화면에 뿌리는 작업의 차별을 둔 것으로 각 작업중간에 필요한 처리가 가능토록 나누어 준것 으로 보면 된다. 보다 쉬운 이해를 위해 하나의 비트맵을 화면에 뿌리는 작업의 소스 를 보자

//

// 화면 Device 를 구한다.

//

CDC *pDC = GetDC();

//

// Bitmap 객체를 만든다.

//

CBitmap TestBmp;

//

// IDB_TESTBMP 의 이름을 가진 Bitmap 이미지를 읽어온다.

//

TestBmp.LoadBitmap( IDB_TESTBMP );

//

// 화면 Device 와 같은 형식의 CDC 객체를 하나 만든다.

//

CDC dcImage;

if (!dcImage.CreateCompatibleDC(pDC))

return;

//

// 현재 메모리로 불려진 TestBmp 를 새로만든 메모리 Device

// 에 할당한다.

//

CBitmap* pOldBitmap = dcImage.SelectObject( &TestBmp );

//

// 현재 메모리에 있는 비트맵 리소스의 크기를 구한다.

//

BITMAP structureBmp;

Bmp.GetBitmap( &structureBmp );

CSize size( structureBmp.bmWidth, structureBmp.bmHeight);

//

// Window 의 Client 영역 크기를 구한다.

//

CRect rcClient;

GetClientRect( &rcClient );

//

// 메모리 Device 의 비트맵이미지를 현재화면의 크기에 맞춰서

// 화면에 뿌려준다.

//

pDC->StretchBlt( 0, 0, rcClient.Width(), rcClient.Height(),

&dcImage, 0, 0, size.cx, size.cy, SRCCOPY);

//

// LoadBitmap 을 한 Cbitmap 객체를 삭제한다.

//

TestBmp.DeleteObject();

//

// 메모리 Device 인dcImage 를 삭제한다.

//

dcImage.DeleteDC();

//

// 화면 Device 를 Windows 에게 다시 돌려준다.

//

ReleaseDC( pDC );

이로써 간단한 비트맵을 화면에 뿌리는 것에 대한 설명을 맺는다. Line 을 긋는 다거나 Box 를 사용하는 것에 대한 내용은 CDC Class Member 함수를 보면 간단히 설명이 되어있다.


4. 기타 프로그래밍에 도움이 되는 팁 소개

프로그램을 하다 보면 일반 C 언어와는 많은 차이가 나는 것이 바로 윈도우 프로그래밍의 큰 벽이 된다. OS 를 몰라도 만들수 있는 DOS, UNIX 와는 달리 윈도우는 자체의 별난 메시 지 처리방식과 각 순서에 맞는 함수가 달라, 이에 익숙해 지는 것은 많은 경험과 많은 고생 뿐이다. 이에 그간 나름대로 많은 중급이상의 프로그래머들에게 짐이 되었던 윈도우 프로그 래밍에 도움이 되는 작은 팁들을 소개한다. 아무쪼록 보다 나은 프로그램 제작에 도움이 되 길 바라는 마음으로 하나씩 방법을 적는다.

1. 프로그래머가 원하는 메시지를 만들어 이에 대한 처리루틴을 만드는 방 법

ㄱ) Header 파일에 원하는 메시지 정의

#define WM_USER_MSG WM_USER+1

ㄴ) Header 파일에 메시지 처리함수를 정의 한다.

afx_msg LONG ShowUserMsg(UINT, LONG);

ㄷ) Cpp 파일에 메시지 핸들을 구성한다

ON_MESSAGE(WM_USER_MSG, ShowUserMsg)

ㄹ) ShowUserMsg 함수를 직접 Type 해서 처리함수를 만든다.

2. Window생성시 System Menu 에서 Close 메뉴를 없애는 방법

- Frame Window 가 Create 될 때 AfxRegisterWndClass 함수를 사용한다.

BOOL CDbFrame::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CMDIFrameWnd* pParentWnd, CCreateContext* pContext)

{

lpszClassName = AfxRegisterWndClass( CS_NOCLOSE );

((CAICPSApp*)AfxGetApp())->m_pDbFrame = this;

return CMDIChildWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, pContext);

}

3. Windows 생성시 크기와 초기 Style 설정하기

- CREATESTRUCT 로 넘어오는 구조체를 사용하여 초기 Style 을 설정해 준다

BOOL CDbFrame::PreCreateWindow(CREATESTRUCT& cs)

{

//윈도우의 크기는 100,100 에서 클라이언트 영역의 70%, 60% 의 크기를

//가지도록 지정한다

cs.x = cs.y = 100;

cs.cx = ::GetSystemMetrics(SM_CXSCREEN) /100 * 70;

cs.cy = ::GetSystemMetrics(SM_CYSCREEN) /100 * 60;

//

// 최초 생성될 때 아이콘으로 나타난다.

//

cs.style |= WS_MINIMIZE ;

return CMDIChildWnd::PreCreateWindow(cs);

}

4. Window Frame 밑에 필요한 만큼 상태창 만들기

- OnCreate Overriding 함수처리에서 Get/SetPaneInfo 함수를 사용한다

int nIndex;

UINT nID;

UINT nStyle;

int cxWidth;

//

// 0 번은 윈도우에서 상태표시로 사용하고

// 1, 2 번은 넓이가 100 Pixel 크기로 사용자가 만든다.

//

m_wndStatusBar.GetPaneInfo( 1, nID, nStyle, cxWidth );

m_wndStatusBar.SetPaneInfo( 1, nID, nStyle, 100 );

m_wndStatusBar.GetPaneInfo( 2, nID, nStyle, cxWidth );

m_wndStatusBar.SetPaneInfo( 2, nID, nStyle, 100 );

5. 기존의 콘트롤에 추가 메시지 삽입하기

- ComboBox 콘트롤에 Enter 키가 입력되면 다음 콘트롤로 이동하는 기능을 추가한다. 이는 Class 를 상속받는 것에 기초하여 하나의 기능만 추가한다.

ㄱ) ClassWizard 를 이용하여 ComboBox 클래스를 BaseClass 로 하는 새로운 Class 를

만든다

ㄴ) 여기에 PreTranslateMessage 함수를 Overriding한다.

BOOL CDWBtn::PreTranslateMessage(MSG* pMsg)

{

if ( pMsg->message == WM_KEYDOWN )

{

// 메세지를 가로채는 부분에서 현재 메세지 처리중이면

if ( pMsg->wParam == VK_RETURN )

{

GetParent()->SendMessage( WM_RETURN_DOWN, 0, 0 );

return 1;

}

}

return CComboBox::PreTranslateMessage(pMsg);

}

ㄷ) ComboBox 를 사용하는 Class 에서는 CDWBtn.h 파일을 Include 시키고

CComboBox 로 정의된 부분을 CDWBtn 으로 바꾸어주면, CDWBtn 을 상속받은

ComboBox 사용시 Enter 키를 치면 WM_RETURN_DOWN 메시지가 날아온다

ㄹ) 이에 WM_RETURN_DOWN 메시지 수신시 다음 콘트롤로 Focus 를 이동시키려면

처리 함수에 NextDlgCtrl() 함수를 사용하면 된다.

LONG CUserGrp::OnEditEnter( UINT a, LONG b )

{

NextDlgCtrl();

return 0L;

}

이는 기존의 Subclassing 보다 훨씬 간단하고 기본적으로 제공하는 Windows 처리 함수는 그대로 사용하는 이점이 있다.

6. 프로그램 실행중에 새로운 ChildWindow 생성시키기.

ㄱ) CwinApp::InitInstane 함수에서 Template 를 만든다.

pBlankTemplate = new CMultiDocTemplate(

IDR_BLANKTYPE,

RUNTIME_CLASS(CBlankDoc),

RUNTIME_CLASS(CBlankFrame),

RUNTIME_CLASS(CBlankView));

AddDocTemplate(pBlankTemplate);

ㄴ) 원하는 메시지처리함수에서 아래의 코드를 써주면 생성된다.

CBlankFrame* m_pBlankFrame

= (CBlankFrame*)pBlankTemplate->CreateNewFrame(NULL, NULL);

pBlankTemplate->InitialUpdateFrame(m_pBlankFrame, NULL);.

MDI 프로그래밍에서 많은 ChildWindow 가 필요한 경우가 생긴다. 이때 중간 중

간에 생성되야 하는 Child Window 생성은 본 방법을 쓰면 해결이 된다.

7. 한번에 하나의 프로그램만 실행이 가능토록 하고자 할 때

ㄱ) CWinApp::InitInstance 함수에서

//

// 현재 프로그램의 Title 을 적어준다

//

CString lpszProgramTitle = "TestProgram";

if ( NULL != FindWindow( NULL,lpszProgramTitle) )

{ // 이미 실행중.

AfxMessageBox( " 이미 프로그램이 실행중입니다 " );

return FALSE;

}

ㄴ) 본 프로그램이 실행되면서, TestProgram 이라는 Title 을 가진 Window 프로그램 이

현재 실행중이면, 바로 프로그램을 종료한다.




8. 원하는 Windows Title 을 적용시키고자 할 때,

CDocument Class 의 SetTitle 멤버 함수를 사용하면 Frame-View-Doc 구조의 Windows Title 이 바뀐다.

// Document class 의 Constructor 에 SetTitle 함수를 추가하고,

CFormDoc::CFormDoc( CString szTitle )

{

SetTitle( szTitle );

}

//

// Document class 생성시 인자로 타이틀 이름을 정해준다.

//

CFormDoc* pDoc = new CFormDoc( " test change title " );

이는 파일을 Open 하여 원하는 Title 을 지정할 때 아주 유용하다.

9. FormView 에서 Dialog-Form 의 크기에 맞게 Frame 을 조정하려면,

View Class 의 OnInitialUpdate 함수에 다음과 같이 써주면 정확한 크기로 화면에

Window Frame 이 형성된다.

GetParentFrame()->RecalcLayout();

ResizeParentToFit( TRUE );

10. Dialog 를 띄울 때 원하는 Title 을 지정하려면

Windows Message 인 WM_SETTEXT 메시지를 송출하면 된다.

BOOL CDlg::OnInitDialog()

{

CDialog::OnInitDialog();

// 다이얼로그의 타이틀을 셋팅한다.

::SendMessage( this->m_hWnd, WM_SETTEXT,

0, (LPARAM)(LPCTSTR)m_szTitle );

return TRUE;

}

11. MainFrame 윈도우의 Title 을 지정하려면,

Windows 기본 메시지인 WM_SETTEXT 메시지를 가로채어

LPARAM 을 마구어 송출하면 된다.

LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

if ( message == WM_SETTEXT )

return CMDIFrameWnd::WindowProc(message, 0, (LPARAM)(LPSTR)" Test ");

return CMDIFrameWnd::WindowProc(message, wParam, lParam);

}

12. Modaless Dialog 생성 방법

ㄱ) 기본 Modal Dialog 에서 Creat 멤버 함수 를 추가한다.

BOOL CDlg::Create()

{

return CDialog::Create( CFindDlg::IDD, m_pParent );

}

ㄴ) 원하는 함수에서 CDlg 를 생성하고 Create() 함수를 호출하면

Focus 가 다른 윈도우로 이동이 가능하다.

CDlg *pDlg = new CDlg(this);

pDlg->Create();

ㄷ) 그럼 new 연산자를 사용하여 생성한 pDlg 객체는 언제 없어지나??

이에 대한 delete 는 CDlg class 의 PostNcDestroy() 함수를 Overriding 한다.

void CDlg::PostNcDestroy()

{

CDialog::PostNcDestroy();

//

// 여기서 Delete 한다. 즉, 자신이 닫힐 때, Delete 된다.

//

delete this;

}







그리 길지 않은 이야기였으나, Visual-C 에 대한 내용과 Windows 의 속성에 대한 이야기를 간단하게 정리해 본다. 윈도우에서으 작업은 다른 OS 기반에서의 작업과는 근본적으로 다르 며, 또한 Win31, win95, winNT 등 형제가 많아 윈도우 프로그래머들은 또다른 골머리를 썩 이는 뜨거운 감자이기도 하다.

MFC 에 익숙해 지는 방법은 Help 파일을 많이 보는 것과 기존의 예제 프로그램을 자세히 뚫어 보는 것이 중요하다. 보다 깊이 있는 이야기는 다음에 기회가 생기면 하나씩 응용 프 로그램 제작에 필수적인 요소를 찾아 폭넓고 간단하게 구현하여, 실질적인 프로그래머는 그 에 맞는 살만 집어 넣을 수 있도록 더욱 쉽게 이야기를 하고 싶다.