C++/CLR의 대상 프레임워크 버전을 수정하는 방법

Visual Studio 2012를 사용해서 C++에다가 .NET Framework를 가져와 사용하는데
대상 프레임워크 버전이 4.5 입니다…
물론 BS의 업무 PC는 당연히 4.5가 설치되어 있지만 다른 직원 PC에는 없는 곳이 많아
대상 프레임워크 버전을 좀 낮춰야 하는데… 어떻게 해야 할까요?

MSDN의 문서에 자세히 나와있네요…
[MSDN 문서 링크]

간단히 설명하자면



  1. 솔루션을 닫는다. (문서에는 프로젝트 언로드라지만 확실하게 하는 걸 좋아함)

  2. 수정을 원하는 vcxproj 파일을 연다. (물론 텍스트 편집기로)

  3. 아래 내용을 찾아 버전을 바꾼다.

    <Project ...>
    	<PropertyGroup Label="Globals">
    		...
    		<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    		...
    	</PropertyGroup>
    	...
    </Project>
    


  4. 저장한다.

  5. 솔루션을 연다.

  6. 변경된 내용을 확인한다.

그런데… Client Profile로 설정하는 방법은 모르겠네요… ㅋ

나만의 파일 매니저 만들기

간단하게 Windows의 쉘을 그대로 당겨와서 파일 매니저를 하나 만들었습니다.
그닥 편리하거나 원래의 Windows Explorer보다 나은 것은 없습니다만
UI를 자신이 원하는대로 만들 수 있고
단축 메뉴나 즐겨찾기 등을 입맛에 맞게 구성할 수 있는 장점이 있습니다.

