core 파일이 생성되는 한가지 상황

it/c 2011/10/31 20:41 Posted by newtype
오늘 하루종일 삽질한 내용이 있어서 기록을 남긴다.

[ 상황 ]
패킷 내용이 추가되어서 기존 모듈을 수정하게 되었다.
컴파일을 잘되는데, 실행하면 계속 core 파일이 생성되면서 프로세스가 죽는다.
수정 한 부분을 전체 주석으로 막아도, Call Stack을 체크 해보면 특정 함수를 진입하는 시점에 죽어 버린다.

[ 원인 ]
원인은 패킷 사이즈가 커지면서, Stack 영역에 할당 할 수 있는 최대 메모리 사이즈를 초과한 것이다.

[ 해결 방안 ]
Stack 영역이 아닌, Heap영역에 메모리를 할당 하면 된다.

원인 밝히느라 오전 내내 삽질, 
정적 메모리 구조를 동적 구조로 바꾸느라고, 오후 내내 디버깅 및 삽질..
오늘 하루도 이렇게 날려 먹었다.

[ Code ]
-. Stack 영역에 할당 할 수 있는 최대 크기 확인 ( 단위 KB )

$ ulimit -s
 8192



-. 테스트 프로그램.

// cc -o maxsize maxsize.c
#include <stdio.h>

typedef char PACKET[5600000];

int main()
{
        PACKET pkt;
        //PACKET pkt2;  
        PACKET *pkt3;

        printf ("\n");
        printf ( "%s: %d\n", "PACKET", sizeof(PACKET) );

        memset ( &pkt, 0, sizeof(pkt) );
        //memset ( &pkt2, 0, sizeof(pkt2) );   // rise core dump  

        pkt3 = (PACKET*)malloc(sizeof(PACKET));
        if ( !pkt3 ) printf("not enough momery\n");
        else memset( pkt3, 0, sizeof(PACKET) );

        return 0;
}

실행 해 보면,  아래와 같은 결과를 얻는다.

$ maxsize
PACKET: 5600000



 하지만, 9, 16 라인의 주석을 풀고 실행해보면, 아래와 같이 core 파일이 생성된다.

$ maxsize
세그멘테이션 결함(Segmentation Fault)(코어 덤프)


[ 참고문서 ]
http://www.joinc.co.kr/modules/moniwiki/wiki.php/article/stack_size 


 
TAG core, heap, stack

Pro*C/C++ Precompiler Programmer's Guide

it/c 2009/03/13 15:03 Posted by newtype

[ 원문 링크 ]

http://download.oracle.com/docs/cd/A58617_01/server.804/a58233/toc.htm

[ 다운로드 링크 ]


TAG oracle, Pro*C

한글이 포함된 문자열의 경우에 한글 중간에 끊기지 않도록 left 함수를 구현 했다.
유니코드 한글을 구분하기 위해서는 메크로 함수를 수정해 주어야 한다.

#include <stdio.h>
    
#define IsHangle(c)  ((unsigned char)(c) > 0x7f)

char* left(char* sz, int len)
{
    int i = 0;
    
    if ( strlen(sz) <= len ) return sz;
    
    for (i=0; i<len; i++)
    {
        if ( IsHangle(sz[i]) )    
        {
            if ( len-1 < i+1 ) break;
            else  i++;
        }
    }
    
    sz[i] = 0;
    
    return sz;
};

int main()
{
    char *szOrg="11가핳a안녕하세요abcdef우하하1";
    char buf[256]="";
    int i;

    printf("Org: %s(%d)\n", szOrg, strlen(szOrg) );
    for(i=0; i<=strlen(szOrg); i++)
    {
        strcpy( buf, szOrg );
        printf( "%02d, %s\n", i, left(buf, i) );
    }
    return 0;
}

DisplayHex

it/c 2008/11/13 08:59 Posted by newtype

패킷 또는 버퍼를 파라미터로 받아 16진수 형태로 출력을 예쁘게 해준다.

* 사용예
DisplayHex( "Send Packet", szBuf, sizeof(szBuf) );

* 결과
[ Send Packet ] =========
-----------------------------------------------------------------------
ADDR  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
-----------------------------------------------------------------------
0000  54 65 73 74 20 50 61 63 6B 65 74 20 53 61 6D 70  Test Packet Samp 
0010  6C 65 20 44 61 74 61 2E 2E 2E 2E 2E 20 5E 5E 00  le Data..... ^^. 
0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
-----------------------------------------------------------------------

* 소스
void DisplayHex( char *title, char *pPacket, int size )
{
    long    i                   = 0;
    long    j                   = 0;
    long    lTotal              = 0;
    char    szBinBuff[64]       = {0,};
    char    szASCIIBuff[18]     = {0,};
    u_char  byBuff              = 0;
    char    cpWrk[256]          = {0};
	char    szBuf[2048]			= {0,};

    printf("[ %s ] =========\n", title );
    printf("-----------------------------------------------------------------------\n");
    printf("ADDR  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF\n");
    printf("-----------------------------------------------------------------------\n");

    for (i = 0; i * 0x10 < size; i++) 
	{
        memset(szBinBuff  , 0, sizeof(szBinBuff  ));
        memset(szASCIIBuff, 0, sizeof(szASCIIBuff));
        for (j = 0; j < 0x10; j++) 
		{
            byBuff = (u_char)*(((u_char*)pPacket) + i * 0x10 + j);
            sprintf(szBinBuff   + j * 3, "%02X ", byBuff);
            sprintf(szASCIIBuff + j    , "%c"   , 
                ((byBuff >= 32) && (byBuff <= 126)) ? byBuff : '.');
            lTotal ++;
            if (lTotal >= size) 
			{
                break;
            }
        }

		sprintf( szBuf, "%04X  %-47.47s  %-17.16s\n", i * 0x10, szBinBuff, szASCIIBuff);
        printf( szBuf );
    }

    printf("-----------------------------------------------------------------------\n");
}


Oracle 에러코드

it/c 2008/07/08 16:49 Posted by newtype

Visual Sutdio 2008 설치 기념 첫 산출물이다.

Oracle 에러코드를 입력 하면 해당 문구를 출력 해주는
완전 초 간단 프로그램

사용자 삽입 이미지

Visual Studio 2005 보다 체감 속도가 빨라진 듯 하다.

전체 소스는 여기
소스는 사실 볼것 없고,
실행 파일만 사용하시길 ㅎㅎ



c ini 읽는 모듈..

it/c 2008/04/30 19:27 Posted by newtype
급하게 필요해서  c 언어로 된 ini 읽는 모듈을 작성했다.

포함 된 내용은 아래와 같다.
  - trim 함수 사용 (http://newtype.pe.kr/361)
  - windows api의 ini 읽는 함수 원형을 흉내 냈다.(MSDN)

사용 설명
    설정파일을 읽어 들이기 위한 Lib

    Pulibc 함수
    int GetProfileString(FILE *fp, char *app, char *key, char *ret, int size);
    int GetProfileInt(FILE *fp, char *app, char *key, int *ret);

    사용법
    FILE *fp=0;
    if ((fp=fopen("/home/config/sample.ini", "r")) == 0 )
    {
        stdout( "File open error! [/home/config/sample.ini]\n" );
        return 0;
    }

    // 서버 정보를 읽는다.
    GetProfileString(fp, "SERVER", "TYPE", serverType, sizeof(serverType));



ini.h 보기



ini.c 보기




tracelog

it/c 2008/01/23 09:47 Posted by newtype
application이 시간대 별로 로그를 남긴다고 가정하자.
tail로 해당 로그를 계속 모니터링 할 경우 매 시간마다 tail을 걸어주는 것이 여간 귀찮은 것이 아니다.

환경변수에 파일명 규칙을 설정해 놓으면,
자동으로 tail과 같이 동작하는 util을 만들어 봤다.

- .profile 환경변수 설정
2008.01.30.   실행파일의 파라미터로 읽어들을 환경 변수명을 읽게 수정

- .profile 환경변수 설정
# tracelog
export PROJECT_HOME=/home/app/myapp
export TRACELOG=$PROJECT_HOME/log/history/20%y%m/ifsvr_20%y%m%d%H.log

- tracelog.c

소스 열기



trim

it/c 2007/09/14 18:02 Posted by newtype
이 함수는 프로젝트 하면서 수 십번은 만든거 같다.
매번 만들고, 보관 안해서..
다음 프로젝트 땐 또 만들고.. 흐흐.
아무리 단순한 함수도 깨작 거리다 보면 1시간인데..
매번 반복.. 이제 잊지 말자 ^^


$ cat trim.c

#include <stdio.h>

/* 앞뒤 공백 제거 */
char* trim( char *s )
{
    char *f=s, *e=0, *c=s;
    
    /* 뒤쪽 공백 제거 */
    e=s +(strlen(s)) -1;
    while(*e==' '&&s<=e) e--;
    *(e+1)=0;
    
    /* 앞쪽 공백 제거 */
    while(*f==' '&&f<=e) f++;
    
    /* 공백 없는 부분 복사 */
    if (s!=f) {
        while(f<=e) *(c++) = *(f++);
        *c=0;
    }
    
    return s;
}


int main()
{
        char org1[]="i am a boy";
        char org2[]="  i am a boy";
        char org3[]="i am a boy  ";
        char org4[]="  i am a boy   ";
        char org5[]="     ";
        char org6[]="";
 
        printf("org:'%s', ", org1 );
        printf("trim:'%s'\n", trim(org1) );
        printf("org:'%s', ", org2 );
        printf("trim:'%s'\n", trim(org2) );
        printf("org:'%s', ", org3 );
        printf("trim:'%s'\n", trim(org3) );
        printf("org:'%s', ", org4 );
        printf("trim:'%s'\n", trim(org4) );
        printf("org:'%s', ", org5 );
        printf("trim:'%s'\n", trim(org5) );
        printf("org:'%s', ", org6 );
        printf("trim:'%s'\n", trim(org6) );
        return 0;
}

$ trim
org:'i am a boy', trim:'i am a boy'
org:'  i am a boy', trim:'i am a boy'
org:'i am a boy  ', trim:'i am a boy'
org:'  i am a boy   ', trim:'i am a boy'
org:'     ', trim:''
org:'', trim:''


TAG C, trim

xor을 이용한 swap 함수

it/c 2007/08/09 16:38 Posted by newtype

#define SWAP( a, b) (a^=b^=a^=b)


예전 같은 직장은 팀장님이 알려주신 방법.
어떤 형이든 상관없다.
심지어 포인터까지도
TAG swap, xor

socket connect timeout

it/c 2007/05/29 17:34 Posted by newtype

connection 할때 timeout 처리에 관해 찾아 보는 중.
nonblocking socket을 이용한 Rechard stevens의 소스가 있더군요.

Rechard stevens 소스 보기



예전에 포스팅 한 http socket client 에 적용하면서 windows 버전으로도 만들어 봤습니다.

int CHttpSocket::connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, struct timeval tval)
{
    int                flags, n, error;
    socklen_t        len;
    fd_set            rset, wset;

#ifdef WIN32
    int nonblocking =1;
    ioctlsocket(sockfd, FIONBIO, (unsigned long*) &nonblocking);
#else
    flags = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
#endif

    error = 0;
    if ( (n = connect(sockfd, (struct sockaddr *) saptr, salen)) < 0)
    {
#ifdef WIN32
        errno = WSAGetLastError();
#endif
        if (errno != EINPROGRESS && errno!= EWOULDBLOCK)
        {
            return(-1);
        }
    }

    /* Do whatever we want while the connect is taking place. */

    if (n == 0)
        goto done;    /* connect completed immediately */

    FD_ZERO(&rset);
    FD_SET(sockfd, &rset);
    wset = rset;

    if ( (n = select(sockfd+1, &rset, &wset, NULL,
                     ((tval.tv_sec>0) || (tval.tv_usec>0))? &tval : NULL)) == 0)
    {
        close(sockfd);        /* timeout */
        errno = ETIMEDOUT;
        return(-1);
    }

    if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset))
    {
#ifndef WIN32
        len = sizeof(error);
        if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
            return(-1);            /* Solaris pending error */
#endif
    } else
        return(-1); //err_quit("select error: sockfd not set");

