WINDOWS API 폴더 선택

C/C++/VC++ / MFC 2011. 1. 21. 21:49
윈도우 프로그래밍을 하다보면은 많이 사용하는 파일, 색상, 폰트, 프린트 등을 공통대화상자로 만들어두워
재사용 할수 있게 했습니다.

그런데 이상하게도 폴더를 선택할수 있는 공통대화 상자는 없습니다. 다른 것보다 덜 사용해서 그런지...
없다고 안되는건 아니고 Shell쪽에서 함수를 제공하고 있습니다.

간단하게 다음과 같이 사용하실수 있습니다.

BOOL ChoiceFolder(HWND hWndOwner, TCHAR* pszFolderPath)
{
	ASSERT( IsWindow(hWndOwner) == TRUE && pszFolderPath != NULL);
	
	LPITEMLIST pidl;
	BROWSEINFO bi;

	bi.hwndOwner      = hWndOwner;
	bi.pidRoot        = NULL;
	bi.pszDisplayName = NULL;
	bi.lpszTitle      = _T("폴더를 선택해 주세요.");
	bi.ulFlags        = BIF_NEWDIALOGSTYLE|BIF_EDITBOX|BIF_RETURNONLYFSDIRS;
	bi.lpfn           = NULL;
	bi.lParam         = 0;

	pidl = SHBrowseForFolder(&bi);
	if ( pidl == NLULL)
		return FALSE;
	
   	return SHGetPathFromIDList(pidl, pszFolderPath);
}



posted by 뚱2
VC++에서 ADO를 사용하는 방법은 크게 2가지가 있다.

1. OLE DB SDK라이브러리를 이용하는 방법
2. #import를 이용하여 Type Library를 이용하는 방법

전 개인적으로 2번째 방법을 선호합니다.
그리고 Type Library를 import 하면 스마트 포인터를 사용할수 있기때문에 편리합니다.

#import를 할려면 msadoxx.tlb 파일이나 msadoxx.dll 파일이 필요합니다.
ado는 버전별로 있기때문에 본인에게 맞는 버전을 사용하면 됩니다.

import할 파일은 C:\Program Files\Common Files\System\ado\ 에 있습니다.
MFC를 이용해서 프로그래밍을 한다면 StdAfx.h에 선언해 주는게  편합니다.
StdAfx.h에 선언하지 않으면 사용하는 곳마다 계속 선언을 해줘야 합니다.

//StdAfx.h
//...
#import "C:\\Program Files\\Common Files\\System\\ado\\msado26.tlb" \
        no_namespace rename("EOF", "adoEOF")
//...

type library를 임포트 할려면 .tlb라는 파일이 필요한데 ado는 .dll에도 type library가 저장되어 있습니다.
따라서 .tlb, .dll 둘중에 아무거나 임포트 하시면 됩니다.
namespace를 사용하지 않게 옵션을 주어서 코딩시 편하게 할 수 있습니다.
간혹 이름 충돌이 발생할수 있는데 그럴때는 rename_namespace("새로운이름") 옵션으로 이름을 변경해
주시면 됩니다.

rename("EOF", "adoEOF")는 혹 다른곳에서 EOF를 사용함으로 해서 충돌나는 것을 방지해줍니다.
posted by 뚱2
Visual Studio 2008에서 C++ 프로그램을 컴파일 하면은 기본적으로 두가지 모드가 있습니다.

Release /Debug 두 버전의 차이점은 다른 소소한 성능 향상을 위한 컴파일 옵션도 있지만

가장 중요한건 디버깅이 가능하느냐 가능하지 않냐의 차이점 이라고 생각입니다.

그런데 기존에 구축되어 있는 프로그램을 유지 보수 하다보면은 디버깅 할일이 발생합니다.

기존의 선임이 Release / Debug  별로 프로젝트를 잘 관리했으면 좋겠지만

세상 사는 일이 다 내맘데로 안되듯이 너무 오랜기간 유지 보수를 하다보니 Debug 정보가 유실되는 경우가 있습니다.

프로젝트가 하나의 exe이면 다시 Debug 컴파일 하면되지만 exe와 dll이 약 40~50개 연결된 프로그램이라면

헉 소리 납니다.

이럴때 기본적으로 되어 있는 Release에서 디버깅 정보를 삽입해서 컴파일 할 수 있습니다.

프로젝트 속성 (ALT + F7) 으로 들어가서 아래 그림과 같이 3개의 설명을 그림에 맞게 해주시면 Release 에서도 

디버깅 가능합니다.

