2010년 12월 5일 일요일

Project H3

 

H3 동영상이 공개가 되었다..

 

참 열악한 상황해서 개발했는데.. 아둥바둥 노력해서 모양세가 나오는것 보니

참 눈물이 앞을 가린다.

 

지금은 자리를 옮겼지만 한때 나의 노력이 녹아있기 때문에 잘되었으면 하는 바램이다.

2010년 12월 3일 금요일

스레드 동기화

펌) http://kslive.tistory.com/23

 

◎ 동기화의 필요성

 다수의 스레드가 하나의 데이터를 동시에 엑세스를 하는 경우는 우리가 스레드 프로그래밍을 하는 동안에는 피할 수 없는 현상이다. 다수의 스레드가 하나의 데이터를 읽기만 한다면야 이 데이터에대한 동기화의 필요성은 필요하지 않을 것이다. 왜냐하면 읽기만 한다는건 데이터가 변하지 않는다는것을 말하기때문이다. 그러므로 어느 스레드에서든 해당 데이터의 값은 동일할 것이다. 하지만 다수의 스레드가 하나의 데이터에 대한 읽기와 쓰기의 작업을 동시에 한다면 스레드의 동기화를 사용하지 않는다면 이 데이터에 대한 무결성을 입증할 방법이 존재할 수 있을까? 답은 없다이다. CPU는 한줄의 대입 연산을 실행하는 경우에도 그에 해당하는 여러줄의 기계어 코드를 만들기때문에 원자적 접근이 되지않기 때문이다.


 ◎ 원자적 접근

 스레드 동기화는 원자적 접근을 보장해주는 일이다. 원자접근은 같은 시간에 같은 리소스에 다른 스레드가 접근하지 않도록 하면서 해당 스레드가 리소스에 접근하도록 하는 일일 말한다.


 ◎ Interlocked 계열의 함수

 Interlocked 계열의 함수는 하나의 long형 변수의 값을 변경할때 원자접근을 수행하는 함수를 말한다. Interlocked 계열의 함수는 다음과 같이 존재한다. (현재는 더 존재할 수 있다.)


. LONG InterlockedIncrement (LONG volatile* Addend) : Addend 에 저장되어있는 long형 변수를 1 증가 시킨다. Rv는 증가된 후의 값이다.


. LONG InterlockedDecrement(LONG volatile* Addend) : Addend 에 저장되어있는 long형 변수를 1 감소 시킨다. Rv는 감소된 후의  값이다


. LONG InterlockedExchage(LONG volatile* Target, LONG Value) : Target에 저장되어있는값을 Value값으로 변경한다. Rv는 최초 Target에 저장되어있는 값이다.


.PVOID InterlockedExchangePointer(PVOID volatile* Target, PVOID Value) : Target에 저장되어있는 값을 Value에 저장되었는 값으로 변경한다. Rv는 최초 Target에 저장되어있는 값이 있는 번지를 리턴한다.


.LONG InterlockedExchangeAdd(LONG volatile* Target, LONG Increment) : Target에 저장되어있는값에 Increment값을 더한다. Rv는 최초 Target에 저장되어있는 값이다.


.LONG InterlockedCompareExchange(LONG volatile* Dest, LONG Exchange, LONG Compare) : Dest에 저장되어있는값이 Compare와 동일하면 Dest의 값을 Exchange으로 변경한다. Rv는 최초 Dest에 저장되어있는 값이다.


.PVOID InterlockedCompareExchangePointer(PVOID* pDest, PVOID pExch, PVOID pCompare) : pDdest에 저장되어있는 값이 pCompare와 동일하면 pDest의값은 pExch값으로 변경된다. Rv는 최초 pDest의 값이다.


 ◎ CRITICAL_SECTION

 CRITICAL_SECTION 은 윈도우에서 제공하는 동기화 객체 중의 하나로서 동기화 객체 중 유일하게 유저모드에서 실행된다.(커널모드로의 전환이 일어나면 CPU사이클을 많이 잡아먹는다. 한마디로 느리다는것이다.)  

  CRITICAL_SECTION의 사용법은 다음과 같다.


1.  CRITICAL_SECTION 구조체를 초기화 한다.


void InitializeCriticalSection(LPCRITICAL_SECTION pCs);


사용할 CRITICAL_SECTION 객체의 주소를 넘겨준다.


2. 임계영역에 진입한다.


VOID EnterCriticalSection(LPCRITICAL_SECTION pCs);


3. 해당작업을 한다. (원자성을 보장한다.)


4. 임계영역을 탈출한다