done:
#ifdef WIN32
    nonblocking =0;
    ioctlsocket(sockfd, FIONBIO, (unsigned long*) &nonblocking);
#else
    fcntl(sockfd, F_SETFL, flags);    /* restore file status flags */
#endif

    if (error) {
        close(sockfd);        /* just in case */
        errno = error;
        return(-1);
    }
    return(0);
}


URL Encoding/Decoding 함수

it/c 2007/04/05 13:25 Posted by newtype
URL을 인코딩/디코딩 하는 함수를 만들어 봤습니다.

URL Encoding
char *URLencpy(char *enc, char *src)
{
    char buf[2+1];
    int i, j;
    unsigned char c;
    if(src==0||enc==0) return 0;

    for(i=j= 0; src[i]; i++)
    {
        c = (unsigned char)src[i];
        if (c == ' ') enc[j++] = '+';
        if ((c >= '0') && (c <= '9')) enc[j++] = c;
        else if ((c >= 'A') && (c <= 'Z')) enc[j++] = c;
        else if ((c >= 'a') && (c <= 'z')) enc[j++] = c;
        else if ((c == '@') || (c == '.')) enc[j++] = c;
        else {
            sprintf(buf, "%02x", c);
            enc[j++] = '%';
            enc[j++] = buf[0];
            enc[j++] = buf[1];
        }
    }
    enc[j] = '\0';

    return enc;
}

URL Decoding
char *URLdecpy(char *dec, char *src)
{
   int i, j;
   unsigned char c=0;
    if(src==0 || dec==0) return 0;

   for(i=j=0; src[i]; i++,j++)
   {
      if(src[i] == '+')
      {
          dec[j] = ' ';
          continue;
      }

      dec[j] = src[i];
      if(src[i] != '%') continue;

      c = src[++i];
      if(c >= '0' && c <= '9')            dec[j] = c - '0';
      else if(c >= 'A' && c <= 'F')        dec[j] = c - 55;
      else if(c >= 'a' && c <= 'f')        dec[j] = c - 87;
      else return 0;

      c = src[++i];
      dec[j] *= 16;
      if(c >= '0' && c <= '9')          dec[j] += (c - '0');
      else if(c >= 'A' && c <= 'F')     dec[j] += (c - 55);
      else if(c >= 'a' && c <= 'f')     dec[j] += (c - 87);
      else return 0;
   }
  
   dec[j] = '\0';
   return dec;
}

SAMPLE 프로젝트



Registry 읽고 쓰기 재활용

it/c 2007/03/31 00:23 Posted by newtype
Registry를 읽고/쓰는 함수를 만들어 놓고, 재활용 할 수 있도록
dll로 만들어 보자.

registry.h
#define REGISTERY_API extern "C" __declspec(dllexport)
REGISTERY_API BOOL RegReadInt(HKEY hKey, LPCTSTR lpKey, LPCTSTR lpValue,INT nDefault);
REGISTERY_API BOOL RegReadString (HKEY hKey, LPCTSTR lpKey, LPCTSTR lpValue, LPCTSTR lpDefault, LPTSTR lpRet, DWORD nSize);
REGISTERY_API BOOL RegWriteInt(HKEY hKey, LPCTSTR lpKey, LPCTSTR lpValue, UINT nData);
REGISTERY_API BOOL RegWriteString( HKEY hKey, LPCTSTR lpKey, LPCTSTR lpValue, LPCTSTR lpData);

registry.cpp
#include "registry.h"
#include <string.h>
#include <tchar.h>

#ifdef _MANAGED
#pragma managed(push, off)
#endif

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

#ifdef _MANAGED
#pragma managed(pop)
#endif


//정수 읽음
REGISTERY_API BOOL RegReadInt(HKEY hKey, LPCTSTR lpKey, LPCTSTR lpValue,INT nDefault)
{
    HKEY key;
    DWORD dwDisp;
    UINT Result;
    DWORD Size;

    if(RegCreateKeyEx(hKey, lpKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &key, &dwDisp)
        != ERROR_SUCCESS)
        return 0;

    Size = sizeof(LONG);

    if (RegQueryValueEx(key, lpValue, 0, NULL, (LPBYTE) &Result, &Size)
        != ERROR_SUCCESS)
    {
        Result = nDefault;
    }
    RegCloseKey(key);
    return    TRUE;
}


//문자열 읽음
REGISTERY_API BOOL RegReadString(HKEY hKey, LPCTSTR lpKey, LPCTSTR lpValue, LPCTSTR lpDefault,
                      LPTSTR lpRet, DWORD nSize)
{
    HKEY key;
    DWORD dwDisp;
    DWORD Size;

    if(RegCreateKeyEx(hKey, lpKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &key,&dwDisp)
        != ERROR_SUCCESS)
        return FALSE;

    Size = nSize;

    if (RegQueryValueEx(key, lpValue, 0, NULL, (LPBYTE) lpRet, &Size)
        != ERROR_SUCCESS)
    {
        memcpy( lpRet, lpDefault, _tcslen(lpDefault) * sizeof(TCHAR) );
//        _tcscpy(lpRet, lpDefault);
        return FALSE;
    }
    RegCloseKey(key);
    return    TRUE;
}

//정수값 씀
REGISTERY_API BOOL RegWriteInt(HKEY hKey, LPCTSTR lpKey, LPCTSTR lpValue, UINT nData)
{
    HKEY key;
    DWORD dwDisp;
   
    if(RegCreateKeyEx(hKey, lpKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key,&dwDisp)
        != ERROR_SUCCESS)
        return FALSE;
   
    if (RegSetValueEx(key, lpValue, 0, REG_DWORD, (LPBYTE) &nData, sizeof(UINT))
        != ERROR_SUCCESS)
        return FALSE;

    RegCloseKey(key);
    return TRUE;       
}

//문자열 씀
REGISTERY_API BOOL RegWriteString( HKEY hKey, LPCTSTR lpKey, LPCTSTR lpValue, LPCTSTR lpData)
{
    HKEY key;
    DWORD dwDisp;
   
    if(RegCreateKeyEx(hKey, lpKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, &dwDisp)
        != ERROR_SUCCESS)
        return FALSE;

    if (RegSetValueEx(key, lpValue, 0, REG_SZ, (LPBYTE) lpData, _tcslen(lpData) * sizeof(TCHAR))
        != ERROR_SUCCESS)
        return FALSE;

    RegCloseKey(key);
    return TRUE;       
}

사용 예
typedef BOOL (*FP_REGREADSTRING)(HKEY hKey, LPCTSTR lpKey, LPCTSTR lpValue, LPCTSTR lpDefault,
                      LPTSTR lpRet, DWORD nSize);

const TCHAR *LPKEY = TEXT("Software\\newtype.pe.kr\\where");
const TCHAR *LPCATEGORY = TEXT("Category");
const TCHAR *LPNAME = TEXT("Name");

int main()
{
    FP_REGREADSTRING RegReadString = NULL;
    HMODULE hModule = LoadLibrary( TEXT("registry.dll") );

    if ( hModule > 0 )
        RegReadString = (FP_REGREADSTRING)GetProcAddress( hModule, "RegReadString");

    if ( RegReadString == NULL )
    {
        int i = GetLastError();
        FreeLibrary(hModule);
        return 0;
    }

    TCHAR lpCategory[MAX_PATH]={0,};

    RegReadString( HKEY_CURRENT_USER, LPKEY, LPCATEGORY, TEXT("내컴퓨터"), lpCategory, sizeof(lpCategory) );
   
    FreeLibrary(hModule);
    return 0;
}

오랜만에 윈도우 프로그래밍을 하려니 버벅되네요 ^^;