* Property Page -> Configuration Properties -> C++ -> General -> Debug Information Format

* Property Page -> Configuration Properties -> C++ -> Optimization -> Optimization 

* Property Page -> Configuration Properties -> Linker -> Debugging -> Generate Debug Info

  
posted by 뚱2

현재 WTL 최신 버전은 WTL81_9127 입니다.

WTL 설치하고 Visual Studio 2008에서 실행시키면

일반 AppWiz는 잘 되는데 모바일 용인 AppWizMobile은 안됩니다.

이리 저리 삽질 하고 수정한 버전 올립니다.

추가로 Windows 7 x64에서도 구동되게 수정했습니다.

posted by 뚱2

저는 대부분 MFC로 응용프로그램을 만들때 공유 DLL로 MFC 라이브러리 포함을 선택합니다. 

Static Library로 해서 소스코드가 커지는게 싫기도 하고

제 프로그램 개발방식이 exe +  확장 dll로 구성되어 있기때문에(확장 dll이 많게는 20개 이상도 됩니다.)

MFC dll 하나더 포함된다고 관리가 불편해 지는 것도 아니기에 소스크기를 줄일려고 DDL로 사용합니다.

그러지만은 디버깅시에는 MFC소스 코드를 쫓아가기 힘듭니다.

왜!! MFC 소스 코드를 쫓아가야 하냐?? 제가 만든 오류와 버그가 MFC쪽 소스코드에서 잡히는 경우가 있기때문입니다.

MFC 소스코드를 보면은 ASSERT로 Validation체크를 많이 해 놓는데 이게 Debug시에만 나타납니다.


그런데 그 소스 코드라인이 제 코드가 아니라 MS MFC 코드 속입니다.
결국 MFC 소스코드를 쫓가가서 직접 눈으로 확인해야 합니다.

이럴때는 Static Library로 연결해 놓으면 디버깅을 걸어서 소스코드를 쫓아갈수 있습니다.

posted by 뚱2

링크 : http://syung1104.blog.me/181447343 

Dynamic-Link Library 가 생성되면 .lib .dll 이 생성된다.
dll를 링크 걸어서 실행시킬때
error LNK2019: unresolved external symbol "__declspec(dllimport)
가 발생하는 경우가 있는데

다음의 2가지를 의심해 본다.
1. 오류가 발생하면 기본적으로 .lib 파일이 제대로 연결되었는지 확인한다.
2. 그래도 찾을수 없다면 dumpbin 도스 유틸리티로 .dll .lib 파일을 직접 확인해서
   함수목록이 제대로 임포트 되었는지 확인해야 한다.

명령>>dumpbin /exports "읽을파일"




ps. 위의 이미지는 SQLite3를 Windows CE용으로 컴파일 한 .lib 파일 입니다.

posted by 뚱2

C++ 캐스트 연산자

C/C++/VC++ / MFC 2009. 9. 15. 14:50

C++로 프로그래밍을 할때도 C 캐스팅을 사용하고 있습니다.
이유는 C++ 캐스트 연산자 보다 타이핑 작성라인이 적어지고 익숙해서 편하기 때문이죠...
그렇지만 여러 자료나 많은 분들이 타입변환시 더 안정적인 C++ 캐스트 연산자를 사용하라고
조언하고 있습니다.


* static_cast<타입>(대상) : 논리적으로 변환 가능한 타입을 변환한다.
설명 : 포인터끼리 타입을 변환할 때는 상속 관계에 있는 포인터끼리만 변환이 허용되면 상속 관계가 아닌
         포인터끼리는 변환을 거부한다.

* dynamic_cast<타입>(대상)
설명 : 부모 타입의 포인터를 자식 타입의 포인터로 다운 캐스팅할 때 무조건 변환을 허용하지 않고
         안전하다고 판단될 때만 허용한다.
         다운 캐스팅 할때 static_casts는 무조건 변환을 허가하지만 dynamic_cast는 실행 중에 타입을
         점검하여 안전한 캐스팅만 허가한다.
         따라서 이 연산자가 변환 가능성을 판한하기 위해서는 실행 중에 객체의 실제 타입을 판별할 수 있어야 한다.

* const_cast<타입>(대상)
설명 : 포인터의 상수성만 변경하고 싶을 때 사용한다.

* reinterpret_cast<타입>(대상)
설명 : 임이의 포인터 타입끼리 변환을 허용하는 상당히 위험한 캐스트 연산자.
         이 연산자는 포인터 타입간의 변환이나 포인터와 수치형 데이터의 변환에만 사용하며 기본 타입들끼리의
         변환에는 사용할 수 없다.