사용한 함수나 인터페이스를 간단히 소개해 보겠습니다.

  • CoCreateInstance()
    쉘 오브젝트를 생성할 때에 사용합니다.
    BS는 Windows Explorer를 그대로 가져다 사용했기 때문에 IExplorerBrowser를 생성하는데 사용하였습니다.
    이외에도 IShellBrowser, ICommDlgBrowser 등등 여러가지 인터페이스를 생성할 수 있습니다.

  • SHCreateItemFromIDList()
    IDList로부터 쉘 아이템을 생성합니다.
    BS는 IShellItem을 생성하는데 사용하였습니다.

  • SHCreateItemFromParsingName()
    문자열(보통은 파일 경로)로부터 쉘 아이템을 생성합니다.

  • SHGetIDListFromObject()
    IShellItem, IShellFolder 등등 쉘 오브젝트로부터 IDList를 구합니다.

  • SHGetStockIconInfo()
    쉘에서 기본으로 제공하는 아이콘을 얻을 수 있습니다.
    예를 들면 폴더 아이콘이나, 일반 파일 아이콘 등등

  • PSGetPropertyKeyFromName()
    폴더 뷰 등에서 컬럼은 PROPERTYKEY라는 타입으로 표현되는데 이를 문자열 이름으로 가져오는 함수입니다.

  • PSGetNameFromPropertyKey()
    PSGetPropertyKeyFromName()와 반대로 문자열 이름에서 PROPERTYKEY 타입을 구하는데 사용하는 함수입니다.

  • SHGetKnownFolderPath()
    바탕화면, 내컴퓨터, 내 문서, 사용자 디렉토리 등등 Windows에서 제공하는 폴더들의 경로를 문자열로 구합니다.

  • SHGetKnownFolderIDList()
    SHGetKnownFolderPath()와 같이 폴더의 경로를 구하는데 타입이 IDList입니다.

  • SHCreateItemInKnownFolder()
    SHGetKnownFolderPath()와 같이 폴더의 경로를 구하는데 타입이 인터페이스 입니다.
    많이 쓰이는 인터페이스는 IShellItem, IShellFolder 등등이 있습니다.

  • SHBindToParent()
    IDList에서 상위 쉘 아이템에 대한 인터페이스와
    주어진 IDList에 해당하는 PCUITEMID_CHILD 타입의 데이터를 구할 수 있습니다.
    PCUITEMID_CHILD는 다른 여러 함수나 인터페이스에서 사용됩니다.

  • RegisterDragDrop()
    쉘 함수가 아니라 OLE 함수입니다.
    IDropTarget 인터페이스를 구현하고, 특정 윈도우에 객체를 등록하여 다른 윈도우로부터 드래그 드랍을 처리할 수 있도록 합니다.

  • IExplorerBrowser
    Windows Explorer를 가져와 사용할 때에 사용하는 인터페이스입니다.
    Windows Explorer를 실행하면 나오는 트리뷰와 오른쪽의 폴더뷰, 상태 표시줄 등이 포함됩니다.
    QueryInterface()를 사용해 IServiceProvider를 얻을 수 있고
    이것을 통해 INameSpaceTreeControl 등등의 객체를 구해 사용할 수 있습니다.

    • Initialize()
      브라우져 윈도우를 생성하는 등의 초기화를 수행합니다.

    • Advise()
      여러가지 이벤트에 대한 콜백을 등록합니다.

    • BrowseToIDList(), BrowseToObject()
      특정 경로를 탐색하도록 합니다.
      입력되는 파라미터에 따라 뒤, 앞, 상위 폴더 등으로도 이동이 됩니다.

    • Destroy()
      브라우저 윈도우를 종료하고 정리합니다.

    • GetCurrentView()
      현재 탐색 중인 쉘 뷰나 폴더 뷰를 가져옵니다.

    • GetOptions()
      현재 탐색에 사용되고 있는 옵션을 가져옵니다.

    • SetOptions()
      현재 탐색에 사용할 옵션을 설정합니다.

  • IUnknown
    COM의 객체들의 최상위 인터페이스입니다.

  • IShellItem
    제어판, 내컴퓨터, 파일, 폴더 등등을 포함하는 쉘 아이템에 대한 인터페이스입니다.
    아이템의 특성이나 경로, 이름 등등 여러가지 정보를 얻어낼 수 있습니다.

    • GetDisplayName()
      아이템의 이름을 얻는 함수

    • GetAttributes()
      아이템의 특성(파일, 폴더, 압축폴더 등등)을 얻을 수 있는 함수

  • IOleWindow
    IShellView, IFolderView 등의 상위 인터페이스입니다.

    • GetWindow()
      해당 윈도우의 핸들을 구할 때 사용합니다.

  • IShellView
    폴더, 제어판 등 쉘에서 탐색할 수 있는 것을 보여주는 뷰입니다.

  • INameSpaceTreeControl
    탐색기의 좌측에 있는 트리를 나타내는 인터페이스입니다.
    QueryInterface()를 호출해서 IOleWindow 인터페이스를 얻을 수 있습니다.

  • IInputObject
    BS는 IExplorerBrowser에서 QueryInterface()를 통해 얻어와
    백스페이스로 이전 폴더를 탐색한다던가 하는 몇가지 키보드 액셀레이터 입력을 처리하도록 했습니다.

  • IShellItemArray
    쉘 아이템 배열 인터페이스 입니다.
    IShellView나 IFolderView에서 보여지거나 선택된 쉘 아이템을 배열로 구할 때에 자주 사용합니다.
    이렇게 구해진 배열에서 GetItemAt() 함수등을 통해 쉘 아이템을 얻을 수도 있습니다.

  • IFolderView2
    폴더 뷰의 2번째 버전입니다.
    COM에서는 이전에 제공하던 인터페이스를 직접 수정하여 기능을 확장하지 않고
    숫자를 뒤에 붙여가며 추가되는 기능을 제공합니다.
    IShellView는 3까지 있던가요?
    IFolderView2는 아이템 정렬에 사용할 수 있습니다.

  • IColumnManager
    폴더 뷰 등에서 컬럼에 대한 정보를 가져오거나 설정하는데 사용합니다.
    BS는 이 인터페이스로부터 사용중인 컬럼의 폭을 가져오거나 설정하는데 사용하였습니다.

  • IDropTarget
    드래그 드랍을 구현하는데 사용되는 인터페이스 입니다.
    특정 윈도우에 드래그 할 경우 표현되는 아이콘이나 툴팁을 설정한다던가
    드랍을 할 수 있는 아이템인지 판단한다던가
    드랍을 했을 때의 처리 등등을 정의합니다.