Visual Studio를 실행 한 김에 예전에 만들어둔 Http Socket Client를 손대봤습니다.
테스트를 하다보니 버그가 있더군요. ㅡ.ㅡ
역시 Windows 환경에서 디버깅이 정말 쉽네요.
문제가 있는지 조차 모르던 코드를 실행만 했더니서 튀어 나오네요.
물론 디버깅 하기까지는 좀 걸렸습니다. ㅎㅎ


원문 출처

http://www-128.ibm.com/developerworks/kr/library/opendw/20061219/


필자는 DVD 레코더와 셋톱박스의 복합 모델을 개발하는 팀에 소속되어 있다. 현재 유럽에서는 아날로그 방송을 디지털로 서서히 대체하고 있기 때문에, 관련 제품의 개발 요청이 쇄도하고 있다.
얼마 전 유럽을 타깃으로 3개의 유사 모델(D197, D198, D199)을 개발하고 있을 때의 일이다. 우여곡절 끝에 기본 모델인 D197 개발을 마치고 양산 시켰으며, D198도 완료하여 QA 그룹에 테스트를 의뢰한 후 결과를 기다리고 있었다. 팀원들 모두, D197 모델이 별 이상 없었으니 부가기능을 조금 추가한 D198 역시 무난히 양상 단계로 넘어갈 것이라고 판단, 모처럼의 한가한 시간을 보내고 있었다.
그런데 그 순간 옆자리에서 통화하는 소리가 들렸다.
"네, 세트가 멎었다고요?"
테스트 그룹으로부터 D198 세트가 오동작 한다는 연락을 받은 것이다. 우리는 즉시 테스트 그룹으로 달려갔다. 노트북을 D198에 연결하고 에러 메시지를 확인했다. 디버그 화면에는 메모리 부족을 나타내는 경고 메시지가 반복해서 출력되고 있었다. 사용할 수 있는 메모리가 바닥나서 더 이상 동작하지 못하고 멈춰버린 것이었다.
D198의 DRAM은 시스템을 동작시키고 남을 넉넉한 크기여서, 정상적인 경우라면 메모리 부족 문제가 발생할 이유가 없었다. D197 모델에서는 없었던 메모리 문제가 왜 발생했을까? 팀원들은 오늘도 일찍 퇴근하기는 틀렸다는 생각에 투덜거리며 D198에서 새로 추가된 코드를 중심으로 살펴보기 시작했다.

메모리 누수

자바나 C# 같은 언어에는 가비지 컬렉터(Garbage Collector)가 있어서, 알아서 메모리 관리를 해주지만 프로그래밍 언어의 대표주자 C/C++ 에서의 메모리 할당과 해제는 프로그래머의 몫이다. 따라서 애플리케이션이 메모리를 할당 받아서 사용했다면, 사용이 끝난 뒤에는 반드시 반납해서 해당 메모리를 재사용할 수 있도록 해야 한다. 그러나 프로그래머도 사람인지라 할당 받은 메모리를 실수로 해제하지 않는 경우가 발생할 수 있다. 이러한 경우 해제되지 않은 메모리는 사용이 끝난 뒤에도 남아있어 공간만 차지하는 상태로 있게 된다. 이것을 메모리 누수라고 한다.
언뜻 생각해보면 메모리를 할당 받고 해제하는 것이 무슨 어렵고 복잡한 일인가? 의문을 가질 수도 있겠지만, 실제로 코딩을 하다 보면 그렇게 간단한 문제가 아니다.
상용코드는 여러 프로그래머들이 개발하고 유지보수하기 때문에 시간이 지나면 일관성을 잃고 난해해지기 쉬울 뿐 아니라 그 양도 방대해진다. 때문에 복잡해진 애플리케이션의 상태나 조건을 후임 개발자가 꿰뚫고 있기는 쉽지 않다.
여기에 새로운 기능을 추가하다 보면 예상치 못한 상태에 빠지기도 하고 복잡해진 조건에 누락되는 부분이 생겨 메모리 누수가 발생하기 쉽다. 특히 문제의 루틴이 단발적으로 사용되는 것이 아니라면 루틴이 실행될 때마다 메모리 누수가 누적되어 언젠가는 메모리 부족 문제가 터질 수밖에 없게 된다.

메모리 감시자

짧은 분량의 간단한 프로그램이라면 차분히 코드를 살펴보면서 메모리 누수를 찾아 나설 수도 있다. 실제로 느긋하게 코드를 읽어 나가는 것은 메모리 누수뿐 아니라 야근하며 작성했던 코드의 결함을 찾아내는 가장 좋은 방법일 수도 있다.
그 러나 방대한 분량의 상용 코드에서 생긴 문제라면 어떻겠는가? 거기에 상용 프로그램 개발에 느긋한 일정을 주는 회사를 본 적이 있는가? 긴박한 일정에 메모리 누수까지 생겼다면 과중한 스트레스로 이력서를 고쳐 쓰고 취업 사이트를 뒤지게 될지도 모르겠다.
방대한 프로그램에 숨겨진 감쪽같은 메모리 누수는 사막에서 잃어 버린 바늘과도 같다. 맨 손으로 사막에서 바늘을 찾으려는 것은 너무도 무모한 짓이다. 사막에서 바늘을 찾으려면 도구가 필요하다. 이를테면 금속탐지기 같은 것이 있으면 큰 도움이 될 것이다. 우리도 메모리 누수를 찾기 위한 금속탐지기부터 구해 보도록 하자.
필자는 도입부의 문제 상황에서 다른 팀원들과 함께 코드를 살펴보는 대신에 메모리 감시 모듈을 작성하여 코드에 추가하는 작업을 했다. 물론 다른 팀원들이 열심히 코드를 분석하는 동안에 다른 코드를 작성하고 있었기 때문에, 자칫 딴짓을 하는 것처럼 보였을 수도 있었겠지만 결국은 필자의 방법으로 문제를 해결할 수 있었다. 두 세시간 정도의 시간이 지나자 코드 분석을 하던 팀원들은 하나 둘씩 지쳐서 포기하기에 이르렀는데, 그 즈음에 필자의 프로그램이 완성되었다.
필자가 개발한 메모리 감시자는 메모리의 할당과 해제 상태를 저장하고 리포트 하는 기능을 하는데, 감시 모듈을 포함시킨 D198을 부팅시켜 기본동작을 수행시켜 본 후 메모리 상태를 확인해 보았다. 예상대로 D198에서 새롭게 추가된 유료방송 처리 부분이 문제였다.
우리는 리포트 받은 부분의 코드를 살펴보고 곧 문제를 찾아내어 메모리 누수를 해결할 수 있었다. 그리고 덤으로 세트에 주는 영향은 미미하지만 감추어져 있던 또 다른 메모리 누수(몇 년 정도 동작 시키면 세트를 멈추게 할 수도 있는 문제)도 찾아낼 수 있었다.

메모리 감시자의 구현

메모리 감시자가 하는 일은 거창해 보이지만 구현은 생각보다 간단하다. C언어에서는 malloc이나 calloc을 이용하여 메모리를 할당하고, free를 이용하여 메모리를 해제한다. 마찬가지로 C++에서는 new와 delete가 같은 동작을 한다. 메모리 감시자는 링크드 리스트로 구현되며, malloc이 호출 되면 할당한 메모리의 주소를 리스트에 저장해 두었다가 free로 해당주소를 해제하면 저장된 리스트에서 해제된 주소 값을 찾아 삭제한다.
이러한 동작으로 메모리 감시자는 현재 할당되어 있는 메모리의 주소만을 보관하고 있게 된다. 따라서 메모리 감시자를 포함한 어플리케이션을 실행시킨 후 리포트를 확인하면, 비정상적으로 여러 번의 메모리 할당을 받은 부분을 어렵지 않게 발견할 수 있다. 메모리 감시자는 헤더 파일 하나(TraceMem.h)와 소스 파일 하나(TraceMem.c)로 구성된다. 각각의 구현 방법에 대해 설명하도록 하겠다.

 

TraceMem.h

메모리 감시자가 애플리케이션이 할당 받은 동적 메모리의 주소 값을 저장하는 역할을 하지만, 메모리 감시자 자체도 링크드 리스트로 구현되기 때문에 스스로가 동작하는 동안에도 동적 메모리의 할당을 하게 된다. 즉, 애플리케이션에서 할당한 메모리 주소를 메모리 감시자의 링크드 리스트에 저장하기 위해 다른 메모리를 할당해야 하는 것이다.
그러면 메모리 감시자는 주소 값을 저장하기 위해 할당 받은 메모리의 주소를 또 다시 저장하려 들것이다. 이 때문에 순환참조가 발생되며, 메모리 누수 잡기를 시작도 하기 전에 시스템이 다운되는 상황이 벌어지게 된다. 이러한 문제를 피하기 위해 메모리 감시자가 스스로의 목적에 의해 사용하는 동적 메모리의 할당과 해제는 메모리 감시자의 감시를 받지 않도록 할 필요가 있다.
아래 코드를 살펴보면 매크로를 이용해서 _MALLOC와 _FREE를 정의하였다. _MALLOC와 _FREE는 realloc을 이용하였는데, realloc은 malloc과 free의 기능도 모두 할 수 있는 범용적인 메모리 함수다. 본래 realloc은 malloc이나 calloc으로 할당한 메모리의 크기를 변경시킬 때 사용하는 함수이다. 기존에 할당 받은 주소를 NULL로 주면 메모리를 새로 할당하는 malloc의 역할을 하고, 새로 할당할 메모리의 크기를 0으로 하면 기존의 메모리만 해제시켜 버리기 때문에 free와 같은 역할을 한다.
메모리 감시자는 malloc과 free가 호출될 때 할당되거나 해제된 주소를 관리하므로, 메모리 감시자 코드를 구현하는 데는 malloc이나 calloc을 사용하지 않고 realloc을 사용한 _MALLOC과 _FREE를 사용할 것이다.
TraceMem은 메모리 감시자의 몸체가 되고 MemItem은 메모리 주소 값을 저장하는 리스트의 노드이다. STL을 사용할 경우는 더욱 편리하게 리스트를 구현할 수 있지만, C에서도 사용할 수 있도록 링크드 리스트를 직접 구현해 보겠다.

