'분류 전체보기'에 해당되는 글 276건
- 2008.07.08 Unix link
- 2008.07.02 link
- 2008.07.02 NSIS Installer 2
- 2008.07.02 add custom interface with MFC
- 2007.12.23 women of agressive inline
- 2007.12.16 RegSvrHelp 6
ubuntu general - http://linuxholic.tistory.com/156
using psftp - http://the.earth.li/~sgtatham/putty/0.55/htmldoc/Chapter6.html#S6.2.13
Mechanism of OutputDebugString - http://www.codeproject.com/KB/winsdk/OutputDebugString.aspx
1. 개요
NSIS(Nullsoft Scriptable Install System)는 스크립트로 작성하는 Windows용 installer이다.
2. 특정
상업적인 용도로 사용 가능하다.
overhead 가 작다( 설치 파일의 크기가 작다)
3. 사용기
프로젝트 때문에 사용했었다.
아주 간단한 기능만을 사용했기 때문에 평가를 내리기는 어렵지만, 간단한 배포인 경우에는 sample을 이용하면 빠르게 제작할 수 있고 installer 프로그램 자체도 가볍고 nsi 파일 하나만 관리하면 되니깐 상업용 installer 보다 낳을 수 있다는 판단이 들었다.
전용의 editor가 몇가지 존재하는 것 같았지만 사용해 보지는 않아서 평가를 내리기는 어렵다.
4. 관련 사이트
http://nsis.sourceforge.net
5. sample 분석(2개의 com object를 배포하는 예제이다. NSIS site에는 많은 종류의 예제가 존재한다.)
; 은 주석이다.
; ----------------------------------------------------------------------------------------------
; setup.nsi
;
; This script is based on example1.nsi, but it remember the directory,
; has uninstall support and (optionally) installs start menu shortcuts.
;
; It will install example2.nsi into a directory that the user selects,
;--------------------------------
; 이름을 지정한다.
; The name of the installer
Name "Setup"
; 생성되는 설치 파일 이름을 지정한다.
; The file to write
OutFile "SetupU.exe"
; 설치될 디렉토리를 지정한다.
; The default installation directory
InstallDir $PROGRAMFILES\RegSvrHelpU
; 레지스트리를 만든다. ( 설치 시 아래 레지스트리의 key에 현재 설치된 위치를 넣었놓고, 이후 재 설치가 이루어지면 기본 설치 디렉토리를 레지스트리에 저장된 경로로 설정한다.)
; Registry key to check for directory (so if you install again, it will
; overwrite the old one automatically)
InstallDirRegKey HKLM "Software\RegSvrHelpU" "Install_Dir"
; Vista인 경우 admin 권한으로 실행되게 한다.( component 등록 작업과 admin 권한이 필요한 파일과 레지스터리 access 때문)
; Request application privileges for Windows Vista
RequestExecutionLevel admin
; install, uninstall 시 상세 정보를 보이게 한다.( 설치 되는 목록들이 UI 상에 나타난다)
; Set ShowInstDetails
ShowInstDetails show
; Set ShowUninstDetails
ShowUninstDetails show
;--------------------------------
; 페이지를 설정한다.
; component는 설치 시 설치 항목이 여러개 있고 사용자가 설치 할 항목등을 선택/비 선택 할 수 있는 페이지를 의미한다.
; directory는 설치 시 사용자가 설치될 디렉토리를 변경할 수 있는 페이지를 의미한다.
; instfiles는 설치 시 progress bar가 진행되면서 위의 ShowInstDetails show가 주어진 경우 설치되는 작업이 각각 텍스트로 표시된다.
; Pages
;Page components
Page directory
Page instfiles
; uninstConfirm uninstall시 사용자에게 uninstall에 대한 confirm을 받는 페이지이다.
; insttfiles 는 설치시와 동일하다.
UninstPage uninstConfirm
UninstPage instfiles
;--------------------------------
; Section은 여러개의 instruction으로 이루어 졌으면 원 예제는 2개의 Section으로 구성되어 있고 component 페이지에서 설치 할 항목을 선택할 수 있었다.
; The stuff to install
Section "RegSvrHelpU"
SectionIn RO
; INSTDIR은 실제로 설치되는 디렉토리 위치이다. 사용자가 변경을 하였다면 변경 된 최종 디렉토리 path가 저정되어 있다.
; Set output path to the installation directory.
SetOutPath $INSTDIR
; 설치될 파일 목록이며 installer를 만드는 PC에서의 상대경로가 지원된다.
; Put file there
File "..\RegCom\RegCom\Release_U\RegComU.exe"
File "..\RegSvrHelp\Release_U\RegSvrHelpU.dll"
; 파일이 설치되고 등록 작업을 수행한다.
; Register component file
Exec '"$INSTDIR\RegComU.exe" /regserver'
RegDLL $INSTDIR\RegSvrHelpU.dll
; 재 설치 시를 위해 현재 설치된 위치를 위에서 만든 레지스터리에 저장한다.
; Write the installation path into the registry
WriteRegStr HKLM SOFTWARE\RegSvrHelpU "Install_Dir" "$INSTDIR"
; 프로그램 추가 제거 에서 UnInstall을 할 수 있도록 setting한다.
; Write the uninstall keys for Windows
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RegSvrHelpU" "DisplayName" "RegSvrHelpU"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RegSvrHelpU" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RegSvrHelpU" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RegSvrHelpU" "NoRepair" 1
WriteUninstaller "uninstall.exe"
SectionEnd
; Optional section (can be disabled by the user)
;Section "Start Menu Shortcuts"
; CreateDirectory "$SMPROGRAMS\Example2"
; CreateShortCut "$SMPROGRAMS\Example2\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
; CreateShortCut "$SMPROGRAMS\Example2\Example2 (MakeNSISW).lnk" "$INSTDIR\example2.nsi" "" "$INSTDIR\example2.nsi" 0
;SectionEnd
;--------------------------------
; Uninstaller
Section "Uninstall"m
; UnInstall 관련 부분 및 재 설치 관련 레지스터리를 지운다.
; Remove registry keys
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\RegSvrHelpU"
DeleteRegKey HKLM SOFTWARE\RegSvrHelpU
; UnRegister component file
; 등록 해제 한다.
UnRegDLL $INSTDIR\RegSvrHelpU.dll
Exec '"$INSTDIR\RegComU.exe" /unregserver'
; 파일을 지운다.
; Remove files and uninstaller
Delete $INSTDIR\RegSvrHelpU.dll
Sleep 1000 ; Wait Unload RegComU process
Delete $INSTDIR\RegComU.exe
Delete $INSTDIR\uninstall.exe
; Remove shortcuts, if any
;Delete "$SMPROGRAMS\Example2\*.*"
; Remove directories used
;RMDir "$SMPROGRAMS\Example2"
; 설치 폴더를 지운다.
RMDir "$INSTDIR"
SectionEnd
![]() |
---|
The following technical note has not been updated since it was first included in the online documentation. As a result, some procedures and topics might be out of date or incorrect. For the latest information, it is recommended that you search for the topic of interest in the online documentation index. |
At the heart of OLE 2 is the "OLE Component Object Model", or COM. COM defines a standard for how cooperating objects communicate to one another. This includes the details of what an "object" looks like, including how methods are dispatched on an object. COM also defines a base class, from which all COM compatible classes are derived. This base class is IUnknown. Although the IUnknown interface is referred to as a C++ class, COM is not specific to any one language — it can be implemented in C, PASCAL, or any other language that can support the binary layout of a COM object.
OLE refers to all classes derived from IUnknown as "interfaces." This is an important distinction, since an "interface" such as IUnknown carries with it no implementation. It simply defines the protocol by which objects communicate, not the specifics of what those implementations do. This is reasonable for a system that allows for maximum flexibility. It is MFC's job to implement a default behavior for MFC/C++ programs.
To understand MFC's implementation of IUnknown you must first understand what this interface is. A simplified version of IUnknown is defined below:
![]() | |
---|---|
class IUnknown { public: virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0; virtual ULONG AddRef() = 0; virtual ULONG Release() = 0; }; |
![]() |
---|
Certain necessary calling convention details, such as __stdcall are left out for this illustration. |
The AddRef and Release member functions control memory management of the object. COM uses a reference counting scheme to keep track of objects. An object is never referenced directly as you would in C++. Instead, COM objects are always referenced through a pointer. To release the object when the owner is done using it, the object's Release member is called (as opposed to using operator delete, as would be done for a traditional C++ object). The reference counting mechanism allows for multiple references to a single object to be managed. An implementation of AddRef and Release maintains a reference count on the object — the object is not deleted until its reference count reaches zero.
AddRef and Release are fairly straightforward from an implementation standpoint. Here is a trivial implementation:
![]() | |
---|---|
ULONG CMyObj::AddRef() { return ++m_dwRef; } ULONG CMyObj::Release() { if (--m_dwRef == 0) { delete this; return 0; } return m_dwRef; } |
The QueryInterface member function is a little more interesting. It is not very interesting to have an object whose only member functions are AddRef and Release — it would be nice to tell the object to do more things than IUnknown provides. This is where QueryInterface is useful. It allows you to obtain a different "interface" on the same object. These interfaces are usually derived from IUnknown and add additional functionality by adding new member functions. COM interfaces never have member variables declared in the interface, and all member functions are declared as pure-virtual. For example,
![]() | |
---|---|
class IPrintInterface : public IUnknown { public: virtual void PrintObject() = 0; }; |
To get an IPrintInterface if you only have an IUnknown, call IUnknown::QueryInterface using the IID of the IPrintInterface. An IID is a 128-bit number that uniquely identifies the interface. There is an IID for each interface that either you or OLE define. If pUnk is a pointer to an IUnknown object, the code to retrieve an IPrintInterface from it might be:
![]() | |
---|---|
IPrintInterface* pPrint = NULL; if (pUnk->QueryInterface(IID_IPrintInterface, (void**)&pPrint) == NOERROR) { pPrint->PrintObject(); pPrint->Release(); // release pointer obtained via QueryInterface } |
That seems fairly easy, but how would you implement an object supporting both the IPrintInterface and IUnknown interface? In this case it is simple since the IPrintInterface is derived directly from IUnknown — by implementing IPrintInterface, IUnknown is automatically supported. For example:
![]() | |
---|---|
class CPrintObj : public CPrintInterface { virtual HRESULT QueryInterface(REFIID iid, void** ppvObj); virtual ULONG AddRef(); virtual ULONG Release(); virtual void PrintObject(); }; |
The implementations of AddRef and Release would be exactly the same as those implemented above. CPrintObj::QueryInterface would look something like this:
![]() | |
---|---|
HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj) { if (iid == IID_IUnknown || iid == IID_IPrintInterface) { *ppvObj = this; AddRef(); return NOERROR; } return E_NOINTERFACE; } |
As you can see, if the interface identifier (IID) is recognized, a pointer is returned to your object; otherwise an error occurs. Also note that a successful QueryInterface results in an implied AddRef. Of course, you'd also have to implement CEditObj::Print. That is simple because the IPrintInterface was directly derived from the IUnknown interface. However if you wanted to support two different interfaces, both derived from IUnknown, consider the following:
![]() | |
---|---|
class IEditInterface : public IUnkown { public: virtual void EditObject() = 0; }; |
Although there are a number of different ways to implement a class supporting both IEditInterface and IPrintInterface, including using C++ multiple inheritance, this note will concentrate on the use of nested classes to implement this functionality.
![]() | |
---|---|
class CEditPrintObj { public: CEditPrintObj(); HRESULT QueryInterface(REFIID iid, void**); ULONG AddRef(); ULONG Release(); DWORD m_dwRef; class CPrintObj : public IPrintInterface { public: CEditPrintObj* m_pParent; virtual HRESULT QueryInterface(REFIID iid, void** ppvObj); virtual ULONG AddRef(); virtual ULONG Release(); } m_printObj; class CEditObj : public IEditInterface { public: CEditPrintObj* m_pParent; virtual ULONG QueryInterface(REFIID iid, void** ppvObj); virtual ULONG AddRef(); virtual ULONG Release(); } m_editObj; }; |
The entire implementation is included below:
![]() | |
---|---|
CEditPrintObj::CEditPrintObj() { m_editObj.m_pParent = this; m_printObj.m_pParent = this; } ULONG CEditPrintObj::AddRef() { return ++m_dwRef; } CEditPrintObj::Release() { if (--m_dwRef == 0) { delete this; return 0; } return m_dwRef; } HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj) { if (iid == IID_IUnknown || iid == IID_IPrintInterface) { *ppvObj = &m_printObj; AddRef(); return NOERROR; } else if (iid == IID_IEditInterface) { *ppvObj = &m_editObj; AddRef(); return NOERROR; } return E_NOINTERFACE; } ULONG CEditPrintObj::CEditObj::AddRef() { return m_pParent->AddRef(); } ULONG CEditPrintObj::CEditObj::Release() { return m_pParent->Release(); } HRESULT CEditPrintObj::CEditObj::QueryInterface( REFIID iid, void** ppvObj) { return m_pParent->QueryInterface(iid, ppvObj); } ULONG CEditPrintObj::CPrintObj::AddRef() { return m_pParent->AddRef(); } ULONG CEditPrintObj::CPrintObj::Release() { return m_pParent->Release(); } HRESULT CEditPrintObj::CPrintObj::QueryInterface( REFIID iid, void** ppvObj) { return m_pParent->QueryInterface(iid, ppvObj); } |
Notice that most of the IUnknown implementation is placed into the CEditPrintObj class rather than duplicating the code in CEditPrintObj::CEditObj and CEditPrintObj::CPrintObj. This reduces the amount of code and avoids bugs. The key point here is that from the IUnknown interface it is possible to call QueryInterface to retrieve any interface the object might support, and from each of those interfaces it is possible to do the same. This means that all QueryInterface functions available from each interface must behave exactly the same way. In order for these embedded objects to call the implementation in the "outer object", a back-pointer is used (m_pParent). The m_pParent pointer is initialized during the CEditPrintObj constructor. Then you would implement CEditPrintObj::CPrintObj::PrintObject and CEditPrintObj::CEditObj::EditObject as well. Quite a bit of code was added to add one feature — the ability to edit the object. Fortunately, it is quite uncommon for interfaces to have only a single member function (although it does happen) and in this case, EditObject and PrintObject would usually be combined into a single interface.
That's a lot of explanation and a lot of code for such a simple scenario. The MFC/OLE classes provide a simpler alternative. The MFC implementation uses a technique similar to the way Windows messages are wrapped with Message Maps. This facility is called Interface Maps and is discussed in the next section.
MFC Interface Maps
MFC/OLE includes an implementation of "Interface Maps" similar to MFC's "Message Maps" and "Dispatch Maps" in concept and execution. The core features of MFC's Interface Maps are as follows:
A standard implementation of IUnknown, built into the CCmdTarget class.
Maintenance of the reference count, modified by AddRef and Release
Data driven implementation of QueryInterface
In addition, interface maps support the following advanced features:
Support for creating aggregatable COM objects
Support for using aggregate objects in the implementation of a COM object
The implementation is hookable and extensible
For more information on aggregation, see the OLE Programmer's Reference.
MFC's interface map support is rooted in the CCmdTarget class. CCmdTarget "has-a" reference count as well as all the member functions associated with the IUnknown implementation (the reference count for example is in CCmdTarget). To create a class that supports OLE COM, you derive a class from CCmdTarget and use various macros as well as member functions of CCmdTarget to implement the desired interfaces. MFC's implementation uses nested classes to define each interface implementation much like the example above. This is made easier with a standard implementation of IUnknown as well as a number of macros that eliminate some of the repetitive code.
To implement a class using MFC's interface maps
Derive a class either directly or indirectly from CCmdTarget.
Use the DECLARE_INTERFACE_MAP function in the derived class definition.
For each interface you wish to support, use the BEGIN_INTERFACE_PART and END_INTERFACE_PART macros in the class definition.
In the implementation file, use the BEGIN_INTERFACE_MAP and END_INTERFACE_MAP macros to define the class's interface map.
For each IID supported, use the INTERFACE_PART macro between the BEGIN_INTERFACE_MAP and END_INTERFACE_MAP macros to map that IID to a specific "part" of your class.
Implement each of the nested classes that represent the interfaces you support.
Use the METHOD_PROLOGUE macro to access the parent, CCmdTarget-derived object.
AddRef, Release, and QueryInterface can delegate to the CCmdTarget implementation of these functions (ExternalAddRef, ExternalRelease, and ExternalQueryInterface).
The CPrintEditObj example above could be implemented as follows:
![]() | |
---|---|
class CPrintEditObj : public CCmdTarget { public: // member data and member functions for CPrintEditObj go here // Interface Maps protected: DECLARE_INTERFACE_MAP() BEGIN_INTERFACE_PART(EditObj, IEditInterface) STDMETHOD_(void, EditObject)(); END_INTERFACE_PART(EditObj) BEGIN_INTERFACE_PART(PrintObj, IPrintInterface) STDMETHOD_(void, PrintObject)(); END_INTERFACE_PART(PrintObj) }; |
The above declaration creates a class derived from CCmdTarget. The DECLARE_INTERFACE_MAP macro tells the framework that this class will have a custom interface map. In addition, the BEGIN_INTERFACE_PART and END_INTERFACE_PART macros define nested classes, in this case with names CEditObj and CPrintObj (the X is used only to differentiate the nested classes from global classes which start with "C" and interface classes which start with "I"). Two nested members of these classes are created: m_CEditObj, and m_CPrintObj, respectively. The macros automatically declare the AddRef, Release, and QueryInterface functions; therefore you only declare the functions specific to this interface: EditObject and PrintObject (the OLE macro STDMETHOD is used such so that _stdcall and virtual keywords are provided as appropriate for the target platform).
To implement the interface map for this class:
![]() | |
---|---|
BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget) INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj) INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj) END_INTERFACE_MAP() |
This connects the IID_IPrintInterface IID with m_CPrintObj and IID_IEditInterface with m_CEditObj respectively. The CCmdTarget implementation of QueryInterface (CCmdTarget::ExternalQueryInterface) uses this map to return pointers to m_CPrintObj and m_CEditObj when requested. It is not necessary to include an entry for IID_IUnknown; the framework will use the first interface in the map (in this case, m_CPrintObj) when IID_IUnknown is requested.
Even though the BEGIN_INTERFACE_PART macro automatically declared the AddRef, Release and QueryInterface functions for you, you still need to implement them:
![]() | |
---|---|
ULONG FAR EXPORT CEditPrintObj::XEditObj::AddRef() { METHOD_PROLOGUE(CEditPrintObj, EditObj) return pThis->ExternalAddRef(); } ULONG FAR EXPORT CEditPrintObj::XEditObj::Release() { METHOD_PROLOGUE(CEditPrintObj, EditObj) return pThis->ExternalRelease(); } HRESULT FAR EXPORT CEditPrintObj::XEditObj::QueryInterface( REFIID iid, void FAR* FAR* ppvObj) { METHOD_PROLOGUE(CEditPrintObj, EditObj) return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj); } void FAR EXPORT CEditPrintObj::XEditObj::EditObject() { METHOD_PROLOGUE(CEditPrintObj, EditObj) // code to "Edit" the object, whatever that means... } |
The implementation for CEditPrintObj::CPrintObj, would be similar to the above definitions for CEditPrintObj::CEditObj. Although it would be possible to create a macro that could be used to automatically generate these functions (but earlier in MFC/OLE development this was the case), it becomes difficult to set break points when a macro generates more than one line of code. For this reason, this code is expanded manually.
By using the framework implementation of message maps, there are a number of things that were not necessary to do:
Implement QueryInterface
Implement AddRef and Release
Declare either of these built-in methods on both of your interfaces
In addition, the framework uses message maps internally. This allows you to derive from a framework class, say COleServerDoc, that already supports certain interfaces and provides either replacements or additions to the interfaces provided by the framework. This is enabled by the fact that the framework fully supports inheriting an interface map from a base class — that is the reason why BEGIN_INTERFACE_MAP takes as its second parameter the name of the base class.
![]() |
---|
It is generally not possible to reuse the implementation of MFC's built-in implementations of the OLE interfaces by inheriting the embedded specialization of that interface from the MFC version. This is not possible because the use of the METHOD_PROLOGUE macro to get access to the containing CCmdTarget-derived object implies a fixed offset of the embedded object from the CCmdTarget-derived object. This means, for example, you cannot derive an embedded XMyAdviseSink from MFC's implementation in COleClientItem::XAdviseSink, because XAdviseSink relies on being at a specific offset from the top of the COleClientItem object. |
![]() |
---|
You can, however, delegate to the MFC implementation for all of the functions that you want MFC's default behavior. This is done in the MFC implementation of IOleInPlaceFrame (XOleInPlaceFrame) in the COleFrameHook class (it delegates to m_xOleInPlaceUIWindow for many functions). This design was chosen to reduce the runtime size of objects which implement many interfaces; it eliminates the need for a back-pointer (such as the way m_pParent was used in the previous section). |
Aggregation and Interface Maps
In addition to supporting stand-alone COM objects, MFC also supports aggregation. Aggregation itself is too complex a topic to discuss here; refer to the OLE Programmer's Reference for more information on aggregation. This note will simply describe the support for aggregation built into the framework and interface maps.
There are two ways to use aggregation: (1) using a COM object that supports aggregation, and (2) implementing an object that can be aggregated by another. These capabilities can be referred to as "using an aggregate object" and "making an object aggregatable". MFC supports both.
Using an Aggregate Object
To use an aggregate object, there needs to be some way to tie the aggregate into the QueryInterface mechanism. In other words, the aggregate object must behave as though it is a native part of your object. So how does this tie into MFC's interface map mechanism? In addition to the INTERFACE_PART macro, where a nested object is mapped to an IID, you can also declare an aggregate object as part of your CCmdTarget derived class. To do so, the INTERFACE_AGGREGATE macro is used. This allows you to specify a member variable (which must be a pointer to an IUnknown or derived class), which is to be integrated into the interface map mechanism. If the pointer is not NULL when CCmdTarget::ExternalQueryInterface is called, the framework will automatically call the aggregate object's QueryInterface member function, if the IID requested is not one of the native IIDs supported by the CCmdTarget object itself.
To use the INTERFACE_AGGREGATE macro
Declare a member variable (an IUnknown*) which will contain a pointer to the aggregate object.
Include an INTERFACE_AGGREGATE macro in your interface map, which refers to the member variable by name.
At some point (usually during CCmdTarget::OnCreateAggregates), initialize the member variable to something other than NULL.
For example:
![]() | |
---|---|
class CAggrExample : public CCmdTarget { public: CAggrExample(); protected: LPUNKNOWN m_lpAggrInner; virtual BOOL OnCreateAggregates(); DECLARE_INTERFACE_MAP() // "native" interface part macros may be used here }; CAggrExample::CAggrExample() { m_lpAggrInner = NULL; } BOOL CAggrExample::OnCreateAggregates() { // wire up aggregate with correct controlling unknown m_lpAggrInner = CoCreateInstance(CLSID_Example, GetControllingUnknown(), CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID*)&m_lpAggrInner); if (m_lpAggrInner == NULL) return FALSE; // optionally, create other aggregate objects here return TRUE; } BEGIN_INTERFACE_MAP(CAggrExample, CCmdTarget) // native "INTERFACE_PART" entries go here INTERFACE_AGGREGATE(CAggrExample, m_lpAggrInner) END_INTERFACE_MAP() |
The m_lpAggrInner is initialized in the constructor to NULL. The framework will ignore a NULL member variable in the default implementation of QueryInterface. OnCreateAggregates is a good place to actually create your aggregate objects. You'll have to call it explicitly if you are creating the object outside of the MFC implementation of COleObjectFactory. The reason for creating aggregates in CCmdTarget::OnCreateAggregates as well as the usage of CCmdTarget::GetControllingUnknown will become apparent when creating aggregatable objects is discussed.
This technique will give your object all of the interfaces that the aggregate object supports plus its native interfaces. If you only want a subset of the interfaces that the aggregate supports, you can override CCmdTarget::GetInterfaceHook. This allows you very low-level hookability, similar to QueryInterface. Usually, you want all the interfaces that the aggregate supports.
Making an Object Implementation Aggregatable
For an object to be aggregatable, the implementation of AddRef, Release, and QueryInterface must delegate to a "controlling unknown." In other words, for it to be part of the object, it must delegate AddRef, Release, and QueryInterface to a different object, also derived from IUnknown. This "controlling unknown" is provided to the object when it is created, that is, it is provided to the implementation of COleObjectFactory. Implementing this carries a small amount of overhead, and in some cases is not desirable, so MFC makes this optional. To enable an object to be aggregatable, you call CCmdTarget::EnableAggregation from the object's constructor.
If the object also uses aggregates, you must also be sure to pass the correct "controlling unknown" to the aggregate objects. Usually this IUnknown pointer is passed to the object when the aggregate is created. For example, the pUnkOuter parameter is the "controlling unknown" for objects created with CoCreateInstance. The correct "controlling unknown" pointer can be retrieved by calling CCmdTarget::GetControllingUnknown. The value returned from that function, however, is not valid during the constructor. For this reason, it is suggested that you create your aggregates only in an override of CCmdTarget::OnCreateAggregates, where the return value from GetControllingUnknown is reliable, even if created from the COleObjectFactory implementation.
It is also important that the object manipulate the correct reference count when adding or releasing artificial reference counts. To ensure this is the case, always call ExternalAddRef and ExternalRelease instead of InternalRelease and InternalAddRef. It is rare to call InternalRelease or InternalAddRef on a class that supports aggregation.
Reference Material
Advanced usage of OLE, such as defining your own interfaces or overriding the framework's implementation of the OLE interfaces requires the use of the underlying interface map mechanism.
This section discusses each macro and the APIs which is used to implement these advanced features.
CCmdTarget::EnableAggregation — Function Description
![]() | |
---|---|
|
Remarks
Call this function in the constructor of the derived class if you wish to support OLE aggregation for objects of this type. This prepares a special IUnknown implementation that is required for aggregatable objects.
CCmdTarget::ExternalQueryInterface — Function Description
![]() | |
---|---|
|
Remarks
Parameters
Remarks
Call this function in your implementation of IUnknown for each interface your class implements. This function provides the standard data-driven implementation of QueryInterface based on your object's interface map. It is necessary to cast the return value to an HRESULT. If the object is aggregated, this function will call the "controlling IUnknown" instead of using the local interface map.
CCmdTarget::ExternalAddRef — Function Description
![]() | |
---|---|
|
Remarks
Call this function in your implementation of IUnknown::AddRef for each interface your class implements. The return value is the new reference count on the CCmdTarget object. If the object is aggregated, this function will call the "controlling IUnknown" instead of manipulating the local reference count.
CCmdTarget::ExternalRelease — Function Description
![]() | |
---|---|
|
Remarks
Call this function in your implementation of IUnknown::Release for each interface your class implements. The return value indicates the new reference count on the object. If the object is aggregated, this function will call the "controlling IUnknown" instead of manipulating the local reference count.
DECLARE_INTERFACE_MAP — Macro Description
![]() | |
---|---|
|
Remarks
Use this macro in any class derived from CCmdTarget that will have an interface map. Used in much the same way as DECLARE_MESSAGE_MAP. This macro invocation should be placed in the class definition, usually in a header (.H) file. A class with DECLARE_INTERFACE_MAP must define the interface map in the implementation file (.CPP) with the BEGIN_INTERFACE_MAP and END_INTERFACE_MAP macros.
BEGIN_INTERFACE_PART and END_INTERFACE_PART — Macro Descriptions
![]() | |
---|---|
|
Remarks
Parameters
Remarks
For each interface that your class will implement, you need to have a BEGIN_INTERFACE_PART and END_INTERFACE_PART pair. These macros define a local class derived from the OLE interface that you define as well as an embedded member variable of that class. The AddRef, Release, and QueryInterface members are declared automatically. You must include the declarations for the other member functions that are part of the interface being implemented (those declarations are placed between the BEGIN_INTERFACE_PART and END_INTERFACE_PART macros).
The iface argument is the OLE interface that you wish to implement, such as IAdviseSink, or IPersistStorage (or your own custom interface).
The localClass argument is the name of the local class that will be defined. An 'X' will automatically be prepended to the name. This naming convention is used to avoid collisions with global classes of the same name. In addition, the name of the embedded member, the same as the localClass name except it is prefixed by 'm_x'.
For example:
![]() | |
---|---|
BEGIN_INTERFACE_PART(MyAdviseSink, IAdviseSink) STDMETHOD_(void,OnDataChange)(LPFORMATETC, LPSTGMEDIUM); STDMETHOD_(void,OnViewChange)(DWORD, LONG); STDMETHOD_(void,OnRename)(LPMONIKER); STDMETHOD_(void,OnSave)(); STDMETHOD_(void,OnClose)(); END_INTERFACE_PART(MyAdviseSink) |
would define a local class called XMyAdviseSink derived from IAdviseSink, and a member of the class in which it is declared called m_xMyAdviseSink.Note:
![]() |
---|
The lines beginning with STDMETHOD_ are essentially copied from OLE2.H and modified slightly. Copying them from OLE2.H can reduce errors that are hard to resolve. |
BEGIN_INTERFACE_MAP and END_INTERFACE_MAP — Macro Descriptions
![]() | |
---|---|
|
Remarks
Parameters
Remarks
The BEGIN_INTERFACE_MAP and END_INTERFACE_MAP macros are used in the implementation file to actually define the interface map. For each interface that is implemented there is one or more INTERFACE_PART macro invocations. For each aggregate that the class uses, there is one INTERFACE_AGGREGATE macro invocation.
INTERFACE_PART — Macro Description
![]() | |
---|---|
|
Remarks
Parameters
Remarks
This macro is used between the BEGIN_INTERFACE_MAP macro and the END_INTERFACE_MAP macro for each interface your object will support. It allows you to map an IID to a member of the class indicated by theClass and localClass. The 'm_x' will be added to the localClass automatically. Note that more than one IID may be associated with a single member. This is very useful when you are implementing only a "most derived" interface and wish to provide all intermediate interfaces as well. A good example of this is the IOleInPlaceFrameWindow interface. Its hierarchy looks like this:
![]() | |
---|---|
IUnknown IOleWindow IOleUIWindow IOleInPlaceFrameWindow |
If an object implements IOleInPlaceFrameWindow, a client may QueryInterface on any of these interfaces: IOleUIWindow, IOleWindow, or IUnknown, besides the "most derived" interface IOleInPlaceFrameWindow (the one you are actually implementing). To handle this you can use more than one INTERFACE_PART macro to map each and every base interface to the IOleInPlaceFrameWindow interface:
in the class definition file:
![]() | |
---|---|
BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow) |
in the class implementation file:
![]() | |
---|---|
BEGIN_INTERFACE_MAP(CMyWnd, CFrameWnd) INTERFACE_PART(CMyWnd, IID_IOleWindow, MyFrameWindow) INTERFACE_PART(CMyWnd, IID_IOleUIWindow, MyFrameWindow) INTERFACE_PART(CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow) END_INTERFACE_MAP |
The framework takes care of IUnknown because it is always required.
INTERFACE_PART — Macro Description
![]() | |
---|---|
|
Remarks
Parameters
Remarks
This macro is used to tell the framework that the class is using an aggregate object. It must appear between the BEGIN_INTERFACE_PART and END_INTERFACE_PART macros. An aggregate object is a separate object, derived from IUnknown. By using an aggregate and the INTERFACE_AGGREGATE macro, you can make all the interfaces that the aggregate supports appear to be directly supported by the object. The theAggr argument is simply the name of a member variable of your class which is derived from IUnknown (either directly or indirectly). All INTERFACE_AGGREGATE macros must follow the INTERFACE_PART macros when placed in an interface map.
See Also

어그레스브 인라인 사이트에서 발견한 사진의 주인공을 그려보았는데...
어안 렌즈(?)로 찍은 듯한 사진인데 배경을 보아하니 보라매 공원의 x-park가 아닌가 한다.
2005년 2월 정도에 그건 것인데..
색칠을 그나마 한다고 시도한 첫 번째 시도가 아닌가 하네.

Unicode

Ascii

Introduction
RegSvrHelp는 ContextMenu( Windows Shell에서 오른쪽 버튼을 누를 때 )에서 Windows COM 객체를 등록하는 기능을 한다.
- dll 형태(ocx 포함): dll이 일반 dll인지 com인지 판단을 하여 context
menu에 나타난다.
- exe 형태: exe는 현재 com인지 일반 exe인지 판단하는 기능이 없으므로 모든 exe에 대해서 context menu가 나타나며 메뉴를 선택 시 (실행파일 에 –RegServer/-UnRegServer 의 argument를 붙여서 실행하게 된다.
특히 Vista OS에서 UAC가 설정 된 경우 command 창의 RegSvr32 파일을 이용해서 COM 객체를 등록할 때는 매우 불편한데 RegSvrHelp를 이용할 경우 좀 더 편하게 작업이 이루어질 수 있다.
OS
Package는 2가지가 제공된다.
- setup.exe: Ascii version으로 Win98 – Vista까지 지원된다.
- setupU.exe: Unicode version으로 Win2000 - Vista까지 지원된다.
Install
설치는 아래 zip 파일 중 하나를 택해서 압축을 풀고 .msi 를 실행 시켜 install를 하면 설치는 완료된다.
ps : 버그 사항은 댓글로 달아 주세요..