static_cast 상속 관계의 클래스 포인터 및 레퍼런스. 기본 타입. 타입 체크 안함.
dynamic_cast 상속 관계의 클래스 포인터 및 레퍼런스. 타입 체크. RTTI 기능 필요
const_cast const. volatile 등의 속성 변경
reinterpret_cast 포인터끼리, 포인터와 수치형간의 변환  
참고 : 혼자 연구하는 C/C++ (김상형 저/와우북스)
posted by 뚱2

타입 변환 연산자

C/C++/VC++ / MFC 2009. 8. 13. 17:05

C++ 에서는 타입 변환 연산자라는 기능이 있습니다.
이걸 사용하면은 클래스가 일반 타입처럼 암시적 변환이 가능합니다.

아래는 예제 클래스 입니다.
차때고 포때고 간단하게 작성했습니다.
클래스의 목적은 int의 기능을 대신하는 클래스입니다.
class MyInt
{
private:
    int m_nNum;

public:
    MyInt(int i) : m_nNum(i)  {}
    operator int() const {
      return m_nNum;
    }
    MyInt& operator =(int i)  { 
         m_nNum = i; return *this; 
    }
};
보통 연산자 오버로딩 후에는 반환값이 있습니다.
    MyInt& operator =(int i)      {   m_nNum = i; return *this; }
이런것 들 말이죠
그렇지만

연산자 오버로딩은 연산자가 함수 형태라고 해서 
리턴값을 써주면 안됩니다.
    operator int() const    {  return m_nNum;  }         // 컴파일됨
    int operator int() const    {  return m_nNum;  }     // 에러


참고 : The C++ Programming Language
posted by 뚱2

PDA 업체에서 제공 하는 API를 사용하기 위해서 .Lib 을 링크하고 .h파일을 인클루드 했는데
위와 같은 에러가 발생했다.
구전(VC++ 6.0 이하)에서는 허용되었던 함수의 리턴값 생략이 VC++ 7.0이상으로 오면서
명시적으로 바뀌면서 생기는 오류이다.
컴파일 옵션을 바꿔서 해도 되지만... 그것보다는
함수를 명시적으로 바꾸는게 바른 방법인데...
이거... 내가 만든것도 아니고 .h 바꾸면 찝찝한데... ㅡㅡ;
그렇다고 컴파일 옵션을 바꾸자니 그건 더 찝찝하고...
결국 난 .h를 변경했다.
posted by 뚱2

C, C++는 파일을 작성할때는

보통 헤더파일에는 선언

구현파일에는 정의를 많이 넣는데요

템플릿을 사용할때는 헤더와 정의를 헤더파일에 넣어야 합니다.

이유인즉슨 컴파일 과정에서 템플릿의 정의부분이 필요하기 때문입니다.


ps. 한파일에 넣지 않고 하는 방법도 있다고 하는데 가장 손쉬은 방법은

      헤더에 다 작성하는 방법입니다.
posted by 뚱2

오늘 하루종일 바보 같은 짓을 한 하루 였습니다.
제가 MFC 클래스 구조를 잘 모르는 상태에서 쓰기 급급했는데
스택으로 생성한 다이알로그도 PostNcDestroy를 상속받아서 거기서
delete this; 해줬습니다.
평소에는 이렇게 해도 괜찮았는데
CString를 선언해주면은 스택오버플로우가 나버립니다.
정확하게는 모르겠으나 스택으로 생성한 것을 자신이 delete this;
해버리는 바람에 CString 안에서 힙으로 생성한걸 해제하지 못해서
오버플로우가 나는것 같습니다.

하나 하나 정확하게 알고 사용해야 한다는 교훈을 ㅡㅡ;
MFC는 심오하네요~~
posted by 뚱2

재미있는 기능도 많이 있고 가장큰 이점은 코드를 만들어 주는 기능이다.
아래에서 다운받을수 있다.
Visual Studio .net Visual Studio 2500

http://www.cupla.net/CodeWiz2/
posted by 뚱2

간단하지만 요긴한 단축키 입니다. ^^;

우선 주석을 하고 싶은 구간을 선택하세요
주석 : Ctrl+K+C
해제 : Ctrl+K+U

PS. Visual Studio C++ 6.0 이하에서는 안됩니다.
posted by 뚱2

오류를 처리하는 가장 발전적이고 효과적인 방법이 C++ 예외 처리 기능입니다.
그런데 VC++ (6 or 7)의 설명서에 약간 오해의 소지가 있는 부분이 있습니다.
예를 들어,