void LeaveCriticalSection(LPCRITICAL_SECTION pCs);


 ※ 인자로 들어가는 CRITICAL_SECTION 구조체의 주소가 동일해야한다.

 ※ 3번에서 원자성을 보장한다는 얘기는 해당작업을 하는 부분에서 사용하는 공통된 데이터에 접근 하는 모든 스레드에서 EnterCriticalSection와 LeaveCriticalSection를 사용해야 하며, 인자로 넘어가는 CRITICAL_SECTION 의 구조체의 주소는 모두 동일해야 한다.


위의 순서에서 사용된 함수들에 대한 설명은 다음과 같다.


void InitializeCriticalSection(LPCRITICAL_SECTION pCs)

 : CRITICAL_SECTION 구조체를 초기화한다. 이 함수는 단지 멤버변수를 설정하기 때문에 실패하지 않는다. 이 함수는 EnterCriticalSection을 호출하기전에 반드시 호출되어야한다. 초기화되지않은 CRITICAL_SECTION 에 진입을 시도할시 결과는 정의되지 않는다고 SDK문서에 명시되어있다.

※ 이 함수가 실패는 하지 않지만 예외상황을 발생시킬수있다. 위 함수는 디버깅 정보가 있는 메모리 블럭을 할당하기 때문에 메모리 블럭 할당에 실패하였을 경우 STATUS_NO_MEMORY 예외를 던진다. SEH를 이용하여 예외를 Catch할수있다.


.void DeleteCriticalSection(LPCRITICAL_SECTION pCs)

 : DeleteCriticalSection은 구조체 내의 멤버 변수를 재설정한다(제거). 어떤 스레드가 여전히 이 구조체를 사용하고있다면 자연히 임계영역은 삭제할 수 없다. SDK 문서에서는 이렇게 할때 결과는 정의되지 않는다고 나와있다.


.VOID EnterCriticalSection(LPCRITICAL_SECTION pCs)

 : 임계영역에 진입을 시도한다.

 ※ 한 CRITICAL_SECTION 에 대해 두번의 EnterCriticalSection은 시도할경우 최초 진입 성공시 두번째 EnterCriticalSection시에는 CRITICAL_SECTION의 접근 스레드를 표시하는 변수를 업데이트하고 바로 리턴한다. (Enter가 두번이면 Leave도 두번호출해야한다.)


BOOL TryEnterCriticalSection(LPCRITICAL_SECTION pCs)

 : 임계영역에 진입을 시도한다. 이 함수는 이함수를 호출하는 스레드가 절대 대기상태로 진입을 하지 않는다. 이 함수가 나타내는 Rv값은 다른 스레드에서 해당 임계영역에 진입을 하고있는 상태면 FALSE, 아니면 TRUE이다. 이 함수는 해당 임계영역에 진입이 가능하면 진입을 시도하고 아닐경우 바로 리턴하여(FALSE) 다른 일을 한다.

※ Windows 2000 이상에서만 지원한다.


.void LeaveCriticalSection(LPCRITICAL_SECTION pCs)

 : 이 함수는 호출한 CRITICAL_SECTION에 접근하고있는 스레드의 개수를 하나 감소시킨다. 접근스레드의 개수가 1이상이면 감소 후 바로 리턴하고.개수가 0개이면EnterCriticalSection을 호출한 다른 스레드가 대기 상태에 있는지 검사 후 존재하면 해당 스레드에게 맞게 CRITICAL_SECTION 구조체를 업데이트하고 바로 해당스레드를 스케쥴한다.(여러개가 존재할경우 공정하게 선택한단다.-_-;;) 대기하는 스레드가 없을 경우 멤버함수를 업데이트하여 접근하는 스레드가 없음을 나타낸다.


 ◎ 스핀록

 스레드가 EnterCriticalSection을 호출했을 시 해당 임계영역을 다른 스레드가 소유하고있으면 호출한 스레드는 바로 대기모드로 전환한다. 이는 유저모드에서 커널모드로의 전환을 의미한다.(CPU사이클을 많이 소모한다.) 이 전환은 소모가 매우 크다. 그래서 MS는 이 비용을 줄이기 위해 임계 영역안에 스핀록을 만들었다. EnterCriticalSection을 호출했을시 EnterCriticalSection은 리소스를 몇번 동안 요청을 시도하기위해 시핀록을 사용해 루프를 돈다. 오직 모든 시도가 실패하게 될때 스레드는 커널모드로(대기모드) 전환된다.


스핀록을 사용하기위해서는 다음과 같은 함수를 사용한다.


BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION pCs, DWORD dwSpinCount);

: InitliazeCriticalSection을 사용하지 말고 위 함수를 사용하여 CRITICAL_SECTION을 초기화 해야한다. dwSpincount는 리소스 재요청 횟수이다. 위 횟수는 0~ 0x00FFFFFF 사이의 어떤값도 될수 있다. InitliazeCriticalSection 은 항상 성공하며 가끔 예외(?)를 던지지만, 이 함수는 메모리 할당에 실패하면  FALSE를 리턴한다. 훨씬 좋다. 사용하자.

 ※ 프로세서가 하나인 머신에서는 dwSpinCount무시되고 항상 0 으로 설정된다.


DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION pCs, DWORD dwSpinCount)

 : 위 함수는 해당 CRITICAL_SECTION객체의 스핀횟수를 변경한다.

 ※ 역시 프로세서가 하나인 머신에서는 위의  dwSpinCount값은 무시되고 0으로 설정된다.

※ 임계영역에서 스핀록을 사용하면 손해보지는 않는다. 지원을 하면 항상 사용하자.


 ◎ 임계영역 & 에러 핸들링

내부적으로 임계 영역은 두개 이상의 스레드가 동시에 임계 영역을 가지고 경쟁할 경우 이벤트 커널 오브젝트를 사용한다. 이런 경쟁이 드물면 시스템은 이벤트 커널 오브젝트를 생성하지 않는다. 메모리가 적은 상황에서 임계 영역에대한 경쟁 상황이 발생할 수 있고 시스템은 요청된 이벤트 커널 오브젝트의 생성에 실패하게 되면  EnterCriticalSection함수는 EXCEPTION_INVALID_HANDLE 예외를 발생한다. 이런 예외에 대한 처리는 대부분 하지 않기 때문에 치명적인 상황이 발생할 수 있다.


 위의 상황을 방지하기 위해서는 SEH로 예외를 핸들링하는방법과(비추이다.)

InitializeCriticalSectionAndSpinCount을 사용해서 임계영역을 생성하는것이다. (dwSpincount를 높게 설정해서) dwSpinCount가 높게 설정되면 이 함수는 이벤트 커널 오브젝트를 생성하여 이것을 임계영역과 연결한다. 만약 높은 비트로 설정이 되었는데 이벤트 커널 오브젝트가 생성되지 않으면 이 함수는 FALSE를 리턴한다. 성공적으로 이벤트 커널 오브젝트가 생성되면 EnterCriticalSection은 잘동작하고 절대 예외를 발생하지 않는다.



※ EnterCriticalSection 실행 시 다른 스레드가 해당 임계영역을 소유하고있다면 무기한 대기를 하는것이 아니고 레지스트리에 등록된 시간만큼만 대기하고 하나의 예외를 발생한다.

위치는 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manger 에 위치한 CriticalSectionTimeout 값이다. 단위는 초이고 기본값으로 2592000초이다. 대략 30일이다.;; (한마디로 무한대기구먼.ㅡ_ㅡ;;)

2010년 11월 25일 목요일

File version resource

/// 버전 리소스 사이즈 얻기

DWORD dwHandle = 0;

DWORD dwSize = ::GetFileVersionInfoSize(szFilePath, &dwHandle);

 

/// 버전 리소스 정보 얻기

LPBYTE lpBuffer = new BYTE[dwSize];

::ZeroMemoty(lpBuffer, dwSize);

::GetFileVersionInfo(szFilePath, 0, dwSize, lpBuffer);

 

/// 리소스 핸들 얻기

HANDLE hResource = ::BeginUpdateResource(szFilePath, FALSE);

 

/// 리소스 정보 갱신

::UpdateResource(hResource, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), wTanslationL/*LCID*/, lpBuffer, dwSize);

 

::EndUpdateResource(hResource, FALSE);

 

/// 버전 스트링 정보 읽기

::VerQueryValue(lpVSVersionInfo, szPath, &lpInfo/*out*/, &dwInfoLen/*out*/)

CString strData = (TCHAR*)lpInfo;

 

/// Translate code 구성

LCID, Code Page

lcid 와 code page 를 이용해 스트링 정보의 주소를 추출한다.

 

정보를 읽을때는 API 를 이용하여 읽기가 가능하지만 버전 정보의 추가 삭제 변경을 위해서는

데이터의 앞에 자신이 가진 Value의 길이 값이 들어가기 때문에 버퍼 클래스가 필요하다.

 

버퍼클래스는 아래의 코드에서 vs_version.h 부분 을 참고 하면 된다.

http://www.codeproject.com/KB/install/VerPatch.aspx 

2010년 11월 23일 화요일

strsafe.h

펌) http://www.jiniya.net/lecture/techbox/strsafe.html

 

C언어 표준에 포함된 문자열 함수들 중에 일부는 매우 위험하다. 대표적인 함수가 strcpy와 sprintf함수다. 이 두 함수의 경우 출력 값으로 문자열 포인터를 전송한다. 하지만 출력 문자열 포인터의 크기를 입력 받지 않기 때문에 버퍼 오버런의 위험을 가지고 있다. 버퍼 오버런의 경우 보안상 취약점이 될 수 있다. 따라서 견고한 프로그램을 작성하기 위해서는 되도록 이 함수들을 사용하지 않는 것이 좋다.