복사 생성 방지용 클래스

C++ 클래스를 만들다보면 복사 생성자나 할당 연산자로 복제하지 않기를 희망하는 경우가 있습니다.
매번 코딩하려면 너무 귀찮네요.

아래 클래스를 상속 받으면 복사 방지 됩니다.

class NonCopyable
{
protected:
	NonCopyable() {}
	~NonCopyable() {}
private:
	NonCopyable(NonCopyable const &);
	NonCopyable const & operator=(NonCopyable const &);
};

이거야 원… 비밀번호 변경하듯이 주민번호는 변경 안되나…

다시 한번 사과 드립니다.
고객님의 정보는 유출되었으나 경찰에 의해 전량 회수조치 되었습니다.
추가피해가 없도록 최선을 다하겠습니다.

고객님의 유출된 고객정보는 아래와 같습니다.
휴대폰번호, 고객명, 고객번호, 주민번호, 단말기모델명, 가입일, 기기변경일, 요금제, 기본요금, 월정액합계

이런 미치겠습니다.
KT… 아주 여러가지로 BS를 괴롭힙니다.
얼른 옮기고 싶은데 아직 할부가 남아있습니다.

BS의 주민번호는 KT를 통해서만 3회 이상 팔렸습니다.
KT는 도대체 뭐하는 건지…

다시 한번 사과 드립니다???
사과 말고 보상을 받고 싶습니다.
뭐 이렇게 고객센터에 연락해도 그냥 죄송합니다. 그러고 말겠지만…

고객님의 정보는 유출되었으나 경찰에 의해 전량 회수조치 되었습니다???
경찰이 무슨 초능력으로 전량 회수조치 하지? MIB인가?
우리나라 경찰의 능력이 그렇게 뛰어나다고 생각되지 않는데…
5개월이면 이미 DVD로 구워져 전국에 팔려나갔을텐데 어떻게 전량 회수 했다는 거짓말을 당당하게 할 수 있는지…
KT는 거짓말하는 기업이라는 결론 뿐

추가피해가 없도록 최선을 다하겠습니다???
매번 최선을 다하는데 왜 매번 털리는 것이냐!!!
말로만 최선을 다한다고 하지… 아무런 조치를 취하지 않을 것이라는 것을 모두 알고 있다!

이전에 피해를 입혔으면 최소한 주민번호 정도는 암호화해서 복호 불가능하도록 만들어 뒀어야지
왜 주민번호를 평문으로 가지고 있는지 이해할 수 없다.
그리고 주민번호가 도대체 KT에게 왜 필요한지 모르겠음…
가입할 때에는 신분증 확인 또는 공인인증서를 통해 확인하면 되고
가입하고 나면 요금 청구를 위한 정보만 있으면 되지… 왜 주민번호를 가지고 있어야 할까?
이건 KT 뿐 아니라 모든 사이트들이 공통되는 의문점…

요새 휴대폰 없는 사람이 없는데 이동통신 3사가 다 털리니 이거야 원…

정말 주민번호 변경하고 싶다.

패치 파일 만드는 도구들 간단 비교 (MS PatchAPI, bsdiff, RTPatch)

이전 포스트 에서 소개했던 bsdiff 와 최근에 테스트 해 본 MS의 Patch API, 그리고 상용 솔루션인 RTPatch를 잠깐 테스트 해 보았습니다.

생성 속도에서는
RTPatch > bsdiff > MS Patch API

패치 파일 크기에서는
bsdiff > RTPatch > MS Patch API

패치 속도에서는
RTPatch > bsdiff > MS Patch API

이렇게 되네요…

MS Patch API 이후에 MSDelta라는 API가 추가되었는데 이 녀석은 Vista 이후에서 사용가능하여
아직 XP 가 많이 쓰이고 있으므로 넘어가겠습니다.
문서의 내용만으로 보면 이전보다는 훨씬 좋아진 듯 합니다.

