[ADO 2.8] HelloData Code
'BeginHelloData Option Explicit Dim m_oRecordset As ADODB.Recordset Dim m_sConnStr As String Dim m_flgPriceUpdated As Boolean Private Sub cmdGetData_Click() GetData If Not m_oRecordset Is Nothing Then If m_oRecordset.State = adStateOpen Then ' Set the proper states for the buttons. cmdGetData.Enabled = False cmdExamineData.Enabled = True End If End If End Sub Private Sub cmdExamineData_Click() ExamineData End Sub Private Sub cmdEditData_Click() EditData End Sub Private Sub cmdUpdateData_Click() UpdateData ' Set the proper states for the buttons. cmdUpdateData.Enabled = False End Sub Private Sub GetData() On Error GoTo GetDataError Dim sSQL As String Dim oConnection1 As ADODB.Connection m_sConnStr = "Provider='SQLOLEDB';Data Source='MySqlServer';" & _ "Initial Catalog='Northwind';Integrated Security='SSPI';" ' Create and Open the Connection object. Set oConnection1 = New ADODB.Connection oConnection1.CursorLocation = adUseClient oConnection1.Open m_sConnStr sSQL = "SELECT ProductID, ProductName, CategoryID, UnitPrice " & _ "FROM Products" ' Create and Open the Recordset object. Set m_oRecordset = New ADODB.Recordset m_oRecordset.Open sSQL, oConnection1, adOpenStatic, _ adLockBatchOptimistic, adCmdText m_oRecordset.MarshalOptions = adMarshalModifiedOnly ' Disconnect the Recordset. Set m_oRecordset.ActiveConnection = Nothing oConnection1.Close Set oConnection1 = Nothing ' Bind Recordset to the DataGrid for display. Set grdDisplay1.DataSource = m_oRecordset Exit Sub GetDataError: If Err <> 0 Then If oConnection1 Is Nothing Then HandleErrs "GetData", m_oRecordset.ActiveConnection Else HandleErrs "GetData", oConnection1 End If End If If Not oConnection1 Is Nothing Then If oConnection1.State = adStateOpen Then oConnection1.Close Set oConnection1 = Nothing End If End Sub Private Sub ExamineData() On Err GoTo ExamineDataErr Dim iNumRecords As Integer Dim vBookmark As Variant iNumRecords = m_oRecordset.RecordCount DisplayMsg "There are " & CStr(iNumRecords) & _ " records in the current Recordset." ' Loop through the Recordset and print the ' value of the AbsolutePosition property. DisplayMsg "****** Start AbsolutePosition Loop ******" Do While Not m_oRecordset.EOF ' Store the bookmark for the 3rd record, ' for demo purposes. If m_oRecordset.AbsolutePosition = 3 Then _ vBookmark = m_oRecordset.Bookmark DisplayMsg m_oRecordset.AbsolutePosition m_oRecordset.MoveNext Loop DisplayMsg "****** End AbsolutePosition Loop ******" & vbCrLf ' Use our bookmark to move back to 3rd record. m_oRecordset.Bookmark = vBookmark MsgBox vbCr & "Moved back to position " & _ m_oRecordset.AbsolutePosition & " using bookmark.", , _ "Hello Data" ' Display meta-data about each field. See WalkFields() sub. Call WalkFields ' Apply a filter on the type field. MsgBox "Filtering on type field. (CategoryID=2)", _ vbOKOnly, "Hello Data" m_oRecordset.Filter = "CategoryID=2" ' Set the proper states for the buttons. cmdExamineData.Enabled = False cmdEditData.Enabled = True Exit Sub ExamineDataErr: HandleErrs "ExamineData", m_oRecordset.ActiveConnection End Sub Private Sub EditData() On Error GoTo EditDataErr 'Recordset still filtered on CategoryID=2. 'Increase price by 10% for filtered records. MsgBox "Increasing unit price by 10%" & vbCr & _ "for all records with CategoryID = 2.", , "Hello Data" m_oRecordset.MoveFirst Dim cVal As Currency Do While Not m_oRecordset.EOF cVal = m_oRecordset.Fields("UnitPrice").Value m_oRecordset.Fields("UnitPrice").Value = (cVal * 1.1) m_oRecordset.MoveNext Loop ' Set the proper states for the buttons. cmdEditData.Enabled = False cmdUpdateData.Enabled = True Exit Sub EditDataErr: HandleErrs "EditData", m_oRecordset.ActiveConnection End Sub Private Sub UpdateData() On Error GoTo UpdateDataErr Dim oConnection2 As New ADODB.Connection MsgBox "Removing Filter (adFilterNone).", , "Hello Data" m_oRecordset.Filter = adFilterNone Set grdDisplay1.DataSource = Nothing Set grdDisplay1.DataSource = m_oRecordset MsgBox "Applying Filter (adFilterPendingRecords).", , "Hello Data" m_oRecordset.Filter = adFilterPendingRecords Set grdDisplay1.DataSource = Nothing Set grdDisplay1.DataSource = m_oRecordset DisplayMsg "*** PRE-UpdateBatch values for 'UnitPrice' field. ***" ' Display Value, UnderlyingValue, and OriginalValue for ' type field in first record. If m_oRecordset.Supports(adMovePrevious) Then m_oRecordset.MoveFirst DisplayMsg "OriginalValue = " & _ m_oRecordset.Fields("UnitPrice").OriginalValue DisplayMsg "Value = " & _ m_oRecordset.Fields("UnitPrice").Value End If oConnection2.ConnectionString = m_sConnStr oConnection2.Open Set m_oRecordset.ActiveConnection = oConnection2 m_oRecordset.UpdateBatch m_flgPriceUpdated = True DisplayMsg "*** POST-UpdateBatch values for 'UnitPrice' field ***" If m_oRecordset.Supports(adMovePrevious) Then m_oRecordset.MoveFirst DisplayMsg "OriginalValue = " & _ m_oRecordset.Fields("UnitPrice").OriginalValue DisplayMsg "Value = " & _ m_oRecordset.Fields("UnitPrice").Value End If MsgBox "See value comparisons in txtDisplay.", , _ "Hello Data" 'Clean up oConnection2.Close Set oConnection2 = Nothing Exit Sub UpdateDataErr: If Err <> 0 Then HandleErrs "UpdateData", oConnection2 End If If Not oConnection2 Is Nothing Then If oConnection2.State = adStateOpen Then oConnection2.Close Set oConnection2 = Nothing End If End Sub Private Sub WalkFields() On Error GoTo WalkFieldsErr Dim iFldCnt As Integer Dim oFields As ADODB.Fields Dim oField As ADODB.Field Dim sMsg As String Set oFields = m_oRecordset.Fields DisplayMsg "****** BEGIN FIELDS WALK ******" For iFldCnt = 0 To (oFields.Count - 1) Set oField = oFields(iFldCnt) sMsg = "" sMsg = sMsg & oField.Name sMsg = sMsg & vbTab & "Type: " & GetTypeAsString(oField.Type) sMsg = sMsg & vbTab & "Defined Size: " & oField.DefinedSize sMsg = sMsg & vbTab & "Actual Size: " & oField.ActualSize grdDisplay1.SelStartCol = iFldCnt grdDisplay1.SelEndCol = iFldCnt DisplayMsg sMsg MsgBox sMsg, , "Hello Data" Next iFldCnt DisplayMsg "****** END FIELDS WALK ******" & vbCrLf 'Clean up Set oField = Nothing Set oFields = Nothing Exit Sub WalkFieldsErr: Set oField = Nothing Set oFields = Nothing If Err <> 0 Then MsgBox Err.Source & "-->" & Err.Description, , "Error" End If End Sub Private Function GetTypeAsString(dtType As ADODB.DataTypeEnum) As String ' To save space, we are only checking for data types ' that we know are present. Select Case dtType Case adChar GetTypeAsString = "adChar" Case adVarChar GetTypeAsString = "adVarChar" Case adVarWChar GetTypeAsString = "adVarWChar" Case adCurrency GetTypeAsString = "adCurrency" Case adInteger GetTypeAsString = "adInteger" End Select End Function Private Sub HandleErrs(sSource As String, ByRef m_oConnection As ADODB.Connection) DisplayMsg "ADO (OLE) ERROR IN " & sSource DisplayMsg vbTab & "Error: " & Err.Number DisplayMsg vbTab & "Description: " & Err.Description DisplayMsg vbTab & "Source: " & Err.Source If Not m_oConnection Is Nothing Then If m_oConnection.Errors.Count <> 0 Then DisplayMsg "PROVIDER ERROR" Dim oError1 As ADODB.Error For Each oError1 In m_oConnection.Errors DisplayMsg vbTab & "Error: " & oError1.Number DisplayMsg vbTab & "Description: " & oError1.Description DisplayMsg vbTab & "Source: " & oError1.Source DisplayMsg vbTab & "Native Error:" & oError1.NativeError DisplayMsg vbTab & "SQL State: " & oError1.SQLState Next oError1 m_oConnection.Errors.Clear Set oError1 = Nothing End If End If MsgBox "Error(s) occurred. See txtDisplay1 for specific information.", , _ "Hello Data" Err.Clear End Sub Private Sub DisplayMsg(sText As String) txtDisplay1.Text = (txtDisplay1.Text & vbCrLf & sText) End Sub Private Sub Form_Resize() grdDisplay1.Move 100, 700, Me.ScaleWidth - 200, (Me.ScaleHeight - 800) / 2 txtDisplay1.Move 100, grdDisplay1.Top + grdDisplay1.Height + 100, _ Me.ScaleWidth - 200, (Me.ScaleHeight - 1000) / 2 End Sub Private Sub Form_Load() cmdGetData.Enabled = True cmdExamineData.Enabled = False cmdEditData.Enabled = False cmdUpdateData.Enabled = False grdDisplay1.AllowAddNew = False grdDisplay1.AllowDelete = False grdDisplay1.AllowUpdate = False m_flgPriceUpdated = False End Sub Private Sub Form_Unload(Cancel As Integer) On Error GoTo ErrHandler: Dim oConnection3 As New ADODB.Connection Dim sSQL As String Dim lAffected As Long ' Undo the changes we've made to the database on the server. If m_flgPriceUpdated Then sSQL = "UPDATE Products SET UnitPrice=(UnitPrice/1.1) " & _ "WHERE CategoryID=2" oConnection3.Open m_sConnStr oConnection3.Execute sSQL, lAffected, adCmdText MsgBox "Restored prices for " & CStr(lAffected) & _ " records affected.", , "Hello Data" End If 'Clean up oConnection3.Close Set oConnection3 = Nothing m_oRecordset.Close Set m_oRecordset = Nothing Exit Sub ErrHandler: If Not oConnection3 Is Nothing Then If oConnection3.State = adStateOpen Then oConnection3.Close Set oConnection3 = Nothing End If If Not m_oRecordset Is Nothing Then If m_oRecordset.State = adStateOpen Then m_oRecordset.Close Set m_oRecordset = Nothing End If End Sub 'EndHelloData
출처 : http://msdn.microsoft.com/en-us/library/ms810711.aspx
ActiveX와 Application에서 Mutex 문제
문제 : MainThread에서 Mutext생성 후 wait 계열 함수로 소유권을 요청하고, 메인쓰레드에서 ActiveX를 생성하면, ActiveX 내에서 다시 동기화를 위해 Mutex를 생성한 후 소유권을 요청하는 데, 이 때 ActiveX 역시 소유권을 가짐. 의도하는 바는 ActiveX랑 메인쓰레드랑 동기화를 하려고 했으나, 아래 예제에서는 동기화가 되지 않음.
아래 첨부를 해결하자! (2003 빌드)
원인 :
ActiveX를 생성하는 Main Thread랑 ActiveX 컨트롤이 같은 쓰레드이므로 Wait 계열 함수에서 항상 뮤텍스에 대한 소유권을 가짐
해결 :
MainThread에서 CreateThread 함수를 이용하여 쓰레드를 생성하고 그 쓰레드 내에서 뮤텍스를 생성하면 메인쓰레드에서 Wait 계열 함수로 소유권을 요청해도 소유권이 반환되지 않으므로 정상적인 동기화가 동작함.
비스타에서 Visual Studio 2003 빌드 시 regsvr32 등록 에러 문제
Open a command prompt by right clicking CMD and using 'run as
administrator', then try registering the library file from there.
--
Best of Luck,
Rick Rogers, aka "Nutcase" - Microsoft MVP
http://mvp.support.microsoft.com/
Windows help - www.rickrogers.org
"hbetancur" <hbetancur@discussions.microsoft.com> wrote in message
news:4CB6AF44-FC48-4C13-9323-E73EDE350824@microsoft.com...
> I'm trying to install manually a ActiveX control using the utility
> regsvr32.exe, but I got this error message. Can anyone give an idea what
> i'm
> doing wrong???
출처 : 구글 검색 후 링크 따라감...
관리자 권한으로 regsvr32 를 실행하면 해결 된다는 말.
더 좋은 방법을 찾아보자!
TN017: Destroying Window Objects
TN017: Destroying Window Objects
이 노트는 CWnd::PostNcDestory 멤버 함수의 사용을 설명한다. 만일 CWnd를 상속한 객체에 최적화된 할당을 만들려고 한다면 이 함수를 사용해라. 이 노트는 또한 아주 중요한 규칙에 대한 몇 가지 이유를 설명한다. C++ 윈도우 객체를 파괴(destroy)하기 위해서는, “delete” 하지 마라. 이것은 중요하다. 만약 아래의 가이드라인을 따른다면, Cleanup 문제를 거의 가지지 않을 것이다. (예를 들면, C++ 메모리를 delete/free 하는 것을 잊어먹거나, HWND 와 같은 시스템 리소스를 해제하는 것을 잊어먹거나, 너무 많이 객체를 해제한다거나)
The Problem
윈도우 객체(CWnd를 상속한 클래스의 객체)는 C++ 객체(애플리케이션의 힙에 할당된) 와 HWND(윈도우 관리자에 의해 시스템 리소스 내에 할당된) 모두를 나타낸다. 윈도우 객체를 파괴하는 방법은 여러 가지가 있기 때문에, 시스템 리소스 또는 애플리케이션 메모리 누수를 방지하는 그리고 객체와 윈도우 핸들이 한번 이상 파괴되는 것을 방지하기 위한 어떤 규칙 세트를 제공해야만 한다.
이것은 메모리 관리 문제 보다 더 심각하다. 윈도우는 사용자-인터페이스를 가진다 : 화면에 그려지는 윈도우; 윈도우가 파괴되면, 시스템 리소스에도 영향이 있다. 애플리케이션 주소 공간 내에서 C++ 메모리 누수는 시스템 리소스가 누수 되는 것 만큼은 나쁘지 않다.
Destroying Windows
윈도우 객체를 파괴하는 두 가지 가능한 방법은 :
- CWnd::DestoryWindow 를 호출하거나 윈도우 API ::DestoryWindow를 호출한다.
- Delete 연산자를 사용하여 명시적으로 삭제 하기
첫 번째 경우는 지금까지 가장 일반적이다. 이 경우는 당신의 코드가 직접적으로
DestoryWindow를 호출하지 않더라도 적용된다. 사용자가 직접적으로 프레임 윈도우를 닫을 때(WM_CLOSE의 기본적인 행위는 DestoryWindow를 호출하는 것이다), 그리고 부모 윈도우가 파괴 될 때, 윈도우는 모든 자식에 대해 DestoryWindow를 호출한다.
두 번째 경우, 윈도우 객체에 대한 delete 연산자의 사용, 는 거의 사용되어서는 안되며 아래 소개된 경우에만 사용되어야 한다. (The second case, the use of the delete operator on Windows objects, should be very rare and only in the cases outlined below.)
Auto Cleanup with CWnd::PostNcDestroy
윈도우를 파괴할 때, 윈도우로 보내지는 마지막 윈도우 메시지는 WM_NCDESTORY 이다. 그 메시지에 대한 디폴트 CWnd 핸들러(CWnd::OnNcDestory)는 C++ 개체와 HWND를 분리할 것이다 그리고 가상 함수인 PostNcDestory를 호출할 것이다. 일부 클래스는 C++ 객체를 삭제하기 위해 이 함수를 재정의(override)한다.
CWnd::PostNcDestory의 디폴트 구현은 아무것도 하지 않는다 그것은 스택 프레임에 할당된 윈도우 객체 또는 다른 객체 내에 내장된(embedded) 윈도우 객체에 대해서는 적절하다. 힙에 할당되도록 디자인 된 (다른 C++ 객체 내에 내장되지 않은) 윈도우 객체에 대해서는 적절하지 않다.
The default implementation of CWnd::PostNcDestroy does nothing which is appropriate for window objects allocated on the stack frame or embedded in other objects. This is not appropriate for window objects that are designed to be allocated by themselves on the heap (not embedded in other C++ object).
힙에 할당되도록 디자인 된 클래스는 “delete this”를 수행하기 위해 PostNcDestory 멤버 함수를 재정의 한다. 이 문장은 C++ 객체와 연결된 모든 C++ 메모리를 해제할 것이다. 만일 m_hWnd 가 NULL 이 아닌 경우 디폴트 CWnd 소멸자가 DestoryWindow를 호출하더라도, 핸들은 분리될 것이고, Cleanup 단계(phase)동안 NULL이 되기 때문에 이것은 무한 재귀(infinite recursion)로 빠지지는 않는다.
Note CWnd::PostNcDestroy은 일반적으로 윈도우 파괴의 일부로서, WM_NCDESTORY 메시지가 처리된 후에 호출된다, 그리고 HWND 와 C++ 윈도우 객체는 더 이상 붙여지지(attached) 않는다. CWnd::PostNcDestory 는 실패(failure)가 일어나면 대부분의 Create 호출의 구현 내에서 또한 호출될 것이다. (자동 Cleanup 규칙에 대해서는 아래를 보라)
Auto Cleanup Classes
아래 클래스는 auto-cleanup을 위해 디자인되지 않았다. 일반적으로 다른 C++ 객체 내에 내장되거나 스택 상에 존재한다:
- All standard Windows controls (CStatic, CEdit, CListBox, and so on).
- Any child windows derived directly from CWnd (for example, custom controls).
- Splitter windows (CSplitterWnd).
- Default control bars (classes derived from CControlBar, see Technical Note 31 for enabling auto-delete for control bar objects).
- Dialogs (CDialog) designed for modal dialogs on the stack frame.
- All the standard dialogs except CFindReplaceDialog.
- The default dialogs created by ClassWizard.
아래 클래스는 auto-cleanup을 위해 디자인되어 있다. 일반적으로 힙에 할당된다.
- Main frame windows (derived directly or indirectly from CFrameWnd).
- View windows (derived directly or indirectly from CView).
만일 이러한 모든 규칙을 깨뜨리고 싶다면, 상속한 클래스 안에 PostNcDestory 멤버 함수를 재정의 해야만 한다. 클래스에 auto-cleanup을 추가하기 위해서는, 기본 클래스를 호출하고 그러고 나서 delete this 를 수행한다. Auto-cleanup을 클래스로부터 제거하기 위해서는, 기본 클래스내에 PostNcDestroy 함수 대신에 바로 CWnd::PonstNcDestroy 를 호출한다.
위에서 언급한 것에 대한 가장 일반적인 사용은 힙에 할당될 수 있는 modeless 다이얼로그를 생성하는 것이다.
When to Call 'delete'
윈도우 객체를 파괴하기 위한 권장되는 방법은 C++ 멤버 함수 또는 전역 ::DestroyWindow API 둘 중 하나, DestroyWindow를 호출하는 것이다.
MDI 자식 윈도우를 파괴하기 위해서는 전역 ::DestroyWindow API를 호출해서는 안된다. 대신에 가상 멤버 함수 CWnd::DestroyWindow 를 사용한다.
Auto-cleanup을 수행하지 않는 C++ 윈도우 객체의 경우, delete 대신에 DestroyWindow 를 사용하는 것은 CWnd::~CWnd 소멸자 내에서 VTBL 이 올바르게 상속된 클래스를 가리키고 있지 않는 DestoryWindow를 호출해야만 하는 문제를 피하게 한다. MFC의 진단 (디버그) 버전이 경고할 미묘한 버그가 이 경우에 만들어 질 수 있다.
Warning: calling DestroyWindow in CWnd::~CWnd
OnDestroy or PostNcDestroy in derived class will not be called
Auto-cleanup을 수행하는 C++ 윈도우 객체의 경우, 반드시 DestroyWindow를 호출해야만 한다. 만약 delete 연산자를 직접적으로 사용한다면, MFC 진단 메모리 할당자(MFC diagnostic memory allocator)는 당신이 메모리를 두 번 해제하고 있다고 알려 줄 것이다 (PostNcDestroy의 auto-cleanup 구현 내에서 delete에 대한 첫 번째 호출뿐만 아니라 “delete this”에 의한 간접적인 호출).
출처 : MSDN 2003 (ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1042/vclib/html/_MFCNOTES_TN017.htm)
TN038: MFC/OLE IUnknown Implementation
IUnknown
OLE는 interface로서 IUnknown 으로부터 상속된 모든 클래스를 참조한다.
IUnknown "interface"는 구현이 되지 않은 추상 인터페이스만을 아래와 같이 정의하고 있다.
class IUnknown
{
public:
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
};
Note : __stdcall 과 같은 필수 호출 규약은 생략하였음
COM은 객체들을 추적하는데 있어 참조 카운트 스키마를 사용한다. 하나의 객체는 C++ 내에서 절대로 직접적으로 참조 될 수 없다. 대신에, COM 객체는 항상 포인터를 통해서 참조된다.
소유자(Owner)가 객체를 사용을 완료했을 때, 그 객체를 해제하기 위해서는, 그 객체의 Release 멤버 함수가 호출 된다. (그와 반대로 전통적인 C++에서는 delete 연산자를 이용한다.)
참조 카운트 메커니즘은 단일 객체에 대해 다중 참조를 관리하게 해준다. AddRef와 Release는 그 객체에 대한 참조 카운트를 관리한다. 객체는 자신의 참조 카운트가 0이 될 때가지 삭제되지 않는다.
아래 AddRef와 Release의 간단한 구현 예이다.
ULONG CMyObj::AddRef()
{
return ++m_dwRef;
}
ULONG CMyObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
QueryInterface는 동일한 객체에 대한 다른 인터페이스를 얻을 수 있도록 한다. 이 인터페이스들은 보통 IUnknown으로부터 상속되고 새로운 멤버 함수를 추가함으로서 부가적인 기능을 추가한다. COM 인터페이스는 인터페이스 내에 선언된 멤버 변수들을 절대로 소유하지 않으며, 모든 멤버 함수는 순수-가상 함수로 선언된다. 아래는 그 예이다.
class IPrintInterface : public IUnknown
{
public:
virtual void PrintObject() = 0;
};
만일 IUnknown 개체만을 가지고 있는 경우에 IPrintInterface 을 얻기 위해서는 IPrintInterface의 IID를 사용하여 IUnknown::QueryInterface를 호출한다. IID는 인터페이스를 식별하는 고유의 128 비트 숫자이다. 각각의 인터페이스마다 IID가 존재한다. pUnk가 IUnknown 객체의 포인터라면, IPrintInterface 를 얻어오는 코드는 다음과 같다.
IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface,
(void**)&pPrint) == NOERROR)
{
pPrint->PrintObject();
pPrint->Release();
// release pointer obtained via QueryInterface
}
객체에 IPrintInterface와 IUnknown 인터페이스 둘다를 지원하기 위해서는 어떻게 해야 할까?
이 경우에, IPrintInterface가 IUnknown을 직접적으로 상속하고 있기 때문에 아주 단순하다. IPrintInterface를 구현함으로써, IUnknown 은 자동적으로 지원된다. 예로 :
class CPrintObj : public CPrintInterface
{
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
virtual ULONG AddRef();
virtual ULONG Release();
virtual void PrintObject();
};
AddRef와 Release의 구현은 위에서 구현된 코드와 같을 것이다. CPintObj::QueryInterface는 아래와 같다.
HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
if (iid == IID_IUnknown || iid == IID_IPrintInterface)
{
*ppvObj = this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
보는바와 같이, Interface Identifier(IID)가 인식되면, 포인터가 개체에 대입된다. 성공적인 QueryInterface 는 암시적인 AddRef를 초래한다는 것을 기억하라.
물론, CEditObj::Print를 또한 구현해야 한다. IPrintInterface는 직접적으로 IUnknown 인터페이스로부터 상속되었기 때문에 간단하다. 그러나 만약 IUnknown 인터페이스로부터 상속된 두개의 다른 인터페이스를 지원하길 원한다면, 어떨지 고려해보자.
class IEditInterface : public IUnkown
{
public:
virtual void EditObject() = 0;
};
C++ 다중 상속을 이용하는 것과 같이 IEditInterface와 IPrintInterface 모두를 지원하는 클래스를 구현하는 방법은 매우 많다. 이 문서는 이것을 구현하기 위한 중첩클래스(Nested class)를 사용하는 것에 집중할 것이다.
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;
};
전체 구현은 아래와 같다.
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);
}
대부분의 IUnknown 구현은 CEditPrintObj::CEditObj 와 CEditPrintObj::CPrintObj내에 코드를 중첩하는 것보다 CEditPrintObj 클래스에 위치시키는 것을 기억하라. 이것은 코드량을 줄여주고 버그를 피할 수 있게 한다. 여기서 핵심은 IUnknown 인터페이스로부터 그 객체가 지원하는 모든 인터페이스를 가져오게 하기 위해 QueryInterface를 호출하는 것이 가능하다는 것이다, 그리고 그러한 각각의 인터페이스로부터도 모든 인터페이스를 가져오는 것 또한 가능하다. 이것은 각각의 인터페이스로터 이용가능한 모든 QueryInerface 함수는 똑같이 동작한다는 것을 의미한다. 이러한 내장 객체들이 외부객체(Outer Object)에 있는 구현을 호출하기 위해서는, Back-Pointer가 사용된다(m_pParent). m_pParent 포인터는 CEditPrintObj 생성자에서 초기화된다.
이제 CEditPrintObj::CPrintObj::PrintObject와 CEditPrintObj::CEditObj::EditObject를 구현해야 한다. 한가지 기능을 추가하기 위해 약간의 코드가 더해졌다. 운이 좋게도, 인터페이스들이 한개의 멤버 함수만을 가지고 있는 경우는 매우 일반적이지 않으며 이러한 경우, EditObject와 PrintObject는 보통 한개의 인터페이스로 합쳐질 것이다.
이러한 단순한 시나리오의 경우에 해당하는 많은 설명과 코드가 있다. MFC/OLE 클래스는 더 심플한 대안을 제공한다. MFC 구현은 윈도우 메시지가 메시지 맵에 래핑되는것과 유사한 테크닉을 사용한다. 이러한 기능은 Interface Map이라 불리우고 다음 섹션에서 논의된다.
MFC Interface Maps
MFC/OLE 는 개념(concept)과 수행(execution)에 있어 MFC의 메시지 맵과 유사한 인터페이스 맵과 디스패치 맵의 구현을 제공한다. MFC 인터페이스 맵의 핵심 기능은 아래와 같다.
* CCmdTarget 클래스내에 구현된 IUnknown의 표준 구현
* AddRef 와 Release에 의해 수정되는 참조 카운트의 관리
* QueryInterface의 데이터 주도 구현(Data driven implementation)
게다가, 인터페이스 맵은 아래의 향상된 기능을 지원한다.
* 집합체가 될 수 있는 COM 개체(aggregatable COM ojbect)를 생성하는 것을 지원
* COM 객체의 구현 안에 집합체(aggregate object)를 사용하는 것을 지원
* 구현은 후킹가능하고 확장할 수 있다.
집합(aggregation)에 대한 자세한 정보는, OLE Programmer's Reference를 보라.
MFC의 인터페이스 맵 지원은 CCmdTarget 클래스 내에 내장되어 있다. CCmdTarget은 참조 카운트 뿐만 아니라 IUnknown 구현과 연결된 모든 멤버함수를 가진다 (예를 들어 참조 카운트는 CCmdTarget내에 있다). OLE COM을 지원하는 클래스를 생성하기 위해서는, CCmdTarget을 상속하고 의도하는 인터페이스를 구현하기 위한 다양한 매크로와 CCmdTarget의 멤버 함수를 사용한다. MFC의 구현은 위에서 설명한 예제와 같이 각각의 인터페이스 구현을 정의하기 위해 중첩 클래스를 사용한다. IUnknown의 표준 구현 뿐만 아니라 몇가지 반복적인 코드를 제거하는 여러 매크로를 이용하여 더 쉽게 구현할 수 있게 해준다
Inteface Map Basic
MFC 인터페이스 맵을 사용하는 클래스를 구현하기
1. 직접 혹은 간접적으로 CCmdTarget을 상속한다.
2. 클래스 정의에 DECLARE_INTERFACE_MAP 함수를 사용한다.
3. 지원하고자 하는 각각의 인터페이스에 대해, 클래스 정의 내에 BEGIN_INTERFACE_PART 와 END_INTERFACE_PART 매크로를 사용한다.
4. 구현 파일 내에, 클래싀 인터페이스 맵을 정의하기 위해 BEGTIN_INTERFACE_MAP과 END_INTERFACE_MAP 매크로를 사용한다.
5. 지원된 각각의 IID에 대해, IID를 클래스의 특정 부분에 맵핑하기 위해 BEGIN_INTERFACE_MAP과 END_INTERFACE_MAP 매크로 사이에 INTERFACE_PART 매크로를 사용한다.
6. 지원하는 인터페이스를 나타내는 각각의 중첩 클래스를 구현한다.
7. 부모, CCmdTarget을 상속한 객체에 접근하기 위해 METHOD_PROLOGUE를 사용한다.
8. AddRef, Release, 그리고 QueryInterface는 이와 같은 함수의 CCmdTaret 구현 (ExternalAddRef, ExternalRelease, 그리고 ExternalQueryInterface)에 위임할 수 있다.
위의 CPrintEditObj 예제는 아래와 같이 구현될 수 있다.
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)
};
위의 선언은 CCmdTarget을 상속한 클래스를 생성한다. DECLARE_INTERFACE_MAP 매크로는 프레임워크에게 이 클래스는 커스텀 인터페이스 맵을 가질것이다라는 것을 알려준다. 게다가, BEGIN_INTERFACE_PART와 END_INTERFACE_PART 매크로는 CEditObj와 CPrintObj로 이름지어진 중첩 클래스를 정의한다. (X는 "C"로 시작하는 전역 클래스와 "I"로 시작하는 인터페이스 클래스와 중첩 클래스를 구분하기 위해서만 사용된다.) 이 클래스들에 대한 두개의 중첩 멤버(nested member)가 생성된다 : m_CEditObj 와 m_CPrintObj. 매크로는 자동적으로 AddRef, Release, 그리고 QueryInterface 함수를 선언한다. 그리하여 이 인터페이스 고유의 함수들만 선언한다. (OLE 매크로 STDMETHOD는 대상 플랫폼에 적합한 _stdcall 과 virtual 키워드를 제공하기 위해 사용된다.)
이 클래스의 경우 인터페이스를 정의하기 위해서는
BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()
이것은 각각 m_CPrintObj와 IID_IPrintInterface를, m_CEditObj와 IID_IEditInterface를 연결한다. QueryInterface의 CCmdTarget 구현(CCmdTarget::ExternalQueryInterface)는 m_CPrintObj와 m_CEditObj에 대한 포인터를 반환하기 위해 이 맵을 사용한다. IID_IUnknown에 대한 엔트리를 포함하는 것은 불필요하다. 프레임워크는 IID_IUnknown이 요구되었을 때 맵에 있는 첫번째 인터페이스(이 경우에, m_CPrintObj)를 사용할 것이다.
비록 BEGIN_INTERFACE_PART 매크로가 자동적으로 AddRef, Release 그리고 QueryInterface 함수를 선언했다고 할지라도, 여전히 그것들을 구현해야할 필요가 있다.
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...
}
CEditPrintObj::CPrintObj에 대한 구현은 위의 CEditPrintObj::CEditObj의 정의와 비슷할 것이다. 비록 이러한 함수들을 자동적으로 생성하기 위해 사용될 수 있는 매크로를 만들수 있을지라도, 매크로가 한줄 이상의 코드를 생성할 때 브레이크 포인터를 걸기가 어려워 질 것이다. 이러한 이유로, 이 코드는 수동으로 확장된다.
메시지 맵의 프레임워크 구현을 사용함으로써, 다음과 같은 작업을 할 필요가 없다.
* QueryInterface 구현
* AddRef와 Release 구현
* 두 인터페이스에 대한 각각의 built-in 메소드 선언
게다가, 프레임워크는 내부적으로 메시지 맵을 사용한다. 이것은 이미 특정 인터페이스를 지원하는 그리고 그 인터페이스에 대한 대체(replacement) 또는 추가(addition)를 제공하는 COleServerDoc와 같은 프레임워크 클래스를 얻어 올 수 있게 한다. 이것은 프레임워크가 베이스 클래스로부터 인터페이스 맵을 상속하는 것을 전적으로 지원한다는 사실에서 가능해진다 - 즉 BEGIN_INTERFACE_MAP이 두번째 파라미터로 베이스 클래스의 이름을 가지는 이유이다.
Aggregation and Interface Maps
stand-along COM 개체를 지원하는 것에 외에, MFC는 또한 집합(aggregation)을 지원한다. 집합은 여기서 다루기에는 너무 복잡하다. 집합에 대한 자세한 정보는 OLE Programmer's Reference를 참조해라. 이 문서는 간단하게 프레임워크와 인터페이스 맵 내에 집합을 구축하는 것에 대해 설명할 것이다.
집합을 사용하는 방법에는 두가지가 있다.
(1) 집합을 지원하는 COM 객체를 사용하는 것
(2) 다른 객체에 의해 집합될 수 있는(can be aggregated) 객체를 구현하는 것
이러한 기능은 "집합체 사용하기(Using an aggregate object)"와 "집합될 수 있는 객체 만들기(Making an object aggregatable)" 로 다루어 진다. MFC는 두가지 모두 지원한다.
Using an aggregate object
집합체를 사용하기 위해서는, 그 집합체를 QueryInterface 매커니즘 내에 묶는 몇가지 방법이 필요한다. 다른 말로 하면, 집합체는 객체의 고유한 일부(native part)인 것처럼 동작해야 한다. 그래서 이것을 어떻게 MFC의 인터페이스 맵 매커니즘으로 묶을수 있을까? INTERFACE_PART 매크로외에, CCmdTarget 을 상속한 클래스의 일부로 집합체를 선언할 수 있다. 그렇게 하기 위해, INTERFACE_AGGREGATE 매크로가 사용된다. 이것은 멤버 변수( IUnknown이나 상속클래스의 포인터이어야 한다)를 지정할 수 있게 한다 그리고 이것은 인터페이스 맵 매커니즘 안으로 통합된다. CCmdTarget::ExternalQueryInterface가 호출될 때 포인터가 NULL이 아니고, 그리고 요청된 IID가 CCmdTarget 개체 자신에 의해 지원되는 고유의 IID가 아니라면 프레임워크는 자동적으로 집합체의 QueryInterface 멤버 함수를 호출할 것이다, .
INTERFACE_AGGREGATE 매크로 사용하기
1. 집합체에 대한 포인터를 포함할 멤버변수(IUnknown 포인터)를 선언한다
2. 인터페이스 맵내에 INTERFACE_AGGREGATE를 포함한다. 이것은 멤버변수를 이름으로 참조한다.
3. 특정 지점(보통 CCmdTarget::OnCreateAggreagets 내에서), 멤버 변수는 NULL이 아닌 다른것으로 초기화 한다.
아래는 예:
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()
m_lpAggrInnter 은 생성자에서 NULL로 초기화 된다. 프레임워크는 QueryInterface의 디폴트 구현안에서 NULL 멤버 변수는 무시할 것이다. OnCreateAggregates는 집합체를 실질적으로 생성하기 위한 좋은 위치이다. 만일 COleObjectFactory의 MFC 구현 외부에서 객체를 생성하고 있다면, 명시적으로 그것을 호출해야만 할 것이다. CCmdTarget::OnCreateAggregates내에서 집합체를 생성하는 이유뿐만 아니라 CCmdTarget::GetControllingUnknown 의 사용은 집합체를 생성하는 것에 대해 논의될 때 명백해 질 것이다.
이 기법은 객체에 집합체가 지원하는 모든 인터페이스를 고유의 인터페이스에 추가해 줄 것이다. 집합체가 지원하는 인터페이스 일부만을 원한다면, CCmdTarget::GetInterfaceHook 을 재정의 할 수 있다. 이것은 QueryInterface와 유사하게, 아주 낮은 레벨의 hookability를 제공한다. 보통, 집합체가 지원하는 모든 인터페이스를 원할 것이다.
Making an object Implemtation Aggregatable
집합될 수 있는 객체의 경우, AddRef, Release, 그리고 QueryInterface의 구현은 "Controlling Unknown"에 위임해야만 한다. 다른 말로 하면, 객체가 다른 객체의 일부가 되기 위해서는, IUnknown을 상속한 다른 객체에 AddRef, Release, 그리고 QueryInterface를 위임해야만 한다. 이 "controlling unknown"는 객체가 생성될 때 그 객체에 제공된다, 즉, COleObjectFactory의 구현에 제공된다. 이것을 구현하는 것은 약간의 오버헤드를 수반하며 어떤 경우에는 바람직하지 않을수 있다. 그래서 MFC는 이것을 선택할 수 있게 만들었다. 객체가 집합가능하게 만들기 위해서는, 객체의 생성자에서 CCmdTarget::EnableAggregation을 호출해야 한다.
그 객체가 또한 집합체를 사용한다면, 그 집합체에다가 올바른 "controlling unknown"을 전달해야만 한다. 보통 이 IUnknown 포인터는 집합체가 생성될 때 객체에 전달된다. 예를 들면, pUnkOuter 파라미터는 CoCreateInstance로 생성된 객체들을 위한 "controlling unknown"이다. 올바른 "controlling unknown" 포인터는 CCmdTarget::GetControllingUnknown을 호출함으로써 얻을 수 있다. 그러나 그 함수에 의해 반환되는 값은 생성자에서는 유효하지 않다. 이 이유로, GetControllingUnknown 의 반환값이 신뢰할 수 있는, 비록 COleObjectFactory 구현으로부터 생성된 것조차, CCmdTarget::OnCreateAggreates 를 재정의하여 집합체를 생성하도록 권유된다.
인위적인 참조 카운트를 증가시키거나 해제할때 객체가 올바른 참조 카운트를 조절하는 것 또한 매우 중요하다. 이것을 보장하기 위해, InternalRelease와 InternalAddRef 대신에 ExternalAddRef와 ExternalRelease를 항상 호출해라. 집합체를 지원하는 클래스에서 InternalRelease 또는 InternalAddRef를 호출하는 것은 거의 볼 수 없다.
참고 : MSDN 2003 Technical Note #38
About Mouse
A single-pixel point called the hot spot, a point that the system tracks and recognizes as the position of the cursor.
The window need not be active or have the keyboard focus to receive a mouse message.
You can use the SystemParametersInfo function with the SPI_GETMOUSE or SPI_SETMOUSE flag to retrieve or set mous speed.
Mouse Capture
An application can chage this behavior by using the SetCapture function to route mouse messages to a specific window. The window receives all mouse messages until the application calls the ReleaseCapture function or specifies another capture window, or until the user clicks a window created by another thread.
When the mouse capture changes, the system sends a WM_CAPTUECHANGED message to the window that is losing the mouse capture.
GetCapture
To determine whether one of its window has captured the mouse
Mouse ClickLock
This feature lets a user lock down the primary mouse button after a single click.
Mouse Configuration
An application can determine whether the system includes a mouse by passing the SM_MOUSEPRESENT value to the GetSystemMetrics function
Mouse Messages
The system converts mouse input events into messages and posts them to the appropriate thread's message queue. When mouse message are posted faster than a thread can process them, the system discards all but the most recent mouse message.
Client Area Mouse Messages
An application can call the TrackMouseEvent function to have the system send two other messages. It posts the WM_MOUSEHOVER message when the cursor hovers over the client area for a certain time period. It posts the WM_MOUSELEAVE message when the cursor leaves the client area.
Message Parameters
lParam
Indicates the position of the cursor hot spot. The low-order word indicates the x-coordinate of the hot spot, and the high-order word indicates the y-coordinate. The coordinates are specified in client coordinates.
wParam
Indicates the status of the other mouse buttons and the CTRL and SHIFT keys at the time of the mouse event.
The wParam parameter can be a combination of the following values.
Value | Meaning |
---|---|
MK_CONTROL | The CTRL key is down. |
MK_LBUTTON | The left mouse button is down. |
MK_MBUTTON | The middle mouse button is down. |
MK_RBUTTON | The right mouse button is down. |
MK_SHIFT | The SHIFT key is down. |
MK_XBUTTON1 | Windows 2000/Windows XP: the first X button is down. |
MK_XBUTTON2 | Windows 2000/Windows XP: the second X button is down. |
Double-Click Messages
The system generates a double-click message when the user clicks a mouse button twice in quick succession.
When the user clicks a button, the system establishes a rectangle centered around the cursor hot spot. It also marks the time at which the click occurred.
When the user clicks the same button a second time, the system determines whether the hot spot is still within the rectangle and calculates the time elapsed since the first click.
If the hot spot is still within the rectangle and the elapsed time does not exceed the double-click time-out value, the system generates a double-click message.
An application can get and set double-click time-out values by using the GetDoubleClickTime and SetDoubleClickTime functions,
Alternatively the application can set the double-click-time-out-value by using the SPI_SETDOUBLECLICKTIME flag with the SystemParametersInfo function. It can also set te size of the rectangle that the system uses to detect double clicks by passing the SPI_SETDOUBLECLKWIDTH and SPI_SETDOUBLECLKHEIGHT flags to SystemParametersInfo.
Note, however, that setting the double-click-time-out vlaue and rectangle affects all applications.
An application-defined window does not, by default, receive double-click messages. Because of the system overhead involved in generating double-click messages, these messages are gnerated only for windows belonging to classes that have the CS_DBLCLKS class style.
A double-click message is always the third message in a four-message series.
For example, double-clicking the left mouse button generates the following message sequences:
1. WM_LBUTTONDOWN
2. WM_LBUTTONUP
3. WM_LBUTTONDBLCLK
4. WM_LBUTTONUP
Because a window always receives a button-down message before receiving a double-click message, an application typically uses a double-click message to extend a task it began during a button-down message.
Nonclient Area Mouse Messages
A window's nonclient area consists of its border, menu bar, title bar, scroll bar, window menu, minimize button, and maximize button.
A window must pass nonclient area mouse message to the DefWindowProc function to take advantage of the built-in mouse interface.
There is a corresponding nonclient area mouse message for each client area mouse message.
lParam
Unlike coordinates of client area mouse messages, the coordinaes are specified in screen coordinates rather than client coordinates.
wParam
contains a hit-test value, a value that indicates where in the nonclient area the mouse event occurred.
The WM_NCHITTEST Message
Whenever a mouse event occurs, the system sends a WM_NCHITTEST message to either the window that contains the cursor hot spot or the window that has captured the mouse. The system uses this message to determine whether to send a client area or nonclient area mouse message.
An application that must receive mouse movement and mouse button messages must pass the WM_NCHITTEST message to the DefWindowProc function.
The lParam parameter of the WM_NCHITTEST message contains the screen coordinates of the cursor hot spot. The DefWindowProc function examines the coordinates and returns a hit-test value that indicates the location of the hot spot. Te hit-test value can be one of the following values.
Value | Location of hot spot |
---|---|
HTBORDER | In the border of a window that does not have a sizing border. |
HTBOTTOM | In the lower-horizontal border of a window. |
HTBOTTOMLEFT | In the lower-left corner of a window border. |
HTBOTTOMRIGHT | In the lower-right corner of a window border. |
HTCAPTION | In a title bar. |
HTCLIENT | In a client area. |
HTCLOSE | In a Close button. |
HTERROR | On the screen background or on a dividing line between windows (same as HTNOWHERE, except that the DefWindowProc function produces a system beep to indicate an error). |
HTGROWBOX | In a size box (same as HTSIZE). |
HTHELP | In a Help button. |
HTHSCROLL | In a horizontal scroll bar. |
HTLEFT | In the left border of a window. |
HTMENU | In a menu. |
HTMAXBUTTON | In a Maximize button. |
HTMINBUTTON | In a Minimize button. |
HTNOWHERE | On the screen background or on a dividing line between windows. |
HTREDUCE | In a Minimize button. |
HTRIGHT | In the right border of a window. |
HTSIZE | In a size box (same as HTGROWBOX). |
HTSYSMENU | In a System menu or in a Close button in a child window. |
HTTOP | In the upper-horizontal border of a window. |
HTTOPLEFT | In the upper-left corner of a window border. |
HTTOPRIGHT | In the upper-right corner of a window border. |
HTTRANSPARENT | In a window currently covered by another window in the same thread. |
HTVSCROLL | In the vertical scroll bar. |
HTZOOM | In a Maximize button. |
Mouse Sonar
This feature briefly shows several concentric circles around the pointer when the user presses and releases the CTRL key.
SystemPrametersInfo
SPI_GETMOUSESONAR
SPI_SETMOUSESONAR
Mouse Vanish
This feature hides the pointer when the user is typing. The mouse pointer reappears when the user moves the mouse.
SstemParametersInfo
SPI_GETMOUSEVANISH
SPI_SETMOUSEVANISH
The Mouse Wheel
When an application processes the WM_MOUSEWHEEL message, it must return zero. When an application processes the MSH_MOUSEWHEEL message, it must return TRUE.
Detecting a Mouse with a Wheel
To determine if a mouse with a wheel is connected, call GetSystemMetrics(SM_MOUSEWHEELPRESENT)
ClipCursor()
To confine the cursor to the client area during the line drawing operation.
Processing a Double Click Message
To receive double-click messages, a window must belong to a window class that has the CS_DBLCLKS class style.
About Keyboard
Scan Code
Assigned to each key on a keyboard s a unique value
a device-dependent identifier for the key on the keyboard
Virtual-Key-Code
The keyboard device driver interprets a scan code and translates (maps) it to a virtual-key-code,
a device-independent value defined by the system that identifies the purpose of a key
After translating a scan code, the key board layout creates a message that includes the scan code, the virtual-key code, and other information about the keystroke, and then places the message in the system message queue.
The system removes the message from the system message queue and posts it to the message queue of the appropriate thread.
Eventually, the trhread's message loop removes the message and passes it to the appropriate window procedure for processing.
The following figure illustrates the keyboard input model :
Keyboard Focus and Activation
GetFocus()
determine which of its windows (if any) currently has the keyboard focus
SetFocus()
A thread can give the keybord focus to one of its windows by calling this.
The concept of keyboard focus is related to that of the active window.
Active Window
The active window is the top-level window the user is currently working with.
SetActiveWindow()
A thread can activate a top-level window by using this
GetActiveWindow()
Determine whether a top-level window it created is active by using this
When one window is deactivated and another activated, the system sends the WM_ACTIVE message.
The low-order word of the wParam parameter is zero,
if the window is being deactivated and nonzero if it is being activated.
BlockInput() & SendInput()
To block keyboard and mouse input events from reaching applications, use BlockInput.
Note, the BlockInput function will not interfere with the asynchronous keyboard input-state table.
This means that calling the SendInput function wihle input is blocked will change the asynchronous keyboard input-state table.
Keystroke Messages
WM_KEYDOWN
WM_SYSKEYDOWN
WM_KEYUP
WM_SYSKEYUP
System and Nonsystem Keystrokes
The system makes a distinction between system keystrokes and nonsystem keystrokes.
If your window procedure must process a system keystroke message, make sure that after processing the message the procedure passes it to the DefWindowProc function.
System Keystroke messages are primarily for use by the system rather than by an application.
Nonsystem Keystroke message are for use by application windows; the DefWindowProc function does nothing with them.
Virtual-Key codes Described
The wParam parameter of a keystroke message contains the virtual-key code of the key that was pressed or released.
Keystroke Message Flags
The lParam parameter of a keystroke message contains additional information about the keystroke that generated the message. This information includes the repeat count, the scan code, the extended-key flag, the context code, the previous key-state flag, and the transition-state flag.
An applicatoin can use the following values to manipulate the keystroke flags.
-----------------------------------------------------------------------------------------
KF_ALTDOWN | Manipulates the ALT key flag, which indicated if the ALT key is pressed. |
KF_DLGMODE | Manipulates the dialog mode flag, which indicates whether a dialog box is active. |
KF_EXTENDED | Manipulates the extended key flag. |
KF_MENUMODE | Manipulates the menu mode flag, which indicates whether a menu is active. |
KF_REPEAT | Manipulates the repeat count. |
KF_UP | Manipulates the transition state flag. |
Repeat Count
Determine whether a keystroke message represents more than one keystroke.
Instead of filling the system message queue with the resulting key-down messages, the system combines the messages into a single key down message and increments the repeat count.
Scan Code
The scan code is the value that the keyboard hardware generates when the user presses a key.
a device dependent value
Extended-Key Flag
Indicates whether the keystroke message originated from one of the additional keys on the enhanced keyboard.
The extended keys consist of :
the ALT and CTRL keys on the right-hand side of the keyboard;
the INS, DEL, HOME, END, PAGE UP, PAGE DOWN,
And arrow keys in the cluster to the left of the numeric keypad;
the NUM LOCK key; the BRAEK(CTRL+PAUSE) key;
the PRINT SCRN key; and divide(/) and ENTER keys in the numeric keypad.
The extended-key flag is set if the key is an extended key.
Context Code
Indicates whether the ALT key was down when the keystroke message was generated.
The code is 1 if the ALT key was down and 0 if it was up.
Previous Key-State Flag
Indicates whether the key that generated the keystroke message was previously up or down.
It is 1 if the key was previously down and 0 if the key was previously up.
You can use this flag to identify keystroke messages generated by the keyboard's automatic repeat feature.
Transition-State Flag
Indicates whether pressing a key or releasing a key generated the keystroke message.
This flag is always set to 0 for WM_KEYDOWN and WM_SYSKEYDOWN messages;
it is always set to 1 for WM_KEUP and WM_SYSKEYUP messages.
Character Messages
To retrieve character codes, an application must include the TranslateMessage function in its thread message loop. TranslateMessage passes a WM_KEYDOWN or WM_SYSKEDOWN message to the keyboard layout. The layout examines the message's virtual-key code and, if it corresponds to a character key, provides the character code equivalent (taking into account the state of the SHIFT and CAPS LOCK keys).
Nontsystem Character Messages
The TranslateMessage function generates a WM_CHAR or WM_DEADCHAR message when it processes a WM_KEYDOWN message. Similarly, it generates a WM_SYSCHAR or WM_SYSDEADCHAR message when it processes a WM_SYSKEYDOWN message.
Note the WM_CHAR uses 16-bit Unicode transformation format (UTF) while WM_UNICHAR uses UTF-32.
The wParam parameter of all character messages contains the character code of the character key that was pressed.
The contents of the lParam parameter of a character message are identical to the contents of the lParam parameter of the key-down message that was translated to produce the character message.
Dead-Character Messages
Some non-English keyboards contain character keys that are not expected to produce characters by themselves. Instead, they are used to add a diacritic to the character produced by the subsequent keystroke. These keys are called dead keys.
The window with the keyboard focus would receive the following sequence of messages:
1. WM_KEYDOWN
2. WM_DEADCHAR
3. WM_KEYUP
4. WM_KEYDOWN
5. WM_CHAR
6. WM_KEYUP
TranslateMessage generates the WM_DEADCHAR message when it processes the WM_KEYDOWN message from a dead key. If the subsequent keystroke generates a character that cannot be combined with a diacritic, the system generates two WM_CHAR mesages.
The TranslateMessage function generates the WM_SYSDEADCHAR message when it processes the WM_SYSKEYDOWN message from a system dead key (a dead key that is pressed in combination with the LT ke)
Key Status
The application can use the GetKeyState function to determine the status of a virtual key at the time the current message was generated; it can use the GetAsyncKeyState function to retrieve the current status of a virtual key.
An application can retrieve the name of any key from the device driver by calling the GetKeyNameText function.
Keystroke and Character Translations
The system includes several special purpose functions that translate scan codes, character codes, and virtual-key codes provided by various keystroke message. These functions include MapVirtualKey, ToAscii, ToUnicode and VkKeyScan.
Hot-Key Support
A hot key is a key combination that generates a WM_HOTKEY message, a message the system places at the top of a thread's message queue, bypassing any existing messages in the queue.
For example, by defining a hot key consisting of the CTRL+C key combination, an application can allow the user to cancel a lengthy operation.
To define a hot key, an application calls the RegisterHotKey function.
Before the application terminates, it should use the UnregisterHotKey function to destroy the hot key.
Applications can use a hot key control to make it easy for the user to choose a hot key. Hot key controls are typicall used to define a hot key that activates a window;
Simulating Input
To simulate an uninterruped series of user input events, use the SendInput function.
The SendInput function works by injecting a series of simulated input events into a device's input stream. The effect is similar to calling the keybd_event or mouse_event function repeatedly, except that the system ensures that no other input events intermingle with the simulated events.
If the return value is zero, then input was blocked.
The SendInput function does not reset the keyboard's current state. Therefore, if the user has any keys pressed when you call this function, they might interfere with the events that this function generates. If you are concerned about possible interference, check the keyboard's state with the GetAsyncKeyState function and correct as necessary.
Translating Character Messages
MSG msg; BOOL bRet; while (( bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0) { if (bRet == -1); { // handle the error and possibly exit } else { if (TranslateAccelerator(hwndMain, haccl, &msg) == 0) { TranslateMessage(&msg); DispatchMessage(&msg); } } }
Windows (창) 개체
키워드 정리:
CFrameWnd; CMDIFrameWnd; CMDIChildWnd; Registerclass; AfxRegisterWndClass; OnClose; DestoryWindow; OnNcDestory; PostNcDestory; Detach; CWindowDC; CMetaFileDC; CClientDC; CPen; CBrush; CFont; CBitmatp; CPalette; CRgn; CImage; DeviceContext; PreCreateWindow; FWS_ADDTOTITLE; CDocTemplate; MDICLIENT: OnActivateView; ON_UPDATE_COMMAND_UI; DragAcceptFiles; LoadStdProfileSettings; DDE 응답; RegisterShellFileTypes; EnableShellOpen;
CObject 관련 정리
키워드:
Serialization; DECLARE_DYNAMIC; DECLARE_DYNCREATE; DECLARE_SERIAL; IsKindOf; CRuntimeClass; DumpallObjectsSince; CDumpContext; AssertValid; ASSERT_VALID;
About Windows
윈도우에 대해서 기본적인 지식을 제공함
MSDN 2003 정리 자료 (영문)
키워드 정리 :
DesktopWindow; Client Area; Nonclient Area; GetDesktopWindow; SystemParametersInfo; SetWindowLong;
CreateWindowEx; GetWindowLong; WindowFromPoint; ChildWindowFromPoint; ChildWindowFromPointEx;
GetSystemMetrics; SM_CXMIN; SM_CYMIN; AdjustWindowRect; AdjustWindowRectEx; EnumWindows;
HWND; FindWindow; FindWindowEx; IsWindow; HWND_BROADCAST; HWND_DESKTOP; MapWindowPoints; CreateMutex; GetLastError; SW_SHOWDEFAULT; IsWindowUnicode; RegisterClass;
WM_NCCREATE; CREATESTRUCT; WM_PARENTNOTIFY; WM_CREATE; EnumThreadWindows;
GetWindowThreadProcessId; ShowWindowAsync; WS_CLIPSIBLING, WS_CLIPCHILDREN; SetParent; GetParent; Descendant Window; IsChild; EnumChildWindows; Layered Window; WS_EX_TRANSPARENT; HWND_MESSAGE; Foreground Window; Background Window; GetForegroundWindow; SetForegroundWindow; LockSetForegroundWindow; AllowSetForegroundWindow; BroadcastSystemMessage; Owned Window; GW_OWNER; Z-Order; topmost window; WS_EX_TOPMOST; BringWindowtoTop; GetTopWindow; DeferWindowPos; GetTopWindow; GetNextWindow; SetActiveWindow; GetActiveWdinow; Active Window; WM_ACTIVEAPP, WM_ACTIVE; Disabled Window; EnableWindow; WM_ENABLE; IsEnabledWindow; Hidden Window; WM_SHOWWINDOW; IsWindowVisibile; ShowOwnedPopups; ArrangeIconicWindows; OpenIcon; ShowWindow; SetWindowPlacement; IsZoomed; IsIconic; GetWindowPlacement; WM_QUERYOPEN; WM_GETMINMAXINFO; WS_THICKFRAME; WM_SYSCOMMAND; ScreenToClient; MapWindowPoints; GetClientRect; CascadeWindow; tileWindow; WM_WINDOWPOSCHANGING; WM_WINDOWPOSCHANGED; WM_NCCALCSIZE; WM_CLOSE;
출처 : ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1042/winui/winui/windowsuserinterface/windowing/windows/aboutwindows.htm