버퍼 오버런

버퍼 오버런이란 프로그램 내부에서 사용하는 메모리 공간을 프로그래머가 기존에 의도했던 것을 넘어서서 덮어 쓰는 것을 말한다. 스택 공간을 침범하는 것을 스택 오버런, 힙 공간을 침범하는 것을 힙 오버런이라 한다. 아래 코드는 어떻게 스택 오버런이 발생하는 지 보여준다.

  1. TCHAR   *src = TEXT("123456789");  
  2. TCHAR   dest[5];  
  3. _tcscpy(dest, src);  

위의 코드를 살펴보면 dest는 최대 4글자를 저장할 수 있다. 왜냐하면 C언어의 경우 끝을 알리기 위해서 NULL 종료 문자를 사용하기 때문이다. 하지만 실제로 복사를 하고자 하는 소스 문자열은 4글자보다 큰 문자다. 따라서 프로그래머가 잡아둔 메모리 공간을 침범해서 덮어쓰게 된다. 이렇게 될 경우 스택이 깨지고 코드가 엉뚱한 곳으로 리턴되는 결과를 만들 수 있다.

Windows에서는 이러한 보안상 취약한 함수들을 대체할 새로운 안전한 함수들을 작성해서 Platform SDK를 통해서 배포하고 있다. 이 함수들은 strsafe.h에 들어있다. 우선 간략하게 어떠한 함수들이 들어 있는지 살펴보도록 하자.

기존함수 대체 함수
strcpy StringCbCopy, StringCbCopyEx
StringCchCopy, StringCchCopyEx
strncpy StringCbCopyN, StringCbCopyNEx
StringCchCopyN, StringCchCopyNEx
strcat StringCbCat, StringCbCatEx
StringCchCat, StringCchCatEx
strncat StringCbCatN, StringCbCatNEx
StringCchCatN, StringCchCatNEx
sprintf StringCbPrintf, StringCbPrintfEx
StringCchPrintf, StringCchPrintfEx
vsprintf StringCbVPrintf, StringCbVPrintfEx
StringCchVPrintf, StringCchVPrintfEx
gets StringCbGets, StringCbGetsEx
StringCchGets, StringCchGetsEx
strlen StringCbLength
StringCchLength

함수 이름이 규칙적으로 지어진 덕분에 함수의 종류를 한눈에 파악할 수 있다. 전체적으로 을 네 가지 종류의 함수가 있다. Cb, Cch계열과 일반 함수와 Ex 함수가 그것이다. Cb계열의 함수는 버퍼 크기를 인자로 받는다. 즉, 버퍼가 몇 바이트 크기를 가지느냐 하는 것을 기준으로 삼는다. 반면에 Cch계열 함수들은 버퍼의 길이를 인자로 받는다. 몇 글자를 저장할 수 있느냐 하는 것을 기준으로 삼는다. Ex 함수는 일반 함수의 기능에 버퍼의 잘림과 패딩을 다루는 추가적인 기능을 가진 함수들이다.

일반 함수의 경우 표준 함수와 동일한 인자를 받도록 되어 있다. 단지 추가적으로 버퍼의 크기를 하나 더 받는다. 따라서 여기서는 StringCbCopy와 StringCchPrintf의 사용법만 살펴보도록 하겠다. 다른 함수들의 자세한 사용방법을 알고 싶다면 MSDN을 참고하도록 하자.

  1. HRESULT StringCbCopy(        
  2.     LPTSTR pszDest,  
  3.     size_t cbDest,  
  4.     LPCTSTR pszSrc  
  5. );  

StringCbCopy 함수의 원형이다. 이 함수는 strcpy와 동일한 기능을 한다. pszDest에는 복사될 버퍼 포인터를, cbDest에는 pszDest의 크기를, 그리고 pszSrc에는 복사할 문자열 포인터를 넣어주면 된다. cbDest를 제외하면 strcpy와 동일한 의미의 인자가 순서대로 입력된다는 것을 알 수 있다. 결과 값은 함수의 성공 여부다. 성공한 경우 S_OK를 리턴 한다. 위에 나열된 모든 String계열 함수의 리턴 값은 HRESULT다. COM에 사용되는 것과 동일한 타입이기 때문에 FAILED, SUCCEEDED매크로를 사용하면 손쉽게 에러 여부를 체크할 수 있다. StringCbCopy함수를 사용해 간단한 문자열을 복사하는 과정은 아래와 같다.

  1. TCHAR dest[6];  
  2. TCHAR *src = "Hello World!";  
  3.  
  4. if(FAILED(StringCbCopy(dest, sizeof(dest), src)))  
  5.     printf(TEXT("실패\n"));  
  6.  
  7. printf(dest);  

