_AtlInitializeCriticalSectionEx 링크 에러에 대해

Visual Studio 2012를 사용하다보니 이런 링크 에러가 발생햇습니다.


4>atlsd.lib(atlbase.obj) : error LNK2019: “int __cdecl ATL::_AtlInitializeCriticalSectionEx(struct _RTL_CRITICAL_SECTION *,unsigned long,unsigned long)” (?_AtlInitializeCriticalSectionEx@ATL@@YAHPEAU_RTL_CRITICAL_SECTION@@KK@Z) 외부 기호(참조 위치: “public: long __cdecl ATL::CComCriticalSection::Init(void)” (?Init@CComCriticalSection@ATL@@QEAAJXZ) 함수)에서 확인하지 못했습니다.


이게 뭘까요? 찾아보니 아래와 같은 내용이 나옵니다.


[Microsoft Connect의 관련 내용]


해결 방법이… 해결이 아니라 우회법이네요.
MS가 제시한 레지스트리 조작 후 업데이트 재설치도 해결이 안됩니다.
젠장… 그런데 뭐가 해결되어 닫힌 이슈야?


아래 내용을 헤더에 추가한 후에

#define _AtlInitializeCriticalSectionEx _AtlInitializeCriticalSectionExBroken
#include <atlbase.h>
#include <atlwinverapi.h>
#undef _AtlInitializeCriticalSectionEx


소스 파일에(또는 헤더 파일에 적절히) 아래와 같이 구현합니다.

BOOL __cdecl _AtlInitializeCriticalSectionEx(__out LPCRITICAL_SECTION lpCriticalSection, __in DWORD dwSpinCount, __in DWORD Flags)
{
#if (NTDDI_VERSION >= NTDDI_VISTA) && !defined(_USING_V110_SDK71_) && !defined(_ATL_XP_TARGETING)
	// InitializeCriticalSectionEx is available in Vista or later, desktop or store apps
	return ::InitializeCriticalSectionEx(lpCriticalSection, dwSpinCount, Flags);
#else
	UNREFERENCED_PARAMETER(Flags);

	// ...otherwise fall back to using InitializeCriticalSectionAndSpinCount.
	return ::InitializeCriticalSectionAndSpinCount(lpCriticalSection, dwSpinCount);
#endif
}

Visual Studio 2012에서 폰트가 흐릿하게 나올 때 해결 방법

BS가 오늘 작업을 하려는데…
텍스트 편집기에서 이상하게 폰트가 흐릿하게 나오는 겁니다.
텍스트 편집기의 폰트를 한글 폰트 그 어떤 것으로 바꾸어도
Windows 의 Clear Type 설정을 아무리 바꾸어도 계속 흐릿합니다.

1차 원인을 찾은 것은…
흐릿하게 나오는 라인 앞에 있는 Tab!!!
Tab을 Space로 바꾸니 멀쩡…

이게 뭐지???

그래서 찾은 글…
[Visual Studio 2010 blurry font]
Visual Studio 2010에서도 발생하는 문제라고 하네요…

여기에 나오는 해결 방법에 따라 도구>옵션>글꼴 및 색 으로 가서

[조사식, 로컬 및 자동 도구 창]의 폰트를 변경
했습니다.
그리고 Visual Studio 2012를 재실행했더니!!!

정말로 해결됩니다. 폰트가 흐릿하지 않게 되었습니다.

이건 무슨 버그인가요?

다중 모니터를 위한 API 소개

아주 가끔 필요할 때가 있네요.

[MSDN 문서 링크]

함수 설명
EnumDisplayMonitors HDC, 클리핑 영역으로 모니터 정보를 찾아내는 함수입니다.
미러링 드라이버등의 가상 모니터도 포함됩니다.
GetMonitorInfo 모니터 정보를 구하는 함수
MonitorEnumProc EnumDisplayMonitors()에서 사용하는 콜백 함수
MonitorFromPoint 좌표로 모니터를 찾는 함수
MonitorFromRect 사각형에서 모니터를 찾는 함수
MonitorFromWindow 창에서 모니터를 찾는 함수

구할 수 있는 모니터 정보는

  • 모니터 영역
  • 작업 영역(작업 표시줄 같은 거 뺀…)
  • 주 모니터 여부
  • 장치 이름

등 입니다. 장치 이름으로부터 또 여러가지 정보를 구할 수도 있겠죠?

WM_NCCREATE를 받아 처리한 후에 TRUE을 반환하면 CreateWindowEx()에서 설정한 제목이 표시되지 않는다.

[MSDN의 WM_NCCREATE 설명문서]

위 문서에 따르면

If an application processes this message, it should return TRUE to continue creation of the window.
If the application returns FALSE, the CreateWindow or CreateWindowEx function will return a NULL handle.