#define _MALLOC(p)     realloc(NULL, p)

#define _FREE(p)       realloc(p, 0)

 

void* dbgMalloc(int size, char* file, int line);

#define malloc(n) dbgMalloc(n, __FILE__, __LINE__)

 

void dbgFree(void* ptr);

#define free(p) dbgFree(p)

 

typedef struct _MemItem

{

        void* ptr;

        char* file;

        int line;

        unsigned long size;

        int num;

        struct _MemItem *next;

} MemItem;

 

typedef struct _TraceMem

{

        MemItem* head;

        MemItem* tail;

        int num;

} TraceMem;

 

TraceMem* TraceMemCreate();

void TraceMemDelete(TraceMem*);

void TraceMemPrint(TraceMem* self);

TraceMem* TraceMemGetSummary(TraceMem* self);

 

extern TraceMem* traceMem;

TraceMem.c

dbgMalloc과 dbgFree는 malloc과 free를 대신하여 수행되며 메모리 감시자에 메모리 주소를 추가하고 삭제하는 역할을 하며 아래 코드는 메모리 감시자의 몸체 부분으로 임베디드 환경에서도 사용할 수 있도록 C를 객체지향 스타일로 작성한 것이다.


void* dbgMalloc(int size, char* file, int line)

{

        void* ptr = _MALLOC(size);

 

        TraceMemAdd(traceMem, ptr,  file, line, size);

 

        return ptr;

}

 

void dbgFree(void* ptr)

{

        TraceMemRemove(traceMem, ptr);

 

        _FREE(ptr);

}

 

TraceMem* traceMem = NULL;

 

/* 링크드 리스트 노드 생성자 */

MemItem* MemItemCreate(void* ptr, char* file, int line, unsigned long size)

{

        MemItem* self = (MemItem*) _MALLOC(sizeof(MemItem));

 

        self->file = (char*)_MALLOC(strlen(file) + 1);

        strcpy(self->file, file);

        self->line = line;

        self->size = size;

        self->num = 1;

        self->ptr = ptr;

 

        return self;  

}

 

/* 소멸자 */

void MemItemDelete(MemItem* self)

{      

        if (self == NULL)

        {

               return;

        }

              

        _FREE(self->file);

        _FREE(self);  

}

 

void MemItemPrint(MemItem* self)

{      

        printf(" ++ [%s:%d:%p] : %d/%d\n", self->file, self->line, self->ptr, self->num, self->size ); 

}

 

/* 메모리 감시자 생성자 */

TraceMem* TraceMemCreate()

{

        TraceMem* self = (TraceMem*) _MALLOC(sizeof(TraceMem));

       

        self->head = MemItemCreate(0, "Head", 0, 0);

        self->tail = MemItemCreate(0, "Tail", 0, 0);

       

        self->head->next = self->tail;

        self->tail->next = NULL;

       

        self->num = 0;

 

        return self;

}

 

/* 소멸자 */

void TraceMemDelete(TraceMem* self)

{

        while ( self->head->next != self->tail )

        {

               MemItemDelete( TraceMemPop(self, self->head->next) );       

        }

 

        if (self->num != 0)

        {

               printf(" ++++ ERROR : TraceMem has Items %d", self->num);

        }

 

        _FREE(self->head);

        _FREE(self->tail);

        _FREE(self);

}

 

/* 주소값을 이용한 노드 검색 */

MemItem* TraceMemFindPtr(TraceMem* self, void* ptr)

{

        MemItem* iter;

 

        for ( iter = self->head->next; iter != self->tail; iter = iter->next )

        {

               if (ptr == iter->ptr) 

                       return iter;

        }

 

        return NULL;  

}

 

int TraceMemPush(TraceMem* self, MemItem* item)

{

        MemItem* next = self->head->next;

 

        self->head->next = item;

        item->next = next;

 

        return (self->num++);

}

 

MemItem* TraceMemPop(TraceMem* self, MemItem* item)

{

        MemItem *iter;

 

        for ( iter = self->head; iter != self->tail; iter = iter->next )

        {

               if (iter->next == item)

               {

                       iter->next = iter->next->next;               

                       item->next = NULL;

                       self->num--;

                      

                       return item;

               }

        }

 

        return NULL;

}

 

/* 메모리 정보를 리스트에 추가 */

int TraceMemAdd(TraceMem* self, void* ptr, char* file, int line, unsigned long size)

{

        MemItem *tar;

       

        if ( (tar = TraceMemFindPtr(self, ptr)) == NULL)

        {

               MemItem* item = MemItemCreate(ptr, file, line, size);       

               TraceMemPush(self, item);

        }

        else

        {

               TraceMemPrint(self);

        }

 

        return 0;

}

 

/* 메모리 정보를 리스트에서 제거 */

int TraceMemRemove(TraceMem* self, void* ptr)

{

        MemItem *tar;

       

        if ( (tar = TraceMemFindPtr(self, ptr)) != NULL)

        {

               MemItemDelete( TraceMemPop(self, tar) );

        }

        else

        {

               TraceMemPrint(self);

        }

 

        return 0;

}

 

/* 메모리 감시자 내용 출력 */

void TraceMemPrint(TraceMem* self)

{

        MemItem *iter;

 

        printf("\n ++ TraceMemPrint\n");

 

        for(iter = self->head->next; iter != self->tail; iter = iter->next)

        {

               MemItemPrint(iter);

        }

}

 

/* 파일명과 라인수로 노드를 찾는 함수 */

MemItem* TraceMemFindFileLine(TraceMem* self, char* file, int line)

{

        MemItem* iter;

 

        for ( iter = self->head->next; iter != self->tail; iter = iter->next )

        {

               if (line == iter->line && strcmp(file, iter->file) == 0)

                       return iter;

        }

 

        return NULL;  

}

 

/* 정리된 메모리 상태를 주는 함수 */

TraceMem* TraceMemGetSummary(TraceMem* self)