위의 코드를 실행해 보면 왜 StringCbCopy가 안전한지를 알 수 있다. 위 프로그램을 실행하면 dest값으로 Hello가 출력된다. 왜냐하면 dest의 크기인 6이 StringCbCopy함수 내부로 들어갔기 때문에 거기까지만 복사가 진행된 것이다. 더 이상 복사할 경우 버퍼 오버런이 발생하기 때문이다.

  1. HRESULT StringCchPrintf(  
  2.     LPTSTR pszDest,  
  3.     size_t cchDest,  
  4.     LPCTSTR pszFormat,  
  5.      ...  
  6. );  

StringCbPrintf 함수의 원형이다. 이 함수는 sprintf와 동일한 기능을 한다. pszDest에는 출력될 버퍼를, cchDest에는 pszDest에 저장할 수 있는 글자 수를, 끝으로 pszFormat에는 포맷 문자열을 넣으면 된다. 아래와 같이 사용할 수 있다.

  1. TCHAR buffer[MAX_PATH];  
  2. StringCchPrintf(buffer, MAX_PATH, "%s", TEXT("Hello World"));  

문자열을 다루는 일은 프로그래밍 과정에서 광범위 하게 사용된다. 일부 프로그램은 문자열 처리 과정이 프로그램의 전부이기도 하다. 이처럼 문자열 처리 작업은 많이 사용되는 만큼 가장 많은 버그와 보안 허점이 나오는 곳이기도 하다. 이러한 문제를 해결하는 가장 좋은 방법은 기존의 불완전한 함수들을 사용하지 않는 것이다. 이런 이유 때문에 strsafe.h를 프로젝트에 포함시키게 되면 표준 문자열 함수를 사용하는 부분에서는 deprecated 경고가 발생한다. 하지만 deprecated 경고를 강제로 무시하고 싶은 상황도 있다. 어쩔 수 없이 써야 하는 라이브러리 코드 등에서 표준 문자열 함수를 사용한 경우가 대표적이다. 이럴 때 경고를 강제로 끄기 위해서는 strsafe.h를 포함시키는 부분 앞에 STRSAFE_DEPRECATE를 정의해주면 된다. 아래와 같이 include를 시키면 표준 문자열 함수에 대한 경고가 발생하지 않는다.

  1. #define STRSAFE_DEPRECATE  
  2. #include <strsafe.h> 

String 계열의 함수가 안전하고 좋은 것임은 사실이다. 하지만 기존의 ANSI C/C++ 의 표준 문자열 함수들로 작성된 프로젝트를 String 계열의 함수로 교체하는 작업은 신중하게 결정해야 한다. 언뜻 보기에는 함수명을 바꾸는 간단한 작업처럼 보이지만 실상은 그렇지 않다. 기존 라이브러리 함수들을 사용하는 대부분의 코드의 경우 함수로 출력 버퍼의 크기를 전송하지 않기 때문에 호출하는 쪽과 함수 코드를 전체적으로 수정해야 한다. 이런 이유로 대부분의 경우 String계열로 코드를 고침으로써 얻는 보안 효과보다 더 많은 버그가 수정 도중에 발생한다. 따라서 기존의 프로젝트 코드를 변경하는 일은 신중히 검토한 후 결정하도록 하자.

말은 쓰는 사람의 혼을 담는 그릇이라고 한다. 이와 마찬가지로 코드는 프로그래머의 혼을 담는 그릇이 될 수 있다. 앞으로 새롭게 작성하는 프로젝트에는 안전한 문자열 함수를 쓰고 문자열 포인터가 전달되는 곳으로는 항상 크기를 같이 전달하도록 하자. 이보다 좀 더 좋은 방법은 되도록 직접적인 문자열 포인터의 사용을 줄이고 string이나 CString등의 C++ 클래스를 사용하는 것이다.

2010년 11월 22일 월요일

Operator Keyword for ~

Operator ~ 는 자료형의 최대값을 기준으로

값을 반전 시켜준다.

 

예를 들어

USHORT wSource = 0xFFFF;

wSource = ~wSource ;

 

이렇게 하였을때 Operator~ 를 적용하기 전인 wSource 의 값이 최대값 65535이기 때문에

현재 wSource 의 값은 0 이다.

 

다시 이야기 하면 wSource + ~wSource 의 값은 자료형의 최대값이다.

 

2010년 11월 14일 일요일

FileExist

BOOL FileExist(LPCTSTR lpszFileName)
{
 HANDLE hFile = CreateFile(lpszFileName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
 
 if(hFile != INVALID_HANDLE_VALUE)
 {
  CloseHandle(hFile);
  return TRUE;
 }

 return FALSE;
}

파일 버전 정보

펌) http://psmon.x-y.net/maniwiki/doku.php