반환값 설명에 위와 같이 나와있다.
BS의 발번역기를 가동해보면
어플리케이션에서 이 메시지를 처리하면, 반드시!!! TRUE를 반환해야 창을 생성하는 것이 계속된다.
어플리케이션이 FALSE를 반환하면, 창을 생성하는 함수는 NULL 핸들을 반환하게 된다.

그런데… 이렇게 했더니 생성된 창의 제목이 보이지 않네요…
버그일까요?
DWM(Desktop Window Manager, 데스크탑 창 관리자) windows에서 창의 제목을 지정해서
CreateWindow() 또는 CreateWindowEx()를 호출할 때에
어플리케이션에서 WM_NCCREATE의 처리후 return TRUE; 를 하면 … 제목이 안 나올 수 있답니다.
이럴때에는 DefWindowProc() 함수로 넘기면 된다고 합니다.
return DefWindowProc(hWnd, msg, wParam, lParam); 이렇게 말이죠…

BS가 만들어 BS만 쓰고 있는 프로그램에서 창 제목이 보이질 않아 찾아보니 DefWindowProc()로 해결되더군요…

간단하게 만든 레이아웃 관리자

2013년 6월 25일) 기능 개선을 조금 더 하고 새로운 레이아웃 관리 클래스를 추가했습니다.
그 모습은 이어서 올릴 ShortcutTool에서…

 .NET Framework를 안쓰고, 다른 라이브러리의 도움없이
창 크기가 변할 때 레이아웃이 바뀌는…
그런 것이 필요했습니다.

그런데 API에서 그런 것을 찾을 수가 없어서

그냥 간단히 만들었습니다.

FLayout은 창의 4개 모서리에서 offset과 크기를 지정하여 컨트롤을 배치하고
TLayout은 테이블 모양으로 컨트롤을 배치하고
RLayout은 테이블 모양인데 크기의 단위가 퍼센트 입니다.

간단하게 예제와 함께 소스를 첨부하였으니 참고하세요.

cfile7.uf.25262B4051CA567412C954.7z

다이얼로그를 대충 만들었습니다.

다이얼로그는 Resizing이 되도록 했습니다.

프로그램을 실행하면 우선 TLayout 예제가 나옵니다.

크기 변경 후

다음은 RLayout 예제 입니다.

크기 변경 후

다음은 FLayout

크기 변경 후

마지막으로 3가지를 혼합한 예제입니다.
TLayout의 2번째 줄에는 RLayout을 넣고 마지막 줄에는 FLayout을 넣었습니다.

크기 변경 후

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로 설정하는 방법은 모르겠네요… ㅋ

Visual Studio 2012 Update 2 설치 후 C++/CLR에서 디버깅이 제대로 되지 않는다.

VIsual Studio 2012를 사용 중인데 Update 2 를 설치했더니 C++/CLR에서 디버깅이 제대로 되지 않습니다.

Update 2의 버그라고 하네요.

그리고 Update 3에서 수정되었다고 합니다.
그런데 Update 3는 어디서 구할까요?
[Microsoft 다운로드 센터: Visual Studio 2012 Update 3 RC 2]
위 링크에 가면 받을 수 있습니다.
아직은 RC 2 이지만 곧 RC가 빠진 정식 버전이 나오리라 생각됩니다.

SHBrowseForFolder() 사용할 때에 특정 폴더를 기본으로 선택하기

SHBrowseForFolder() 함수는 사용자가 Shell Folder를 선택할 수 있는 대화상자를 표시하는 함수입니다.
이 함수를 사용할 때에 프로그램이 원하는 특정 경로를 미리 찾아서 선택하도록 하는 방법을 소개합니다.

// 콜백에 전달하는 파라미터 구조체
struct BFFParam
{
	const void* pData;
	bool isString;
};

// 콜백
int CALLBACK _BFFProc(HWND hwnd, UINT msg, LPARAM lparam, LPARAM data)
{
	lparam;	// 경고 회피용

	// 이 메시지가 오면
	if (msg == BFFM_INITIALIZED)
	{
		// 이렇게 해서 원하는 것을 선택하도록 하면 된다.
		auto pData = (BFFParam*)data;
		if (pData != nullptr)
			SendMessage(hwnd, BFFM_SETSELECTION, (pData->isString) ? TRUE : FALSE, (LPARAM)(pData->pData));
	}

	// 별다른 일이 없으면 0을 반환해야 한다
	return 0;
}