{

        MemItem *iter, *tar;

        TraceMem* sum = TraceMemCreate();

 

        for (iter = self->head->next; iter != self->tail; iter = iter->next)

        {             

               if ( (tar = TraceMemFindFileLine(sum, iter->file, iter->line)) == NULL)

               {

                       MemItem *item = MemItemCreate(0, iter->file, iter->line, iter->size);

                       TraceMemPush(sum, item);

               }

               else

               {

                       tar->num ++;

                       tar->size += iter->size;

               }

        }

 

        return sum; 
 

Test 수행하기

#include <iostream>

#include "TraceMem.h"

 

using namespace std;

 

int main()

{

        traceMem = TraceMemCreate();

 

        int *ptr = (int*) malloc(100);  // Line 11

        int *ptr2 = (int*) malloc(200);  // Line 12

 

        for (int i=1; i<=100; i++)

        {

               malloc(i); // Line 16

               if (i%4 == 0)

               {

                       malloc(i);  // Line 19

               }

        }

 

        free(ptr);

        free(ptr2);

 

        TraceMem* summary = TraceMemGetSummary(traceMem);

        TraceMemPrint(summary);

       

        TraceMemDelete(summary);

        TraceMemDelete(traceMem);

 

        return 0;

}

 

 > Report

 ++ TraceMemPrint

 ++ [D:\Works\VCxx\MemTest\Main.cpp:16:00000000] : 100/ 5050

 ++ [D:\Works\VCxx\MemTest\Main.cpp:19:00000000] : 25/ 1300

 

위의 예는 Main.cpp의 11, 12 번째 줄에서 할당한 메모리는 정상적으로 해제한 반면 16, 19번째 줄에서 할당한 메모리는 해제하지 않았다. Report는 해제되지 않은 메모리를 보여주는데, Main.cpp의 16번째 줄의 두 숫자 100은 해제되지 않은 메모리의 개수이고 5050은 메모리 사이즈의 합이다. 테스트 코드에서는 1부터 100까지의 크기로 메모리를 할당했었기 때문에, 할당된 횟수는 100회이고 사이즈는 1부터 100의 합인 5050이 된다.

결론

이상으로 메모리 감시자를 이용하여 메모리 누수를 손쉽게 찾을 수 있는 방법에 대해 알아봤다. 소개한 코드에서는 malloc과 free를 사용한 경우를 예로 들었지만 calloc에 대해서도 적용할 수 있으며, 약간의 매크로 트릭과 연산자 오버로딩을 이용하면 C++의 new와 delete에도 적용할 수 있다. 물론 소개한 알고리즘을 응용하여 가비지 컬렉터가 없는 여타의 언어에 대해서도 모두 적용할 수 있다.
메모리 감시자는 간단하지만 부담스러운 가격의 상용 디버깅 툴을 사용하지 않더라도 메모리 누수를 찾아내는 강력한 기능을 제공한다. 더구나 대부분 C를 이용하여 작성되는 임베디드 환경에서라면, 그나마도 변변한 디버깅 툴도 없는 실정이어서 특히 유용한 툴이 될 수 있을 것이다.

 

http socket client

it/c 2006/11/23 10:47 Posted by newtype
급하게 필요해 날림으로 하루만에 뚝딱 만들었습니다.
아주 기본적인 동작만 합니다.

청크드 모드는 아직 구현되지 않았습니다.


-- History -------------------------------------------------------------------------

2006.08.31 : 생성 by newtype 
2006.11.24 : CHttpSocket::Request      메모리 릭 수정.
2007.03.31 : CHttpReqHeader::toString  메모리 덮어 쓰는 버그 수정
                 Windows에서도 컴파일 되게 수정
2007.05.29 : Connection 시점에도 timeout 적용(nonblocking socket사용)
2011.05.13 : Transfer-Encoding: chunked 모드 지원

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

사용예 보기


헤더 파일 보기


소스 파일 보기




유용한 소켓 강좌

it/c 2006/09/20 00:07 Posted by newtype
** 원본 소켓 강좌 보기 **


----  이하 퍼온 강좌  ---------

Network의 기본


소켓 API


I/O 모델


POSIX Thread


TAG socket, 소켓

함수 포인터 관련..

it/c 2006/09/19 18:26 Posted by newtype
http://www.newty.de/fpt/index.html

windows 기반의 c++에서는

[CODE type=c++]
int (CFunc::*func)( int, int );
func = CFunc::GetArea;
CFunc A;
(A.*func)( x, y );
[/CODE]

[출처]
http://izeph.com/tt/blog/155


위 코드 처럼 Class의 맴버 메소드를 함수포인터로 사용할 수 있지만,
unix 기반에서는 사용 할 수가 없다.

unix 기반에서 사용하려면,

[CODE type=c++]
class TClassA
{
public:
  void Display(const char* text) { cout << text << endl; };
  static void Wrapper_To_Call_Display(void* pt2Object, char* text);
};
void TClassA::Wrapper_To_Call_Display(void* pt2Object, char* string)
{
  TClassA* mySelf = (TClassA*) pt2Object;
  mySelf->Display(string);
};
void DoItA(void* pt2Object, void (*pt2Function)(void* pt2Object, char* text))
{
  pt2Function(pt2Object, "hi, i'm calling back using a argument ;-)");  // make callback
};
void Callback_Using_Argument()
{
  TClassA objA;
  DoItA((void*) &objA, TClassA::Wrapper_To_Call_Display);
}
[/CODE]

[출처]
http://www.newty.de/fpt/callback.html#member

이렇게 래퍼를 한번 거쳐 사용해야 한다.

UTF-8 을 EUC-KR 로 변환

it/c 2006/09/04 21:47 Posted by newtype
libxml2를 사용하여 파싱을 하게되면 한글이 무조건 utf-8로 변환되어 버리더군요.
문제 해결을 위해 iconv라이브러리를 사용했습니다.

[CODE type=c++]
#ifndef _UTF8TOEUCKR
#define _UTF8TOEUCKR

#include
static int UTF8toEUCKR( char *outBuf, int outLength, char *inBuf, int inLength )
{
iconv_t cd = iconv_open( "EUC-KR", "UTF-8" );

int ires = (int)iconv( cd, &inBuf, (size_t*)&inLength, &outBuf, (size_t*)&outLength );

iconv_close(cd);

return ires;
}

#endif
[/HTML][/CODE]
[참고]
http://www.redwiki.net/wiki/wiki.php/iconv#s-3.1.1
http://www.pie.pe.kr/cgi-bin/moin.cgi/LibIconv
http://www.gnu.org/software/libiconv/

MFC용 ADO클래스 (버전업)

it/c 2006/07/24 13:31 Posted by newtype
미루고 미루던 작업을 디드어 했습니다. ㅎㅎ
하나의 커넥션을 끊지 않고, 계속 활용하도록 수정했습니다.

원본보기

[CAdoDB.h] =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

[CODE type=c++]
#ifndef __ADODB_HEADER_B694B3CA_2D77_4AC8_95E3_DBA44C3DB329__

#define __ADODB_HEADER_B694B3CA_2D77_4AC8_95E3_DBA44C3DB329__
/*******************************************************************
*
* MDB를 OLE로 연결한다.
* 하나의 connection로 모든 처리를 한다.
*
* ** 반드시 클래스 사용전 CoInitialize()함수를 호출해
* COM Object사용을 초기화 해야 한다.
*
*
* Usage:
* CAdoDB db;
* db.Init( "test.mdb" );
* db.Open( sql );
* while( db.IsEOF() == FALSE )
* {
* value = db.GetValueString( "field" );
* db.MoveNext();
* }
* db.Close();
*
* db.Excute( sql );
*
*
*
* 2006-07-21
*
* By newtype / newtype@newtype.pe.kr / http://newtype.pe.kr/
*
/****************************************************************/

#pragma once

#import "msado26.tlb" rename ("EOF","adoEOF") no_namespace
/*
#import "C:\program files\common files\system\ado\msado15.dll" \
no_namespace \
rename("EOF", "adoEOF")
*/


class CAdoDB
{
public:
CAdoDB(void);
~CAdoDB(void);
BOOL Init(CString Filename);
BOOL Connection(CString Filename = _T(""));
BOOL CloseConneciton();
BOOL Open(CString sql, CString Filename = _T(""));
BOOL Close();
BOOL IsEOF();
CString GetValueString(CString Field);
int GetValueInt(CString Field);
BOOL MoveNext();
BOOL Excute(CString sql, CString Filename = _T(""));

_ConnectionPtr GetConnection();
void AttachConnection( _ConnectionPtr conn );
void DetachConnection();


private:
_ConnectionPtr m_pConn;
_RecordsetPtr m_pRs;
CString m_strConnection;
BOOL m_IsOpen;
BOOL m_IsConn;

BOOL FileCmp( CString Filename );
};


#endif // __ADODB_HEADER_B694B3CA_2D77_4AC8_95E3_DBA44C3DB329__

[/HTML][/CODE]
[CAdoDB.cpp] =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
[CODE type=c++]
/* AdoDB.cpp */
#include "adodb.h"

/*******************************************************************
* OLE DB connection string
*******************************************************************/
const TCHAR *oleString = _T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=");


/********************************************************************
* 기본 생성자
*
* connection객체와 recordset객체를 생성한다.
********************************************************************/
CAdoDB::CAdoDB(void)
{
if ( FAILED(m_pConn.CreateInstance(__uuidof(::Connection))) )
AfxMessageBox(_T("Connection COM 객체 생성에 실패 하였습니다."), MB_OK, NULL);

if ( FAILED(m_pRs.CreateInstance(__uuidof(Recordset))) )
AfxMessageBox(_T("Recordset COM 객체 생성에 실패 하였습니다."), MB_OK, NULL);

m_strConnection = _T("");
m_IsOpen = FALSE;
}



/********************************************************************
* 기본 소멸자
*
* connection객체와 recordset객체를 close하고 반환 한다.
********************************************************************/
CAdoDB::~CAdoDB(void)
{
Close();
CloseConneciton();

m_pRs.Release();
m_pConn.Release();
}



/********************************************************************
* 연결 문자열을 만든다.
* 기존 파일명과 틀릴때에만 connection을 닫고, 연결문자열을 만든다.
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::Init(CString Filename)
{
if ( m_pRs==NULL || m_pConn==NULL )
return FALSE;

if ( Filename.IsEmpty() )
return FALSE;

if ( FileCmp( Filename ) == TRUE )
return TRUE;

Close();

m_strConnection = oleString + Filename;

return FALSE;
}



/********************************************************************
* DB에 연결한다.
* 기존 connection이 있으면 유지 한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::Connection(CString Filename)
{
if ( Filename.IsEmpty() && m_strConnection.IsEmpty() )
return FALSE;

if ( Filename.IsEmpty() == FALSE )
{
Init( Filename );
}

if ( m_pConn == NULL )
return FALSE;

if ( m_IsConn == TRUE )
return TRUE;

try
{
m_pConn->Open( (_bstr_t)m_strConnection, "","" ,adConnectUnspecified );

m_IsConn = TRUE;
}
catch(_com_error &err)
{

m_IsConn = FALSE;

TRACE("Error : %s : DB CONNECTION FAIL\n", err.Description() );
}

return m_IsConn;
}



/********************************************************************
* 레코드 셋을 오픈 한다.
* 기존 recordset이 있으면 close 한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString sql : SQL Query SELECT 구문
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::Open(CString sql, CString Filename)
{
HRESULT hr;
VARIANT conn;

if ( m_pConn == NULL || m_pRs == NULL )
return FALSE;

if ( Filename.IsEmpty() == FALSE )
{
Connection(Filename);
}
else
{
Connection();
}

if ( m_IsConn == FALSE )
return FALSE;

if ( m_IsOpen == TRUE )
Close();

try
{
//TRACE( sql );
TRACE( "\n" );

VariantInit(&conn);
conn.pdispVal=m_pConn;
conn.vt=VT_DISPATCH;

hr = m_pRs->Open( (LPTSTR)(LPCTSTR)sql, conn,
adOpenForwardOnly, adLockOptimistic, adCmdUnknown);
}
catch(_com_error &e)
{
TRACE("\tDescription: %s\n", (LPCTSTR) e.Description());
}
catch(...)
{
TRACE("*** Unhandled Exception ***\n");
}

if ( SUCCEEDED( hr ) )
m_IsOpen = TRUE;

if ( sql.TrimLeft().Left(7).CollateNoCase( "SELECT " ) )
{
m_IsOpen = FALSE;
}

return SUCCEEDED( hr );
}



/********************************************************************
* 더 이상 튜플이 없으면 TRUE를 반환 한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter : None
********************************************************************/
BOOL CAdoDB::IsEOF()
{
if ( !m_IsOpen )
return TRUE;

return (BOOL)m_pRs->GetadoEOF();
}



/********************************************************************
* 현재 튜플의 Field에 해당하는 Instance를 문자열 형태로 얻는다.
*
* return : CString : Value
*
* parameter :
* [in] CString Field : 값을 가져올 Attribute Name
********************************************************************/
CString CAdoDB::GetValueString(CString Field)
{
if ( !m_IsOpen || IsEOF() )
return "";

_variant_t vt = m_pRs->GetCollect( (LPTSTR)(LPCTSTR)Field );

return ( (vt.vt == VT_NULL)? ("") : vt );
}


/********************************************************************
* 현재 튜플의 Field에 해당하는 Instance를 정수 형태로 얻는다.
*
* return : int : Value
*
* parameter :
* [in] CString Field : 값을 가져올 Attribute Name
********************************************************************/
int CAdoDB::GetValueInt(CString Field)
{
return atoi( GetValueString(Field) );
}


/********************************************************************
* 다음 튜플로 이동한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter : None
********************************************************************/
BOOL CAdoDB::MoveNext()
{
if ( !m_IsOpen )
return FALSE;

return SUCCEEDED( m_pRs->MoveNext() );
}


/********************************************************************
* recordset을 닫는다.
*
* return : BOOL : TRUE | FALSE
*
* parameter : None
********************************************************************/
BOOL CAdoDB::Close()
{
if ( m_IsOpen == FALSE )
return TRUE;

m_IsOpen = FALSE;

return SUCCEEDED( m_pRs->Close() );
}


/********************************************************************
* connection을 끊는다.
*
* return : BOOL : TRUE | FALSE
*
* parameter : None
********************************************************************/
BOOL CAdoDB::CloseConneciton()
{
if ( m_IsConn == FALSE )
return TRUE;

m_IsConn = FALSE;

return SUCCEEDED( m_pConn->Close() );
}


/********************************************************************
* DML 쿼리를 실행한다.
* connection이 없으면 연결한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString sql : SQL Query DML 구문
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::Excute(CString sql, CString Filename /*= _T("")*/)
{
if ( m_pConn == NULL )
return FALSE;

if ( Filename.IsEmpty() == FALSE )
{
Connection(Filename);
}
else
{
Connection();
}

if ( m_IsConn == FALSE )
return FALSE;

try
{
TRACE( sql );
TRACE( "\n" );

m_pConn->Execute( (_bstr_t)sql, NULL, adCmdText );
}
catch(_com_error &err)
{
TRACE("Error : %s : DB Execute FAIL\n", err.Description() );
return FALSE;

}

return TRUE;
}