기타 파일 버전 관련 소스) http://www.codeproject.com/KB/install/VerPatch.aspx

MSDN)

http://msdn.microsoft.com/en-us/library/ms647003.aspx :: GetFileVersionInfo

http://msdn.microsoft.com/en-us/library/ms647005(VS.85).aspx :: GetFileVersionInfoSize

http://msdn.microsoft.com/en-us/library/ms647464(VS.85).aspx :: VerQueryValue

http://msdn.microsoft.com/en-us/library/ms646997.aspx :: VS_FIXEDFILEINFO

http://msdn.microsoft.com/en-us/library/ms647001(VS.85).aspx :: VS_VERSIONINFO

 

 

#pragma once


#include <Version.h>
#pragma comment(lib,"version")

struct VS_VERSIONINFO
{
 WORD                wLength;
 WORD                wValueLength;
 WORD                wType;
 WCHAR               szKey[1];
 WORD                wPadding1[1];
 VS_FIXEDFILEINFO    Value;
 WORD                wPadding2[1];
 WORD                wChildren[1];
};

struct
{
 WORD wLanguage;
 WORD wCodePage;
} *lpTranslate;


struct Vs_info
{
 DWORD high;
 DWORD low;

};

void CreschangerDlg::SetVersionInfo(char *cfilename,char *cfileversion,char *cproductversion,char *ccomment)
{
 UpdateData(TRUE);

 PS_PRJINFO edit_one;
 edit_one.exefullname = cfilename;
 edit_one.fileversion=cfileversion;
 edit_one.productversion=cproductversion;
 edit_one.comment=ccomment;
 edit_one.filename=m_strFileName.GetBuffer(0);

 m_prj.EditPrj(&edit_one);


 //버젼 유효성검사
 if(false == validversiondata(PVER_FILEVERSION,cfileversion) )
 {
  MessageBox(m_verLastError);
  return;

 }


 if(false == validversiondata(PVER_PRODUCTVERSION,cproductversion) )
 {
  MessageBox(m_verLastError);
  return;

 }


 if(false == validversiondata(PVER_COMMENT,ccomment) )
 {
  MessageBox(m_verLastError);
  return;

 }

 

 VS_VERSIONINFO      *pVerInfo;

 

 LPBYTE     pOffsetBytes;
 VS_FIXEDFILEINFO    *pFixedInfo;
 LPCTSTR     lpszFile = _T(cfilename);

 DWORD     dwHandle, dwSize, dwResult = 0 ;


 LPBYTE lpBuffer;

 // determine the size of the resource information
 dwSize = GetFileVersionInfoSize(lpszFile, &dwHandle);
 if (0 < dwSize)
 {
  lpBuffer = new BYTE[dwSize];

  memset(lpBuffer,0,dwSize);

  if (GetFileVersionInfo(lpszFile, 0, dwSize, lpBuffer) != FALSE)
  {
   // these macros help to align on r-byte boundaries (thanks Ted Peck)
#define roundoffs(a,b,r) (((BYTE *) (b) - (BYTE *) (a) + ((r) - 1)) & ~((r) - 1))
#define roundpos(a,b,r) (((BYTE *) (a)) + roundoffs(a,b,r))

   // 'point to' the start of the version information block
   pVerInfo = (VS_VERSIONINFO *) lpBuffer;

   // the fixed section starts right after the 'VS_VERSION_INFO' string
   pOffsetBytes = (BYTE *) &pVerInfo->szKey[16];

   //int x = _tcslen((char*)pVerInfo->szKey);


   pFixedInfo = (VS_FIXEDFILEINFO *) roundpos(pVerInfo, pOffsetBytes, 4);

   // increment the numbers!


   int left = 0;
   int right = 0;

 

   left = GetDigtFromVerString(cfileversion,0);
   right = GetDigtFromVerString(cfileversion,1);
   pFixedInfo->dwFileVersionMS    = (0x00010000 * left) + (0x00000001 * right);

   left = GetDigtFromVerString(cfileversion,2);
   right = GetDigtFromVerString(cfileversion,3);
   pFixedInfo->dwFileVersionLS    = (0x00010000 * left) + (0x00000001 * right);

 

   left = GetDigtFromVerString(cproductversion,0);
   right = GetDigtFromVerString(cproductversion,1);
   pFixedInfo->dwProductVersionMS = (0x00010000 * left) + (0x00000001 * right);


   left = GetDigtFromVerString(cproductversion,2);
   right = GetDigtFromVerString(cproductversion,3);
   pFixedInfo->dwProductVersionLS = (0x00010000 * left) + (0x00000001 * right);

 


   LPVOID lpInfo;
   UINT  unInfoLen;

   if (VerQueryValue(lpBuffer, _T("\\"), &lpInfo, &unInfoLen))
   {
    //ASSERT(unInfoLen == sizeof(m_FileInfo));
    //if (unInfoLen == sizeof(m_FileInfo))
    // memcpy(&m_FileInfo, lpInfo, unInfoLen);
   }

   // find best matching language and codepage
   VerQueryValue(lpBuffer, _T("\\VarFileInfo\\Translation"), &lpInfo, &unInfoLen);

   DWORD dwLangCode = 0;

 

   if (GetTranslationId(lpInfo, unInfoLen, MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), dwLangCode, TRUE))
    dwLangCode = *((DWORD*)lpInfo);

   if(dwLangCode < 1 )
   {
    MessageBox("영문버젼정보가 있질않습니다. 리소스의 국가설정을 확인하세요!");
    delete [] lpBuffer;
    return;

   }

 


   CString m_strProductName;
   CString strSubBlock;

 

   HANDLE hResource = BeginUpdateResource(lpszFile, FALSE);
   if (NULL != hResource)
   {
    UINT uTemp;

    wchar_t tt[128];

 

    char xx[128];    

    char* w_version;
    w_version= new char[dwSize];

    for(int i=0;i<dwSize;i++)
    {
     w_version[i] = lpBuffer[i];

    }


    bool  is_lenok = true;

    LPVOID p_fileVer;

    memset(&tt[0],0,sizeof(wchar_t) * 128 );
    p_fileVer = SearchText(w_version,"FileVersion",dwSize);
    strcpy(xx,cfileversion);
    for(int i=0;i<strlen(xx);i++)
    {
     tt[i]=std::wcout.widen(xx[i]);

    }
    strSubBlock.Format(_T("\\StringFileInfo\\%04X%04X\\FileVersion"), dwLangCode&0x0000FFFF, (dwLangCode&0xFFFF0000)>>16);
    if (VerQueryValue(lpBuffer, (LPTSTR)(LPCTSTR)(strSubBlock), (LPVOID *) &pValueBuffer, &unInfoLen))
    {

     m_strProductName = CString((LPCTSTR)pValueBuffer);

     if(strlen(xx) <= m_strProductName.GetLength())
     {
      ZeroMemory(p_fileVer, m_strProductName.GetLength() * sizeof(wchar_t));
      wcscpy((wchar_t*)p_fileVer,   &tt[0] );

     }
     else
     {
      is_lenok =false;

     }

    }
    else
    {
     is_lenok =false;

    }


    memset(&tt[0],0,sizeof(wchar_t) * 128 );
    p_fileVer = SearchText(w_version,"ProductVersion",dwSize);
    strcpy(xx,cproductversion);
    for(int i=0;i<strlen(xx);i++)
    {
     tt[i]=std::wcout.widen(xx[i]);

    }
    strSubBlock.Format(_T("\\StringFileInfo\\%04X%04X\\ProductVersion"), dwLangCode&0x0000FFFF, (dwLangCode&0xFFFF0000)>>16);
    if (VerQueryValue(lpBuffer, (LPTSTR)(LPCTSTR)(strSubBlock), (LPVOID *) &pValueBuffer, &unInfoLen))
    {

     m_strProductName = CString((LPCTSTR)pValueBuffer);

     if(strlen(xx) <= m_strProductName.GetLength())
     {
      ZeroMemory(p_fileVer, m_strProductName.GetLength() * sizeof(wchar_t));
      wcscpy((wchar_t*)p_fileVer,   &tt[0] );

     }
     else
     {
      is_lenok =false;

     }

    }
    else
    {
     is_lenok =false;

    }

 

    memset(&tt[0],0,sizeof(wchar_t) * 128 );
    p_fileVer = SearchText(w_version,"Comments",dwSize);
    strcpy(xx,ccomment);
    for(int i=0;i<strlen(xx);i++)
    {
     tt[i]=std::wcout.widen(xx[i]);

    }
    strSubBlock.Format(_T("\\StringFileInfo\\%04X%04X\\Comments"), dwLangCode&0x0000FFFF, (dwLangCode&0xFFFF0000)>>16);
    if (VerQueryValue(lpBuffer, (LPTSTR)(LPCTSTR)(strSubBlock), (LPVOID *) &pValueBuffer, &unInfoLen))
    {

     m_strProductName = CString((LPCTSTR)pValueBuffer);

     if(strlen(xx) <= m_strProductName.GetLength())
     {
      ZeroMemory(p_fileVer, m_strProductName.GetLength() * sizeof(wchar_t));
      wcscpy((wchar_t*)p_fileVer,   &tt[0] );

     }
     else
     {
      is_lenok =false;

     }

    }
    else
    {
     is_lenok =false;

    }

 


    memcpy(lpBuffer,w_version,dwSize); 
    delete [] w_version;


    if(!is_lenok)
    {
     delete [] lpBuffer;
     MessageBox("바꾸려는 버젼정보문자열이, Original버젼의 문자열길이를 초과하거나 없습니다..");
     return;

    }


    // could probably just use LANG_NEUTRAL/SUBLANG_NEUTRAL
    if (UpdateResource(hResource, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), dwLangCode&0x0000FFFF, lpBuffer, dwSize) != FALSE)    
    {
     // 언어한글 강제..업데이트
     //UpdateResource(hResource, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), 1042, lpBuffer, dwSize);

     if (EndUpdateResource(hResource, FALSE) == FALSE)
      dwResult = GetLastError();
    }
    else
     dwResult = GetLastError();

 


   }
   else
    dwResult = GetLastError();


   delete [] lpBuffer;

  }
  else
  {
   dwResult = GetLastError();
   delete [] lpBuffer;
  }
 }
 else
  dwResult = GetLastError();


 if(dwResult != 0)
 {
  sprintf(m_verLastError,"VerChanger Failed Reason:%d",dwResult);
  MessageBox(m_verLastError);
 }
 else
 {
  MessageBox("버젼업이 성공했습니다.");

 }

 

}