try 
{ 
    int* p = 0; 
    *p = 0; 
} 
catch (...) 
{ 
    // do something 
} 

이렇게 하면, 위 *p = 0 줄에서 access violation 예외가 발생하고
그 예외가 아래 catch 블럭에서 잡힙니다.

문제는 debug 빌드인 경우에는 예상대로 예외 처리가 되는데
release 빌드일 때는 그렇지 않다는 데 있습니다.

MSDN이나 VC++ 도움말에서 예외 처리에 관련된 부분을 보면
항상 컴파일러 옵션 /GX 또는 /EHsc를 지정하라는 말이 나옵니다.
"win32 structured exception" 방식을 쓰려면 /GX 없이 그냥 컴파일하고
C++ 예외 처리 방식을 쓰려면 /GX로 컴파일하라..." 이런 식으로 설명되고 있죠.

디폴트 옵션도 /EHsc 입니다.
/EHs 는 syncronous 예외 처리 모델을 가리키고
/EHc 는 C 함수는 예외를 throw하지 않는 다고 가정하는 것을 가리킵니다.

syncronous 모델은 뭐냐하면, 명시적으로 throw가 사용되지 않은 함수에 대해서는
예외 처리에 대한 지원을 하지 않는 것을 말합니다.

따라서

try 
{ 
    int* p = 0; 
    *p = 0; 
} 
catch (...) 
{ 
    // do something 
} 

이런 프로그램은 디버그 빌드일 때는 잘 돌아가고
예외 처리도 잘 되고,
잘못된 동작이 있을 때 이쁘게 메시지 박스 띄우고 부드럽게 종료하고
다 잘 됩니다.

그러다가 릴리즈 빌드로 바꾸면 디폴트로 /EHsc 옵션이 먹으면서
예외 처리도 안 되고
access violation 같은 거 발생할 때 프로그램이 죽어버립니다.

해결책은 단순하죠.
컴파일러 옵션 대화상자에서 C++ 예외 처리를 사용하지 않음으로 선택하고
직접 /EHa 옵션을 추가해주면 됩니다.

/EHa 옵션을 주고
메인 프로그램 진입점을

try 
{ 

} 
catch (...) 
{

} 

이런 블럭으로 감싸면 프로그램이 예기치 않게 종료되는 일을 방지할 수 있습니다.
위 내용은 VC6, VC7 공통입니다.
이상입니다.

** 경고
예외 처리는 복잡한 고려사항이 많기 때문에
충분히 공부한 후 사용해야 합니다.
예외 처리는 체계적으로 사용해야만 효과적이며
그렇지 않으면 부작용이 더 클 수 있습니다.

posted by 뚱2

LNK 4099 에러관련

C/C++/VC++ / MFC 2008. 9. 18. 16:27
posted by 뚱2

VC++ 빠르게 컴파일 하기 위해서 Precompiled header 라는 것을 만들어둡니다.
그런데 다른 외부의 컴파일러로 만들어진 라이브러리(소스)에서는 이게 없습니다.
그래서  VC++에서는 프리컴파일 헤더가 필요하다고 에러를 발생시킵니다.

해결방법은 포함하는 라이브러리 소스(.cpp) 맨 윗줄에 #include "stdafx.h"
추가해 주시면 됩니다.

posted by 뚱2

RasAPI를 사용하여 간단하게 접속하는 프로그램을 작성했습니다.
그런데 디버그 모드에서는 너무나도 잘 되는게 릴리즈 모드에서만 되면은
안되더라구요.

이것 저것 하루종일 삽질(?)을 하다가 결국 초기화 문제였습니다. ㅠㅠ
초기화 되지 않은 포인터의 경우 디버그 모드에서는 자동으로 임의값 0xCD로
초기화를 수행하지만 릴리즈에서는 수행하지 않는다고 합니다.
그래서

// 수정전
m_RasDialParams.dwSize = sizeof(RASDIALPARAMS);

...

// 수정후
ZeroMemory(&m_RasDialParams, sizeof(RASDIALPARAMS));
m_RasDialParams.dwSize = sizeof(RASDIALPARAMS);

...

이렇게 해주니까 잘~~~~~ 됩니다.
정말 단순한거지만 너무 중요한 사항같습니다.
초기화 생활화 합시다.

posted by 뚱2

간단하지만 유용하게 사용하는 함수입니다.

CCmdTarget::BeginWaitCursor(); // 모래시계중 작업들... CCmdTarget::EndWatiCursor();

