블로그 이미지
fiadot_old

칼퇴근을 위한 게임 서버 개발 방법론에 대한 심도있는 고찰 및 성찰을 위한 블로그!

Rss feed Tistory
Technical Article 2007. 8. 23. 11:58

_tcscpy, _tcsncpy, _tcsncpy_s 비교 및 올바른 사용법

TCHAR pszMsg[256];

 _tcscpy(pktSend.pszMsg, _T("Hello~ Everyone~"));// , sizeof(pktSend.pszMsg));

 _tcsncpy(pktSend.pszMsg, _T("5555555555555555"), sizeof(pktSend.pszMsg)); // it's wrong

 _tcsncpy(pktSend.pszMsg, _T("5555555555555555"), _countof(pktSend.pszMsg)); // it's right


_tcsncpy의 count인자는 ( Number of characters to be copied.)

 복사될 글자수를 의미하는데,

 UNICODE나 _UNICODE가 define되어있을때는 wchar가 아닐때는 char가 들어가게 되는데

 sizeof를 사용하게 되면 wchar일때 TCHAR pszMsg[256]이

 wchar pszMSg[256]이 되어 실질적인 사이즈가 516바이트가 된다.
 
 즉 ,다른부분의 스택 영역에 오버런이 발생하게 되는것이다.

 VS2005로 오면서 _countof키워드가 추가되었는데 이 키워드를 이용하게 되면,
 
 Compute the number of elements in a statically-allocated array.
 
 MSDN에 있는 설명대로 배열의 갯수를 반환하게 되는것이다.

  single byte나 multi byte나 신경안쓰고 배열의갯수 = 글자의 갯수로 보게 되면
 
 UNICODE선언 유무에 따라 유니코드인지 안시코드인지 결정하게 되는 스트링 관련
 
 함수들(_tcs 접두어가 붙는 CRT String macro functions)은 sizeof대신에 _countof를
 
 사용하면 된다.

 
 좀더 깊이 있게 살펴보자면 Security Enhanced 함수들도 찾아볼수 있는데,
 
 
  • Parameter Validation. Parameters passed to CRT functions are validated, in both secure functions and in many preexisting versions of functions. These validations include:
    • Checking for NULL values passed to the functions,
    • Checking enumerated values for validity,
    • Checking that integral values are in valid ranges.
  • For more information, see Parameter Validation.
  • There is also a handler for invalid parameters which is accessible to the developer. When an invalid parameter is encountered, instead of asserting and exiting the application, the CRT provides a way to check these problems with the _set_invalid_parameter_handler function.
  • Sized Buffers. The secure functions require that the buffer size be passed to any function that writes to a buffer. The secure versions validate that the buffer is large enough before writing to it, helping to avoid dangerous buffer overrun errors which could allow malicious code to execute. These functions will usually return an errno type of error code and invoke the invalid parameter handler if the size of the buffer is too small. Functions which read from input buffers, such as gets, have secure versions that require you to specify a maximum size.
  • Null termination. Some functions which left potentially non terminated strings have secure versions which ensure that strings are properly null terminated.
  • Enhanced error reporting. The secure functions return error codes with more error information than was available with the preexisting functions. The secure functions and many of the preexisting functions now set errno and often return an errno code type as well, to provide better error reporting.
  • Filesystem security. Secure file I/O APIs support secure file access in the default case.
  • Windows security. Secure process APIs enforce security policies and allow ACLs to be specified.
  • Format string syntax checking. Invalid strings are now detected, for example using incorrect type field characters in printf format strings.
  • Additional security enhancements are described in the documentation for each function.

     
    이런 기능들을 해준다.

    어렵게 생각할것없이 strcpy_s( ... ) ; 이런식으로, 일반 CRT스트링 함수에 _s 접미사를 붙여주면 된다.

    다만 dest영역의 크기를 지정해줘야 하는데, 이부분이 귀찮다면

    #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1

    이걸 헤더에 선언해줌으로써 전처리 과정에서 strcpy를 strcpy_s로 바꿔주게 된다.



    마지막으로, _tcsncpy와 _tcsncpy_s의 count부분에 sizeof를 사용해서 두 결과의 차이를 비교해보면

    _tcsncpy(pktSend.pszMsg, _T("5555555555555555"), sizeof(pktSend.pszMsg));

    _tcsncpy_s(pktSend.pszMsg, sizeof(pktSend.pszMsg), _T("5555555555555555"), sizeof(pktSend.pszMsg));

    _tcsncpy는 오버런 됐을때 0x00으로 채우게 되지만,

    _tcsncpy_s는 0xFD로 채워줌으로 바로 access viloation을 발생시켜준다.


  • ,
    Technical Article/펌 2007. 8. 8. 13:22

    Big O Expression

    보호되어 있는 글입니다.
    내용을 보시려면 비밀번호를 입력해주세요.

    Technical Article/펌 2007. 7. 31. 18:38

    Windows Console Application 추천 사이트

    Turbo C를 사용했던 개발 자라면 gotoxy가 친근할것이다.
    Windows Console Application에서도 유사한 기능이 있다는걸 아시는가??

    콘솔어플에 키이벤트를 날리려고 삽질하다가
    발견한 사이트...

    콘솔을 좀더 아름답게~~

    Working with Windows Consoles
    • A multi part tutorial demostrating features of Win32 Console Applications or "DOS boxes".
    • Part 1 describes consoles
    • Part 2 naming consoles, obtaining standard handles, moving the cursor, blocks of characters and clearing the screen
    • Part 3 drawing lines, boxes and grids and hiding/modifying the cursor
    • Part 4 colour
    • Part 5 keyboard and mouse events
    • Part 6 console size issues
    • There are some more parts of this tutorial to come



      http://www.adrianxw.dk/SoftwareSite/index.html
    ,
    Technical Article 2007. 7. 25. 23:53

    [기술세미나] Matlab을 이용한 OCR(문자인식)을 위한 이미지 프로세싱

    저번 창의과제에서 문자인식이 중요한 부분이였다.

    인식전에 영상처리 전처리가 필요한데,

    단순히 이진화, 라벨링, 분할 정도만 하면 끝인데

    C++로 코딩을 하자니 귀찮고 해서..

    '물론 나중에는 전부 C++로 다시 코딩했지만 ㅡㅡ;'

    Matlab가지고 영상처리를 하면서 정리해놨던 문서다.



    <<다운받기>>


     



     

    p.s matlab help랑 네이버 opencv카페에서 정보를 많이 구했다.
    ,
    Technical Article 2007. 7. 24. 10:41

    VS2005에서 Wizard를 통해 OLEDB Consumer생성시 Stored Procedure Accessor bind 실패 원인 분석

    VS2005에서 Wizard를 통해 OLEDB Consumer생성시 Stored Procedure Accessor bind 실패 원인 분석

    Visual Studio 6에서는 Insert - new ATL Object - Data Access - Consumer를 통해
    Stored Procedure(이하SP)에 대한 Accessor를 자동으로 생성할수 있다.
    이를 통해 다음과 같은 코드를 얻게된다.


    // dboSBuddyList1.H : Declaration of the CdboSBuddyList1 class

    #ifndef __DBOSBUDDYLIST1_H_
    #define __DBOSBUDDYLIST1_H_

    class CdboSBuddyList1Accessor
    {
    public:
     LONG m_RETURNVALUE;
     TCHAR m_userid[13];
     BYTE m_usertype;
     TCHAR m_coluserid[13];
     BYTE m_colusertype;
     TCHAR m_colnick[13];
     BYTE m_collev;
     TCHAR m_colcomment[61];

    BEGIN_PARAM_MAP(CdboSBuddyList1Accessor)
     SET_PARAM_TYPE(DBPARAMIO_OUTPUT)
     COLUMN_ENTRY(1, m_RETURNVALUE)
     SET_PARAM_TYPE(DBPARAMIO_INPUT)
     COLUMN_ENTRY(2, m_userid)
     COLUMN_ENTRY(3, m_usertype)
    END_PARAM_MAP()

    BEGIN_COLUMN_MAP(CdboSBuddyList1Accessor)
     COLUMN_ENTRY(1, m_coluserid)
     COLUMN_ENTRY(2, m_colusertype)
     COLUMN_ENTRY(3, m_colnick)
     COLUMN_ENTRY(4, m_collev)
     COLUMN_ENTRY(5, m_colcomment)
    END_COLUMN_MAP()

    DEFINE_COMMAND(CdboSBuddyList1Accessor, _T("{ ? = CALL dbo.S_BuddyList;1 (?,?) }"))

     // You may wish to call this function if you are inserting a record and wish to
     // initialize all the fields, if you are not going to explicitly set all of them.
     void ClearRecord()
     {
      memset(this, 0, sizeof(*this));
     }
    };

    class CdboSBuddyList1 : public CCommand<CAccessor<CdboSBuddyList1Accessor> >
    {
    public:
     HRESULT Open()
     {
      HRESULT  hr;

      hr = OpenDataSource();
      if (FAILED(hr))
       return hr;

      return OpenRowset();
     }
     HRESULT OpenDataSource()
     {
      HRESULT  hr;
      CDataSource db;
      CDBPropSet dbinit(DBPROPSET_DBINIT);

      dbinit.AddProperty(DBPROP_AUTH_PASSWORD, OLESTR("wing_access"));
      dbinit.AddProperty(DBPROP_AUTH_USERID, OLESTR("xxxxx"));
      dbinit.AddProperty(DBPROP_INIT_CATALOG, OLESTR("xxxxx"));
      dbinit.AddProperty(DBPROP_INIT_DATASOURCE, OLESTR("xx.xx.xx.xx"));
      dbinit.AddProperty(DBPROP_INIT_LCID, (long)1042);
      dbinit.AddProperty(DBPROP_INIT_PROMPT, (short)4);
      hr = db.Open(_T("SQLOLEDB.1"), &dbinit);
      if (FAILED(hr))
       return hr;

      return m_session.Open(db);
     }
     HRESULT OpenRowset()
     {
      return CCommand<CAccessor<CdboSBuddyList1Accessor> >::Open(m_session);
     }
     CSession m_session;
    };

    #endif // __DBOSBUDDYLIST1_H_


    이를 사용 하는 방법은

    #include "dboSBuddyList1.H"
    #define DB_STRING_FIELD_COPY(dest, src)  _tcsncpy(dest, src, sizeof(dest))


    void CDBTT2Dlg::OnButton1()
    {
     // TODO: Add your control notification handler code here
     
     CdboSBuddyList1 rs;
     HRESULT hr;

     DB_STRING_FIELD_COPY(rs.m_userid,  _T("lovydayz"));
     rs.m_usertype = 0;

     hr = rs.Open();

     hr = rs.MoveFirst();

     rs.Close();

    }



    이런식이다. 아이디와 타입을 SP의 인자로 넘기고 레코드셋을 받아오는 SP이다.


    VS2005에서는 추가-클래스-ATL OLEDB 소비자 를 통해서 생성하는데
    SP에 대해서는  insert, delete, update옵션이 적용되지 않으면 Accessor인지, Table인지
    설정하는 옵션이 disable되고 파일을 생성해준다.


    class CS_BuddyListAccessor
    {
    public:

     // 다음 마법사 생성 데이터 멤버에는  열 맵의 해당
     // 필드에 대한 상태 값이 들어 있습니다. 이 값을
     // 사용하여 데이터베이스에서반환하는 NULL 값을
     // 보유하거나 컴파일러에서 오류를 반환할 때
     // 오류 정보를 보유할 수 있습니다. 이러한 필드 사용에
     // 대한 자세한 내용은 Visual C++ 설명서의
     //  "마법사 생성 접근자"에서 "필드 상태 데이터 멤버"를 참조하십시오.
     // 참고: 데이터를 설정/삽입하기 전에 이들 필드를 초기화해야 합니다.

     DBSTATUS m_dwuserid1Status;
     DBSTATUS m_dwusertype1Status;
     DBSTATUS m_dwnickStatus;
     DBSTATUS m_dwlevStatus;
     DBSTATUS m_dwcommentStatus;

     // 다음 마법사 생성 데이터 멤버에는 열 맵의 해당 필드에 대한
     // 길이 값이 들어 있습니다.
     // 참고: 가변 길이 열의 경우 데이터를 설정/삽입하기 전에
     //       이러한 필드를 초기화해야 합니다.

     DBLENGTH m_dwuserid1Length;
     DBLENGTH m_dwusertype1Length;
     DBLENGTH m_dwnickLength;
     DBLENGTH m_dwlevLength;
     DBLENGTH m_dwcommentLength;


     TCHAR m_userid1[13];
     BYTE m_usertype1;
     TCHAR m_nick[13];
     BYTE m_lev;
     TCHAR m_comment[61];

     // 인자들은 리턴되는 레코드셋보다 상단에 있어야 한다!!! - 이근호 070724
     LONG m_RETURN_VALUE;
     TCHAR m_userid[13];
     BYTE m_usertype;


     void GetRowsetProperties(CDBPropSet* pPropSet)
     {
      pPropSet->AddProperty(DBPROP_CANFETCHBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
      pPropSet->AddProperty(DBPROP_CANSCROLLBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
      pPropSet->AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);
     }

     HRESULT OpenDataSource()
     {
      CDataSource _db;
      HRESULT hr;
    // #error Security Issue: The connection string may contain a password
    // 아래 연결 문자열에 일반 텍스트 암호 및/또는
    // 다른 중요한 정보가 포함되어 있을 수 있습니다.
    // 보안 관련 문제가 있는지 연결 문자열을 검토한 후에 #error을(를) 제거하십시오.
    // 다른 형식으로 암호를 저장하거나 다른 사용자 인증을 사용하십시오.
      hr = _db.OpenFromInitializationString(L"Provider=SQLOLEDB.1;Password=xxxxx;Persist Security Info=True;User ID=xxxxx;Initial Catalog=xxxx;Data Source=xx.xx.xx.xx;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=LEEGUNHO;Use Encryption for Data=False;Tag with column collation when possible=False");
      if (FAILED(hr))
      {
    #ifdef _DEBUG
       AtlTraceErrorRecords(hr);
    #endif
       return hr;
      }
      return m_session.Open(_db);
     }

     void CloseDataSource()
     {
      m_session.Close();
     }

     operator const CSession&()
     {
      return m_session;
     }

     CSession m_session;

     // 일부 공급자와 관련된 몇몇 문제점을 해결하기 위해 아래 코드에서는
     // 공급자가 보고하는 것과 다른 순서로 열을 바인딩할 수 있습니다.

     BEGIN_COLUMN_MAP(CS_BuddyListAccessor)
      COLUMN_ENTRY_LENGTH_STATUS(1, m_userid1, m_dwuserid1Length, m_dwuserid1Status)
      COLUMN_ENTRY_LENGTH_STATUS(2, m_usertype1, m_dwusertype1Length, m_dwusertype1Status)
      COLUMN_ENTRY_LENGTH_STATUS(3, m_nick, m_dwnickLength, m_dwnickStatus)
      COLUMN_ENTRY_LENGTH_STATUS(4, m_lev, m_dwlevLength, m_dwlevStatus)
      COLUMN_ENTRY_LENGTH_STATUS(5, m_comment, m_dwcommentLength, m_dwcommentStatus)
     END_COLUMN_MAP()

     BEGIN_PARAM_MAP(CS_BuddyListAccessor)
      SET_PARAM_TYPE(DBPARAMIO_OUTPUT)
      COLUMN_ENTRY(1, m_RETURN_VALUE)
      SET_PARAM_TYPE(DBPARAMIO_INPUT)
      COLUMN_ENTRY(2, m_userid)
    //  SET_PARAM_TYPE(DBPARAMIO_INPUT)
      COLUMN_ENTRY(3, m_usertype)
     END_PARAM_MAP()

     DEFINE_COMMAND_EX(CS_BuddyListAccessor, L"{ ? = CALL dbo.S_BuddyList;1 (?,?) }")

    };

    class CS_BuddyList : public CCommand<CAccessor<CS_BuddyListAccessor> >
    {
    public:
     HRESULT OpenAll()
     {
      HRESULT hr;
      hr = OpenDataSource();
      if (FAILED(hr))
       return hr;
      __if_exists(GetRowsetProperties)
      {
       CDBPropSet propset(DBPROPSET_ROWSET);
       __if_exists(HasBookmark)
       {
        if( HasBookmark() )
         propset.AddProperty(DBPROP_IRowsetLocate, true);
       }
       GetRowsetProperties(&propset);
       return OpenRowset(&propset);
      }
      __if_not_exists(GetRowsetProperties)
      {
       __if_exists(HasBookmark)
       {
        if( HasBookmark() )
        {
         CDBPropSet propset(DBPROPSET_ROWSET);
         propset.AddProperty(DBPROP_IRowsetLocate, true);
         return OpenRowset(&propset);
        }
       }
      }
      return OpenRowset();
     }

     HRESULT OpenRowset(DBPROPSET *pPropSet = NULL)
     {
      HRESULT hr = Open(m_session, NULL, pPropSet);
    #ifdef _DEBUG
      if(FAILED(hr))
       AtlTraceErrorRecords(hr);
    #endif
      return hr;
     }

     void CloseAll()
     {
      Close();
      ReleaseCommand();
      CloseDataSource();
     }
    };


     

    이와 같이 생성된 코드를 테스트 해보면 결과값을 얻어오지 못함을 알수 있다.

    Bind부분에서 ATLASSERT(m_pAccessor != NULL) 에서 걸린다.


    왜 바인딩을 못하는것인가??

    Wizard page를 통해 생성한 코드가 제대로 실행이 안된다는것은 말이 안된다는 가정하에
    문제에 접근해 보았다. ATL코드를 죽 훑어보며 DynamicAccssor를 사용하니 문제없이 되었다.
    그렇다면 문제는 ATL코드 자체에 있거나 생성한 코드에 있다는 2가지 결론에 도달할수 있었다.

    VS6에서 생성한 코드와 비교하며 매크로의 차이, 멤버변수의 차이, 메소드의 차이 등에 대해서
    비교를 해보았다.
    DEFINE_COMMAND가 DEFINE_COMMAND_EX로 변경되고 CALL시에 SP이름뒤에 ;가 붙는다는것 이외에는 별 문제가 없었다. 결국 binding시의 변수 순서에 문제가 있는것으로 생각하고,
    몇번의 삽질을 통해 SP의 인자로 들어가는 멤버변수는 결과 레코드셋이 담기는 변수들 위에
    위치해야 된다는 결과를 얻게 되었다.

    거의 3일동안 삽질하다가 오늘도 안되면 그냥 DynamicAccessor를 이용해서 돌아가려고 했는데,
    다행히 문제가 해결됐다 ^^;

    ,
    Technical Article 2007. 7. 14. 15:29

    VS2005에 추가된 키워드 for each

    STL을 사용하다 전체 노드에 대해 모두 어떠한 함수를 적용해야 할때

    for_each란 generic algorithm을 사용하게 된다.

    그런데 이 부분이 좀 문제가 있는게 함수를 쓰던지 클래스를 새로 생성해서

    operator()를 구현해줘야 한다.

    심플하게 끝내자고 해놓은 것이 복잡도만 높이고 있는 형국이다.

    이를 타계하기 위해서는 2가지 방법이 있다.

    첫번째는 for_each에 대해 적용될 함수셋을 구현해놓는방법.

    http://levites.pe.kr/tt/index.php?pl=20 

    여기에 잘나와있던데 좀 불편하다.

    두번째는 Visual Studio 2005에 추가된 키워드인 for each를 사용하는 방법이다.


    class CBroadCast
    {
    public:
     CBroadCast(CPacket &refSendPkt) : m_refSendPkt(refSendPkt), m_nSendCnt(0)
     {
     
     }

     void operator()(CFiaSession* pNode)
     {
      pNode->SendPacket(m_refSendPkt);
      m_nSendCnt++;
     }


     CPacket &m_refSendPkt;
     int  m_nSendCnt;
    };


     

    int  CRoom::BroadCasting( CFiaSession *pSession, CPacket &refSendPkt)
    {
    ..
    CBroadCast bc(refSendPkt);
    bc += for_each(m_vecUser.begin(), m_vecUser.end(), bc);
    ..
    }


    이런 형태로 STL for_each를 사용 했다면,

     int nCnt = 0;
     for each(CFiaSession* it in m_vecUser)
     {
      it->SendPacket(refSendPkt);
      nCnt +=1;
     }

    2005에서는 이렇게 for each를 사용하면 된다.


    일반적으로 iterator를 이용해서 순회하면서 for each의 형태로 구성 해서 사용했다.

    하지만, OS종속적이며 퍼포먼스를 지향하는 서버 어플리케이션에서

    OS호환성은 고려대상의 범위에서 벗어나기에, for each로 구현을 했다.


    지은이햄의 테스트 결과로 "for_each보다 for each가 퍼포먼스 면에서도 우수했다"고 한다. ^^

    코드면에서도 깔끔하고 성능도 나으니 안쓸이유가 없네..

    ,
    Technical Article 2007. 7. 9. 11:53

    NateON dispatch Interface를 통한 친구상태 체크하기

    #import "progid:NateOn.NateMessengerApi.1" no_namespace
    void CAutoTestDlg::OnBnClickedButton1()
    {
     // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.

    //  uuid(A02AC169-B150-43DE-8BD4-109B9DE6B34B),
    //
    //  INateOnApiAtx
    //
    //  dispinterface INateMessengerApi
    //
    //  [id(0x0000000c), helpstring("method GetMyId")]
    //  BSTR GetMyId();
     
     INateMessengerApiPtr m_pNate;

     m_pNate = INateMessengerApiPtr(__uuidof(NateMessengerApi));

     BSTR pID = m_pNate->GetMyId();

     TCHAR szFinal[255];
     // direct conversion from BSTR to LPCTSTR only works
     // in Unicode
     _stprintf(szFinal, _T("%s"), (LPCTSTR)pID);
     AfxMessageBox(szFinal);

     CString str;

    VARIANT_BOOL bOnline = m_pNate->IsBuddyOnline("dlsdoㅌㅌㅌ@nate.com");
     if ( VARIANT_TRUE  == bOnline )
      str.Format(L"true");
     else
      str.Format(L"false");

     AfxMessageBox(str);


    COM 공부하다가 뻘짓 -_-;;;

    OLE View로 타입라이브러리 보다가 '참 많은 놈들이 COM으로 짰구나' 생각하다

    아니 네이트~~ 이러면서 그냥 테스트 해본거..

    물론 ATL은 필수!
    ,
    Technical Article/펌 2007. 7. 1. 00:38

    CxImage 메소드 요약

    - Cximage api에 대해 잘 정리해 놓은것 퍼옴.^^
    http://katalog.egloos.com/tb/2626276


    출처 : http://hogwarts.tistory.com/tag/CxImage 

    ,
    Technical Article/펌 2007. 6. 12. 09:08

    MFC 헤더와 라이브러리 설명 (The Foundation Classes Headers and Libraries)

    보호되어 있는 글입니다.
    내용을 보시려면 비밀번호를 입력해주세요.

    Technical Article 2007. 5. 12. 00:18

    [기술세미나] How to use OLEDB using ATL

    invalid-file

    ATL을 이용해서 OLEDB를 쉽고 편하게 사용하는 방법에 관한 기술세미나



    기술공유에 대한 의지보다 발표 능력 향상과 내가 보유한 기술에 대한 정리의 목적이 더 크다.

    나눔으로써 얻게되는게 많다는 말이 와닿는 ... ^^


    2002년인가 2003년부터 OLEDB에 관심을 갖기시작해서

    인터페이스를 접하고 msdn보면서 하나하나 구축해보다

    ATL로 사용하면서 그 편의성을 확실히 깨닿고 나서

    시간이 지나서 더 까먹기 전에

    기초만이라도 정리해놓자는 생각으로 만든 PPT...


    아무래도 중고급 기술은 빠져있어서

    많은 사람들에게 도움이 될지는 모르겠지만

    어쨌든

    OLEDB + ATL의 조합은 환상적 +_+


    ,
    TOTAL TODAY