BOOL CreschangerDlg::GetTranslationId(LPVOID lpData, UINT unBlockSize, WORD wLangId, DWORD &dwId, BOOL bPrimaryEnough/*= FALSE*/)
{
 for (LPWORD lpwData = (LPWORD)lpData; (LPBYTE)lpwData < ((LPBYTE)lpData)+unBlockSize; lpwData+=2)
 {
  if (*lpwData == wLangId)
  {
   dwId = *((DWORD*)lpwData);
   return TRUE;
  }
 }

 if (!bPrimaryEnough)
  return FALSE;

 for (lpwData = (LPWORD)lpData; (LPBYTE)lpwData < ((LPBYTE)lpData)+unBlockSize; lpwData+=2)
 {
  if (((*lpwData)&0x00FF) == (wLangId&0x00FF))
  {
   dwId = *((DWORD*)lpwData);
   return TRUE;
  }
 }

 return FALSE;
}


LPVOID CreschangerDlg::SearchText(char* Search,char* findstring,int maxlen)
{
 int comlen = strlen(findstring);

 bool b_ser=false;

 int comcount=0;

 for(int i=0;i<maxlen ; i++)
 {
  comcount=0;

  for(int x=0;x<comlen;x++)
  {


   char cur = Search[i];  

   if(cur == findstring[x])
   {
    comcount++;
    i = i + 2;
   }
   else
   {
    break;
   }


   if(comcount == comlen)
   {
    int xxx=999;
    if(0 ==strcmp("ProductVersion",findstring))
    {
     return (LPVOID)(&Search[i+2]);

    }

    if(0 == strcmp("Comments",findstring))
    {
     return (LPVOID)(&Search[i+2]);

    }

    return (LPVOID)(&Search[i+4]);
   }

  }

 

 }

 return NULL;

}