저 같은 경우는 MFC로 프로그래밍 할때 중간에 로딩시간이나 소켓통신이 길어질때
사용합니다.

posted by 뚱2

대부분 모달(Modal)은 스택으로 생성을 합니다.
그리고 모달리스(Modeless)는 힙을 사용해서 동적으로 사용을 많이 합니다.
이럴때 모달리스 다이알로그를 종료시키고 다시 실행시켜도 안나타날때가 있습니다.
이건 대부분 힙으로 할당한 모달리스 다이알로그를 해제 안해줘서 그렇습니다.
모달리스 다이알로그의 CWnd::PostNcDestroy()의 가상함수를 만들어서 그 안에서
메모리를 해제 시켜 주면 됩니다.
PostNcDestory() 함수는 윈도우가 소멸된 후 OnNcDestroy함수에 의하여 불려지는 함수입니다.

ps. 모달리스 다이알로그 종료시 메모리 해제 방법은 아래와 같이 해주시면 됩니다.

void CXXXXXXDlg::OnClose() 
{ 
        // TODO: Add your message handler code here and/or call default 
        DestroyWindow(); 
       //CDialog::OnClose(); 
} 

posted by 뚱2
warning C4013: 'XXXXX' undefined; assuming extern returning int
요로코롬 생긴 warning이 뜰때가 있습니다. 뭐 컴파일 제대로 되고 실행시켜도 제대로 됩니다.
그렇지만 warning!! 이게 찝찝하죠... ^^;

우선 warning은 외부 어딘가에 선언은 되어 있는데 선언을 안하고 사용해서 그렇습니다.
그래서 선언이 없어니까 컴파일러가 알아서 대~~~충 int return 한다고 가정하는
것입니다.

대부분의 경우는 선언된 .h파일이 인클루드 되어 있는지 확인해 보면은 인클루드가 안되있는
경우입니다.
posted by 뚱2

저는 MFC에서 폼베이스로 작업할때 주로 리소스 창에서 컨트롤 대충 그려넣고
서브클래싱할 클래스 하나씩 만들어주고
컨트롤 클래스의 SubClassDlgItem() 함수를 이용해서 서브클래싱합니다.
오늘 아주 간단한 리스트컨트롤을 클릭하면 세부내용을 다른 에디트 컨트롤에
뿌려주는 부분을 작성했습니다.
서브클래싱한 컨트롤의 OnClick을 잡고서 아래와 같이 코딩했습니다.

void CSMSList::OnClick(NMHDR* pNMHDR, LRESULT* pResult) 
{
    // TODO: Add your control notification handler code here
    NM_LISTVIEW *pList = (NM_LISTVIEW*)pNMHDR;
    int nIndex = pList->iItem;
    *pResult = 0;
}


분명히 리스트에서 클릭한 아이템의 인덱스를 가져와야 하는데 이상하게 자꾸 -1로
나오더군요.. ㅠㅠ

그러다 알아냈습니다.
네~~~ 맞습니다. 결국 또 저에 실수 입니다.

서브클래싱한 컨트롤의 PreSubclassWindow() 에서 아래와 같이 코딩해주니 바로 해결
됬습니다.

void CSMSList::PreSubclassWindow() 
{
    // TODO: Add your specialized code here and/or call the base class
    SetExtendedStyle(LVS_EX_FULLROWSELECT);
    CListCtrl::PreSubclassWindow();
}



PreSubclassWindow() 함수는 SubClassDlgItem()으로 서브클래싱 했을때 초기화 해주는
멤버 함수입니다.
SetExtendedStyle() 함수는 확장 스타일 함수로 LVS_EX_FULLROWSELECT를 인자로
클릭시 리스트 한줄전체(Row)가 선택되어 집니다.

위와 같이 코딩했더니 OnClick함수에서 제대로 인텍스를 얻어왔습니다.
아~!! 오늘도 하나 실수했고 한가지 배웠습니다.

posted by 뚱2

윈도우의 일반적인 컨트롤은 기능에 중점을 준것이기 때문에
모양새가 별로이다.
그럴때 색상을 변경할때 많이 사용하는게 WM_CTRCOLOR 이다.
static, Edit의 배경색을 바꿀때 요긴하다.

WM_CTLCOLOR 메세지는 컨트롤의 부모가 받는다.

조건 : 바꾸고자 하는 Edit의 ID --> IDC_EDIT_USERID
         컨트롤의 부모는           --> CDemoDlg

1. 부모인 CDemoDlg의 WM_CTLCOLOR을 등록한다.
2. 메세지 핸들러를 클래스 위져드로 등록하면 컨트롤의 부모는

