메모장_C
#. 매크로를 활용한 (goto) Error; 처리
//Error handling macro for join failures
#define JOIN_ERROR(x) {eLoginError = (x); goto Error;}
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
static BOOL sPlayerJoinGame(
GAME * game, APPCLIENTID idAppClient, MSG_CCMD_PLAYERNEW * msg)
{
...
LOGINERROR eLoginError = LOGINERROR_UNKNOWN;
JOIN_ERROR(LOGINERROR_UNKNOWN);
...
return TRUE;
Error:
SendError(idAppClient, eLoginError);
return FALSE;
}
#. fatal error C1083: 미리 컴파일된 헤더 파일을 열 수 없습니다.
1. 프로젝트 속성 -> 미리 컴파일된 헤더: 사용(/Yu)
2. 미리 컴파일된 헤더로 지정된 파일이
StdAfx.h라면
StdAfx.cpp 속성 -> 미리 컴파일된 헤더: 만들기(Yc)
* 경로 알아보기
char path
255;
char drive
_MAX_DRIVE, dir
_MAX_DIR, char fname
_MAX_FNAME, ext
_MAX_EXT;
GetModuleFileName(NULL, path, 512);
_splitpath(path, drive, dir, fname, ext);
//----------------------------------------------------------------------------
* 릴리즈 모드에서 출력창에 문자열 표시
char str
256;
wsprintf(str,">> Language set to '%s'\n",
LanguageGetCurrentName() );
OutputDebugString(str);
* 별개의 window에 printf()를 사용해서 출력하는 방법
-.등록; #pragma comment(linker, "/entry:
WinMainCRTStartup /subsystem:console" )
-.사용; 일반적인 printf()를 사용 - printf("
%f %f %f\n",vNodePos.x,vNodePos.y,vNodePos.z);
//----------------------------------------------------------------------------
*
COM+/DCOM 시작을 위한 선행조건 (인터넷에서 퍼온 글)
1.C++을 이용한 객체지향(Class개념) 개념의 이해.
-> 시중에 나와있는 c++에 관련된 입문서를 한번 보시길 바랍니다. 그중overriding/overloading 에 대한 이해까지만 마치면 준비 끝(teach youself C++ 정보문화사 권장)
2.Window 프로그래밍에 대한 이해.
-> Visual C++을 사용한 어플리케이션 개발방법에 대한 기본적인 이해. 많은 걸 할 필요는 없습니다.
1) MFC의 기본적인 라이브러리 사용법
2) SDI 및 다이얼로그 인터페이스
3) 트리컨트롤, 리스트 컨트롤, ODBC TRANSECTION
이상 딱 세가지 할 정도만 필요합니다.
두꺼운 Bible 처음부터 끝까지 하지 마시고 필요부분만 골라서 하시길...
//----------------------------------------------------------------------------
*
Font 설정
char str
100;
SetTextColor(hdc,RGB(255,0,255)); // Font 색상
SetBkColor(hdc,RGB(0,0,255));
SetBkMode(hdc,TRANSPARENT); // 글자 뒷배경 바탕색과 같이 처리
wsprintf(str," Total_Point = %d ",
TotalPoint);
TextOut(hdc,10,10,str,22);
SetTextAlign(hdc,TA_LEFT); 정렬방식
SetTextColor(hdc,RGB(0,0,0)); 문자열 색상 조정
( (
CMainFrame *)(this->
GetParentFrame())Mytab->m_Page3.
CheckNormal(g->m_togDisplayNormal);
( (
CDirect9MFCView *)(((
CMainFrame *)m_pMainWnd)->
GetActiveView()) )->
OpenFileMD2Data();
#.Printf Format Specification
• %l64d : signed 64-bit integer
• %l64u : unsigned 64-bit integer
# 정수형 데이타 타입(값의 범위에 따라 short, int, long으로 분류)
• short - char를 제외한 최소의 정수형이며, 16비트 이상의 환경에서 2바이트(16비트)로 처리
hort형이 가질 수 있는 값의 범위는 -32,768 ~ 32,767이며,
16비트 환경(DOS, win95환경)이나 32비트 환경(win2000환경)에서 값의 범위는 동일.
• int - 시스템의 기본적인 정수형 변수이며,
16비트 환경에서는 2바이트, 32비트 환경에서는 4바이트(32비트)로 처리
int형이 가질 수 있는 값의 범위는 16비트, 32비트에 따라 각각 다릅니다.
16비트 환경에서는 short형과 값의 범위(-32,768 ~ 32,767)가 동일하며
32비트 환경에서는 -2,147,483,648 ~ 2,147,438,647.
• long - 정수형 중 가장 큰 사이즈를 표현할 수 있으며,
16비트 환경과 32비트 환경에서 크기가 동일하게 4바이트(32비트)로 처리
long형의 값의 범위는 -2,147,483,648 ~ 2,147,438,647.
• unsigned short - unsigned 즉, 0과 양수만을 가질 수 있다는 것을 제외하고는 short형과 동일
단, 음수를 가지지 않으므로 부호비트가 필요없기 때문에 표현할 수 있는 값의 범위가 더 넓습니다.
unsigned short형의 값의 범위는 0 ~ 65,535.
• unsigned int - unsigned 즉, 0과 양수만을 가질 수 있다는 것을 제외하고는 int형과 동일
부호비트가 필요없기 때문에, 표현할 수 있는 값의 범위는 0 ~ 4,294,967,295.
• unsigned long - unsigned 즉, 0과 양수만을 가질 수 있다는 것을 제외하고는 long형과 동일.
부호비트가 필요없기 때문에, 표현할 수 있는 값의 범위는 0 ~ 4,294,967,295.
Visual C++ 기준 표기법 | 윈도우에 정의된 데이터형 |
Name | Description | Name | Description |
CS | 클래스 스타일 옵션 | BOOL | 논리형 TRUE 또는 FALSE 값만 가질 수 있다 |
CW | 윈도우 생성 옵션 | BYTE | unsigned char (8 bit) |
DT | 문자열 그리기 옵션 | DWORD | unsigned long (32 bit) |
IDI | 아이콘에 대한 ID | DWORDLONG | unsigned double (64 bit) |
IDC | 커서에 대한 ID 숫자 | FLOAT | float |
MB | 메시지 박스 | LONG | signed long (32 bit) |
SND | 사운드 옵션 | LONGLONG | signed double (64 bit) |
WM | 윈도우 메시지 | LPARAM | 32 bit 메세지 파라미터 |
WS | 윈도우 스타일 | LPCSTR | 널 문자로 끝나는 윈도우 문자열 상수의 포인터 |
MSG | 메시지 구조체 | LPCTSTR | 널 문자로 끝나는 유니코드 또는 윈도우 문자열 상수의 포인터 |
WNDCLASS | 윈도우 클래스 구조체 | LPSTR | 널 문자로 끝나는 윈도우 문자열의 포인터 |
PAINTSTRUCT | Paint 구조체 | LPTSTR | 널 문자로 끝나는 유니코드 또는 윈도우 문자열의 포인터 |
RECT | 사각형 구조체 | TCHAR | 유니코드 또는 윈도우 문자 |
HINSTANCE | 프로그램 인스턴스에 대한 핸들 | UINT | unsigned int (32 bit) |
HWND | 윈도우에 대한 핸들 | WORD | unsigned short (16 bit) |
HDC | DeviceContext(장치 컨텍스트)에 대한 핸들 | WPARAM | 16 bit 메세지 파라미터 |
* 32비트 운영체제에서 작동되는 컴퓨터들은 모두 주소값의 크기가 32 비트 (즉, 4 바이트)로 나타냄.
즉 주소값이 0x00000000 ~ 0xFFFFFFFF 까지의 값을 가진다.
주소값의 가지수는 2 의 32 승 바이트, 즉 RAM 은 최대 4 GB 까지 밖에 사용할 수 없다. 이 때문에 32 비트 운영체제에서는 RAM의 최대 크기가 4 GB 로 제한된다.
Bit연산 #
연산자 | 연산 | 설명 |
& | AND | 둘 다 1이면 1 |
| | OR | 하나만 1이면 1 |
^ | XOR | 다르면 1 |
~ | NOT | 1이면 0, 0이면 1 |
<< | LEFT SHIFT | 왼쪽으로 비트 이동 |
>> | RIGHT SHIFT | 오른쪽으로 비트 이동 |
#. & (AND) 연산자 ; 원하는 비트만 변경
-. 공통부분 구한기
약간 제한된 경우지만 에라토스테네스의 체를 이용하여 소수를 구할 때 처럼 배열에 1과 0만 넣어, 참, 거짓을 구별할 때 if문을 사용하는 것 보다 빠르게 작동
void samesame(int *a, *b, int *c) // a, b, c 후보숫자 배열의 교집합부분을 구함
{ // cf.이 함수는 C언어 스도쿠 자동풀이 프로그램에 사용
int i;
for( i=1; i<=9; i++ )
a[i]=a[i]&b[i]&c[i]; // 셋 다 1이면 1저장, 하나라도 0이면 0저장
}
#. | (OR) 연산자 ; 원하는 비트를 1로 바꾸고 싶을 때, & 연산처럼 사용
#. ^ (XOR) 연산자 ; 값 교환 (동작 느림)
void swap(int *a, *b)
{
*a^=*b;
*b^=*a;
*a^=*b; // 이 세번의 동작으로 a와 b의 값을 교환
}
cf.0으로 초기화 할 때, a^=a를 하면 a가 0으로 초기화
#. ~ (NOT) 연산자 : 정수형 변수의 최댓값을 구할 때
#include <stio.h>
int main()
{
int a=1;
char b=1;
__int64 c=1;
a<<=31;
b<<=7;
printf("%d %d, ~a, ~b);
// 가장 오른쪽의 비트를 가장 왼쪽으로 이동시키고 ~연산으로 가장 왼쪽비트만 빼고 전부 1로 만들어 최대값을 출력
// (부호있는 변수는 가장 왼쪽의 비트는 음수를 나타낼 때 사용)
}
해당 비트만 접근/변경 하는 방법 #
데이터의 한 비트만 조작하는 방법으로 AND(&) 또는 OR(|) 연산자를 이용한다. 예를 들면,
unsigned char a = 0; 로 선언/정의되었을 때

'1'로 세트하는 방법 : a |= 0x08; // 비트 3번 세트

'0'으로 클리어 하는 방법 : a &= ~0x08; // 비트 3번 클리어
Shift 연산자 (빠름) #
1. 왼쪽shift (<<)
왼쪽으로 1칸 이동시마다 *2씩 커진다(빈공간은 0으로 채움)
2. 오른쪽shift (>>)
오른쪽으로 1칸 이동시마다 /2씩 작아진다.
┌ JAVA에서는 signed right shift operator라 한다.
└ 빈공간은 부호비트으로 채운다(양수와 음수의 처리가 다름)
3. 오른쪽shift (>>>)(양수화 시킴)
┌ JAVA에서는 unsigned right shift operator라 한다.
└ 빈공간은 0으로 채운다.
a << 1; 2 배
a << 2; 4 배
a << 3; 8 배
a << 4; 16 배
a << 4 - a; 15 배
a << 4 + a; 17 배
a << 5; 32 배
a << 6; 64 배
a << 6 - a << 2; 60배
#.Shift 연산
// cf. enum...
Inline DWORD SETBIT(DWORD & dwBitSet, int bit)
{
Return dwBitSet |= (1<<bit);
}
연산자 오버로딩(operator overloading) 활용예 #
class cGroup
{
public:
// Group Types
const static enum EGROUP_TYPES
{
KGROUP_TYPE_1,
KGROUP_TYPE_2,
KGROUP_TYPE_3,
KGROUP_TYPE_4,
KGROUP_TYPE_COUNT
};
int m_GroupTypeValues[ KGROUP_TYPE_COUNT ];
public:
cGroup( void );
~cGroup();
void operator/= ( int nValue );
void operator*= ( int nValue );
void operator/= ( float fValue );
void operator*= ( float fValue );
void operator+= ( cGroup group );
void operator-= ( cGroup group );
BOOL operator== ( cGroup group );
BOOL operator!= ( cGroup group );
BOOL operator>= ( cGroup group );
BOOL operator<= ( cGroup group );
BOOL operator> ( cGroup group );
BOOL operator< ( cGroup group );
cGroup operator+ ( cGroup group );
cGroup operator- ( cGroup group );
};
void cGroup::operator/= (int nValue)
{
m_GroupTypeValues[ KGROUP_TYPE_1 ] /= nValue;
m_GroupTypeValues[ KGROUP_TYPE_2 ] /= nValue;
m_GroupTypeValues[ KGROUP_TYPE_3 ] /= nValue;
m_GroupTypeValues[ KGROUP_TYPE_4 ] /= nValue;
}
HIWORD(I)의 매크로함수 정의 #
((WORD)(((DWORD)(l) >> 16) & 0xFFFF))
HIWORD(I)의 매크로함수 정의이군요.
32비트 정수의 상위 16비트를 구하는 데 쓸 수 있습니다.
1. (I) : 매크로 정의이므로 I를 괄호로 싸서 매크로가 전개될 때 오류가 되는 것을 방지함
2. (DWORD)(I) : I 자리에 치환되는 수를, 타입에 상관없이 무조건 부호없는 32비트 정수로 변환
3. (DWORD)(I) >> 16 : 오른쪽으로 16비트 쉬프트. 하위 16비트를 버리고 상위 16비트를 하위로 자리 옮김
4. ((DWORD)(I) >> 16) & 0xFFFF : 사실 필요없는 것이지만 상위 16비트 부분을 모두 0으로 채움
5. (WORD)(((DWORD)(I) >> 16) & 0xFFFF) : 부호없는 16비트 정수형으로 변환
6. ((WORD)(((DWORD)(I) >> 16) & 0xFFFF)) : 매크로 함수이므로 좌우에 괄호를 쌈
* char* 형을 STL string 형으로 바꾸는 방법
#include <iostream>
#include <string>
using namespace std;
void main(){
char *s = "123";
string s1 = (string)s;
string s2 = string(s);
cout << s1 << endl;
cout << s2 << endl;
}
* 기존의 C API에 string을 넘기는 방법
void doSomething(const char * pString){...};
string s;
doSomething( s.c_str() );
#.ANSI 코드에서 UTF-8로 가기 위해서는 다음과 같은 변환과정을 거쳐야 합니다.
ANSI(
MutiByte ) -> UCS-2(
WideByte ) -> UTF-8(
MultiByte )
void foo( const char *in, char *out, int nOut )
{
USES_CONVERSION;
wchar_t *wc = A2W( in ); // ANSI to UCS-2
WideCharToMultiByte( CP_UTF8, 0, wc, -1, out, nOut, 0, 0 ); // UCS-2 to UTF-8
}
윈도우에서는
MultiByteToWideChar()를 제공합니다.
유사 포맷으로는 mbstowcs()가 있지만 이 함수는 UTF-8은 제공하지 않습니다.
참고로
A2W나 USES_CONVERSION은 ATL 관련 함수입니다.
반대로 변환할 떄는
MultiByteToWideChar()와
W2A()를 이용하시면 됩니다.
위에도 적어놨지만 ANSI나 UTF-8이나 다
MultiByte입니다.
A2W()는 내부적으로
MultiByteToWideChar()를,
W2A()는 내부적으로
WideCharToMultiByte()를 호출합니다.
* char --> wchar로 변환
char szSource[] = "Text to convert";
wchar szDest[100];
MultiByteToWideChar( GetACP(), 0, szSource, -1, szDest, sizeof(szDest)/sizeof(szDest[0]) );
* #include <atlbase.h>
T2A <-- tchar를 멀티바이트 스트링으로..
T2W <-- tchar를 유니코드 스트링으로...
W2A <-- 유니코드를 멀티바이트로
A2W <-- 멀티바이트를 유니코드로
W2T
A2T
* 문자열 비교
if (strcmp(szJin,"jin")==0) {
// 하고싶은 일
}
대소문자 구분없이 비교하려면 stricmp 함수를, 유니코드를 사용하시려면 lstrcmp 함수를 쓴다.
#pragma comment(lib, "imm32.lib")
#include <imm.h>
// 한영 전환
void CChattingDlg:
:SetIMEMode( HWND hWnd, BOOL bHanMode )
{
HIMC hIMC =
ImmGetContext( hWnd );
DWORD dwConv, dwSent;
DWORD dwTemp;
ImmGetConversionStatus(hIMC,&dwConv,&dwSent);
dwTemp = dwConv & ~IME_CMODE_LANGUAGE;
// 상태를 바꿉니다.
if( bHanMode )
dwTemp |= IME_CMODE_NATIVE; // 한글
else
dwTemp |= IME_CMODE_ALPHANUMERIC; // 영문
dwConv = dwTemp;
ImmSetConversionStatus(hIMC,dwConv,dwSent);
ImmReleaseContext(hWnd,hIMC);
}
◈ 정수화 함수
• 정수화 함수는 실수형 데이터에서 정수부만을 취하는, 즉 소수점 이하의 소수부를 잘라 버리는 함수이다. 소수부를 잘라 버린다고 해서 계산 결과가 정수가 되는 것은 아니며 리턴값은 여전히 실수이다. 실수값의 소수부만을 0으로 만든다고 생각하면 된다. 정수화 함수에는 다음 두 가지가 있다
▷ double floor( double x ); // 주어진 인수보다 크지 않은 최대 정수
소수점 이하를 자르는 방식으로 소수점 이하를 버리고 정수부만을 취함
floor(3.14); // 결과는 3.0
floor(-3.14); // 결과는 -4.0
▷ double ceil( double x ); // 주어진 인수보다 작지 않은 최소 정수
소수점 이하를 올림해서 정수부를 1증가
ceil(3.14); // 결과는 4.0
ceil(-3.14) // 결과는 -3.0
• C 언어는 반올림 함수를 따로 제공하지 않고, 기존 함수를 응용한다.
1.실수 x를 반올림한 값 = floor(x+0.5); // 0.5 이상의 값을 반올림
cf.ceil(x-0.5); // 0.5 초과 값을 반올림
2.소수점 둘 째자리에서 반올림 = floor(x*10+0.5)/10;
3.소수점 n번째 자리에서 반올림되는 함수를 만들 수 있는데 아주 간단하므로 매크로 함수로 정의
#define banollim(x,dig) (floor((x)*pow(10,dig)+0.5)/pow(10,dig))
4.double round(double x); // 반올림 함수, 음수일 경우 반내림을 한다.
#include <math.h>
double round( double x)
{
return ( (x>0> ? floor(x+0.5) : ceil(x-0.5);
}
◈ 호출관행
_cdecl - 마지막 인자부터 스택에 저장하며, 함수를 호출한 쪽에서 스택을 정리한다.
_stdcall - 마지막 인자부터 스택에 저장하며, 호출된 함수가 스택을 정리한다.
_fastcall - 레지스터에 인자 두 개를 넣고, 마지막 인자부터 스택에 저장하며 호출된 함수가 스택을 정리한다.
_stdcall 은 보통 API들이 사용하고 있다. 그 이유는 스택을 정리하는 코드가 API 쪽에 있게 되면 그 만큼 메모리를 절약할 수 있기 때문이다. API는 여러 다른 클라이언트에 의해서 호출된다는 가정 하에 기동한다.
◈ void *realloc( void *memblock, size_t size );
조정 대상이 되는 메모리 블럭을 size만큼 다시 할당한다.(이때, 메모리량을 바이트 단위로 표현)
메모리가 부족하여 할당하지 못했을 경우 NULL을 리턴하며, 메모리는 제한적이기 때문에 동적 할당시 항상 에러를 검사 해야한다.
◈ 시스템 해상도 구하기
HDC hDC = ::
GetDC(NULL);
// HDC hDC =
CreateDC("DISPLAY", NULL, NULL, NULL);
int width =
GetDeviceCaps(hDC, HORZRES);
int height =
GetDeviceCaps(hDC, VERTRES);
-.
GetDeviceCaps()함수는 Get device capabilities로써 DC와 연관된 device의 성능을 구하고자 할 때 사용
인자로 옵션을 주면 그 옵션에 해당하는 값이 나오는데, (인자 종류 많음 ^^)
HORZRES, VERTRES를 인자로 넣으면 디스플레이의 폭과 높이가 픽셀 단위로 나온다.
◈ 전처리 응용
void
ConsoleCommandTableInit( void )
{
#define CONSOLE_DESC( desc ) sCommandTableAddCommandDescription(L##desc)
#define CONSOLE_CMD( cmd, cheat, appType, gameType, func, valParam )\
STATIC_CHECK( (cheat) != NOT_CHEATS ||
(gameType) != COMMAND_SERVER, SERVER_SIDE_COMMANDS_NOT_ALLOWED_IN_RELEASE );\
sCommandTableRegisterCommand( NULL, L##cmd, cheat, appType, gameType, func, valParam)
...
ConsoleCommandTableFree();
...
콘솔 명령 정의(함수 호출)
#undef CONSOLE_CMD
#undef CONSOLE_DESC
...
ConsoleAddEmotes(); // 이모션 관련 콘솔명령 추가
}
*
WM_MOUSEWHEEL 메시지가 Undeclared identifier로 나올 때
1.
VC6의 include 폴더에 있는 WINUSER.H 또는 zmouse.h 사용
#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
#define WM_MOUSEWHEEL 0x020A
#define WM_MOUSELAST 0x020A
#else
#define WM_MOUSELAST 0x0209
#endif /* if (_WIN32_WINNT < 0x0400) */
2.#define WM_MOUSEWHEEL 0x020A 추가
*
error LNK2001: unresolved external symbol _main 이 에러가 발생하는 두가지 원인
1.Visual C++에서 프로젝트 셋팅의 문제
프로젝트를 만들 때 Win32 Application :
WinMain 함수에서 시작하게 되는 GUI 프로그램
Win32 Console Application : main 함수에서 시작하는 console 프로그램으로 크게 구분이 된다.
위의 에러는 대부분 GUI 프로그램을 만드려고
WinMain로 시작했는데
프로젝트를 만들 때 console application을 선택한 경우로, 반대로
WinMain이 없다고 에러가 나오면 GUI 프로젝트를 선택하고서 main 함수를 작성한 경우이다.
해결은 설정을 바꿔주면 되는데, Link탭에서 /subsystem:console을 /subsystem:windows로 바꿔준다(또는 상황에 따라서 그 반대로 설정)
이 옵션의 역할은 링커에게 있어서 이 프로그램이 어떤 함수로부터 시작해야 하는지를 알려주는 역할을 한다. console이라면 main을, windows라면
WinMain을 찾아서 그 함수에서부터 프로그램이 시작한다.
(cf. Jeffrey Ritcher저 Programming Applications for Windows 4th.)
2. ATL에서 컨트롤을 만들 때 발생
디버그에서는 잘 컴파일이 되던게 릴리즈로 빌드하면 저 에러가 날 수 있다. ATL에서는 생성되는 코드의 크기를 최소화 하기 위해서 CRT (C
RunTime) 함수의 일부를 제한하고 있기 때문이다(정확히 말하자면 start up 코드를 넣지 않는다). 그런데 프로젝트에서 특정 C 함수를 사용하면 ATL에서 제한한 CRT의 기능이 필요해지고 따라서 에러가 발생한다.
해결은 a. 프로젝트 세팅에서 predefined keyword를 찾아서 _ATL_MIN_CRT를 지우고,
b. 초기화가 필요한 CRT 함수를 사용하지 않는다(MSDN에 따르면 strcmp대신 lstrcmp를 사용하는 식으로 피해갈 수 있다고 함)
(cf.MSDN에서 _ATL_MIN_CRT로 색인에서 검색)
1.
GetAsyncKeyState() ; 함수가 호출된 시점의 키 상태를 조사해서 리턴
(
GetAsyncKeyState( VK_LEFT )) ? (St_run=TRUE, St_rundir=2, Move_x -= 4 ) : NULL;
(
GetAsyncKeyState( VK_RIGHT ))? (St_run=TRUE, St_rundir=3, Move_x += 4 ) : NULL;
cf.
GetKeyState() ; 마지막으로 키메시지 발생 후의 상태 변화를 리턴합니다.
따라서
GetKeyState함수는 키보드 메시지 처리 루틴 내에서만 유효한 값을 리턴한다고 생각하면 됩니다.
2.
GetKeyboardState() - 한번에 256개의 키상태를 다 받아오는 함수구요
unsigned char keys[256];
GetKeyboardState( keys );
if( keys[VK_UP] & 0x80 ) St_run=TRUE;
3.
WndProc()에서 처리
BYTE m_bKey[256]; // 전역으로 설정 한다면
memset (m_bKey, 0, sizeof (m_bKey)); // 키보드 제어를 위한 초기치
...
LRESULT CALLBACK
WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch( message )
{
case WM_KEYUP: //mark a key up
m_bKey /wParam/ = FALSE;
break;
case WM_KEYDOWN: //mark a key down
m_bKey /wParam/ = TRUE;
break;
}
return
DefWindowProc( hWnd, message, wParam, lParam );
}
...
if(m_bKey
'R' )
ResetPhysics(); // 처리하고 싶은 곳에서 처리
4.Shift키와 조합 여부 확인
if(
GetKeyState(VK_SHIFT) & 0x8000) // Shift +
if(
GetKeyState(VK_SHIFT) < 0) // Shift키가 눌려지면 음수값을 리턴
BOOL bControlKeyDown1 =
GetAsyncKeyState (VK_SHIFT) >> ((sizeof(SHORT) * 8) - 1);
if( bControlKeyDown1 ) return TRUE;
case WM_SYSKEYDOWN: 은 Alt 키가 눌려졌을 때 이벤트 발생
후킹을 키로거 와 같은 악성코드/스파이웨어로 악용 되고 있기 때문에, MS에서 윈도우 포커스가 잃게 되도 마우스/키보드 이벤트를 얻어올수 있는 방법을 만들었다.
RegisterRawInputDevices함수로 시스템에 키보드, 마우스 이벤트를 등록,
시스템은 해당 윈도우에 WM_INPUT메세지를 던져 사용자로 부터 Input 이벤트가 들어왔는지를 알 수 있다.
RAWINPUTDEVICE rawInputDev[2];
ZeroMemory(rawInputDev, sizeof(RAWINPUTDEVICE)*2);
// 키보드 RAWINPUTDEVICE 구조체 설정
rawInputDev[0].usUsagePage = 0x01;
rawInputDev[0].usUsage = 0x06;
rawInputDev[0].dwFlags = RIDEV_INPUTSINK;
rawInputDev[0].hwndTarget = GetSafeHwnd();
//마우스 RAWINPUTDEVICE 구조체 설정
rawInputDev[1].usUsagePage = 0x01;
rawInputDev[1].usUsage = 0x02;
rawInputDev[1].dwFlags = RIDEV_INPUTSINK;
rawInputDev[1].hwndTarget = GetSafeHwnd();
if(FALSE == RegisterRawInputDevices(rawInputDev, 2, sizeof(RAWINPUTDEVICE)))
{
CString str;
str.Format(_T("RegisterRawInputDevices Error %d"), GetLastError());
MessageBox(str);
}
LRESULT CRegisterRawInputDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
if(message == WM_INPUT)
{
//여기서 마우스/키보드 입력 메시지를 확인할 수 있습니다.
// GetRawInputDevices 함수를 이용하여 입력된 정보를 알 수 있습니다.
}
return CDialog::WindowProc(message, wParam, lParam);
}
#if defined( _DEBUG ) || defined( DEBUG )
sprintf(str,"x: %f, y: %f, z: %f",vPos.x,vPos.y,vPos.z);
MessageBox(NULL, str ,"Pick OK",MB_OK);
#endif
▷ VOID
OutputDebugString( LPCTSTR lpOutputString ); // string to be displayed
WIn32 API 중에 MFC의 TRACE와 같은 기능을 하는 Debug 함수로써,
이함수를 사용하시면 Debug창에 메지시를 보낼 수 있고
그리고
DebugView라는 유틸리티에서도 확인할 수 있다.(Sysinternal제품)
if (InitScene())
{
OutputDebugString(" Scene initialisation failed\n");
exit(5);
}
▷ cf.
DXUTmisc.h
//--------------------------------------------------------------------------------------
// Debug printing support
// See dxerr.h for more debug printing support
//--------------------------------------------------------------------------------------
void
DXUTOutputDebugStringW( LPCWSTR strMsg, ... );
void
DXUTOutputDebugStringA( LPCSTR strMsg, ... );
▷ trace 범위 설정
//--------------------------------------------------------------------------------------
// debug constants & macros
//--------------------------------------------------------------------------------------
#define TRACE_LEVEL 2 // 0 = no tracing, 1 = errors, 2 = major ops, 3 = minor operations
#if DEBUG && ISVERSION(DEVELOPMENT) && !ISVERSION(SERVER_VERSION)
# if( TRACE_LEVEL >= 1 )
# define
TRACE1(fmt, ...) Trace(fmt, __VA_ARGS__)
# endif
# if( TRACE_LEVEL >= 2 )
# define
TRACE2(fmt, ...) Trace(fmt, __VA_ARGS__)
# endif
# if( TRACE_LEVEL >= 3 )
# define
TRACE3(fmt, ...) Trace(fmt, __VA_ARGS__)
# endif
#endif
#ifndef
TRACE1
#define
TRACE1(fmt, ...)
#endif
#ifndef
TRACE2
#define
TRACE2(fmt, ...)
#endif
#ifndef
TRACE3
#define
TRACE3(fmt, ...)
#endif
클래스 상속시 부모 클래스 함수와 자식 클래스 함수의 사이의 관계에서 부모 클래스 함수가 가상함수인지, 일반 함수 인지에 따라
자식 클래스에서 함수를 호출 했을 때 동작 방식이 달라진다.
1.정적연결;
항상 부모의 포인터로 함수를 호출하며,
만일 자식 클래스에 존재하는 함수를 호출하기 위해서는 (자식클래서*) 캐스팅 한다.
2.동적연결; virtual 사용
가상테이블이 생성되어 부모가 아닌 자식의 포인터가 얻어진다.
만일 자식이 아닌 부모 함수를 호출하기 위해서는 p2->부모클래스::함수(); 식으로 사용한다.
class person {
(virtual) void print(); // ---> 함수의 연결 방식 결정
};
class student : public person {
void print();
};
int main( ) {
person* p1 = new person();
p1->print();
person* p2 = new student();
((student*)p2)->print(); // ---> 정적연결에서 자식 클래스의 함수 호출
p2->person::print(); // ---> 동적연결에서 부모 클래스의 함수 호출
return 0;
}
// 방식 1 ---------------------------------------------
class
TestClass
{
public:
void
TestFunc(int n)
{
}
void
RunFunction( void (TestClass:
:*Func)(int) )
{
(this->*Func)(6); //클래스 멤버 함수의 포인터는 항상 그 클래스의 함수 라는것을 지정 해야 한다.
}
};
// 방식 2 ---------------------------------------------
class Test
{
public:
static void (*p)(int);
public:
static void Print( int a )
{
}
};
// void (Test:
:*p)(int) = &(Test:
:Print); // 에러
void (*Test::p)(int) = Test:
:Print; // 실행
int main(int argc, char* argv[])
{
printf("Hello World!\n");
TestClass ccc;
ccc.
RunFunction(&TestClass:
:TestFunc);
Test a;
a.p(3);
Test::p(4);
return 0;
}
-1.정확히 (Test:
:*p)와 (*Test::p)는 차이가 있다.
VC++에서
(Test:
:*p)하면 컴파일러는 이것을 __thiscall로 인식하지만
(*Test::p)하면 __cdecl로 인식해서 오류가 나지 않습니다.
정적 멤버함수는 기본적으로 __cdecl이기 때문에 발생한 오류는 conversion context가 없다는 걸로 나온다.
그리고 어차피 정적 멤버 함수 포인터이므로
a.p(3)를 하나 Test::p(3)를 하나 어차피 class-scope이기 때문에 내부 동작은 똑 같다.
-2.멤버 함수의 포인터를 취하기 위해선 해당 멤버 함수를 static 키워드를 사용해서 선언한다.
클래스는 런타임으로 생성되기 때문에, 일반 멤버 함수의 포인터는 코드가 실행되기 위해 스택에 복사된 순간 유효한 포인터가 아니다.
두 클래스가 아주 밀접한 관련이 있고 서로 숨겨진 멤버를 자유롭게 읽어야 하는 상황이라면
클래스를 통째로 프렌드로 지정할 수 있다. 클래스 선언문내에 프렌드로 지정하고 싶은 클래스의 이름을 밝히면 된다. 다음 예는 Any 클래스를 Some 클래스의 프렌드로 지정하는 것이다.
class Some {
friend class Any;
....
};
Any가 Some의 프렌드로 지정되었으므로
Any의 모든 멤버 함수들은 Some의 모든 멤버를 마음대로 액세스할 수 있다. 두 클래스가 협조적으로 동작해야 한다거나 상호 종속적인 관계에 있을 때 프렌드로 지정하면 편리하다.
예)
#include <Turboc.h>
class Time {
friend class Date;
private:
int hour,min,sec;
public:
Time(int h,int m,int s) { hour=h;min=m;sec=s; }
};
class Date {
private:
int year,month,day;
public:
Date(int y,int m,int d) { year=y;month=m;day=d; }
void
OutToday(Time &t) {
printf("오늘은 %d년 %d월 %d일이며 지금 시간은 %d:%d:%d입니다.\n",
year,month,day,t.hour,t.min,t.sec);
}
};
void main()
{
Date D(2005,01,02);
Time T(12,34,56);
D.
OutToday(T);
}
OutToday 함수는 Date 클래스의 멤버 함수로 선언되었지만 Date가 Time의 프렌드 클래스로 지정되어 있으므로
OutToday는 Time 객체의 모든 멤버를 읽을 수 있다. 실행 결과는 앞의 예제와 동일하다. 개념적으로 이해하기 쉬운 간단한 예제를 보였는데 실제로 프렌드 클래스 지정이 꼭 필요한 예는 다소 크고 복잡하다. MFC 라이브러리의 경우 다음과 같은 프렌드 클래스의 예가 많이 존재한다.
▶
CDocument가
CView의 프렌드
▶
CTime이
CTimeSpan의 프렌드
▶
CToolBar가
CToolTipCtrl의 프렌드
▶
CPropertySheet가
CPropertyPage의 프렌드
모두 아주 밀접한 관계에 있는 클래스들인데 MFC의 구조를 공부해 보면
이 클래스들이 왜 프렌드여야 하는지 알게 될 것이다.
CView와
CDocument는 하나의 실체에 대해 각각 외부와 내부를 다루는 관련있는 클래스이다.
CView는 자신이 화면에 출력할 데이터를 읽기 위해
CDocument의 멤버를 마음대로 읽을 수 있어야 하며
CToolTipCtrl 클래스는 툴팁을 보여줄 버튼이나 영역을 구하기 위해
CToolBar의 멤버를 액세스해야 한다.
수업 내용 #
20030422
-.로직상에 직접적인 상수의 대입은 지향
-.break, continue 의 사용상 차이점
20030423 - 배열
-.char a = getch();
-.Type(형)에 대한 정의
-.배열은 0부터 시작
char a
10; 문자 배열은 상수로 취급되기 때문에 1:1로 대응함
--> 문자열을 비교할 때는 각각의 요소를 전부 확인
printf("%s", a); 문자열 출력
-.포인터; 메모리에 대한 기본적인 이해 필요(4바이트)
p=malloc(sizeof(char)*10); /* 동적으로 메모리를 할당 */
memset(p,'&',sizeof(char)*10);
p[0]='a';
p[1]='b';
p[8]='q';
p[9]='\0';
printf("%s = %d \n",p,sizeof(p));
free(p);
20030424 - 포인터
-.포인터는 번지를 가르치는 숫자개념이기 때문에 연산이 가능
cf. 인수를 가르칠 때는 반복자 개념이 있다.
int *a;
int b=10;
a= &b; &은 주소 연산자
int *a;
20030425 - 함수
-.각 기능을 함수화시켜서 분산구조로 정의 --> 필요할 때 기능별로 호출
-.함수가 끝나면 함수의 가인수(지역변수)는 사라지고,
전역변수는 프로그램 전체에 영향을 미치기 때문에 가능한 최소한만 사용
-.scanf()로 문자를 입력 받을 때는 문자가 차지할 영역의 메모리를 미리 잡는다.
(배열이나 malloc 등)
20030428 - 구조체; 구조체 내에 다른 구조체 선언도 가능
-.데이타 처리를 위해 DB의 필드와 레코드 개념을 사용
-.구조체 선언 부분 내에서 바로 초기화할 수 없다
-.struct의 pointer내의 구성인자(필드)를 지정하기 위해선 (->)필드명 형식
-.한 개의 파일을 사용할 경우,
열고 (파일 입력 또는 저장) 닫고 > 열고 (파일 입력 또는 저장) 닫고 ...
20030429
-.head 파일로 사용자 정의 부분 따로 저장
-.#define으로 정의된 상수이름은 일반적으로 모두 대문자로 사용