bool CreschangerDlg::validversiondata(int ntype,char* verstring)
{


 int i_verlen = strlen(verstring);

 if( (COMMENT_MAX < i_verlen) && (i_verlen < 1) )
 {
  strcpy(m_verLastError,"Comment의 Rev이 지정된 길이를 초과했거나, 1보다 작습니다.");
  return false;
 }

 if(ntype == PVER_COMMENT)
 {
  strcpy(m_verLastError,"Comment의 유효성 통과");
  return true;
 }

 if( VERSION_MAX < i_verlen)
 {
  strcpy(m_verLastError,"Version정보가 제한된 길이를 초과했습니다.");
  return false;
 }

 int comCount = 0;

 for(int i=0 ;i<i_verlen;i++)
 {

  // ,의 개수를 카운팅한다.
  if(verstring[i] == ',' || verstring[i] == '.')
  {
   if(i > 0)
   {
    if(verstring[i-1] == ',' || verstring[i-1] == '.')
    {
     strcpy(m_verLastError,"버젼 구분자사이에 값이 없습니다.");
     return false;
    }

   }

   comCount++;
   continue;
  }

  //버젼정보에 해당하는 문자열값이 숫자인지 체크
  if(  (NUM_START < verstring[i]) && (NUM_END > verstring[i])  )
  {

  }
  else
  {
   strcpy(m_verLastError,"Version정보에 ,를 제외한 문자가 들어올수 없습니다.");
   return false;
  }


 }


 if(COMA_COUNT != comCount )
 {
  strcpy(m_verLastError,"Version정보에 ,의 카운트는 3이어야합니다.");
  return false;

 }

 strcpy(m_verLastError,"Version정보 유효성 통과");

 return true;

}