HBRUSH CDemoDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
{
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
 
    // TODO: Change any attributes of the DC here
    // 새로 작성한 부분
    if ( pWnd->GetDlgCtrlID() == IDC_EDIT_USERID )
    {
        pDC->SetBkColor( RGB(249, 225, 155) );
        pDC->SetBkMode( OPAQUE );

        return m_brText;
    }
    // TODO: Return a different brush if the default is not desired
    return hbr;
}


특이한것은 변경하고자 하는 컨트롤의 리턴 HBRUSH는 hbr과 다른 것을
리턴해야 한다.
그렇지 않고 그냥 return hbr 을 리턴하면 색상이 변경되지 않는다.

ps. m_brText는 CBrush로 생성자에서 m_brText.CreateSolidBrush( RGB(249, 225, 155) )로
     생성해줬습니다.

posted by 뚱2
보통 메뉴는 CMainFrame에 있습니다.
그래서 클래스 위져드에서 UPDATE_COMMAND_UI를 이용해서
만들어 주면은 아래와 같은 모양의 함수가 생성됩니다.

OnUpdateXXXXXXXX(CCmdUI* pCmdUI) { }

보통 위 합수에서
OnUpdateXXXXXXXX(CCmdUI* pCmdUI) { pCmdUI->Enable( TRUE ); // 활성화 pCmdUI->Enable( FALSE ); // 비활성화 }

해주면 메뉴의 항목이 활성/비활성 되는데

제가 이번에 만든건 특수하게 CTreeCtrl을 상속받아서 만든 객체에서 팝업메뉴를
만들었더니 'pCmdUI->Enable( FALSE );'를 아무리 해도 안되더라구요
CMenu::EnableMenuItem  함수를 사용해서 활성/비활성을 해줬습니다.
함수의 자세한 사용법은 아래에 ^^;

ps. EnableMenuItem() 함수는 TrackPopupMenu() 함수 전에 호출해 줘야 합니다.

CMenu::EnableMenuItem

UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );

Return Value

Previous state (MF_DISABLED, MF_ENABLED, or MF_GRAYED) or –1 if not valid.

Parameters

nIDEnableItem

Specifies the menu item to be enabled, as determined by nEnable. This parameter can specify pop-up menu items as well as standard menu items.

nEnable

Specifies the action to take. It can be a combination of MF_DISABLED, MF_ENABLED, or MF_GRAYED, with MF_BYCOMMAND or MF_BYPOSITION. These values can be combined by using the bitwise OR operator. These values have the following meanings:

  • MF_BYCOMMAND   Specifies that the parameter gives the command ID of the existing menu item. This is the default.

  • MF_BYPOSITION   Specifies that the parameter gives the position of the existing menu item. The first item is at position 0.

  • MF_DISABLED   Disables the menu item so that it cannot be selected but does not dim it.

  • MF_ENABLED   Enables the menu item so that it can be selected and restores it from its dimmed state.

  • MF_GRAYED   Disables the menu item so that it cannot be selected and dims it.

Remarks

Enables, disables, or dims a menu item. The CreateMenu, InsertMenu, ModifyMenu, and LoadMenuIndirect member functions can also set the state (enabled, disabled, or dimmed) of a menu item.

Using the MF_BYPOSITION value requires an application to use the correct CMenu. If the CMenu of the menu bar is used, a top-level menu item (an item in the menu bar) is affected. To set the state of an item in a pop-up or nested pop-up menu by position, an application must specify the CMenu of the pop-up menu.

When an application specifies the MF_BYCOMMAND flag, Windows checks all pop-up menu items that are subordinate to the CMenu; therefore, unless duplicate menu items are present, using the CMenu of the menu bar is sufficient.


posted by 뚱2

이번에 프로그램 개발하면서 2주동안 삽질 한 것 입니다. ㅡㅡ;
분명히 Dialog 에서는 잘 되는 TVN_BEGINDRAG 메세지가
FormView에서만 하면 메세지가 먹통이 되는 것입니다.
그리고 컨트롤의 SubclassDlgItem 함수도 안되구요.....
그래서 엄청 삽질 하다가 제가 결정적 실수 한걸 알았습니다.

Dialog에서는 대부분 OnInitDialog에서 컨트롤을 초기화 해줍니다.
그런데 FormView에서는 OnInitDialog가 없어서 전 OnCreate에서 작업해 줬습니다.
이게 문제였던 것입니다.
아직 FormView의 모든것이 생성되기도 전에 안에서 자식 컨트롤들의 작업을 했던게
문제가 발생한 원인이었습니다. (네 ~~~~ 저 초보 맞습니다. ㅠㅠ)