/********************************************************************
* 기존에 연결 파일명과 비교해 틀리면 FALSE를 반환한다.
* (개선의 여지가 있음.
* 파일명에 경로가 포함될 경우 정확한 비교가 되지 않음 )
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::FileCmp( CString Filename )
{
if ( m_IsConn == FALSE )
return FALSE;

return m_strConnection.CompareNoCase( oleString + Filename ) == 0;
}


/********************************************************************
* 현재 connection을 얻는다.
*
* return : _ConnectionPtr
*
* parameter : None
* [in] CString Filename : MDB 파일명
********************************************************************/
_ConnectionPtr CAdoDB::GetConnection()
{
return m_pConn;
}



/********************************************************************
* connection을 새로 Attach한다.
* 이미 사용중인 connection 및 Recordset는 닫는다.
*
* return : None
*
* parameter :
* [in] _ConnectionPtr conn : 사용할 Connection Object
********************************************************************/
void CAdoDB::AttachConnection( _ConnectionPtr conn )
{
if ( m_IsOpen == TRUE )
Close();

if ( m_IsConn == TRUE )
{
CloseConneciton();
m_pConn.Release();
}

m_pConn = conn;
m_IsConn = TRUE;
}

/********************************************************************
* connection을 새로 Attach한다.
* 이미 사용중인 connection 및 Recordset는 닫는다.
*
* return : None
*
* parameter :
* [in] _ConnectionPtr conn : 사용할 Connection Object
********************************************************************/
void CAdoDB::DetachConnection()
{
if ( m_IsConn == FALSE )
return;

m_pConn = NULL;
m_IsConn = FALSE;
}
[/HTML][/CODE]
TAG ADO, MFC

소스상에서 lib링크 하기

it/c 2006/07/07 18:21 Posted by newtype
아래의 프리컴파일러 헤더를 사용 합니다. [CODE type=c++] #pragma comment(lib, "library.lib") [/HTML][/CODE]

socket 타임아웃처리

it/c 2006/07/07 10:03 Posted by newtype
Unix socket 에서 타임아웃 처리를 하는 방법중에 select를 이용한 방법입니다.

[CODE type=c++]
/********************************************************************
* 데이타를 보낸다.
*
* return : int Error Code
* parameter :
* [in] int sock : 연결할 소켓 핸들
* [in] void *data : 전송할 데이타
* [in] unsigned int size : 데이타의 크기
* [in] int timeout : 타임아웃 처리를 할 시간
********************************************************************/
int gaSend(int sock, void *data, unsigned int size, int timeout)
{
    int sockNum = sock +1;
    fd_set sockSet;
    struct timeval selTimeout = { timeout, 0 }; /* 초, 마이크로초 */
    int result;

    if ( sock == 0 )
        return CONNECTION_REFUSED;

    FD_ZERO(&sockSet);
    FD_SET(sock, &sockSet);

    result = select( sockNum, NULL, &sockSet, NULL, &selTimeout );
    if ( result == 0 )
    {
        return CONNECTION_TIMEOUT;
    }
    else if ( result ==-1 )
    {
        return CONNECTION_REFUSED;
    }

    result = socketSend( sock, (char*)data, size) ;
    if ( result < 0 )
    {
        return CONNECTION_REFUSED;
    }

    return SUCCESS;
}

/********************************************************************
* 데이타를 받는다.
*
* return : int Error Code
* parameter :
* [in] int sock : 연결할 소켓 핸들
* [in] void *data : 전송받을 데이타
* [in] unsigned int size : 데이타의 크기
********************************************************************/
int gaRecive(int sock, void *data, unsigned int size, int timeout)
{
    int sockNum = sock +1;
    fd_set sockSet;
    struct timeval selTimeout = { timeout, 0 };
    int result;

    if ( sock == 0 )
        return CONNECTION_REFUSED;

    FD_ZERO(&sockSet);
    FD_SET(sock, &sockSet);

    result = select( sockNum, &sockSet, NULL, NULL, &selTimeout );
    if ( result == 0 )
    {
        return CONNECTION_TIMEOUT;
    }
        else if ( result ==-1 )
    {
        return CONNECTION_REFUSED;
    }


    result = socketRecv(sock, (char*)data, size, timeout);
    if( result < 0 )
    {
        return CONNECTION_REFUSED;
    }

    return SUCCESS;
}
[/HTML][/CODE]

LPTSTR 자료형의 문자열 치환

it/c 2004/07/28 20:55 Posted by newtype
UINT Replace( LPTSTR target, LPCTSTR source, LPCTSTR find, LPCTSTR replace )
{
       UINT i, nCount=0;
       BOOL bFind;
       LPCTSTR ps, pf;
       LPTSTR buf=NULL, pt = target;

       size_t nLenSrc = _tcslen( source );

       buf = new TCHAR[nLenSrc+1];
       if ( buf == NULL )
               return NULL;

       _tcsncpy( buf, source, nLenSrc );
       buf[nLenSrc] = 0;


       for(ps=buf; *ps; ps++)
       {
               if ( find[0] == *ps )
               {
                       bFind = TRUE;
                       for(pf=find+1, i=1; *pf; pf++, i++)
                       {
                               if ( *pf != ps[i] )
                               {
                                       bFind = FALSE;
                                       break;
                               }
                       }

                       // 찾았다!
                       if ( bFind )
                       {
                               for(pf=replace; *pf; pf++)
                                       *(pt++) = *pf;
                               ps += (--i);

                               nCount++;

                               continue;
                       }
               }

               *(pt++) = *ps;
       }

       *pt = 0;

       delete[] buf;

       return nCount;
}

UTF8, 유니코드, 아스키 파일의 구별

it/c 2004/01/03 09:28 Posted by newtype
* ANSI 텍스트 파일
- 특별한 표식이 없습니다.

* 유니코드 (little endian)
파일 처음에 0xFF 0xFE 의 두바이트로 시작합니다.

* 유니코드 (big endian)
파일 처음에 0xFE 0xFF 의 두바이트로 시작합니다.

* UTF-8
파일을 덤프 해본 결과 파일 처음에 0xEF 0xBB 0xBF 의 세바이트로 시작합니다.



한번 파일을 만든후 덤프 해보시기 바랍니다
그럼 즐삽

항상 위 효과 내기

it/c 2003/07/18 17:43 Posted by newtype
[CODE type=c++]
// CDialog를 상속받아
// OnCancel()과 DoModal()을 오버라이딩 한다.