bsdiff의 최대 단점으로는 패치 실행시에 Old 파일에 적용 가능한 패치 파일인지 검사를 하지 않는다는 것…
패치 파일을 만든 것을 완전히 상관없는 텍스트 문서에 패치를 적용해도 에러를 내지 않고 동작합니다.
결국 이 부분을 다시 검증하는 부분을 추가해야 실제 패치 시스템에서 활용할 수 있다는 거죠.

결론…

확실히 상용 소프트웨어가 괜히 돈 받고 팔리는 것이 아니구나.

입니다. ^_^

MS의 Patch API 사용해보기

MSDN에서 찾아볼 수 있는 Patch API를 간단히 소개합니다.
MSDN 문서 링크에 자세한 설명이 있습니다.
문서의 내용대로 Win32 실행파일 형태를 위한 도구라서 일반적인 파일 패치에는 조금 부적합합니다.

이전 버전의 파일, 새로운 버전의 파일과 약간의 생성 옵션을 더하면 패치 파일을 생성해 줍니다.
패치 파일은 자체로 압축까지 되어 있어 용량이 상당히 작게 생성됩니다.

주요 함수로는 CreatePatchFileW, ApplyPatchToFileW, TestApplyPatchToFileW 정도입니다.
Windows SDK의 include 폴더에서 patchapi.h 헤더 파일을 찾을 수 있습니다.
패치를 생성하는 DLL은 Windows SDK의 bin 폴더에 mspatchc.dll이란 파일이며
패치를 적용하는 DLL은 Windows XP이후의 Windows에서 System32 폴더에 존재합니다.
설명문서를 보시면 알 수 있듯이 .lib 파일이 없습니다. 그래서 LoadLibrary로 로드해서 사용해야 합니다.

1. CreatePatchFileW
OldFileName 파일과 NewFileName 파일을 비교하여 PatchFileName 파일로 패치 파일을 생성합니다.

BOOL PATCHAPI CreatePatchFileW(
	LPCWSTR				OldFileName,		// 이전 버전의 파일
	LPCWSTR				NewFileName,		// 새로운 버전의 파일
	LPCWSTR				PatchFileName,		// 생성될 패치 파일
	ULONG				OptionFlags,		// 패치 생성 옵션
	PPATCH_OPTION_DATA	OptionData			// 패치 옵션 데이터 (무시할 구간을 설정하는 등등)
);

2. ApplyPatchToFileW
PatchFileName 파일의 패치 내용을 OldFileName 파일에 적용하여 NewFileName 파일을 생성합니다.

BOOL PATCHAPI ApplyPatchToFileW(
	LPCWSTR		PatchFileName,		// 패치 파일
	LPCWSTR		OldFileName,		// 이전 버전 파일
	LPCWSTR		NewFileName,		// 생성될 새로운 버전의 파일
	ULONG		ApplyOptionFlags	// 패치 적용 옵션
);

3. TestApplyPatchToFileW
PatchFileName 패치 파일 내용이 OldFileName 파일에 적용될 수 있는지 테스트합니다.

BOOL PATCHAPI TestApplyPatchToFileW(
	LPCWSTR		PatchFileName,		// 테스트할 패치 파일
	LPCWSTR		OldFileName,		// 테스트할 이전 버전 파일
	ULONG		ApplyOptionFlags	// 적용 옵션
);

테스트 결과 입니다.
테스트 대상은 BS가 다니는 회사의 게임 파일로 하였고 용량은 약 22.6 MB입니다.
패치 생성 로그입니다. 22.6 MB의 파일의 패치 데이터 생성에 약 80초가 소요되었습니다.
생성된 패치 파일의 용량은 4.81 MB 로 7-Zip으로 최대 압축을 하여도 오히려 용량이 늘어나는 것으로 보아
상당히 잘 압축되어 있는 것 같습니다.

Create Patch File:
Old: Old\XXX.exe
New: New\XXX.exe
Output: XXX.exe.patch
Elapsed: 80465 milliseconds
Completed!

패치 적용 로그입니다. 1초도 안되는 시간에 적용되었습니다.

Testing
Elapsed: 78 milliseconds
Apply
Elapsed: 530 milliseconds
Completed!

막코딩 테스트 소스를 첨부하오니 참고하세요.

cfile9.uf.1917E93C501862893BA72E.7z