결론으로 해결책은 FormView에서는 OnInitUpdate라는 함수가 있습니다.
여기에서 각각의 컨트롤 들을 초기화 해주면 Dialog와 같이 사용 하실수 있습니다.

ps. SubclassDlgItem을 이용해서 서브클래싱을 하면 서브클래싱한 컨트롤 클래스는
     PreSubclassWindow 에서 초기화 작업을 해주셔야 합니다.
posted by 뚱2

1. SS_NOTIFY (Static의 인자)
   : 서브 클래싱한 컨트롤 클래스에 SS_NOTIFY를 지정하면 해당 컨트롤이 메세지를 받을수
   있습니다.
   SS_NOTIFY를 추가하는 방법은 Resource View에서 컨트롤 속성 --> Style --> Notify를
   체크해도 되고
   PreSubclassWindow() 함수를 오버라이드 해서
   ::SetWindowLong()함수를 사용해서 설정을 변경해 주어도 됩니다.

2. Reflection Message
  : 대부분의 컨트롤은 컨트롤의 배경이나 색상을 부모 윈도우가 변경할 수 있도록 통지 메세지를
    부모윈도우에게 보낸다.
    그런데 MFC4.0부터 이러한 통지 메세지를 부모 윈도우뿐만 아니라 컨트롤 자체에서 처리 할
    수 있도록 추가된 기능이다.
    클래스 위져드를 띄워보면 =WS_XXXX 란 메세지가 Reflection Message 이다.


참고자료 : MFC 정복

posted by 뚱2
이번 회사 프로젝트 진행중 스레드 안에서 컨트롤을 생성해야 하는 일이 생겼는데
해결도 못하는 시스템 오류가 ㄷㄷㄷ....

결국 사용자 정의 메세지를 사용하기로 했습니다.

1. 메세지를 정의한다
:
 윈도우 자체적으로 WM_USER 라는 사용자가 정의해서 사용 할 수 있는 메세지가 있습니다.
 WM_USER+1, WM_USER+2 이렇게 사용합니다.
 그렇지만 좀더 멋드러지게 사용하고 싶다면...
#define WM_MY_MESSAGE     WM_USER+1

  
이렇게 정의해서 사용하시면 좀더 폼납니다. ^^;
메세지는 보통 따로 헤더파일을 연결해서 사용하기도 하고
전역적으로 사용하고 싶으면 StdAfx.h에 선언합니다.

2. 실제 사용할 윈도우 객체의 메세지 멤버함수를 정의합니다.
:

 
 //{{AFX_MSG(CThreadDlg)
 // Generated message map functions
virtual BOOL OnInitDialog();
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon();
 afx_msg void OnDestroy();
 afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
 afx_msg void OnClose();
 //}}AFX_MSG
afx_msg LONG OnMyMessage(UINT wParam, LONG lParam);
 DECLARE_MESSAGE_MAP()

위에 보시면 헤당 .h 파일의 클래스 안에 선언해 줍니다.
//{{AFX_MSG(CThreadDlg)
...
//}}AFX_MSG
위 안에는 선언하지 마세요 주석으로 보이지만 이건 클래스 위져드가 사용하는
코드입니다. 여기 잘 못 선언해 주시면 클래스 위져드가 알아져 지우거나 위져드 상에서
다른 코드들이 안보이는 불상사가 발생합니다.
그래서 저 같은 경우는
//}}AFX_MSG 여기 다음줄에 선언해 줍니다.
그리고 .cpp 파일에 위에서 선언한 사용자 정의 함수를 정의해 줍니다.

3. .cpp 파일의 메세지 맵에서 연결해 줍니다.

BEGIN_MESSAGE_MAP(CThreadDlg, CDialog)
 //{{AFX_MSG_MAP(CThreadDlg)
 ON_WM_SYSCOMMAND()
 ON_WM_PAINT()
 ON_WM_QUERYDRAGICON()
 ON_WM_DESTROY()
 ON_WM_LBUTTONDOWN()
 ON_WM_CLOSE()
 //}}AFX_MSG_MAP
 ON_MESSAGE(WM_CREATEVS, OnCreateVS)
END_MESSAGE_MAP()

보시면 알겠지만 2번과 같이 여기도  //}}AFX_MSG_MAP 다음에 선언해주세요

4. 실제 사용은 SendMessage() 함수나, PostMessage() 함수를 이용해서 해주시면 됩니다.



  
posted by 뚱2