// 닫아도 닫히지 않는다 숨길뿐...
void CMyDlg::OnCancel()
{
       ShowWindow( SW_HIDE );
       SetWindowPos( &wndNoTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
       AfxGetMainWnd()->SetFocus();
}

// Dlg를 모달로 연다
INT_PTR CMyDlg::DoModal()
{
       ShowWindow( SW_SHOW );
       SetWindowPos( &wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);

       return 0;
}



// 생성한 후
CMyDlg *pMyDlg = new CMyDlg();
pMyDlg->Create( IDD_DIALOG_MY, CWnd::FromHandle(::GetDesktopWindow()) );
pMyDlg->DoModal();



// 숨겨져 있으면 보여준다.

if ( m_pMyDlg->ShowWindow( SW_SHOWNA ) == SW_HIDE )
        pMyDlg->DoModal();

[/CODE]

문자열 분할시, 특정 번째 문자열 얻기

it/c 2003/07/18 17:31 Posted by newtype
제목이 어렵네요.. ㅡㅡ;
소스 보시면 어려운것 없으니 금방 이해 하실겁니다.
좋은 시간 되세요..^^


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

//--------------------------------------------------------------------------//
//Fuction        : Splite
//Parameter : LPTSTR lpszDest, LPCTSTR lpcszSorc, TCHAR cSep, int nIndex
//Return        : LPCTSTR
//Note                : lpcszSorc를 cSep로 나누어서 nIndex번째 문자열을 lpszDest에 넣는다.
//--------------------------------------------------------------------------//
LPCTSTR Splite( LPTSTR lpszDest, LPCTSTR lpcszSorc, TCHAR cSep, int nIndex)
{
       int i, j, k, l;
       int count;

       count = (int)strlen(lpcszSorc);
       for (i=j=k=l=0; i<count && l<=nIndex; i++)
       {
               if ( *(lpcszSorc+i) == cSep )
               {
                       k=i;
                       strncpy( lpszDest, lpcszSorc+j, k-j );
                       lpszDest[k-j] = 0;
                       j=k+1;
                       l++;
               }
       }

       return lpszDest;
}
TAG C, MFC, 문자열

CListCtrl에서 깜박임이 덜한 SetItemText

it/c 2003/07/18 17:30 Posted by newtype
//--------------------------------------------------------------------------//
//Fuction        : UpdateListItem
//Parameter : CListCtrl* pList, int nIndex, int nSubitem, LPCTSTR lpszText
//Return        : BOOL
//Note                : pList의 nIndex, nSubitem의 텍스트를 lpszText로 바꾼다.
//            같은 값이 벌써 들어가 있으면 FALSE, 새로운 값으로 대체 하면 TRUE
//--------------------------------------------------------------------------//
BOOL UpdateListItem(CListCtrl* pList, int nIndex, int nSubitem, LPCTSTR lpszText)
{

   CString strText = pList->GetItemText( nIndex, nSubitem );
       if ( strText.CompareNoCase( lpszText ) )
       {
       pList->SetItemText( nIndex, nSubitem, lpszText );
               return TRUE;
       }

       return FALSE;
}

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

원리는 간단합니다.
쓰려는 Text와 쓰여져있는 Text를 비교해 틀릴때만 써주는거죠.
각 Item별로 해줘도 되지만..
함수하나로 만들어 봤습니다.

좋은 시간 되세요.
TAG CListCtrl, MFC

CComboBox에서 ItemData와 ItemText찾기

it/c 2003/07/18 17:27 Posted by newtype
//--------------------------------------------------------------------------//
//Fuction        : FindComboString
//Parameter : CComboBox* pCombo, DWORD dwSearch, CString *pstrDest
//Return        : int
//Note                : pCombo에서 Callback Item중 dwSearch를 찾아
//            Index를 리턴하고, 해당 Index의 List Text를 pstrDest로 넘긴다.
//--------------------------------------------------------------------------//
int FindComboString(CComboBox* pCombo, DWORD dwSearch, CString *pstrDest)
{
       for(int i=pCombo->GetCount()-1; i>=0; i--)
       {
               if ( pCombo->GetItemData( i ) == dwSearch )
               {
                       pCombo->GetLBText( i, *pstrDest );
           return i;
               }
       }

       return -1;
}



//--------------------------------------------------------------------------//
//Fuction        : FindComboString
//Parameter : CComboBox* pCombo, CString strSearch, DWORD* pdwDest
//Return        : int
//Note                : pCombo에서 List Text중 strSearch를 찾아
//            Index를 리턴하고, 해당 Index의 Callback Item을 pdwDest로 넘긴다
//--------------------------------------------------------------------------//
int FindComboDword(CComboBox* pCombo, CString strSearch, DWORD* pdwDest)
{
       CString str="";
       for(int i=pCombo->GetCount()-1; i>=0; i--)
       {
               pCombo->GetLBText( i, str );
               if ( !str.Compare( strSearch ) )
               {
           *pdwDest = (DWORD)pCombo->GetItemData( i );
                       return i;
               }
       }
       return -1;
}
TAG CComboBox, MFC
//-------------------------------------------------------------------------------------//
// Function                        : OnlyDirecory
// Parameter                : CString strMoney
// Return                        : LPTSTR strFull                        
// Note                                : 인자로 받은 전체파일이름의 파일이 있는 경로를 리턴한다.
//-------------------------------------------------------------------------------------//
LPTSTR OnlyDirecory(LPTSTR strFull)
{
       TCHAR* pPos = strFull + strlen( strFull );

       while ( strncmp( pPos, "\\", 1 ) && pPos != strFull )
               pPos--;
       *pPos = '\0';

       return strFull;
}

TAG C, MFC, 경로

CTreeCtrl에서 ItemData 찾기 (자식노드 포함)

it/c 2003/07/18 17:25 Posted by newtype
//-------------------------------------------------------------------------------------//
// Function         : FindTreeData
// Parameter        : HTREEITEM hItem, DWORD dwData
// Return           : HTREEITEM
// Note             : hItem이하 모든 모든 노드에서 dwData를 찾는다.
//-------------------------------------------------------------------------------------//
HTREEITEM FindTreeData(CTreeCtrl* pTree, HTREEITEM hItem, DWORD dwData)
{
       HTREEITEM hitemFind, hItemChile, hItemSibling;
       hitemFind = hItemChile = hItemSibling = NULL;


       if ( pTree->GetItemData( hItem ) == dwData )
       {
               hitemFind = hItem;
       }
       else
       {
               // 자식 노드를 찾는다.
               hItemChile = pTree->GetChildItem( hItem );
               if ( hItemChile )
               {
                       hitemFind = FindTreeData( pTree, hItemChile, dwData );

               }

               // 형제노드를 찾는다.
               hItemSibling = pTree->GetNextSiblingItem( hItem );
               if ( hitemFind==NULL && hItemSibling )
               {
                       hitemFind = FindTreeData( pTree, hItemSibling, dwData );
               }
       }
   
       return hitemFind;
}

TAG CTreeCtrl, MFC

MFC에서 .NET스타일의 메뉴를 사용해 보자

it/c 2003/02/08 12:20 Posted by newtype

NewMenu


The NewMenu is published on
CodeProject
. There you will find the discussion board.






This is a prerelease of NewMenu 1.13 (will be published shortly)





Released NewMenu 1.11 (Published on codeproject)
















STYLE_XPSTYLE_ICYSTYLE_ORIGINAL


Introduction


This class, CNewMenu, implements owner drawn menus derived
from the CMenu class. The coal of this class is to have menus with
the same look as the new menu from the Microsoft products. In the other hand an
easy use of a new menu class with Icons and Title support.
I was inspired from the great class BCMenu from Brent Corkum. I took some ideas
and almost every function, but reimplemented almost the whole code and expanded
it with some other nice functions. It was a hard work to change the look of the
whole menu that belongs to an application, changing border and adding shading.
Furthermore, it was tricky changing all menus under windows 2000 or system menu
under Windows XP when you are using themes. Over all, I hope it's now easy
enough for you to use this class in new applications.


This new menu works under Windows 95/98/Me and Windows NT4.0/2000/XP. When you
like to use Chinese or some other special characters, you have to compile your
project with Unicode. But keep in mind you must have installed the Unicode
libraries with the Visual studio otherwise you get a linking error.


Additional when you want that your program works on all platforms, you have to
compile it with the Visual Studio 6.0. I did not find out why the drawing don't
work fine when it is compiled with the Visual Studio 7.0 under Windows NT 4.0.
Furthermore when you do not like the flat border you can use the
STYLE_XXX_NOBORDER and then menus will have the standard menu border from the
system.


Nice Title in Menus











Oh yes, its also possible to add a title to menu. You can add a title on the
top or on the left side of a menu. You can have a gradient or a solid color as
background. With the function SetMenuTitle you can add or remove a
title from a menu. The gradient drawing works also on Windows 95.
// Set a title to the system menu 
m_SystemNewMenu.SetMenuTitle(_T("New Menu Sample"),
MFT_GRADIENT|MFT_LINE|MFT_CENTER);

New icons effects brightening, glooming and graying








Under menu style XP you have the possibility to enable or
disable the additional drawing style to icons. Disabled icons become a grayed
look and non selected icons become a gloom look. You can see the
different between those drawing styles in the sample.

How to use the CNewMenu in a project




  1. The easiest way is to replace all CDialog, CFrameWnd, CMDIFrameWnd,
    CMDIChildWnd, CMiniFrameWnd
    and CMultiDocTemplate through
    the new classes CNewDialog, CNewFrameWnd,
    CNewMDIFrameWnd, CNewMDIChildWnd, CNewMiniFrameWnd

    and CNewMultiDocTemplate
    in your project.

  2. Set in the application, derived from CWinApp, the menustyle just
    before you create the frame or a dialog window in the function InitInstance.
    // Set drawing style of all menus to XP-Mode or STYLE_ORIGINAL
    CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);


  3. When you have a multi document template, add the loading code for menu icons
    just after creating the template.
    CNewMultiDocTemplate* pDocTemplate; 
    pDocTemplate = new CNewMultiDocTemplate(IDR_MDINEWTYPE,
    RUNTIME_CLASS(CMDINewMenuDoc),
    // custom MDI child frame
    RUNTIME_CLASS(CChildFrame),
    RUNTIME_CLASS(CMDINewMenuView));

    AddDocTemplate(pDocTemplate);


    // Loading toolbar icons into the menu
    pDocTemplate->m_NewMenuShared.LoadToolBar(IDR_DRAWBAR);



  4. Insert at the end of OnCreate function of the MainFrame the loading code for
    the icons. For example:
    m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);


  5. Finally don't forget to include the definition from the "NewMenu.h" in stdafx.h
    and add NewMenu.cpp to the project.


  6. Now you are ready to use the class.

How to use the CNewMenu in a dialog-Application




  1. Set in the application the menustyle before you create the dialog in the
    function InitInstance.
    // Set drawing style of all menus to XP-Mode
    CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);


  2. Replace all CDialog to CNewDialog in your Project.
    For example base class from CAboutDlg.


  3. For loading bitmaps in to the menu the best place is in OnInitDialog
    function after calling the base class function.
    // CDialogDlg could be your Dialog.
    BOOL CDialogDlg::OnInitDialog()
    {
    // Call the baseclass
    CNewDialog::OnInitDialog();


    // Load icons to the menu


    m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);
    }


  4. Don't forget to include the the definition from the "NewMenu.h" in stdafx.h and
    add NewMenu.cpp to the project.



