ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 간단한 프로그램에 벌써 3번째 비정상종료 발견 --;
    개발 2007.10.26 19:16

    며칠 전에 배포한 티스토리용 댓글 알리미용 프로그램
    2007/10/23 - [프로그램] - 티스토리 라이브 Ver. 0.1b

    에서 벌써 3번째 비정상적으로 종료되는 문제를 발견해서 패치 했습니다.

    너무 오랫동안 강호(프로그램 세계)를 떠나있었다고 위안을 삼고는 있지만, 요 작은 프로그램을 만들면서 비정상 종료와 같은 큰 문제를 3개나 일으키다니 유구무언입니다.

    후배들의 코드를 맨날 코드 리뷰 한답시고, 문제를 찾는 제가 오히려 문제를 만드는 사람이란 것도 아이러니 하네요. 코딩 안 하는 게 회사를 돕는 일이겠습니다 ^^;

     

    코딩 할 때 항상 같은 문제를 또 일으키는 경우가 많은데, 그럴 때를 대비해서라도 이번에 발생한 3가지 비정상 종료의 원인이 된 코드를 적어 보려고 합니다.

     

    1.     STL::string 의 헷갈리는 생성자

    첫 번째 Crash string 생성자를 잘못 사용해서 발생했습니다.

    코드는 http://eslife.tistory.com/ 과 같은 주소를 입력 받아 맨 끝에 ‘/’ 가 있을 경우 이를 제거하는 간단한 코드였습니다. 문제가 된 코드는 이렇습니다.

    string sURL = (LPCSTR)m_sBlogURL;

    // 맨 끝에 / 가 있으면 제거

    string::size_type idx = sURL.find_last_of("/");

    if (idx == sURL.size() - 1)

    {

               sURL = string(sURL, sURL.size() - 1);                 // 비정상종료 원인

               m_sBlogURL = sURL.c_str();

    string sNakidURL = string(sURL, 7, sURL.npos);      // 비정상종료 발생

    }


    STL
    string 에는 다음과 같이 문자열의 일부만을 가져가는 편리한 생성자가 있습니다.  

    string s(string str, stridx)

    string s(char* chars, chars_len)

    문제는 비슷하게 생긴 이 두 생성자가 하는 일이 전혀 다릅니다.

    첫 번째 생성자는 stridx 에서부터 문자열을 복사하고

    두 번째 생성자는 문자열의 처음부터 chars_len 만큼 문자열을 복사합니다.

    그래서 sURL http://eslife.tistory.com/가 들어 있을 경우

    sURL = string(sURL, sURL.size() - 1);          → sURL = "/"

    이 리턴 되고

    sURL = string(sURL.c_str(), sURL.size() - 1);   → sURL = "http://eslife.tistory.com"

    이 됩니다.

    아 간단한 내용인데 설명하려니 기네요.

    결론은 제가 string 의 생성자에 char* 형을 넘겼어야 하는데, string 객체를 잘못 넘겨서 예상과 다른 값을 리턴 받았고 이로 인해 프로그램이 죽었습니다. (장합니다.--)

     

    2.     STL::string NULL 을 세팅 하면 죽는다 --;

    어찌 보면 당연한 일입니다. 문제가 된 코드는 웹 문서를 파싱하면서 ATL W2A 매크로를 사용해서 BSTR 데이터를 string 에 할당하면서 발생했습니다.


    CComBSTR bOutText;

    hr = pElmtChild->get_innerText(&bOutText);

    string sText = W2A(bOutText);  // 프로그램Crash!!


    get_innerText
    에서 오류가 났거나 해서 bOutText 에는 NULL 이 들어갔나 봅니다. NULL 값을 string 에 할당하면 바로 죽네요. 문자열이 NULL 로 끝나기 때문에 NULL 을 할당하더라도 얘교로라도 죽지 않아야 할 것 같기도 한데 우리 예민한 STL::string 은 바로 죽습니다.

    string sText = NULL; // 수행 즉시 죽습니다.!

     

    3.     리스트컨트롤 칼럼 헤더 타이틀 변경하다 비정상종료

    이 부분은 좀 할말 없게 되었습니다. 정말이지 워낙 오래간만에 코딩하다보니, 윈도우 기본 확장 컨트롤인 리스트 컨트롤 사용법도 잊었나 봅니다.

    원래 하려던 일은 리스트 컨트롤의 칼럼 타이틀 속성을 읽어 들여, 텍스트 값만 변경하는 거였습니다. 문제의 코드는 다음과 같습니다.


    CListCtrl m_Result;

    LVCOLUMN colInfo;

    m_Result.GetColumn( 0, &colInfo );

    // ... 중략

    colInfo.pszText = "최근댓글";

    m_Result.SetColumn(0, &colInfo);


    위 코드는 죽을 때도 있고 안 죽을 때도 있습니다.  늘 죽지 않는 코드는 아래와 같이 구하려는 칼럼에 대한 MASK 와 문자열 배열을 넘겨주어야 합니다. (윈도우 API를 보면 모 하나 가져오기 참 복잡합니다. 상용컨트롤이나, 직접 만든 컨트롤들은 원하는 값을 구하기 쉬운데 반해 리스트나 트리 같은 확장 컨트롤들은 개발한지 몇 년 지나니 전혀 생각이 안납니다. --)


    LVCOLUMN colInfo;

    TCHAR sColText[160];

    colInfo.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;

    colInfo.pszText = sColText;

    colInfo.cchTextMax = 160;

    m_Result.GetColumn( 0, &colInfo );

    colInfo.pszText = "최근댓글";

    m_Result.SetColumn(0, &colInfo);

     

     

     

    3가지 문제 모두 공통적인 문제가 있네요

    • MSDN 을 제대로 확인하지 않고 코딩 했다.
    • 오래 전의 희미한 기억에 따라 날로 먹으려고 했다.
    • 함수의 리턴 값을 제대로 테스트 하지 않았다
    • 코드 검토를 전혀 하지 않았다.
       

    반성하고, 자주 코딩 하는 수 밖에 없을 것 같습니다.

    다른 얘기로 요즘 들어 “Programming Rubby” 를 읽고 있는데 언어가 참 마음에 듭니다.

    “abc”.length

    -1942.abs

    와 같이 상수들도 객체로 인식되어 바로 객체의 함수를 부를 수도 있고 배열과 map [] {} 만으로 간단하게 정의해 버리네요.

    세상은 참 모르는 것 천지입니다.

    배울 건 많은데, 기존에 알고 있던 건 다 잊어 버리고 있고, 머리가 텅 빈 것 같습니다.

    주말에, 텅 빈 머리에 가을 바람이라도 채우러 가야겠습니다. ^^;

     

     

    Daum 블로거뉴스에서 이 포스트를 추천해주세요. [추천]


    댓글 12

    • 프로필사진

      이해를 못하겠어요..

      2007.10.26 21:09 신고
      • 프로필사진

        C++ 과 윈도우 프로그램에 대한 이야기라 이해가 안되시는게 맞습니다 --; 안 읽으셔도 되는데 머리만 어지럽혀 드렸네요 ^^;

        2007.10.27 05:07 신고
    • 프로필사진

      저도 뭔가 이해하려고 뚫어지게 쳐다봤는데 모르겠네요 ^^;;;

      2007.10.26 21:32 신고
      • 프로필사진

        저도 우성군님이 병리학에 대한 깊은 지식을 글로 쓴다면 전혀 못알아 들을거예요 ^^;
        이해하실 필요 전혀 없는 얘기들입니다. :-)

        2007.10.27 05:08 신고
    • 프로필사진

      너무 자책하실 필요 없을것 같아요. ㅎㅎ
      사람이 만든것에 결함이 없는게 더 이상하지 않나요?
      빨리 원인을 찾아낸 것이 더 대단하다고 생각됩니다.

      2007.10.26 22:07
      • 프로필사진

        테스트 없이 좀 급하게 공개하다 보니. 이래 저래 문제가 많았던것 같습니다.
        원인은 찾은건 문제가 있음을 바로 보고 해 준 사용자들 덕분이예요 ^^;
        프로그래머의 길은 참 멀고도 험합니다. 나이가 들면 들수록 말이예요 ~

        2007.10.27 05:10 신고
    • 프로필사진

      오옷~ 안그래도 그거 말씀드릴려고 했는데... ^^
      고맙습니다. ㅎㅎ
      tialert.exe와 사용후 비교 글을 써볼려고 하고 있었거던요.
      미리 말씀드리면 tlite로 정했습니다만,
      버전업 하실때 참고 해주시면 감사감사... ^^
      1. 트레이아이콘(지금버전은 트레이아이콘이 없지만)과 창이 서로 전환될수 있으면 좋겠습니다. 넘 어려운가요. -_-;
      2. 자동 로그인 선택사항도 있으면 좋겠네요. ^^;
      3. tialert.exe처럼 더블클릭으로 내 블로그가기도 있으면 좋겠습니다.
      ㅎㅎ 넘 요구가 많나요. 아무튼 고맙습니다. 잘 쓰고 있습니다.
      초초강추에요. ^^

      2007.10.26 23:17 신고
      • 프로필사진

        문제를 바로 보고 해 주셔서 감사합니다. ^^;
        말씀하신 내용들은
        1. 항상 보이도록 하고 있어서 트레이로 할 필요가 있을지 모르겠습니다. 어려운건 아니예요 ^^
        2. 자동로그인 할려면 비밀번호를 PC 에 저장해야 하는데 비밀번호가 PC 에 기록하는 것 자체가 좀 위험하다 싶어서 하지 않았습니다. 꼭 필요한 경우엔 고려해 보겠습니다.
        3. 좋은 아이디어네요. 고려해 보겠습니다. ^^;
        감사합니다 ~

        2007.10.27 05:17 신고
    • 프로필사진

      저도 한번 당했는데....^^;
      그런 문제가 있었군요. (사실은 글을 전혀 이해하지 못했음. ㅜㅜ)
      아무튼..... ㅋㅋ

      컴퓨터를 부팅했을 때 네트워크가 연결되기 전에 로그인을 시도하면 접속에 실패했다고 바로 튕겨져 나오는데, 좀 기다려주는 센스를 보여줬으면 좋겠습니다. ㅎㅎㅎ

      2007.10.27 00:37 신고
      • 프로필사진

        아마 다른 분들도 당했을것 같습니다 --;;
        제 불찰이예요. 천천히 올리더라도 좀 더 테스트를 많이 해 봐야겠습니다.

        그리고 pc 부팅시 네트워크 연결전에 연결을 시도하는 경우는 전혀 생각 못해 봤습니다 --;; 일단 적어 놓고 공부해 보겠습니다. ^^;

        2007.10.27 05:20 신고
    • 프로필사진

      string s = NULL; 이 사실 NULL(0)을 char* -> string으로 캐스팅, 사실은 복사 생성자가 불릴텐데...
      소스 보니 복사 생성자 내부에 메모리 할당 전 char*에 대한 strlen을 콜하는게 있네요.
      요게 char*가 NULL이면 access violation을 날려버리는군요.

      2007.10.29 10:15
      • 프로필사진

        네 ^^;
        ATL 의 ComPtr 같은 경우는 = 연산자에 NULL 이 들어올 경우 특별히 처리해 주는데, 쩝.
        쓰는 사람이 잘못 쓴거니 담부터는 char* 를 string 으로 넣을때 조심해야겠어요^^;

        2007.10.29 10:22 신고
Designed by black7375.