이번에 프로젝트를 하면서 전역객체를 사용할 일이 생겼었습니다.
전역객체로 진행하던중 이왕이면 디자인패턴을 사용해보자!!!
그래서 싱글턴을 이용해 보려고 합니다.

* Singleton Patterns ::
   해당 클래스의 인스턴스가 한개만 생성해서 그 인스턴스를 전역객체 처럼 어디서든지
   사용 할 수 있게 하는 디자인패턴입니다.

// Singleton.h
class CSingleton
{
private:
    static CSingleton * m_pUniqueInst;
    CSingleton();

public:
    ~CSingleton();
    static CSingleton & GetInstance();
};

// Singleton.cpp
#include "Singleton.h"

// static 초기화
CSingleton* CSingleton::m_pUniqueInst = NULL;

CSingleton::CSingleton()
{
}
CSingleton::~CSingleton()
{
    if (m_pUniqueInst)
        delete m_pUniqueInst;
}
CSingleton & CSingleton::GetInstance()
{
    if (m_pUniqueInst == NULL)
        m_pUniqueInst = new CSingleton;

    return *m_pUniqueInst;
}



간단하게 만들어본 싱클턴입니다.
GetInstance() 멤버 함수가 static으로 선언되어 있기 때문에 아무곳에서나
호출 가능합니다. 그리고 생성자가 private로 선언되어 있기때문에 CSingleton의
인스턴스를 만들려고 하면은 컴파일 에러가 발생합니다.


ps. 위 싱글턴은 싱글스레드 용입니다. 멀티스레드에서는 문제가 생길수 있습니다.

posted by 뚱2

이번에 개발하는 프로그램에서 Log 파일을 기록하려고 CLogFile이라는 클래스를 하나
만들었습니다.
그런데 이 클래스는 스레드문제로 인해서 제가 생성자에서 InitializeCriticalSection(&m_csKey)
소멸자에서 DeleteCriticalSection(&m_csKey)을 호출 합니다.
그리고 생성자에서 여러가지 작업을 해야 합니다.
그러니까 생성자 소멸자는 전역적으로 한번만 호출하고 실제 로그 파일을 기록하는 멤버 함수
SetLog() 함수만 필요한 곳에서 호출 하는 방법 입니다.
그렇게 위해서는 결국 전역 적으로 클래스의 인스턴스를 생성해야 했습니다.

그래서 결국 쓴 방법이

StdAfx.h 에서
extern CLogFile g_LogFile;
이라고 선언해 줬습니다.

그리고
StdAfx.cpp에서
CLogFile g_LogFile
이렇게 정의 해주고

필요한 곳의 파일마다 StdAfx.h을 인클루드 해서 사용하는 방법으로 해결봤습니다.
우선 테스트 하기로는 잘 되네요

ps. 조금 불편하더라도 StdAfx.h StdAfx.cpp에서 작성하는게 찜찜하다면 해당 .cpp
      파일에서 CLogFile g_LogFile; 해주시고 사용하실려는 .cpp 파일에서
      extern CLogFile g_LogFile이라고 해주셔도 됩니다.

posted by 뚱2

MFC에서 자동으로 소스를 생성하다보면 .h 파일에

#if !defined(AFX_SVRRSDLG_H__B0E12C65_26A3_4782_8556_8AE0D6107F9C__INCLUDED_)
#define AFX_SVRRSDLG_H__B0E12C65_26A3_4782_8556_8AE0D6107F9C__INCLUDED_

// 소스 ...

#endif


위와 같은 소스 비스무리하게 생긴다..
위 예제에서 defined를 찾아볼수 있는데 defined는 #define 되어 있는지 확인하는 연산자이다.
대부분 중복 컴파일 방지를 위해서 사용한다.

posted by 뚱2

[MFC] HitTest()

C/C++/VC++ / MFC 2008. 3. 3. 11:05

대부분 끌어서 놓기 동작(Drag And Drop)을 수행할 때에 끌기(Drag) 대상 항목을
현재 위치에 사용할 수 있는지를 결정하는 용도로 가장 많이 사용됩니다.

MFC의 컨트롤 중에 HitTest() 메소드가 있는 컨트롤들이 있습니다.
현재 선택된 항목(찍힌 위치의 대상)의 정보를 알수 있는 용도로 사용합니다.

각 컨트롤 마다 HitTest()의 세부 항목이 다르기 때문에 자세한 사항은 MSDN 참고하면
됩니다.

posted by 뚱2