// title 제목에 pInitFolder를 선택되도록 폴더 선택 대화상자를 표시한다.
PIDLIST_ABSOLUTE browseForFolder(HWND hwnd, wchar_t (&dispName)[MAX_PATH], const wchar_t* title, const void* pInitFolder, bool bIsString)
{
	BFFParam param;
	param.pData = pInitFolder;
	param.isString = bIsString;

	BROWSEINFO bi = {0};
	bi.hwndOwner = hwnd;
	bi.pszDisplayName = dispName;
	bi.lpszTitle = title;
	bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_DONTGOBELOWDOMAIN | BIF_RETURNFSANCESTORS | BIF_NONEWFOLDERBUTTON;
	bi.lParam = (LPARAM)¶m;
	bi.lpfn = _BFFProc;
	return SHBrowseForFolder(&bi);
}

간단한 창 분할 기능 만들기

BS가 파일 관리자를 만드는데 필요해서 간단히 구현해 봤습니다.

HSplitView는 수평으로 분할하여 위, 아래로 자식 윈도우를 배치하는 것이고
VSplitView는 수직으로 분할하여 왼쪽, 오른쪽에 자식 윈도우를 배치합니다.

생성자에서 크기를 비율 또는 화면 유닛 단위(보통은 픽셀)로 정의할 수 있습니다.
비율로 정의할 경우에는 HSplitView는 분할된 위쪽(VSplitView는 왼쪽)의 최소 최대 크기를 정하게 되어 있고
화면 유닛 단위로 정의할 경우에는 각 분할된 면의 최소 크기를 정하도록 되어 있습니다.
왜 단위에 따라 다르냐고 물으신다면… BS가 그렇게 필요했다 말하겠습니다. ㅋㅋ

Visual Studio 2012에서 마법사가 생성해주는 Win32 프로그램의 WndProc를 아래와 같이 수정했습니다.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	typedef std::tuple<HSplitView*, VSplitView*, VSplitView*> SplitViewTuple;	// Main, Upper, Lower

	switch (message)
	{
	case WM_CREATE:
		{
			auto pViews = new SplitViewTuple;
			auto pMainView = std::get<0>(*pViews) = new HSplitView(0.1, 0.9);
			auto pUpperView = std::get<1>(*pViews) = new VSplitView(0.1, 0.9);
			auto pLowerView = std::get<2>(*pViews) = new VSplitView(0.1, 0.9);

			pMainView->create(hInst, hWnd);
			pUpperView->create(hInst, pMainView->getWindow());
			pLowerView->create(hInst, pMainView->getWindow());

			HWND bt1 = CreateWindow(WC_BUTTON, L"Button1", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, pUpperView->getWindow(), nullptr, hInst, nullptr);
			HWND bt2 = CreateWindow(WC_BUTTON, L"Button2", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, pUpperView->getWindow(), nullptr, hInst, nullptr);
			HWND bt3 = CreateWindow(WC_BUTTON, L"Button3", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, pLowerView->getWindow(), nullptr, hInst, nullptr);
			HWND bt4 = CreateWindow(WC_BUTTON, L"Button4", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, pLowerView->getWindow(), nullptr, hInst, nullptr);

			pUpperView->setLeft(bt1);
			pUpperView->setRight(bt2);
			pLowerView->setLeft(bt3);
			pLowerView->setRight(bt4);
			pMainView->setTop(pUpperView->getWindow());
			pMainView->setBottom(pLowerView->getWindow());

			pUpperView->show();
			pLowerView->show();
			pMainView->show();

			SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pViews);
		}
		break;
	case WM_SIZE:
		{
			RECT rect;
			GetClientRect(hWnd, &rect);
			auto pMainView = std::get<0>(*(SplitViewTuple*)GetWindowLongPtr(hWnd, GWLP_USERDATA));
			MoveWindow(pMainView->getWindow(), rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
			pMainView->recalculate();
		}
		break;
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// 메뉴 선택을 구문 분석합니다.
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: 여기에 그리기 코드를 추가합니다.
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		{
			auto pViews = (SplitViewTuple*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
			delete std::get<2>(*pViews);
			delete std::get<1>(*pViews);
			delete std::get<0>(*pViews);
			delete pViews;
			PostQuitMessage(0);
		}
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

그리고 실행하면 아래와 같이 나옵니다.



이제 중앙에 있는 구분자를 이리저리 드래그 해 보고 창 크기도 조정해 보겠습니다.



구분자를 더블클릭하면 기본 크기로 돌아갑니다.
아래는 하단의 버튼 3과 4 사이의 구분자를 더블 클릭한 모습입니다.



간단한 소스이므로 수정해서 사용하시면 됩니다.
소스는 SplitView.h와 SplitView.cpp 두개를 보시면 됩니다.


cfile23.uf.11308042516773660A1164.7z

나만의 파일 매니저 만들기

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