'3. Implementation/MFC'에 해당되는 글 52건
- 2011.08.08 DLLHUSK Sample: Dynamically Links the MFC Library
- 2010.03.11 Adding automation to MFC applications
- 2010.01.06 다중 OLE 객체 인스턴스 지원하기
- 2009.10.22 CExpire - A C++ class that implements time and run based restrictions
- 2009.10.22 2K/XP 에서 포커스를 훔치는 방법
- 2009.10.22 모달 대화상자를 숨겨진 채로 시작하기
- 2009.10.16 CHtmlView 관련 몇가지 팁
- 2009.10.16 Automated IE SaveAs MHTML
- 2009.09.13 Multithreading: Programming Tips
- 2009.09.13 애플리케이션 리소스 Localization하기
- 2009.09.13 Accessing MFC Objects from Non-MFC Threads
- 2009.09.13 MFC DLL 간단 설명
- 2009.09.13 멀티스레드와 MF
- 2009.09.10 MFC DLL 간단 설명
- 2009.09.10 Multithreading: Programming Tips
- 2009.09.02 CWnd Derived 클래스 Drag and drop 구현하기 (With OLE)
- 2009.07.04 MFC에서 Undo 와 Redo 구현하기
- 2009.06.29 MDI에서 Subclassing 활용
- 2009.06.04 MFC 프로젝트에 ATL 지원 추가하기
- 2009.06.01 MFC Technical Notes
DLLHUSK Sample: Dynamically Links the MFC Library
DLLHUSK는 동적으로 MFC DLL (Extension dll) 을 로드하는 방법에 대해서 설명하고 있습니다.
MFC 를 오래동안 사용 해왔지만, Regular DLL 의 동적 로딩에만 신경썼을 뿐, MFC Dll 자체가 동적으로 될거냐라는 질문을 해본적이 없네요. 항상 느끼지만 호기심을 억누르는 선입견 또는 그냥 있는 그대로만 받아들이는 수동적인 자세를 경계해야 겠습니다.
디자인 관점에서 보면, View, Document 를 외부 DLL 에서 구현하고, 이를 로딩할 수만 있다면 다큐먼트 확장에 유연한 MFC Application 을 만들수 있지 않느냐라는 생각이 들었습니다. 또, 가끔 이런 요구사항이 극히 드물게 있기도 했습니다. 하지만 이를 유연하게 구현하는 App 은 보지 못한거 같습니다. (주변에서요....;;)
어쨋든 생각한 순간 예상했던 대로 Microsoft 는 이를 위한 방법을 제공하고 있으며, 친절하게도 그 샘플까지 공유하고 있습니다.
확장성은 trade-off 가 있을 수 있지만, 디자인 관점에서 보면 항상 극복하고 싶은 도전 과제라는 생각이 듭니다.
Adding automation to MFC applications
Introduction
Automation support in MFC is a great example of why some people don't like MFC: it works great as long as you follow the pre-made examples and use MFC exactly like it was designed, but as soon as you get even a little bit off the beaten path, you're on your own. There are some tutorials in MSDN and articles online that will teach you how to use the 'New project' wizard to generate an automation-enabled application, and some of those even explain some of the working of the code the wizard generates - but unfortunately, that's not always how things go in the Big Bad Real World. Sometimes, you inherit an application that is so old that the initial skeleton was probably generated with the wizard of Visual Studio 4; that has been expanded and hacked on by dozens of people, each with their own understanding (or lack thereof) of the MFC doc/view architecture; that has mutated from an afternoon-hack-proof-of-concept to a piece of software that is critical for the survival of the company; that has had so much surgery that its innards have started to resemble (metaphorically speaking) the leftovers of the struggle that a 3-year old had with a big bowl of spaghetti bolognese. Believe me - not pretty.
So, what can you do when you are asked to add automation support to such an application? Starting over with a fresh application and moving the functionality of the old code back in is not an option. And even if it was, you would find out that the wizard-generated code is built for the idea that the central element of your application's object model is the Document. In some MFC applications, that is simply not the case any more. The only recourse is to figure out exactly what it is that makes automation tick in an MFC application - and add those pieces into the old application.
The obvious way to start is to take an application that is wizard-generated with automation support, and compare it to an application that doesn't have automation support. Add those differences to the old application and you're all set, right? Well, that's partially true - as long as the object model that you want to implement is centered around a Document, as I said before. If you have an object model that doesn't work this way, and you wonder how to get automation to work, read on.
......
http://www.codeproject.com/KB/COM/mfc_autom.aspx
다중 OLE 객체 인스턴스 지원하기
#define MY_IMPLEMENT_OLECREATE(class_name, external_name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ COleObjectFactory class_name::factory(class_name::guid, \ RUNTIME_CLASS(class_name), TRUE, _T(external_name)); \ const GUID CDECL class_name::guid = \ { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; \ MY_IMPLEMENT_OLECREATE(CMyClass, "Test", 0xc9c99ae0, 0xad41, 0x101b, 0x83, 0xea, 0x0, 0x0, 0x83, 0x78, 0xac, 0x8b) |
그리고 항상 새로운 인스턴스를 생성하기 위해서는 :
( 그냥 이 매크로만 재정의해서 사용하는 경우에는 COM 객체가 연결되지 않은 기존의 인스턴스가 실행되어 있는 경우 이 인스턴스로부터 COM 객체를 생성한다. )
InitInstance 에서 COleTemplateServer::RegisterAll(); 함수를 적절한 곳에 이동시킨다.
// App was launched with /Embedding or /Automation switch. // Run app as automation server. if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated) { COleTemplateServer::RegisterAll(); // Don't show the main window return TRUE; } |
참고 : http://support.microsoft.com/kb/141154/en-us/
CExpire - A C++ class that implements time and run based restrictions
Purpose
This class is something I wrote to kill time. But by the time I finished it, I thought it might be pretty useful for shareware developers. It can be used to implement limited-number-of-runs and limited-time-period-only programs. Means if you want to allow only 25 runs, then this class will do that for you. Or if you want to restrict execution to 15 days from first run, this program will do that too. By the way, even if the user changes his system date back to the allowed-time-period after it has expired, he/she won't be able to run it.
Disclaimer
This is by no means a fool-proof protection scheme, but it serves its purpose at the intermediate level. And usually crackers manage to actually patch the executable to disable these checks or they manage to write key generators for your protected programs. But this class will save some time for you in the sense, it actually does what you would otherwise have to code on your own.
Protection Mechanism
It uses a combination of registry and file based protection mechanisms. You should be smart enough to pick the right innocent sounding registry keys and filenames to prevent the casual user from simply deleting a few odd keys and break the protection.
How to use the class.
You must instantiate an object of the class as early as possible. You may
declare it as a global object if you would like to. In an SDK program, you might
want to instantiate the class on the first line in your WinMain()
and in an MFC program you might want to put it in your CWinApp
derived class's InitInstance()
.
There are two ways in which you can use the class. In the first way you specify a specific number of times that the program can be run. In the second you set a number of days so that the program is active for that many days from the time of first execution. I shall demonstrate both methods below.
The constructor takes 4 parameters.
CExpire(const char* ProgName,const char* KeyName, UINT Num, UINT ExpireType);
ProgName
- This is a unique value that will be used to create
the unique registry key for protection. It is a good idea to choose a
GUID for this key. It will be placed in a location confusing enough for
most of the average users of your program.
KeyName
- This is again used as part of the unique registry key
as well as in the generation of the special file. You can use a unique, but
natural sounding name like MouseDrv98 or something that would not actually stand
out.
Num
- This is either the number of days from the date of
execution or the total number of runs allowed. This value's interpretation
depends upon the ExpireType
parameter.
ExpireType
- This can be one of two values as shown below
TYPERUNS - This implies run-count based protection
TYPEDAYS - This implies date-interval based protection
There are only three public functions that you'll need to call.
bool HasExpired();
It returns true
if your program has expired and
false
if the user is still within his allowed limits.
UINT GetDaysLeft();
This returns the number of days left before the program expires. Use this if
you have chosen ExpireType
as TYPEDAYS
UINT GetRunsLeft();
This returns the number of runs left before the program expires. Use this if
you have chosen ExpireType
as TYPERUNS
Example Usage
SDK example
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE,LPSTR,int) |
MFC Example
BOOL CTest_deleteApp::InitInstance() |
Important Warning
DON'T choose a value for the KeyName
parameter that might
already define a file in the Windows System directory. It will be over-written.
The best thing to do is to choose a filename and then add some numbers to the
end like 32_32 or something natural sounding.
Thank you.
From : http://www.voidnish.com/Articles/ShowArticle.aspx?code=cexpire01
2K/XP 에서 포커스를 훔치는 방법
How to steal focus on 2K/XP
I bet that sometimes you long for the old days when a simple
SetForegroundWindow
brought your dialog into focus. Sigh! Now with
2K/XP things have sorta changed so that if you try a simple
SetForegroundWindow
you end up flashing the taskbar icon a few
times (I never counted but something tells me it flashes thrice). Not exactly
what you wanted to do, eh? Luckily there are ways to bring your dialog into the
foreground.
The trick is to use AttachThreadInput
to attach the thread that
owns the current foreground window to our thread, then call
SetForegroundWindow
and then detach the attached thread, again
using AttachThreadInput
. Cool, huh?
//Attach foreground window thread |
From : http://www.voidnish.com/Articles/ShowArticle.aspx?code=dlgboxtricks
모달 대화상자를 숨겨진 채로 시작하기
Starting a modal dialog hidden
You often hear people complain that despite putting a
ShowWindow(SW_HIDE)
in their OnInitDialog
their modal
dialog still starts up in a shown state. The problem here is that when
CDialog::OnInitDialog()
finishes it will call
ShowWindow(SW_SHOW)
. Thus your dialog box is again made visisble.
But then as, is to be expected, people have worked around this. Here is what you
need to do.
Add a BOOL
member to your dialog class and call it something,
say visible
.
Now in your dialog constructor set visible
to
false
.
visible = false;
Now you need to override WM_WINDOWPOSCHANGING
. You might have to
change your message filtering options to have this message show up in the Class
Wizard.
void CTest_deleteDlg::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
{
if(!visible)
lpwndpos->flags &= ~SWP_SHOWWINDOW;
CDialog::OnWindowPosChanging(lpwndpos);
}
That's it. Now your modal dialog actually starts up in a hidden state. And when you want to make it visible this is what you need to do.
visible = true;
ShowWindow(SW_SHOW);
From : http://www.voidnish.com/Articles/ShowArticle.aspx?code=dlgboxtricks
CHtmlView 관련 몇가지 팁
DeleteUrlCacheEntry(lpURL);
나) 브라우져컨트롤(CHtmlView)에서 스크립트 오류안나오게하기 - CHtmlView::OnAmbientProperty 오버로딩해서 구현하면 됩니다( http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=6243&ref=6243 사이트에서정보얻음)
BOOL CHtmlView::OnAmbientProperty(COleControlSite* pSite, DISPID dispid, VARIANT* pvar)
{
// TODO: Add your specialized code here and/or call the base class
if (pvar && dispid == DISPID_AMBIENT_DLCONTROL) {
V_VT(pvar) = VT_I4;
V_I4(pvar) =
DLCTL_DLIMAGES|
DLCTL_VIDEOS|
DLCTL_BGSOUNDS|
DLCTL_NO_SCRIPTS|
0;
return TRUE;
}
else
{
return CHtmlView::OnAmbientProperty(pSite, dispid, pvar);
}
//return CHtmlView::OnAmbientProperty(pSite, dispid, pvar);
}
출처 : http://eslife.tistory.com/entry/MFC-STL-%EA%B0%84%EB%8B%A8-%EC%82%AC%EC%9A%A9%ED%8C%81
Automated IE SaveAs MHTML
아래 링크에서는 Web Browser 에서 MHTML 로 변환할 수 있게 해준다.
http://www.codeproject.com/KB/shell/iesaveas.aspx?display=Print
아래는 관련 소스
되게 유용한데 현재 프로젝트에 적용하는데는 문제가 있다. App 내에서 노출된 자동화 개체와의 연결 때문이다. 이런 html 파일을 mhtml 로 저장하더라도 보이는 화면 자체가 mhtml 으로 저장되는 게 아니라 아쉽게도 javascript 가 포함된 문서가 저장된다. 그렇게 되면 동적으로 생성된 그림등은 mhtml 문서에서도 역시 동적으로 생성되기때문에 스크립트 에러가 나고 이미지도 저장되지 않는다.
과연... javascript 가 완전히 번역된 후의 html 을 기반으로 mhtml 을 저장할 수 는 없을까?
항상 난관이 있네...~ ㅎㅎ
Multithreading: Programming Tips
Multithreaded applications require stricter care than single-threaded applications when accessing data. Because there are multiple, independent paths of execution in use simultaneously in multithreaded applications, either the algorithms, the data, or both must be aware that data could be used by more than one thread at a time. This topic explains techniques for avoiding potential problems when programming multithreaded applications with the Microsoft Foundation Class (MFC) library.
Accessing Objects from Multiple Threads
For size and performance reasons, MFC objects are not thread-safe at the object level, only at the class level. This means that you can have two separate threads manipulating two different CString objects, but not two threads manipulating the same CString object. If you absolutely must have multiple threads manipulating the same object, protect such access with appropriate Win32 synchronization mechanisms, such as critical sections. For more information about critical sections and other related objects, see Synchronization in the Platform SDK.
The class library uses critical sections internally to protect global data structures, such as those used by the debug memory allocation.
Accessing MFC Objects from Non-MFC Threads
If you have a multithreaded application that creates a thread in a way other than using a CWinThread object, you cannot access other MFC objects from that thread. In other words, if you want to access any MFC object from a secondary thread, you must create that thread with one of the methods described in Multithreading: Creating User-Interface Threads or Multithreading: Creating Worker Threads. These methods are the only ones that allow the class library to initialize the internal variables necessary to handle multithreaded applications.
Windows Handle Maps
As a general rule, a thread can access only MFC objects that it created. This is because temporary and permanent Windows handle maps are kept in thread local storage to help maintain protection from simultaneous access from multiple threads. For example, a worker thread cannot perform a calculation and then call a document's UpdateAllViews member function to have the windows that contain views on the new data modified. This has no effect at all, because the map from CWnd objects to HWNDs is local to the primary thread. This means that one thread might have a mapping from a Windows handle to a C++ object, but another thread might map that same handle to a different C++ object. Changes made in one thread would not be reflected in the other.
There are several ways around this problem. The first is to pass individual handles (such as an HWND) rather than C++ objects to the worker thread. The worker thread then adds these objects to its temporary map by calling the appropriate FromHandle member function. You could also add the object to the thread's permanent map by calling Attach, but this should be done only if you are guaranteed that the object will exist longer than the thread.
Another method is to create new user-defined messages corresponding to the different tasks your worker threads will be performing and post these messages to the application's main window using ::PostMessage. This method of communication is similar to two different applications conversing except that both threads are executing in the same address space.
For more information about handle maps, see Technical Note 3. For more information about thread local storage, see Thread Local Storage and Using Thread Local Storage in the Platform SDK.
Communicating Between Threads
MFC provides a number of classes that allow threads to synchronize access to objects to maintain thread safety. Usage of these classes is described in Multithreading: How to Use the Synchronization Classes and Multithreading: When to Use the Synchronization Classes. For more information about these objects, see Synchronization in the Platform SDK.
See Also
애플리케이션 리소스 Localization하기
CWinApp상속받은 나의 App 클래스의 맴버로
HINSTANCE m_hInstDLL;
를 추가한 후
OnInitInstance() 첫줄에
if ((m_hInstDLL = ::LoadLibrary("mylocal.dll")) < HINSTANCE_ERROR)
{
return FALSE; // failed to load the localized resources
}
else
{
AfxSetResourceHandle(m_hInstDLL); // get resources from the DLL
}
하고 OnExitIntance() 부에
int CMyApp::ExitInstance()
{
FreeLibrary(m_hInstDLL);
return CWinApp::ExitInstance();
}
출처 : 인터넷 검색하면 나오는데 원본 출처는 불분명
Accessing MFC Objects from Non-MFC Threads
출처 : 인터넷 검색하면 나오는데 원본 출처는 불분명
MSDN : Multithreading: Programming Tips
만일 CWinThread 객체를 이용하지 않고 멀티스레드 애플리케이션을 만들었다면 다른 스레드의
MFC 객체를 접근할 수 없을 것이다. 반대로 말해 MFC객체를 이차 스레드에서 접근하려면
반드시 AfxBeginThread를 사용해 CWinThread의 GUI 스레드 방식을 쓰거나 . 워커 스레드 방식
으로 써야 한다.
이는 멀티스레드 애플리케이션이 클래스 라이브러리를 초기화하고 필요한 내부변수를 핸들링
하기 위한 것이다.
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
int nPriority, UINT nStackSize, DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
pfnThreadProc;
pParam;
nPriority;
nStackSize;
dwCreateFlags;
lpSecurityAttrs;
return NULL;
#else
ASSERT(pfnThreadProc != NULL);
CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
ASSERT_VALID(pThread);
if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
lpSecurityAttrs)) // CreateThread에서
{
pThread->Delete();
return NULL;
}
VERIFY(pThread->SetThreadPriority(nPriority));
if (!(dwCreateFlags & CREATE_SUSPENDED))
VERIFY(pThread->ResumeThread() != (DWORD)-1);
return pThread;
#endif //!_MT)
}
BOOL CWinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
dwCreateFlags;
nStackSize;
lpSecurityAttrs;
return FALSE;
#else
ASSERT(m_hThread == NULL); // already created?
// setup startup structure for thread initialization
_AFX_THREAD_STARTUP startup; memset(&startup, 0, sizeof(startup));
startup.pThreadState = AfxGetThreadState(); // 스레드 상태를 얻어온다.
startup.pThread = this;
startup.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
startup.hEvent2 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
startup.dwCreateFlags = dwCreateFlags;
if (startup.hEvent == NULL || startup.hEvent2 == NULL)
{
TRACE0("Warning: CreateEvent failed in CWinThread::CreateThread.\n");
if (startup.hEvent != NULL)
::CloseHandle(startup.hEvent);
if (startup.hEvent2 != NULL)
::CloseHandle(startup.hEvent2);
return FALSE;
}
// create the thread (it may or may not start to run)
m_hThread = (HANDLE)_beginthreadex(lpSecurityAttrs, nStackSize,
&_AfxThreadEntry, &startup, dwCreateFlags | CREATE_SUSPENDED, (UINT*)&m_nThreadID);
if (m_hThread == NULL)
return FALSE;
// MFC 스레드 상태 구조체 안에는 여러 핸들맵등 중요한 스레드별로 관리되는
// 핸들들이 있다. 따라서 AfxBeginThread를 이용해야만 2차 스레드가 핸들맵을
// 옳바로 전달 받을 수 있게 된다.
// AFX_MODULE_THREAD_STATE (local to thread *and* module)
class AFX_MODULE_THREAD_STATE : public CNoTrackObject
{
public:
AFX_MODULE_THREAD_STATE();
virtual ~AFX_MODULE_THREAD_STATE();
// current CWinThread pointer
CWinThread* m_pCurrentWinThread;
// list of CFrameWnd objects for thread
CTypedSimpleList<CFrameWnd*> m_frameList;
// temporary/permanent map state
DWORD m_nTempMapLock; // if not 0, temp maps locked
CHandleMap* m_pmapHWND;
CHandleMap* m_pmapHMENU;
CHandleMap* m_pmapHDC;
CHandleMap* m_pmapHGDIOBJ;
CHandleMap* m_pmapHIMAGELIST;
// thread-local MFC new handler (separate from C-runtime)
_PNH m_pfnNewHandler;
#ifndef _AFX_NO_SOCKET_SUPPORT
// WinSock specific thread state
HWND m_hSocketWindow;
#ifdef _AFXDLL
CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapSocketHandle;
CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapDeadSockets;
CEmbeddedButActsLikePtr<CPtrList> m_plistSocketNotifications;
#else
CMapPtrToPtr* m_pmapSocketHandle;
CMapPtrToPtr* m_pmapDeadSockets;
CPtrList* m_plistSocketNotifications;
#endif
#endif
};
class _AFX_THREAD_STATE : public CNoTrackObject
{
public:
_AFX_THREAD_STATE();
virtual ~_AFX_THREAD_STATE();
// override for m_pModuleState in _AFX_APP_STATE
AFX_MODULE_STATE* m_pModuleState;
AFX_MODULE_STATE* m_pPrevModuleState;
// memory safety pool for temp maps
void* m_pSafetyPoolBuffer; // current buffer
// thread local exception context
AFX_EXCEPTION_CONTEXT m_exceptionContext;
// CWnd create, gray dialog hook, and other hook data
CWnd* m_pWndInit;
CWnd* m_pAlternateWndInit; // special case commdlg hooking
DWORD m_dwPropStyle;
DWORD m_dwPropExStyle;
HWND m_hWndInit;
BOOL m_bDlgCreate;
HHOOK m_hHookOldCbtFilter;
HHOOK m_hHookOldMsgFilter;
// other CWnd modal data
MSG m_lastSentMsg; // see CWnd::WindowProc
HWND m_hTrackingWindow; // see CWnd::TrackPopupMenu
HMENU m_hTrackingMenu;
TCHAR m_szTempClassName[96]; // see AfxRegisterWndClass
HWND m_hLockoutNotifyWindow; // see CWnd::OnCommand
BOOL m_bInMsgFilter;
// other framework modal data
CView* m_pRoutingView; // see CCmdTarget::GetRoutingView
CFrameWnd* m_pRoutingFrame; // see CCmdTarget::GetRoutingFrame
// MFC/DB thread-local data
BOOL m_bWaitForDataSource;
// common controls thread state
CToolTipCtrl* m_pToolTip;
CWnd* m_pLastHit; // last window to own tooltip
int m_nLastHit; // last hittest code
TOOLINFO m_lastInfo; // last TOOLINFO structure
int m_nLastStatus; // last flyby status message
CControlBar* m_pLastStatus; // last flyby status control bar
// OLE control thread-local data
CWnd* m_pWndPark; // "parking space" window
long m_nCtrlRef; // reference count on parking window
BOOL m_bNeedTerm; // TRUE if OleUninitialize needs to be called
};
EXTERN_THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
_AFX_THREAD_STATE* AFXAPI AfxGetThreadState();
MFC DLL 간단 설명
MFC에서 DLL의 종류는 두가지이다. _USRDLL(MFC정규DLL)과 _AFXDLL(MFC확장DLL)이다.
_USRDLL은 MFC라이브러리의 초기화 종료를 위해 하나의 CWinApp 객체를 필요로한다.
_AFXDLL은 CWinApp 객체를 필요로 하지 않는다. 이는 애플리케이션의 MFC 클래스들을
공유하기 때문이다.
AFXEXT는 AFXDLL이다.
_AFXDLL이 정의가 되면, AfxGetResourceHandle함수로 리턴되는 값은 MFC의 전역 변수로
저장된 값이 리턴되는데 이 값은 EXE나 확장 DLL또는 MFC DLL 모두가 공유하는 값이다.
엄격히 말해, 리소스를 로드한다는 말은 인스턴스 핸들 보다는 모듈 핸들가 연관있다.
(인스턴스는 각 다른 데이타를 갖는 모듈(즉, 코드와 리소스들)들을 공유한다. )
DLL은 EXE의 핸들과 다른 모듈 핸들을 갖는다.
::GetModuleHandle을 DLL의 모듈핸들을 얻기 위해 쓸수 있고, AfxSetResourceHandle을
통해서 DLL이 리소스를 찾을려고 할때 바로 찾을수 있게 처음에 위치하게 할 수 있다.
그러나 알아둘점은 이렇게 하게 되면 .EXE 모듈을 찾을 수 없게 된다. 그래서 보통
AfxSetResourceHandle을 호출하기전에 AfxGetResourceHandle로 핸들값을 복사해 저장한다.
DLL리소스의 로드가 끝나면 다시 EXE모듈 핸들을 세팅하면된다.
출처 : 검색하면 여기저기에서 나옴.
멀티스레드와 MF
MSDN : CWnd Derived MFC Objects and Multi-threaded Applications
멀티스레드 애플리케이션을 MFC를이용하여 짠다면, MFC 객체를 스레드간에 넘겨서는
안된다. 이 일반적인 룰에서 한 스레드는 오직 자신이 생성한 MFC객체들만 접근해야한다.
이렇게 하지 않으면 assertion이나 기대치 못한 행동등의 런타임 문제를 발생시킨다.
Win32 프로세스 주소영역 안에서 동작하는 모든 스레드는 모든 전역,static 자료를
접근할 수 있다. 스레드는 TLS(Thread-Local-Storage)로 스레드 자신만의 자료를 쓸수있다.
윈도우는 스레드들에 의해 관리되므로 멀티스레드 환경하에서, MFC는 Thread Local Storage
안에 임시적적이거나 영구적인(temporary and permanent) 윈도우 핸들 맵을 관리한다.
물론 다른 GDI 객체나 디바이스 컨텍스트를 위한 핸들 맵 역시 그렇게 관리한다.
TLS에 관리되는 윈도우 핸들 맵은 여러 스레드들 사이에서 동시에 접근되어선 안된다.
CHandleMap::LookupPermanent()와 CHandleMap::LookupTemporary() 함수는 이런 사실들의
직접적인 예이다. 주어진 윈도우 핸들에 대해 이런 함수들은 MFC CWnd를 상속받은 객체와
연관있는 현재 스레드의 permenent하거나 temporary한 윈도우 핸들맵을 체크한다.
만일 그런 함수로 다른 스레드에 의해 생성된 윈도우 객체에 대한 MFC 객체를 찾으려고 한다면
함수 호출은 실패가 될것이다.
CHandleMap::LookupPermanent()와 CHandleMap::LookupTemporary()를 호출하는 함수들이
몇개 있다. CWnd::AssertValid()( ASSERT_VALID라는 매크로도 역시) 그런 함수들중 하나다.
이 함수는 객체의 유효성을 체크하기 위해 쓰인다. 만일 AssertValid()가 MFC 객체의 m_hWnd
맴버를 핸들맵에서 찾지 못하거나, 잘못된 엔트리에서 찾게되면 이 함수는 assertion을 일으킨다.
VC++ 2.1에서는 이런 assertion은 Wincore.cpp파일의 797과 788번재줄이며
VC++ 2.2에서는 Wincore.cpp파일의 804와 805번째줄,
VC++ 4.0에서는 Wincore.cpp파일의 871과 872번째 줄에 assertion이 생기게 된다
ASSERT_VALID 매크로는 MFC 소스코드 전반에 걸쳐 많이 존재한다. 따라서 만일 특정스레드에서
다른 스레드에 속하는 MFC 윈도우 객체에 ASSERT_VALID를 호출한다면, assertion이 발생한다.
만일 assertion이 발생하지 않았더라도, 다른 스레드에 생성된 윈도우를 조작하는것은 허락
된 것이 아니므로 비정상적으로 동작할 것이다.
이런 경우 MFC 객체를 이용하지 말고 윈도우 핸들로 작업해야 한다.
윈도우 핸들을 스레드간에 전달하는 것은 안전하다. 만일 스레드 A에서 스레드 B로
윈도우 핸들을 전달했다면, 스레드 B는 이 윈도우 핸들로 Send 나 Post Message 를 할 수 있다. 이 메시지가 처리될 때, 스레드 A의 컨텍스트로 바뀌게 되고, Cwnd::AssertVaild() 은 스레드 A의 윈도우 핸들맵을 체크하고 성공을 리턴할 것이다.
이런 시나리오에서 스레드 B는 역시 CWnd::Fromhandle() 함수를 통해서 스레드 B의 임시적인(temporary) 핸들맵에 임시적인 CWnd 객체를 생성할 수 있다. 그러나 이 객체는 스레드 A의 핸들맵에 존재하는 MFC 객체와 동기화 하는 방법이 없으므로 사용상 제약을 받을 것이다.
출처 : 인터넷 검색하면 나오는데 원본 출처는 불분명
MFC DLL 간단 설명
오래전에 쓰인 글이지만 좋은 글.
출처 : 검색하면 여기저기에서 나옴.
관련 MSDN 참고 자료 : http://msdn.microsoft.com/en-us/library/h14y172e%28VS.80%29.aspx#_core_windows_handle_maps
오후 5:05 2002-01-23
조경민 MFC DLL 간단 설명
========================================================================
MFC에서 DLL의 종류는 두가지이다. _USRDLL(MFC정규DLL)과 _AFXDLL(MFC확장DLL)이다.
_USRDLL은 MFC라이브러리의 초기화 종료를 위해 하나의 CWinApp 객체를 필요로한다.
_AFXDLL은 CWinApp 객체를 필요로 하지 않는다. 이는 애플리케이션의 MFC 클래스들을
공유하기 때문이다.
AFXEXT는 AFXDLL이다.
_AFXDLL이 정의가 되면, AfxGetResourceHandle함수로 리턴되는 값은 MFC의 전역 변수로
저장된 값이 리턴되는데 이 값은 EXE나 확장 DLL또는 MFC DLL 모두가 공유하는 값이다.
엄격히 말해, 리소스를 로드한다는 말은 인스턴스 핸들 보다는 모듈 핸들가 연관있다.
(인스턴스는 각 다른 데이타를 갖는 모듈(즉, 코드와 리소스들)들을 공유한다. )
DLL은 EXE의 핸들과 다른 모듈 핸들을 갖는다.
::GetModuleHandle을 DLL의 모듈핸들을 얻기 위해 쓸수 있고, AfxSetResourceHandle을
통해서 DLL이 리소스를 찾을려고 할때 바로 찾을수 있게 처음에 위치하게 할 수 있다.
그러나 알아둘점은 이렇게 하게 되면 .EXE 모듈을 찾을 수 없게 된다. 그래서 보통
AfxSetResourceHandle을 호출하기전에 AfxGetResourceHandle로 핸들값을 복사해 저장한다.
DLL리소스의 로드가 끝나면 다시 EXE모듈 핸들을 세팅하면된다.
- 멀티스레드와 MFC
MSDN : CWnd Derived MFC Objects and Multi-threaded Applications
멀티스레드 애플리케이션을 MFC를이용하여 짠다면, MFC 객체를 스레드간에 넘겨서는
안된다. 이 일반적인 룰에서 한 스레드는 오직 자신이 생성한 MFC객체들만 접근해야한다.
이렇게 하지 않으면 assertion이나 기대치 못한 행동등의 런타임 문제를 발생시킨다.
Win32 프로세스 주소영역 안에서 동작하는 모든 스레드는 모든 전역,static 자료를
접근할 수 있다. 스레드는 TLS(Thread-Local-Storage)로 스레드 자신만의 자료를 쓸수있다.
윈도우는 스레드들에 의해 관리되므로 멀티스레드 환경하에서, MFC는 Thread Local Storage
안에 임시적적이거나 영구적인(temporary and permanent) 윈도우 핸들 맵을 관리한다.
물론 다른 GDI 객체나 디바이스 컨텍스트를 위한 핸들 맵 역시 그렇게 관리한다.
TLS에 관리되는 윈도우 핸들 맵은 여러 스레드들 사이에서 동시에 접근되어선 안된다.
CHandleMap::LookupPermanent()와 CHandleMap::LookupTemporary() 함수는 이런 사실들의
직접적인 예이다. 주어진 윈도우 핸들에 대해 이런 함수들은 MFC CWnd를 상속받은 객체와
연관있는 현재 스레드의 permenent하거나 temporary한 윈도우 핸들맵을 체크한다.
만일 그런 함수로 다른 스레드에 의해 생성된 윈도우 객체에 대한 MFC 객체를 찾으려고 한다면
함수 호출은 실패가 될것이다.
CHandleMap::LookupPermanent()와 CHandleMap::LookupTemporary()를 호출하는 함수들이
몇개 있다. CWnd::AssertValid()( ASSERT_VALID라는 매크로도 역시) 그런 함수들중 하나다.
이 함수는 객체의 유효성을 체크하기 위해 쓰인다. 만일 AssertValid()가 MFC 객체의 m_hWnd
맴버를 핸들맵에서 찾지 못하거나, 잘못된 엔트리에서 찾게되면 이 함수는 assertion을 일으킨다.
VC++ 2.1에서는 이런 assertion은 Wincore.cpp파일의 797과 788번재줄이며
VC++ 2.2에서는 Wincore.cpp파일의 804와 805번째줄,
VC++ 4.0에서는 Wincore.cpp파일의 871과 872번째 줄에 assertion이 생기게 된다
ASSERT_VALID 매크로는 MFC 소스코드 전반에 걸쳐 많이 존재한다. 따라서 만일 특정스레드에서
다른 스레드에 속하는 MFC 윈도우 객체에 ASSERT_VALID를 호출한다면, assertion이 발생한다.
만일 assertion이 발생하지 않았더라도, 다른 스레드에 생성된 윈도우를 조작하는것은 허락
된 것이 아니므로 비정상적으로 동작할 것이다.
이런경우 MFC 객체를 이용하지 말고 윈도우 핸들로 작업을 해야 한다. 윈도우 핸들을 스레드간
에 전달하는것은 안전하다. 만일 스레드 A에서 스레드 B로 윈도우 핸들을 전달했다면, 스레드 B는
이 윈도우 핸들로 send 나 post message를 할 수 있다. 이 메시지가 처리될때, 스레드 A의
컨텍스트로 바뀌게 되고, CWnd::AssertValid()은 스레드 A의 윈도우 핸들맵을 체크하고 성공을
리턴할 것이다.
이런 시나리오에서 스레드 B는 역시 CWnd::FromHandle()함수를 통해서 스레드 B의 임시적인
(temporary)핸들맵에 임시적인 CWnd객체를 생성할 수 있다. 그러나 이 객체는 스레드 A의 핸들
맵에 존재하는 MFC 객체와 동기화하는 방법이 없으므로 사용상 제약을 받을 것이다.
-Accessing MFC Objects from Non-MFC Threads
MSDN : Multithreading: Programming Tips
만일 CWinThread 객체를 이용하지 않고 멀티스레드 애플리케이션을 만들었다면 다른 스레드의
MFC 객체를 접근할 수 없을 것이다. 반대로 말해 MFC객체를 이차 스레드에서 접근하려면
반드시 AfxBeginThread를 사용해 CWinThread의 GUI 스레드 방식을 쓰거나 . 워커 스레드 방식
으로 써야 한다.
이는 멀티스레드 애플리케이션이 클래스 라이브러리를 초기화하고 필요한 내부변수를 핸들링
하기 위한 것이다.
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
int nPriority, UINT nStackSize, DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
pfnThreadProc;
pParam;
nPriority;
nStackSize;
dwCreateFlags;
lpSecurityAttrs;
return NULL;
#else
ASSERT(pfnThreadProc != NULL);
CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
ASSERT_VALID(pThread);
if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
lpSecurityAttrs)) // CreateThread에서
{
pThread->Delete();
return NULL;
}
VERIFY(pThread->SetThreadPriority(nPriority));
if (!(dwCreateFlags & CREATE_SUSPENDED))
VERIFY(pThread->ResumeThread() != (DWORD)-1);
return pThread;
#endif //!_MT)
}
BOOL CWinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
dwCreateFlags;
nStackSize;
lpSecurityAttrs;
return FALSE;
#else
ASSERT(m_hThread == NULL); // already created?
// setup startup structure for thread initialization
_AFX_THREAD_STARTUP startup; memset(&startup, 0, sizeof(startup));
startup.pThreadState = AfxGetThreadState(); // 스레드 상태를 얻어온다.
startup.pThread = this;
startup.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
startup.hEvent2 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
startup.dwCreateFlags = dwCreateFlags;
if (startup.hEvent == NULL || startup.hEvent2 == NULL)
{
TRACE0("Warning: CreateEvent failed in CWinThread::CreateThread.\n");
if (startup.hEvent != NULL)
::CloseHandle(startup.hEvent);
if (startup.hEvent2 != NULL)
::CloseHandle(startup.hEvent2);
return FALSE;
}
// create the thread (it may or may not start to run)
m_hThread = (HANDLE)_beginthreadex(lpSecurityAttrs, nStackSize,
&_AfxThreadEntry, &startup, dwCreateFlags | CREATE_SUSPENDED, (UINT*)&m_nThreadID);
if (m_hThread == NULL)
return FALSE;
// MFC 스레드 상태 구조체 안에는 여러 핸들맵등 중요한 스레드별로 관리되는
// 핸들들이 있다. 따라서 AfxBeginThread를 이용해야만 2차 스레드가 핸들맵을
// 옳바로 전달 받을 수 있게 된다.
// AFX_MODULE_THREAD_STATE (local to thread *and* module)
class AFX_MODULE_THREAD_STATE : public CNoTrackObject
{
public:
AFX_MODULE_THREAD_STATE();
virtual ~AFX_MODULE_THREAD_STATE();
// current CWinThread pointer
CWinThread* m_pCurrentWinThread;
// list of CFrameWnd objects for thread
CTypedSimpleList<CFrameWnd*> m_frameList;
// temporary/permanent map state
DWORD m_nTempMapLock; // if not 0, temp maps locked
CHandleMap* m_pmapHWND;
CHandleMap* m_pmapHMENU;
CHandleMap* m_pmapHDC;
CHandleMap* m_pmapHGDIOBJ;
CHandleMap* m_pmapHIMAGELIST;
// thread-local MFC new handler (separate from C-runtime)
_PNH m_pfnNewHandler;
#ifndef _AFX_NO_SOCKET_SUPPORT
// WinSock specific thread state
HWND m_hSocketWindow;
#ifdef _AFXDLL
CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapSocketHandle;
CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapDeadSockets;
CEmbeddedButActsLikePtr<CPtrList> m_plistSocketNotifications;
#else
CMapPtrToPtr* m_pmapSocketHandle;
CMapPtrToPtr* m_pmapDeadSockets;
CPtrList* m_plistSocketNotifications;
#endif
#endif
};
class _AFX_THREAD_STATE : public CNoTrackObject
{
public:
_AFX_THREAD_STATE();
virtual ~_AFX_THREAD_STATE();
// override for m_pModuleState in _AFX_APP_STATE
AFX_MODULE_STATE* m_pModuleState;
AFX_MODULE_STATE* m_pPrevModuleState;
// memory safety pool for temp maps
void* m_pSafetyPoolBuffer; // current buffer
// thread local exception context
AFX_EXCEPTION_CONTEXT m_exceptionContext;
// CWnd create, gray dialog hook, and other hook data
CWnd* m_pWndInit;
CWnd* m_pAlternateWndInit; // special case commdlg hooking
DWORD m_dwPropStyle;
DWORD m_dwPropExStyle;
HWND m_hWndInit;
BOOL m_bDlgCreate;
HHOOK m_hHookOldCbtFilter;
HHOOK m_hHookOldMsgFilter;
// other CWnd modal data
MSG m_lastSentMsg; // see CWnd::WindowProc
HWND m_hTrackingWindow; // see CWnd::TrackPopupMenu
HMENU m_hTrackingMenu;
TCHAR m_szTempClassName[96]; // see AfxRegisterWndClass
HWND m_hLockoutNotifyWindow; // see CWnd::OnCommand
BOOL m_bInMsgFilter;
// other framework modal data
CView* m_pRoutingView; // see CCmdTarget::GetRoutingView
CFrameWnd* m_pRoutingFrame; // see CCmdTarget::GetRoutingFrame
// MFC/DB thread-local data
BOOL m_bWaitForDataSource;
// common controls thread state
CToolTipCtrl* m_pToolTip;
CWnd* m_pLastHit; // last window to own tooltip
int m_nLastHit; // last hittest code
TOOLINFO m_lastInfo; // last TOOLINFO structure
int m_nLastStatus; // last flyby status message
CControlBar* m_pLastStatus; // last flyby status control bar
// OLE control thread-local data
CWnd* m_pWndPark; // "parking space" window
long m_nCtrlRef; // reference count on parking window
BOOL m_bNeedTerm; // TRUE if OleUninitialize needs to be called
};
EXTERN_THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
_AFX_THREAD_STATE* AFXAPI AfxGetThreadState();
- 애플리케이션 리소스 Localization하기
CWinApp상속받은 나의 App 클래스의 맴버로
HINSTANCE m_hInstDLL;
를 추가한 후
OnInitInstance() 첫줄에
if ((m_hInstDLL = ::LoadLibrary("mylocal.dll")) < HINSTANCE_ERROR)
{
return FALSE; // failed to load the localized resources
}
else
{
AfxSetResourceHandle(m_hInstDLL); // get resources from the DLL
}
하고 OnExitIntance() 부에
int CMyApp::ExitInstance()
{
FreeLibrary(m_hInstDLL);
return CWinApp::ExitInstance();
}
Multithreading: Programming Tips
Multithreaded applications require stricter care than single-threaded applications when accessing data. Because there are multiple, independent paths of execution in use simultaneously in multithreaded applications, either the algorithms, the data, or both must be aware that data could be used by more than one thread at a time. This topic explains techniques for avoiding potential problems when programming multithreaded applications with the Microsoft Foundation Class (MFC) library.
Accessing Objects from Multiple Threads
For size and performance reasons, MFC objects are not thread-safe at the object level, only at the class level. This means that you can have two separate threads manipulating two different CString objects, but not two threads manipulating the same CString object. If you absolutely must have multiple threads manipulating the same object, protect such access with appropriate Win32 synchronization mechanisms, such as critical sections. For more information about critical sections and other related objects, see Synchronization in the Platform SDK.
The class library uses critical sections internally to protect global data structures, such as those used by the debug memory allocation.
Accessing MFC Objects from Non-MFC Threads
If you have a multithreaded application that creates a thread in a way other than using a CWinThread object, you cannot access other MFC objects from that thread. In other words, if you want to access any MFC object from a secondary thread, you must create that thread with one of the methods described in Multithreading: Creating User-Interface Threads or Multithreading: Creating Worker Threads. These methods are the only ones that allow the class library to initialize the internal variables necessary to handle multithreaded applications.
Windows Handle Maps
As a general rule, a thread can access only MFC objects that it created. This is because temporary and permanent Windows handle maps are kept in thread local storage to help maintain protection from simultaneous access from multiple threads. For example, a worker thread cannot perform a calculation and then call a document's UpdateAllViews member function to have the windows that contain views on the new data modified. This has no effect at all, because the map from CWnd objects to HWNDs is local to the primary thread. This means that one thread might have a mapping from a Windows handle to a C++ object, but another thread might map that same handle to a different C++ object. Changes made in one thread would not be reflected in the other.
There are several ways around this problem. The first is to pass individual handles (such as an HWND) rather than C++ objects to the worker thread. The worker thread then adds these objects to its temporary map by calling the appropriate FromHandle member function. You could also add the object to the thread's permanent map by calling Attach, but this should be done only if you are guaranteed that the object will exist longer than the thread.
Another method is to create new user-defined messages corresponding to the different tasks your worker threads will be performing and post these messages to the application's main window using ::PostMessage. This method of communication is similar to two different applications conversing except that both threads are executing in the same address space.
For more information about handle maps, see Technical Note 3. For more information about thread local storage, see Thread Local Storage and Using Thread Local Storage in the Platform SDK.
Communicating Between Threads
MFC provides a number of classes that allow threads to synchronize access to objects to maintain thread safety. Usage of these classes is described in Multithreading: How to Use the Synchronization Classes and Multithreading: When to Use the Synchronization Classes. For more information about these objects, see Synchronization in the Platform SDK.
출처 : http://msdn.microsoft.com/en-us/library/h14y172e%28VS.80%29.aspx#_core_windows_handle_maps
참고 :http://www.webdizen.net/blog/2520
CWnd Derived 클래스 Drag and drop 구현하기 (With OLE)
2. 생성할 때, register(this)만 호출만 해주면 끝.
MFC에서 Undo 와 Redo 구현하기
MSDN의 How Do I: Implement Undo and Redo in MFC 의 내용을 정리한 것입니다. 링크는 맨 아래.
이 강좌에서 중요하다고 여겨지는 부분은 Undo 와 Redo 를 구현하기 위해 데이터를 중복해서 저장하는 것을 방지하는 것이다. 그래서 이 강좌에서는 효율적으로 메모리를 사용하기 위해, 전체 Array 를 복사하는 것이 아니라 꼭 필요한 데이터만 백업해 놓고, 그 데이터를 이용하여 Undo/Redo 를 구현하였다. 동시에 Undo와 Redo 의 제한을 가하여 과다하게 메모리가 사용되는 것을 금지하였다.
강좌에서 사용된 예제는 마우스가 클릭될 때마다 그 지점에 원을 그리는 것이다. 그 코드는 아래와 같다.
Doc 클래스(CDocument를 상속)에 먼저 Array를 하나 선언하고,
//
Attributes public: CArray<CPoint> m_Points; |
Doc 클래스에 AddPoint 함수를 추가한다.
void AddPoint(CPoint point) { m_Points.Add(point); UpdateAllViews(NULL); } |
View 클래스에 마우스 클릭시 점을 추가하는 코드와 그리는 코드를 추가한다.
void CUndoRedoView::OnLButtonDown(UINT
nFlags, CPoint
point) { CUndoRedoDoc*
pDoc = GetDocument(); pDoc->AddPoint(point); CView::OnLButtonDown(nFlags,
point); } void CUndoRedoView::OnDraw(CDC* pDC) { CUndoRedoDoc*
pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add
draw code for native data here for (int i = 0; i < pDoc->m_Points.GetCount();
i++) { CPoint&
pt = pDoc->m_Points[i]; pDC->Ellipse(pt.x - 10, pt.y - 10, pt.x + 10, pt.y + 10); } } |
이제 본격적으로 Undo/Redo를 구현해보자.
Undo/Redo 와 관련된 정보를 저장하는 CAction 클래스를 클래스를 정의한다. 이 클래스의 인스턴스는 CList 에 저장되므로 복사생성자 또한 구현한다.
class CAction { public: enum Actions { actionAdd, actionDelete, actionMove }; CAction() { } CAction(CAction& action) { m_nAction
= action.m_nAction; m_nInfo
= action.m_nInfo; m_Point
= action.m_Point; } int m_nAction; int m_nInfo; CPoint
m_Point; }; |
Doc에 Undo/Redo 를 위한 리스트 멤버 변수를 추가한다.
protected: CList<CAction> m_Undo; CList<CAction> m_Redo; |
이제 AddPoint 를 수정하고, Undo, Redo, CanUndo, CanRedo 함수를 구현한다.
#define MAX_UNDO 50 void AddPoint(CPoint point) { // Record
action for undo CAction
action; action.m_nAction = CAction::actionAdd; action.m_nInfo = (int)m_Points.Add(point); action.m_Point = point; m_Undo.AddHead(action); while
(m_Undo.GetCount()
> MAX_UNDO) m_Undo.RemoveTail(); // Redo
sequence m_Redo.RemoveAll(); UpdateAllViews(NULL); } bool CanUndo() { return
(m_Undo.GetCount()
> 0); } void Undo() { CAction
action = m_Undo.RemoveHead(); switch
(action.m_nAction) { case
CAction::actionAdd: m_Points.RemoveAt(action.m_nInfo); m_Redo.AddHead(action); while (m_Redo.GetCount() > MAX_UNDO) m_Redo.RemoveTail(); break; } UpdateAllViews(NULL); } bool CanRedo() { return
(m_Redo.GetCount()
> 0); } void Redo() { CAction
action = m_Redo.RemoveHead(); switch
(action.m_nAction) { case
CAction::actionAdd: m_Points.Add(action.m_Point); m_Undo.AddHead(action); while (m_Undo.GetCount() > MAX_UNDO) m_Undo.RemoveTail(); break; } UpdateAllViews(NULL); } |
여기서 주의깊게 봐야 할 부분은 AddPoint 함수내에서 m_nInfo 에 저장되는 값이다.
action.m_nInfo = (int)m_Points.Add(point); |
CArray<>::Add 에 의해 반환되는 값은 추가된 엘리먼트의 인덱스값이다. 이 값은 Undo 가 수행될 때 이 인덱스를 이용하여 Array 에서 해당 값을 삭제한다.
m_Points.RemoveAt(action.m_nInfo); |
이처럼 Redo/Undo 시에 Doc 에 존재하는 전체 데이터의 Snapshot 을 가지는 게 아니라, 최소한의 데이터만으로 구현하는 것이 젤 중요한 포인트일 것이다.
참고 : http://msdn.microsoft.com/ko-kr/visualc/cc948995(en-us).aspx
MDI에서 Subclassing 활용
CMDIFrameWnd 의 OnCreate 에서
LRESULT FAR PASCAL SubClassFunc2(HWND hWnd,UINT Message,WPARAM wParam, LPARAM lParam); FARPROC lpfnOldWndProc2 = NULL; .... CMainFrame::OnCreate() { if(lpfnOldWndProc2 == NULL && m_hWndMDIClient != NULL) lpfnOldWndProc2 = (FARPROC)SetWindowLong(m_hWndMDIClient, GWL_WNDPROC, (DWORD) SubClassFunc2); } |
그리고 핸들러는 다음과 같다.
LRESULT FAR PASCAL SubClassFunc2( HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) { if( Message == WM_MDICREATE) ASSERT(FALSE); return CallWindowProc((WNDPROC)lpfnOldWndProc2, hWnd, Message, wParam, lParam); } |
OnDestroy 에서 다시 원본 프로시저를 반환하는 것은 좋은 습관일 것이다.
void CMainFrame::OnDestroy() { SetWindowLong(GetSafeHwnd(), GWL_WNDPROC, (DWORD) lpfnOldWndProc); ... } |
이런 식으로 SubClassing 을 활용하면 여러 메시지를 재정의할 수 있을 것이다. 후킹도 이와 비슷하지만 조금 사용하기가 까칠하잖아...
근데 의아해 할지도... 굳이 WM_MDICREATE 를 재정의할 필요가라고? .. ㅎㅎ 그러게...
그치만, CMainFrame 의 멤버로 있는 m_hWndMDIClient 관련 메시지는 재정의할 수 없을거라 생각했다. 주로 MFC를 다루다 보니, 서브클래싱보다는 메시지 핸들러를 이용하고 HWND 보다는 CWnd 를 주로 사용하기 때문이 아닐까. 이런식으로 하나를 오랫동안 다루다 보면 가끔씩 사고가 갑갑하고 막힐때가 있다.
MFC 프로젝트에 ATL 지원 추가하기
If you have already created an MFC-based application, then you can add support for the Active Template Library (ATL) easily by running the Add ATL Support to MFC Project Wizard.
Note |
---|
This support applies only to simple COM objects added to an MFC executable or DLL project. You can add other COM objects (including ActiveX controls) to MFC projects, but the objects might not operate as expected. |
To add ATL support to your MFC project
In Solution Explorer, right-click the project to which you want to add ATL support.
On the shortcut menu, click Add, and then click Add Class.
Select the Add ATL Support to MFC Project icon.
Note This icon is located in the ATL folder in the Categories pane.
When prompted, click Yes to add ATL support.
For more information about how adding ATL support changes your MFC project's code, see Details of ATL Support Added by the ATL Wizard
출처 : http://msdn.microsoft.com/en-us/library/f8bk32xd(VS.80).aspx
MFC Technical Notes
Visual Studio 영문 MSDN 링크는 아래와 같다.
ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.en/dv_vclib/html/8aa01d66-0b07-4d0a-a080-4bb00c7baaac.htm
만약 로컬 MSDN에서 찾으려면 Technicals notes 를 입력한 후 MFC를 선택한다.
Microsoft MSND 웹 링크는
http://msdn.microsoft.com/en-us/library/h6h0eact(VS.80).aspx
숫자와 카테고리별로 분류가 잘되어 있다.
OLE와 관련된 내용이 많다. 아래는 그 항목들.