How to use the CNewMenu in a special frame window




  1. Normally you should overwrite measureitem, menuchar and
    initmenupopup. For this purpose I use the CNewFrame template
    class. So you can replace 'CYourSpecialBase' with 'CNewFrame'
    in your project.


  2. But don't change it in IMPLEMENT_DYNAMIC, IMPLEMENT_SERIAL
    or IMPLEMENT_DYNACREATE the base class to the new one.
    IMPLEMENT_DYNAMIC(CYourNewSpecialFrame, CYourSpecialBase)


  3. Don't forget to change the baseclass in BEGIN_MESSAGE_MAP.
    BEGIN_MESSAGE_MAP(CYourNewSpecialFrame, CNewFrame) 
    //{{AFX_MSG_MAP(CYourNewSpecialFrame)
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()


  4. For loading bitmaps in to the menu, you can add the code in OnCreate
    function.


  5. Finally don't forget to include the the definition from the "NewMenu.h" in
    stdafx.h and add NewMenu.cpp to the project.



How to use the CNewMenu in a MDI-Application




  1. Change in the the application class in the BOOL InitInstance() function
    the CMultiDocTemplate to CNewMultiDocTemplate
    CNewMultiDocTemplate* pDocTemplate; 
    pDocTemplate = new CNewMultiDocTemplate(IDR_MDINEWTYPE,
    RUNTIME_CLASS(CMDINewMenuDoc),
    // custom MDI child frame
    RUNTIME_CLASS(CChildFrame),
    RUNTIME_CLASS(CMDINewMenuView));
    AddDocTemplate(pDocTemplate);


  2. Now insert the following code for loading additional bitmaps from a toolbar
    into the menus.
    // Loading toolbar icons into the menu 
    pDocTemplate->m_NewMenuShared.LoadToolBar(IDR_DRAWBAR);


    // Set drawing style of all menus to XP-Mode
    CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);


  3. Change the base class from the main frame class (CMainFrame) from
    CMDIFrameWnd
    to CNewMDIFrame in definition and
    implementation file.
      // Change the base class in the following makro
    IMPLEMENT_DYNAMIC(CMainFrame, CNewMDIFrameWnd)

    // don't forget to change here the baseclass
    BEGIN_MESSAGE_MAP(CMainFrame, CNewMDIFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    // NOTE - the ClassWizard will add and remove mapping macros here.
    // DO NOT EDIT what you see in these blocks of generated code !
    ON_WM_CREATE()
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()


  4. Add at the end of int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    for loading bitmap in the default menu.
    m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);


  5. Finally change the baseclass of the child frame from CMDIChildWnd to
    CNewMDIChildWnd. Do the same for all derived class CDialog
    to the base class of CNewDialog.


  6. Don't forget to include the the definition from the "NewMenu.h" in stdafx.h and
    add NewMenu.cpp to the project.



How to use the CNewMenu in a SDI-Application




  1. Set in the application the menustyle before you create the dialog in the
    function InitInstance.
    // Set drawing style of all menus to XP-Mode
    CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);


  2. Replace all CDialog to CNewDialog in your Project.


  3. Change the base class from the main frame class (CMainFrame) from
    CFrameWnd
    to CNewFrameWnd
    in definition and implementation file.

  4. Add at the end of int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    for loading bitmap in the default menu.
    m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);


  5. Don't forget to include the the definition from the "NewMenu.h" in stdafx.h and
    add NewMenu.cpp to the project.



How to replace system menu in system dialogs


Sometimes it would be pretty to change the menu in MessageBox or in
CFileDialog too. But unfortunately a MessageBox has no base class for
owerwriting. An other problem has the file dialog, there are at least two
dialog and the CFileDialog does only subclass the child dialog of the shown
FileDialog. For those dialog, and some more, I added a special subclassing
mechanism for our work.


// Subclass dialog with system menu 
CNewMenuHelper myHelper(NEW_MENU_DIALOG_SUBCLASS|NEW_MENU_DIALOG_SYSTEM_MENU);
AfxMessageBox(_T("You must restart the application"),MB_OK);

Even thought what can I do when I do not want to have a menu border
replacing in the next shown dialog? It is very simple! Just place the
CNewMenuHelper before your call like in the following sample.


// Subclass dialog with normal border painting 
CNewMenuHelper myHelper(NEW_MENU_DEFAULT_BORDER);
AfxMessageBox(_T("You must restart the application"),MB_OK);

Color replacing of bitmaps with 16 colors


The MFC replace some colors depended of the chosen system colors. So when you
use a black line in the bitmap, the color black will be replaced with the
system color of COLOR_BTNTEXT. Following the color mapping map. By
the way, it is the same color mapping of the toolbars.


The color RGB(0x00,0x00,0x00) will be changed to COLOR_BTNTEXT       // black
The color RGB(0x80,0x80,0x80) will be changed to COLOR_BTNSHADOW // dark gray
The color RGB(0xC0,0xC0,0xC0) will be changed to COLOR_BTNFACE // bright gray
The color RGB(0xFF,0xFF,0xFF) will be changed to COLOR_BTNHIGHLIGHT // white

Adding Icons to Menus


There are several possibilities adding icons to menu. An easy way is with a
toolbar resource. But the developer studio support only 16 color for
toolbar-bitmaps. What you can do is: first define your toolbar; then replace
the saved toolbar-bitmap with a hicolor bitmap by hand. Additional you can
define every icon in a different bitmap and assign it to a menuitem. My way was
define a bitmap like in an image list, make a helper table with the resource id
of the bitmap, size of an icon and followed by the ids of the icons. The end of
the table must be marked with an id number NULL.


// Define the table
static WORD ToolId[] =
{ IDR_NEWMENU256, // Resource id of the bitmap-icons
16, 15, // Dimension of each icon


ID_FILE_NEW, // First icon's ID
ID_FILE_OPEN, // Second icon's ID
ID_FILE_SAVE, // and so on...

NULL}; // Marker for end of table

//Load the definition into menu with transparent color
m_DefaultNewMenu.LoadToolBar(ToolId,RGB(192,192,192));

How to add an icon to a submenu


It is very simple to add or change the title of a submenu entry when you know
the menu text. The helper function ModifyODMenu can be used.


// set the icon IDB_MORE_C to the submenu "More A"
m_NewMenuShared.ModifyODMenu(0,_T("More A"),IDB_MORE_C);

Menu under Windows 2000


It was a strange thing to detect, that under Windows 2000, you have a window
menu with handle 0x10012, which is shared to all application. (I think under
Windows 98 is the menu 0x0090). I don't know why, but this is also a reason of
the specialty for some effects belonging to subclassing menu. First when you
have disabled menu effects, the menu windows are alternating created or showed.
That means, first showed menu is mostly the special menu, never created nor
destroyed only showed and disabled, and the second is normal created and
destroyed after closing. The third time, sometime, the special window is showed
again and then the next menu is created normal. For this reason, when you
subclass the special menu, you are responsible to restore all values before
unsubclassing it. Be careful when you changing some flags or styles, because
the other applications like having the standard menu. You can also receive
bluescreen when you forget to unsubclass or when changing painting areas
belongs to that menu. Really ugly!!! Second, when you enabled menu effects
every shown menu is created new from scratch. For this reason it doesn't matter
when you don't restore all flags or styles!


But almost I forgot to explain how I got the goal to subclass all menus. Well,
for first, you can't subclass the system menu class #32768 from an application;
also I was looking for other possibility. I decide to create a windows hook for
all windows messages and catching all messages for creating a window. After
checking for the window class, when I found the menu class, I subclassed it.
But, how I can subclass the special menu, which is never created? I found, that
one of the first messages, which were sending to that menu, was 0x01E2 and one
of the latest WM_SHOWWINDOW. So I caught these messages for sub-
and unsubclassing the special menu. Only between those two messages you have
access to the special window!! Why? I don't know, but be careful.


How to paint the border


Well after I got it to subclass the menu window I can draw the border by
myself by processing WM_NCPAINT message. But how is the shade
made? Just before the menu is showed on the screen I save the menu region in a
bitmap, this is a reason why it does not work when the underground has been
changed, and combine it with a shade drawing. An other way would be using
layered windows, but this will not work on all systems like windows 95/98.


System menu under Windows XP


I thought owner drawing a menu is all time easy. But on Windows XP, with
themes enabled, it is a curious thing. Especially the main frame when the
system menu were displayed. Instead of owner drawing, the menu items were drawn
by the frame. The frame had painted the windows button of the width of the menu
at the place of the menu item :-) instead of drawing it. Strange! So I found a
workaround. I increased the menu items identifier before showing the menu and
restored after closing it. So, it works under Windows 2000 and XP.


Important info for debugging the new menu under Windows 2000


You should never stop debugging when you are in middle painting of the menus,
because the special menu wouldn't be restored nor unsubclassed. Furthermore
there were a lot of strange effects. It is not forbidden, but be careful and
have a lot time for rebooting the system when it doesn't work correctly after
stopping the debugger.


History


23 January 2003 - Version 1.13




  • SetMenuText fixed.

  • Menubar color with theme fixed.

  • Added some helper for borderhandling of non CNewMenu

10 November 2002 - Version 1.11




  • Border drawing with menu animation under Win 98 corrected.

  • Border drawing / repainting when submenu closed fixed.

  • New menu style ICY.

  • Helperclasses for changing system menu for CommonDialog.

30 July 2002 - Version 1.10




  • Sharing Icons between menu for saving GDI-Resources.

  • New effects for drawing icons under STYLE_XPxx.

  • Recoloring of menu icons by changing system colors.

  • Drawing of the scroll button by extreme big menus a little bit better.

  • Mixing different icon sizes in a menu is now supported but they are not zoomed
    to the same size.

Known bugs




  • Menu border with shade is not updated when underground has been changed.

  • Menu border is drawn in a wrong size after changing the menu border style under
    windows settings. But restart the application and then it will work again fine.

  • The High Contrast Mode is not supported with all right colors.






<출처>
http://www.podetti.com/NewMenu/
TAG menu, MFC

MFC에서 타이틀 변경은 어떻게 하나요?

it/c 2003/02/06 15:26 Posted by newtype
메인 프레임의 타이틀은 변경을 해도
다른 작업을 하면 다시 원래대로 돌아갑니다.

"프로젝트이름 - 문서이름"

이렇게 되는데
바꾸려면 어떻게 하나요?
TAG MFC, Title