마이그레이션에 관련 된 일반적인 문제
MFC 2.5에서 (및 높은) OLE 2 클래스의 디자인 목표 중 하나는 많은 OLE 1.0 지원에 대 한 MFC 2.0에서 장소에 동일한 아키텍처를 유지 했다. 그 결과, MFC 2.0에서 같은 OLE 클래스의 많은 여전히 MFC (COleDocument, COleServerDoc, COleClientItem, COleServerItem)의이 버전에 존재합니다. 또한, 이러한 클래스에서 Api 많은 정확히 동일 합니다. 그러나, OLE 2 이므로 OLE 1.0에서 크게 다른 내용의 일부 변경 되었습니다 기대할 수 있습니다. MFC 2.0은 지원에 잘 알고 있다면, 당신은 집에서 MFC의 2.0 지원 느낄 거 야。
기존 MFC/은 응용 프로그램을 복용 하 고 OLE 2 기능 추가,이 노트를 먼저 읽어야 한다. 이 노트 MFC/OLE 2은 기능을 이식 하는 동안 발생할 수 있습니다 및 다음 MFC 2.0에 포함 된 두 개의 응용 프로그램을 이식 하는 동안 발견 하는 문제에 설명 합니다 몇 가지 일반적인 문제를 다룹니다: MFC OLE 샘플 OCLIENT 및 HIERSVR。
MFC 문서/뷰 아키텍처는 중요 한
응용 프로그램이 MFC의 문서/뷰 아키텍처를 사용 하지 않는 응용 프로그램에 OLE 2 지원을 추가 하려는 경우 지금 문서/뷰를 이동 하는 시간이입니다. MFC의 OLE 2 클래스의 혜택의 많은 응용 프로그램에 기본 아키텍처 및 MFC의 구성 요소를 사용 하는 일단 실현만 됩니다.
MFC 아키텍처를 사용 하지 않고 서버 또는 컨테이너를 구현 하는 방법이 가능 하지만 권장 하지 않음。
고객님 대신 MFC 구현 사용
MFC "통조림 구현" CToolBar, CStatusBar, CScrollView 와 같은 클래스는 OLE 2 지원에 대 한 특별 한 경우 코드를 내장. 따라서, 응용 프로그램에서 이러한 클래스를 사용 하는 경우 그들 OLE 인식 하 게 그들 넣어 노력에서 ll 이점. 다시 말하지만, 이러한 목적을 위해 "롤-네-자신의" 클래스는 여기 가능 하다 하지만 권장 하지. MFC 소스 코드 (특히에 관해서 활성화 장소) OLE의 미세한 점 중 일부를 다루는 우수한 참조입니다 유사한 기능을 구현 해야 하는 경우。
MFC 샘플 코드 검사
OLE 기능을 포함 하는 MFC 샘플 수가 있습니다. 이러한 응용 프로그램의 각 다른 각도에서 OLE 구현:
HIERSVR -서버 응용 프로그램으로 사용 하기 위해 주로 의미 합니다. 그것은 MFC/은 응용 프로그램으로 MFC 2.0에 포함 된 및 MFC/OLE 2로 이식 되었으며 다음 OLE 2에서 사용할 수 있는 많은 OLE 기능을 구현 하는 확장。
OCLIENT -컨테이너의 관점에서 다양 한 OLE 기능을 입증 하는 것을 의미 하는 독립 실행형 컨테이너 응용 프로그램입니다. 그것은 너무 MFC 2.0에서 포팅 했다 다음 다양 한 사용자 지정 클립보드 형식 및 포함 된 항목에 링크와 같은 더 많은 고급 OLE 기능을 지원 하도록 확장 하는 고。
DRAWCLI -이 응용 프로그램 OCLIENT 처럼 기존 개체 지향 그리기 프로그램의 틀 안에서 수행 한다는 점을 제외 하면 OLE 컨테이너 지원 많이 구현 합니다. OLE 컨테이너 지원을 구현 하 고 기존 응용 프로그램에 통합할 수 있습니다 방법을 보여줍니다.
SUPERPAD -되 고 잘 독립 실행형 응용 프로그램을 뿐 아니라이 응용 프로그램은 OLE 서버 이기도 합니다. 그것을 구현 하는 서버 지원 꽤 최소 이다입니다. 특히 관심의 방법 그것 OLE 클립보드 서비스를 사용 하 여 데이터를 클립보드에 복사 하지만 Windows에 내장 된 기능을 사용 하 여 "편집" 클립보드 붙여넣기 기능을 구현 하는 컨트롤. 이것은 전통적인 Windows API 사용 뿐만 아니라 새로운 OLE Api와의 통합의 흥미로운 믹스를 보여줍니다.
샘플 응용 프로그램에 대 한 자세한 내용은 해당 MFC 샘플 "도움말"을 참조 하십시오.
사례 연구: OCLIENT MFC 2.0에서
위에서 설명한 것 처럼 OCLIENT MFC 2.0에 포함 된 및 MFC/OLE1 함께 OLE 구현. 이 응용 프로그램은 처음 변환 MFC/OLE 2 클래스를 사용 하는 단계에 대 한 설명입니다. 초기 포트 MFC/OLE 클래스를 설명 하기 위해 더 나은 완료 된 후에 다양 한 기능이 추가 되었습니다. 이러한 기능은 여기; 적용 되지 않을 것입니다. 이러한 고급 기능에 대 한 자세한 내용은 예제를 참조 하십시오。
메모nbsp;??컴파일러 오류 및 단계별 과정은 Visual c + + 2.0으로 만들어졌습니다. Visual c + + 4.0, 특정 오류 메시지와 위치 변경 되었을 수 있습니다 하지만 개념 정보 유효 하 게 유지(&N)。
지 고 실행
OCLIENT 샘플을 MFC/OLE 포트 촬영 방법은 건물과 될 뻔한 컴파일러 오류를 수정 하 여 시작 합니다. MFC 2.0에서 OCLIENT 샘플을가지고이 버전의 MFC에서 컴파일 하는 경우 거기에 없는 많은 오류를 해결 하려면 찾을 거 야. 아래 순서 대로 발생 하는 오류를 설명 하는。
컴파일 및 오류 수정
\oclient\mainview.cpp(104): 오류 C2660: '무승부': 함수 4 매개 변수를 받지 않습니다
첫 번째 오류 COleClientItem::Draw우려. MFC/은 MFC/OLE 버전은 보다 더 많은 매개 변수가 했다. 추가 매개 변수가 필요 하 고 일반적으로 NULL (이 예에서)에서 자주 하지 했다. 이 버전의 MFC 자동으로 때에 그려진 되 고 CDC 메타 파일 DC는 Lpwbounds에 대 한 값을 확인할 수 있습니다. 또한, 이후 프레임 워크 구축 "DC"의 특성에 전달 하는 Pdc에서 pFormatDC 매개 변수는 필요 이상. 이 문제를 해결 하려면 단순히 두 제거 그래서 여분의 그리기 호출에 매개 변수를 NULL。
\oclient\mainview.cpp(273): 오류 C2065: 'OLE_MAXNAMESIZE': 선언 되지 않은 식별자
\oclient\mainview.cpp(273): C2057 오류: 예상 상수 식
\oclient\mainview.cpp(280): 오류 C2664: 'CreateLinkFromClipboard': 변환할 수 없습니다 매개 변수 1 'char [1]'에서 ' enum:: tagOLERENDER '
\oclient\mainview.cpp(286): 오류 C2664: 'CreateFromClipboard': 변환할 수 없습니다 매개 변수 1 'char [1]'에서 ' enum:: tagOLERENDER '
\oclient\mainview.cpp(288): 오류 C2664: 'CreateStaticFromClipboard': 변환할 수 없습니다 매개 변수 1 'char [1]'에서 ' enum:: tagOLERENDER '
위의 오류를 항목을 나타내는 고유한 이름을 전달 될 필요한 모든 MFC/은 COleClientItem::CreateXXXX 기능 사실에서 기인 합니다. 이것은 기본 OLE API 요구 했다. 이 때문에 OLE 2 (이름을 DDE 대화에 사용 되었다) 기본 통신 메커니즘으로 DDE를 사용 하지 않는 MFC/OLE 2에서 필요 하지 않습니다. 이 문제를 해결 하려면 CreateNewName 함수를 대 한 뿐만 아니라 모든 참조를 제거할 수 있습니다. 그것은 각 MFC/OLE 함수는 예상이 버전에서는 단순히 전화에 커서를 놓고 F1 키를 누르면 하 여 쉽게。
크게 다른 또 다른 영역은 OLE 2 클립보드 처리입니다. OLE1와 클립보드와 상호 작용 하는 Api는 Windows 클립보드를 사용 합니다. OLE 2이 다른 메커니즘으로 이루어집니다. MFC/OLE1 Api는 클립보드 COleClientItem 개체를 클립보드에 복사 하기 전에 열려 가정 합니다. 이것은 더 이상 필요 하 고 모든 MFC/OLE 클립보드 작업이 실패 하면. CreateNewName에 대 한 종속성을 제거 하는 코드를 편집 하는 동안에 코드를 열리고 닫힙니다 Windows 클립보드 제거 해야。
\oclient\mainview.cpp(332): 오류 C2065: 'AfxOleInsertDialog': 선언 되지 않은 식별자
\oclient\mainview.cpp(332): 오류 C2064: 용어는 함수에 계산 하지 않습니다
\oclient\mainview.cpp(344): C2057 오류: 예상 상수 식
\oclient\mainview.cpp(347): 오류 C2039: 'CreateNewObject': 'CRectItem'의 구성원이 아닙니다
CMainView::OnInsertObject 처리기에서 이러한 오류가 발생합니다. "새 개체 삽입" 명령 처리 영역이 다른 것 들을 조금 변경 되었습니다. 이 경우 단순히 새 OLE 컨테이너 응용 프로그램에 대 한 마법사가 제공 하는 원래 구현 병합 쉽습니다. 사실, 이것은 다른 응용 프로그램 이식에 적용할 수 있는 기술입니다. MFC/은 AfxOleInsertDialog 함수를 호출 하 여 "삽입 개체" 대화 상자를 표시 합니다. 이 버전에서 COleInsertObject dialog 개체를 구성 하 고 DoModal을 호출 합니다. 또한, 새 OLE 항목 classname 문자열 대신 CLSID 를 사용 하 여 만들어집니다. 최종 결과이 같은 것을 보아야 한다
COleInsertDialog dlg;
경우 (dlg.DoModal()! = IDOK)
nbsp;? 반환;
BeginWaitCursor();
CRectItem * pItem = NULL;
시도
{
?? / / 먼저 c + + 개체를 만듭니다
?? pItem = GetDocument()-> CreateItem();
?? ASSERT_VALID(pItem);
/ / 대화 데이터에서 항목을 초기화 합니다.
?? 경우 (! dlg.CreateItem(pItem))
?????? AfxThrowMemoryException();
????????? / / 제외할 할 것입니다
?? ASSERT_VALID(pItem);
??????
?? / / 개체를 실행 하는 경우 적절 한
?? 경우 (dlg.GetSelectionType() = = COleInsertDialog::createNewItem)
?????? pItem-> DoVerb(OLEIVERB_SHOW, this);
??????
?? / / 즉시 업데이트
?? pItem-> UpdateLink();
?? pItem-> UpdateItemRectFromServer();
??????
?? / / 새로 삽입된 된 항목을 선택 설정
?? SetSelection(pItem);
?? pItem-> Invalidate();
}
CATCH (CException, e)
{/ / 항목 정리
?? 경우 (pItem! = NULL)
?????? GetDocument()-> DeleteItem(pItem);
??????????
?? AfxMessageBox(IDP_FAILED_TO_CREATE);
}
END_CATCH
??
EndWaitCursor()
메모nbsp;??삽입 새 개체는 응용 프로그램에 대 한 다를 수 있습니다)(&N):
그것은 또한 lt;afxodlgs.h를 포함 하는 데 필요한 > COleInsertObject 대화 상자 클래스 뿐 아니라 MFC 제공한 다른 표준 대화 상자에 대 한 선언을 포함 하는。
\oclient\mainview.cpp(367): 오류 C2065: 'OLEVERB_PRIMARY': 선언 되지 않은 식별자
\oclient\mainview.cpp(367): 오류 C2660: 'DoVerb': 함수 1 매개 변수를 받지 않습니다
개념은 동일 하더라도 이러한 오류 OLE 2에서 일부 OLE1 상수 변경 사실에 기인한 다. 이 경우 OLEVERB_PRIMARY 는 OLEIVERB_PRIMARY로 변경 되었습니다. OLE1 및 OLE 2에서 기본 동사는 일반적으로 실행 컨테이너에 의해 항목에 클릭할 때。
DoVerb 여분의 매개 변수를 사용 하는 이외에 이제 ? 보기에 대 한 포인터 (CView*). 이 매개 변수는만 "비주얼 편집" (또는 현재 위치에서 활성화)를 구현 하는 데 사용 됩니다. 이제 당신은 매개 변수를 NULL로 설정 때문에이 시점에서이 기능을 구현 하지는。
프레임 워크를 결코 시도-장소를 활성화 하는 있는지 확인 하려면, 당신은 다음과 같은 COleClientItem::CanActivate 을 재정의 해야
BOOL CRectItem::CanActivate()
{
nbsp;? FALSE; 반환
}
\oclient\rectitem.cpp(53): 오류 C2065: 'GetBounds': 선언 되지 않은 식별자
\oclient\rectitem.cpp(53): 오류 C2064: 용어는 함수에 계산 하지 않습니다
\oclient\rectitem.cpp(84): 오류 C2065: 'SetBounds': 선언 되지 않은 식별자
\oclient\rectitem.cpp(84): 오류 C2064: 용어는 함수에 계산 하지 않습니다(&N)
MFC/은 COleClientItem::GetBounds 및 SetBounds 쿼리하고 ( 왼쪽 및 위쪽 구성원은 항상 0) 항목의 범위를 조작 하는 데 사용 했다. COleClientItem::GetExtent 및 SetExtent, 크기 또는 CSize 대신 거래 지원 더 직접적이 MFC/OLE 2。
새로운 Setitemrecttoserver에 대 한 코드 및 UpdateItemRectFromServer 호출이 처럼 보이는:
BOOL CRectItem::UpdateItemRectFromServer()
{
nbsp; ASSERT(m_bTrackServerSize);
? CSize 크기;
? if (!.GetExtent(&size))
???? FALSE; 반환??? / 빈 /
/ / 화면 좌표에서 HIMETRIC 지도
? {
???? CClientDC screenDC(NULL);
???? screenDC.SetMapMode(MM_HIMETRIC);
???? screenDC.LPtoDP(&size);
? }
? / / 그냥 항목 크기 설정
? 경우 (m_rect.Size()! = 크기)
? {
???? / / 크기/위치를 무효화
???? Invalidate();
???? m_rect.right = m_rect.left + size.cx;
???? m_rect.bottom = m_rect.top + size.cy;
???? / / 뿐 새로운 크기/위치
???? Invalidate();
? }
? TRUE를 반환 합니다.
}
BOOL CRectItem::SetItemRectToServer()
{
? / / 포함된 된 항목에 대 한 공식 범위 설정
? CSize 크기 m_rect =.Size();
? {
???? CClientDC screenDC(NULL);
???? screenDC.SetMapMode(MM_HIMETRIC);
???? screenDC.DPtoLP(&size);
? }
? 시도
? {
???? SetExtent(size);? / 대기 할 수 있습니다
? }
? CATCH (CException, e)
? {
???? FALSE; 반환? / / 링크 Setbounds를 허용 하지 것입니다
? }
? END_CATCH
? TRUE를 반환 합니다.
}
\oclient\frame.cpp(50): 오류 C2039: 'InWaitForRelease': 'COleClientItem'의 구성원이 아닙니다
\oclient\frame.cpp(50): 오류 C2065: 'InWaitForRelease': 선언 되지 않은 식별자
\oclient\frame.cpp(50): 오류 C2064: 용어는 함수에 계산 하지 않습니다
본질적으로 대부분의 경우에서 비동기 이었기 MFC/은 컨테이너의 동기식 API 호출 서버에 시뮬레이션을 했다. 그것은 사용자 로부터 명령을 처리 하기 전에 진행 중인 뛰어난 비동기 호출에 대 한 확인 하는 데 필요한 했다. MFC/은 이렇게 COleClientItem::InWaitForRelease 함수를 제공 합니다. MFC/OLE 2이 필요 하지 않습니다, 그래서 Cmainframe에 OnCommand 재정의 모두 함께 제거할 수 없습니다.
OCLIENT는 컴파일하고 링크 하는 시점에서。
기타 필요한 사항
그러나 몇 가지가 다 하지 않습니다 실행, OCLIENT 계속 됩니다. 나중에 대신의 이제 이러한 문제를 해결 하는 것이 좋습니다.
모두의 1 번째 그것은 OLE 라이브러리를 초기화 하는 데 필요한입니다. InitInstance 에서 AfxOleInit 를 호출 하 여 이렇게:
if (!.AfxOleInit())
{
AfxMessageBox ("실패를 OLE 라이브러리를 초기화");
FALSE; 반환
}
또한 매개 변수 목록 변경 내용에 대 한 가상 함수를 확인 하는 좋은 아이디어 이다. 하나 같은 기능은 COleClientItem::OnChange, 모든 MFC/OLE 컨테이너 응용 프로그램에서 재정의 합니다. 온라인 도움말을 보고, 추가 'DWORD dwParam' 추가 되었습니다 보자. 새로운 CRectItem::OnChange 다음과 같습니다.
void CRectItem::OnChange (OLE_NOTIFICATION wNotification, DWORD dwParam)
{
경우 (m_bTrackServerSize amp; 및
?????? !UpdateItemRectFromServer())
{
?? / / 빈 개체
?? 경우 (wNotification = OLE_CLOSED)
?? {
???? / / 개체-에 대 한 수신 데이터가 그것을 파괴
???? 주장 (!.IsVisible());
???? GetDocument()-> DeleteItem(this);
???? 반환;?? / / 아무 업데이트 (항목은 사라지고 지금은)
?? }
}
경우 (wNotification! = OLE_CLOSED)
???? Dirty();
Invalidate();? / / 어떤 변화를 다시 그리기 발생
}
MFC/은 컨테이너 응용 프로그램 COleClientDoc에서 문서 클래스를 파생 합니다. MFC/OLE 2에서이 클래스 제거 되었으며 COleDocument (이 새로운 조직을 쉽게 컨테이너/서버 응용 프로그램)으로 대체 합니다. 거기에는 # define COleClientDoc COleDocument MFC/OLE 2 OCLIENT 같은 MFC/은 응용 프로그램의 이식 단순화 하에 매핑하는. COleDocument COleClientDoc 에 의해 제공 된 제공 하지 기능 중 하나는 표준 명령 메시지 맵 항목입니다. 이 또한 하지 COleDocument (직접)를 사용 하는 서버 응용 프로그램 수행 하지 않습니다 그들과 함께 이러한 명령 처리기의 오버 헤드가 발생 하지 않는 한 그들은 컨테이너/서버 응용 프로그램은 이루어집니다. CMainDoc 메시지 맵에 다음 항목을 추가 해야 하는 등:
ON_UPDATE_COMMAND_UI (ID_EDIT_PASTE, OnUpdatePasteMenu)
ON_UPDATE_COMMAND_UI (ID_EDIT_PASTE_LINK, OnUpdatePasteLinkMenu)
ON_UPDATE_COMMAND_UI (ID_OLE_EDIT_LINKS, OnUpdateEditLinksMenu)
ON_COMMAND (ID_OLE_EDIT_LINKS, COleDocument::OnEditLinks)
ON_UPDATE_COMMAND_UI (ID_OLE_VERB_FIRST, OnUpdateObjectVerbMenu)
ON_UPDATE_COMMAND_UI (ID_OLE_EDIT_CONVERT, OnUpdateObjectVerbMenu)
ON_COMMAND (ID_OLE_EDIT_CONVERT, OnEditConvert)
이러한 명령의 모든 구현입니다 귀하의 문서에 대 한 기본 클래스 COleDocument, 인。
이 시점에서, OCLIENT 기능 OLE 컨테이너 응용 프로그램입니다. (OLE1 또는 OLE 2) 어떤 종류의 항목을 삽입 하는 것이 불가능 합니다. 현재 위치에서 활성화를 사용 하는 데 필요한 코드를 구현 하는 이후 항목이 많이 ole1와 같은 별도 창에서 편집 됩니다. 다음 섹션에서는 제자리에서 편집 ("비주얼 편집"이 라고도 함) 수 있도록 필요한 사항을 설명 합니다.
"비주얼 편집" 추가
OLE의 가장 흥미로운 기능 중 하나는 장소에 활성화 (또는 "비주얼 편집")입니다. 이 기능은 서버 응용 프로그램을 일부 컨테이너의 사용자 인터페이스를 통해 사용자에 대 한 보다 원활한 편집 인터페이스를 제공 합니다. OCLIENT 현재 위치에서 활성화를 구현 하려면 몇 가지 특별 한 리소스 뿐만 아니라, 몇 가지 추가 코드 추가 해야 합니다. 이러한 리소스와 코드는 일반적으로 AppWizard 제공한-사실, 많은 여기에 코드의 "컨테이너" 지원 신선한 AppWizard 응용 프로그램에서 직접 빌린 했다。
첫째로 모두의 그것은 내부 활성 항목 때 사용할 메뉴 리소스를 추가 하는 데 필요한. IDR_OCLITYPE 리소스를 복사 하 고 파일 및 창 팝업을 제외한 모든 제거 하 여 Visual c + +에서이 추가 메뉴 리소스를 만들 수 있습니다. 그룹 분리를 나타내는 파일 및 창 팝업 사이 두 구분선 삽입 됩니다 (처럼 보이게 한다: 파일 | 창)입니다. 이러한 구분은 무엇을 의미 하 고 서버 및 컨테이너 메뉴 병합 방법에 대 한 자세한 내용은 "메뉴 및 리소스: 메뉴 병합" OLE 2 수업에서 을 참조 하십시오.
이 메뉴를 생성 하 고 나면 프레임 워크 그들에 대해 알고 있도록 해야 합니다. 이 문서 서식 파일에 대 한 귀하의 Initinstance에서 문서 서식 파일 목록에 추가 하기 전에 CDocTemplate::SetContainerInfo 를 호출 하 여 이루어집니다. 문서 서식 파일을 등록 하는 새로운 코드 다음과 같이
CDocTemplate * pTemplate 새로운 CMultiDocTemplate (=
nbsp;? IDR_OLECLITYPE,
?? RUNTIME_CLASS(CMainDoc),
?? RUNTIME_CLASS(CMDIChildWnd) / / 표준 MDI 자식 프레임
?? RUNTIME_CLASS(CMainView));
pTemplate-> SetContainerInfo(IDR_OLECLITYPE_INPLACE);
AddDocTemplate(pTemplate)
IDR_OLECLITYPE_INPLACE 리소스는 Visual c + +에서 만든 특별 한 장소에서 리소스。
현재 위치에서 활성화를 사용 하기 위해서는 몇 가지 모두 CView 에서 변경 해야 하는 (CMainView) 파생 클래스와 COleClientItem 파생 클래스 (CRectItem). Appwizard에서 제공 하는 모든 이러한 재정의 하 고 구현 대부분 기본 AppWizard 응용 프로그램에서 직접 올 것 이다。
이 포트의 첫 번째 단계에서 현재 위치에서 활성화는 전적으로 COleClientItem::CanActivate를 재정의 하 여 사용할 수 없습니다. 이 재정의 위치에서 활성화를 허용 하도록 제거 되어야 한다. 또한, 보기는 위치에서 활성화 하는 데 필요한만 제공 하기 때문에 NULL DoVerb (두 가지 그들의) 모든 호출에 전달 되었습니다. 현재 위치에서 활성화를 완벽 하 게 구현 하려면 올바른 보기 DoVerb 호출에 전달 하는 데 필요한입니다. 이러한 호출 중 하나는 CMainView::OnInsertObject 에
pItem-gt; DoVerb(OLEIVERB_SHOW, this)(&G)
다른 CMainView::OnLButtonDblClk 에
m_pSelection-gt; DoVerb(OLEIVERB_PRIMARY, this)(&G)
그것은 COleClientItem::OnGetItemPosition를 재정의 하는 데 필요한입니다. 이 서버 위치 항목이 내부에서 활성화 하는 경우 컨테이너의 창을 기준으로 윈도우를 알려줍니다. OCLIENT에 대 한 구현은 사소한
void CRectItem::OnGetItemPosition (CRectamp; rPosition)
{
?? rPosition = m_rect;
}(&A)
무슨 "에서-장소 크기 조정." 라고 대부분의 서버 구현 이 크기를 사용자가 항목을 편집 하는 동안 이동 서버 창이 있습니다. 컨테이너 이동 또는 일반적으로 창 크기 조정 위치와 컨테이너 문서 자체 내에서 크기를 영향을 주므로이 작업에 참여 해야 합니다. OCLIENT의 구현을 동기화 새 위치와 크기는 m_rect에 의해 유지 하는 내부 직사각형。
BOOL CRectItem::OnChangeItemPosition(const CRectamp; rectPos)
{
?? ASSERT_VALID(this);
if (!.COleClientItem::OnChangeItemPosition(rectPos))
?????? FALSE; 반환
Invalidate();
?? m_rect = rectPos;
?? Invalidate();
?? GetDocument()-> SetModifiedFlag();
TRUE를 반환 합니다.
}
이 시점에서, 제자리에서 활성화 될 때 활성 항목 이동 및 크기 조정 처리 한 항목 수 있도록 충분 한 코드 하지만 편집 세션을 종료할 수 있도록 코드가 발생 하지 않습니다. 일부 서버이 기능을 제공이 스스로 esc 키를 처리 하 여, 비록 컨테이너 항목을 비활성화 하려면 두 가지 방법으로 제공 하는 것이 좋습니다: 항목, 밖으로 클릭 하 여 (1) 및 (2) esc 키를 침 으로써。
탈출에 대 한 키 추가 VK_ESCAPE 키를 명령에 매핑하는 Visual c + + 가속기 ID_CANCEL_EDIT에 추가 됩니다 자원. 이 명령에 대 한 처리기는 다음과 같습니다.:
/ / 다음 명령 처리기는 표준 제공
/ / 키보드 제자리에서 취소 하려면 사용자 인터페이스
/ / session.void CMainView::OnCancelEdit() 편집
{
nbsp;? / /이 보기에 내부 활성 항목을 닫습니다.
?? COleClientItem * pActiveItem = GetDocument()-> GetInPlaceActiveItem(this);
?? 경우 (pActiveItem! = NULL)
?????? pActiveItem-> close ();
?? ASSERT(GetDocument()-> GetInPlaceActiveItem(this) = = NULL);
}
어디 사용자 항목 바깥쪽을 클릭할 경우를 처리 하려면 다음 코드에 추가한 CMainView::SetSelection 의 시작
경우 (pNewSel! = m_pSelection | pNewSel = = NULL)
{
nbsp;? COleClientItem * pActiveItem = GetDocument()-> GetInPlaceActiveItem(this);
?? 경우 (pActiveItem! = NULL & & pActiveItem! = pNewSel)
?????? pActiveItem-> close ();
}
?& nbsp
항목 장소에서 활성 때 포커스가 있어야 합니다. 이 경우 확인 하는 포커스는 항상 활성 항목에 옮겨진 보기 포커스를 받을 때 OnSetFocus 처리:
/ / OnSetFocus 및 Onsize에 대 한 특수 처리 필요 / 때 개체가 제자리에서 편집 하 고 /.
void CMainView::OnSetFocus (CWnd * pOldWnd)
{
nbsp;? COleClientItem * pActiveItem = GetDocument()-> GetInPlaceActiveItem(this);
?? 경우 (pActiveItem! = NULL & &
?? pActiveItem-> GetItemState() = COleClientItem::activeUIState)
?? {
?????? / / 필요한 경우 동일한 보기가이 항목에 포커스를 설정 하려면
?????? CWnd * pWnd = pActiveItem-> GetInPlaceWindow();
?????? 경우 (pWnd! = NULL)
?????? {
?????????? pWnd-> SetFocus();? / / 기본 클래스를 호출 하지 마십시오
?????????? 반환;
?????? }
?? }
CView::OnSetFocus(pOldWnd);
}
보기 크기를 조정할 때 클리핑 사각형 변경 된 활성 항목을 통보 해야 합니다. 이렇게 OnSize 에 대 한 처리기를 제공 하려면:
void CMainView::OnSize (UINT nType, int cx int cy)
{
nbsp;? CView::OnSize (nType, cx, cy);
?? COleClientItem * pActiveItem = GetDocument()-> GetInPlaceActiveItem(this);
?? 경우 (pActiveItem! = NULL)
?????? pActiveItem-> SetItemRects();
}
사례 연구: HIERSVR MFC 2.0에서
HIERSVR 도 MFC 2.0에 포함 된 및 MFC/OLE1 함께 OLE 구현. 이 노트는이 응용 프로그램은 처음 변환 MFC/OLE 2 클래스를 사용 하는 단계를 간략하게 설명 합니다. 초기 포트 MFC/OLE 2 클래스를 설명 하기 위해 더 나은 완료 된 후에 다양 한 기능이 추가 되었습니다. 이러한 기능은 여기; 적용 되지 않을 것입니다. 이러한 고급 기능에 대 한 자세한 내용은 예제를 참조 하십시오。
메모nbsp;??컴파일러 오류 및 단계별 과정은 Visual c + + 2.0으로 만들어졌습니다. Visual c + + 4.0, 특정 오류 메시지와 위치 변경 되었을 수 있습니다 하지만 개념 정보 유효 하 게 유지(&N)。
지 고 실행
HIERSVR 샘플을 MFC/OLE 포트 촬영 방법은 건물과 될 뻔한 컴파일러 오류를 수정 하 여 시작 합니다. MFC 2.0에서 HIERSVR 샘플을가지고이 버전의 MFC에서 컴파일 하는 경우는 없습니다 (비록 OCLIENT 샘플을 보다 더 있다)를 해결 하기 위해 많은 오류를 찾을 거 야. 아래는 일반적으로 발생 하는 순서에 있는 오류를 설명 하는。
컴파일 및 오류 수정
\hiersvr\hiersvr.cpp(83): 오류 C2039: 'RunEmbedded': 'COleTemplateServer'의 구성원이 아닙니다
이 첫 번째 오류는 서버에 대 한 훨씬 더 큰 문제가 InitInstance 함수를 지적 한다. OLE 서버에 필요한 초기화 아마 MFC/은 응용 프로그램 실행을 하 게 할 것 이다 가장 큰 변화 중 하나입니다. 할 가장 좋은 것은 AppWizard OLE 서버에 대 한 만듭니다 확인 하 고 적절 한 코드를 수정 합니다. 명심에서 해야 할 몇 가지 포인트를 운항:
그것은 AfxOleInit 를 호출 하 여 OLE 라이브러리를 초기화 하는 데 필요한
서버 리소스 핸들 및 CDocTemplate 생성자로 설정할 수 없는 런타임 클래스 정보를 설정 하려면 문서 서식 파일 개체에 대해 Setserverinfo를 호출。
명령줄에 /Embedding 있으면 응용 프로그램의 주 창 표시 안 함。
귀하의 문서에 대 한 GUID 필요 합니다. 귀하의 문서 유형 (128 비트)에 대 한 고유 식별자입니다. AppWizard 당신을 위해 하나를 만들 것입니다-그래서 만약 당신이 여기 설명 하는 기술을 사용 하 여 새 마법사 생성 서버 응용 프로그램에서 새 코드를 복사, 당신은 단순히 "훔칠 수" 해당 응용 프로그램에서 GUID. 그렇지 않으면 해당 GUIDGEN을 사용할 수 있습니다.BIN 디렉터리에 EXE 유틸리티。
그것은 "" COleTemplateServer 개체에 연결할 문서 서식 파일 COleTemplateServer::ConnectTemplate 를 호출 하 여 필요한。
응용 프로그램이 독립 실행형 실행 때 시스템 레지스트리를 업데이트 합니다. 사용자가 이동 하는 경우이 방법이 합니다.EXE 응용 프로그램에 대 한 새 위치를 가리키도록 Windows 시스템 등록 데이터베이스를 업데이 트 됩니다 새로운 위치에서 실행。
AppWizard InitInstance에 대 한 생성에 따라 이러한 변경 내용을 모두 적용 한 후 HIERSVR 위해 InitInstance (및 관련된 GUID) 읽어야 한다 다음과 같습니다.:
// this is the GUID for HIERSVR documents
static const GUID BASED_CODE clsid =
{ 0xA0A16360L, 0xC19B, 0x101A, { 0x8C, 0xE5, 0x00, 0xDD, 0x01, 0x11, 0x3F, 0x12 } };
/////////////////////////////////////////////////////////////////////////////
// COLEServerApp initialization
BOOL COLEServerApp::InitInstance()
{
// OLE 2 initialization
if (!AfxOleInit())
{
AfxMessageBox("Initialization of the OLE failed!");
return FALSE;
}
// Standard initialization
LoadStdProfileSettings(); // Load standard INI file options
// Register document templates
CDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_HIERSVRTYPE,
RUNTIME_CLASS(CServerDoc),
RUNTIME_CLASS(CMDIChildWnd),
RUNTIME_CLASS(CServerView));
pDocTemplate->SetServerInfo(IDR_HIERSVRTYPE_SRVR_EMB);
AddDocTemplate(pDocTemplate);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
SetDialogBkColor(); // gray look
// enable file manager drag/drop and DDE Execute open
m_pMainWnd->DragAcceptFiles();
EnableShellOpen();
m_server.ConnectTemplate(clsid, pDocTemplate, FALSE);
COleTemplateServer::RegisterAll();
// try to launch as an OLE server
if (RunEmbedded())
{
// "short-circuit" initialization -- run as server!
return TRUE;
}
m_server.UpdateRegistry();
RegisterShellFileTypes();
// not run as OLE server, so show the main window
if (m_lpCmdLine[0] == '\0')
{
// create a new (empty) document
OnFileNew();
}
else
{
// open an existing document
OpenDocumentFile(m_lpCmdLine);
}
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
return TRUE;
}
위의 코드 IDR_HIERSVRTYPE_SRVR_EMB 새 리소스 ID를 가리킵니다을 확인할 수 있습니다. 이것은 또 다른 컨테이너에 포함 된 문서를 편집할 때 사용 되는 메뉴 리소스입니다. MFC/OLE1 메뉴 항목이 포함 된 항목을 편집 하려면 특정 즉석에서 수정 되었습니다. 파일 기반 문서를 편집 하는 대신 포함 된 항목을 편집할 때 완전히 다른 메뉴 구조를 사용 하 여 훨씬 쉽게 이러한 두 가지 별도 모드에 대 한 다른 사용자 인터페이스를 제공 합니다. 완전히 별도 메뉴 리소스는 포함 된 개체에서 장소를 편집할 때 사용 됩니다 나중에 살펴보겠지만。
이 리소스를 만들려면 Visual c + + 리소스 스크립트를 로드 하 고 기존 IDR_HIERSVRTYPE 메뉴 리소스를 복사 합니다. 새 리소스를 IDR_HIERSVRTYPE_SRVR_EMB (이것이 Appwizard를 사용 하 여 동일한 명명 규칙) 이름을 바꿉니다. 다음 "파일 업데이트"; "파일 저장"을 변경 명령 ID ID_FILE_UPDATE제공 합니다. 또한 변경 "파일 저장" 파일 복사 "으로" 저장; 명령 ID ID_FILE_SAVE_COPY_AS제공 합니다. 이러한 명령은 모두의 구현을 제공 하는 프레임 워크。
\hiersvr\svritem.h(60): 오류 C2433: 'OLESTATUS': ', ' 가상에서 허용 되지 않습니다 데이터 선언
\hiersvr\svritem.h(60): 오류 C2501: 'OLESTATUS': decl 지정자를 누락
\hiersvr\svritem.h(60): 오류 C2146: 구문 오류: 누락 된 'OnSetData' 식별자 앞에 ';'
\hiersvr\svritem.h(60): 오류 C2061: 구문 오류: 식별자 'OLECLIPFORMAT'
\hiersvr\svritem.h(60): 오류 C2501: 'OnSetData': decl 지정자를 누락
OnSetData, 재정의에서 발생 하는 때문에 OLESTATUS 형식으로 참조 하는 오류 수가 있습니다. OLESTATUS 은 오류를 반환 하는 방법 이었다. 이 MFC 일반적으로 변환 된 HRESULT 오류가 있는 COleException 비록 HRESULT OLE 2에서 변경 되었습니다. 이 특정 경우에 OnSetData 의 재정의 이므로 더 이상 필요 할 가장 쉬운 것은 그것을 제거 하。
\hiersvr\svritem.cpp(30): 오류 C2660: 'COleServerItem::COleServerItem': 함수 1 매개 변수를 받지 않습니다
COleServerItem 생성자 추가 'BOOL' 매개 변수를 사용 합니다. 이 플래그는 COleServerItem 개체에 대해 메모리 관리를 수행 하는 방법을 결정 합니다. 프레임 워크 이러한 개체의 메모리 관리를 처리 하는 true로 설정 하 여-그들은 더 이상 필요한 경우 삭제 합니다. HIERSVR CServerItem ( COleServerItem에서 파생) 개체 사용 하 여 네이티브 데이터의 일환으로이 플래그를 false로 설정 거 야. 그러면 각 서버 항목은 삭제 되는 시기를 결정 하는 HIERSVR。
\hiersvr\svritem.cpp(44): 오류 C2259: 'CServerItem': 추상 클래스를 인스턴스화하는 불법 시도
\hiersvr\svritem.cpp(44): 오류 C2259: 'CServerItem': 추상 클래스를 인스턴스화하는 불법 시도
이러한 오류에서 알 수 있듯이 일부 ' 순수 가상 ' 함수는 Cserveritem에서 재정의 되지 않습니다. 가장 가능성이이 Ondraw의 매개 변수 목록에서 변경 된 사실에 의해 발생 합니다. 이 오류를 해결 하려면 CServerItem::OnDraw 같이 변경 (뿐만 아니라 svritem.h에 선언)
BOOL CServerItem::OnDraw(CDC* pDC, CSizeamp; rSize)
{
?? / / 노드를 그리는 OLE에서 요청
?? pDC-> SetMapMode(MM_TEXT); / / 항상 픽셀
?? 반환 DoDraw (pDC, CPoint(0,0), FALSE);
}
새 매개 변수는 'rSize'. 이 편리 하 게 하는 경우 드로잉의 크기에 채울 수 있습니다. 이 크기는 HIMETRIC여야 합니다. 이 경우 그것은 아니므로이 값을 채우기 위해 편리 하 게 범위를 검색 하려면 OnGetExtent 를 호출 하는 프레임 워크. 그 작동, OnGetExtent 구현 해야:
BOOL CServerItem::OnGetExtent(DVASPECT dwDrawAspect, CSizeamp; rSize)
{
?? 경우 (dwDrawAspect! = DVASPECT_CONTENT)
?????? COleServerItem::OnGetExtent (dwDrawAspect, rSize); 반환
??????
?? rSize = CalcNodeSize();
?? TRUE를 반환 합니다.
}
\hiersvr\svritem.cpp(104): 오류 C2065: 'm_rectBounds': 선언 되지 않은 식별자
\hiersvr\svritem.cpp(104): 오류 C2228: 왼쪽의 '.SetRect' 클래스/구조체/union 형식이 있어야 합니다.
\hiersvr\svritem.cpp(106): 오류 C2664: ' void __pascal __far DPtoLP (구조체: __far tagPOINT *, int) __far const ': 매개 변수 1에서 변환할 수 없습니다 ' __far int *'을 ' 구조체: tagPOINT __far *'(&A)
Cserveritem::calcnodesize에서 함수 항목 크기 HIMETRIC 변환 하 고 m_rectBounds에 저장 됩니다. 불법 체류자 'm_rectBounds' 소속 COleServerItem 존재 하지 않습니다 ( m_sizeExtent에 의해 부분적으로 대체 되었습니다 하지만 OLE 2에서이 멤버 m_rectBounds ole1에서 했던 것 보다 약간 다른 사용을 하고있다). 이 멤버 변수를 HIMETRIC 크기를 설정 하는 대신이 반환 합니다. 이 반환 값은 OnGetExtent, 이전 구현에 사용。
CSize CServerItem::CalcNodeSize()
{
nbsp;? CClientDC dcScreen(NULL);
m_sizeNode (m_strDescription, dcScreen.GetTextExtent =
???? m_strDescription.GetLength());
?? m_sizeNode + = CSize (CX_INSET * 2, CY_INSET * 2);
/ / 제안된 HIMETRIC 크기 설정
?? CSize 크기 (m_sizeNode.cx, m_sizeNode.cy);
?? dcScreen.SetMapMode(MM_HIMETRIC);
?? dcScreen.DPtoLP(&size);
?? 반환 크기;
}
Cserveritem는 또한 COleServerItem::OnGetTextData를 재정의합니다. 이 함수는 MFC/OLE에서 사용 되지 않습니다 및 다른 메커니즘으로 대체 됩니다. MFC OLE 샘플의 MFC 3.0 버전 HIERSVR COleServerItem::OnRenderFileData를 재정의 하 여이 기능을 구현 합니다. 이 기능은 OnGetTextData 재정의 제거할 수 있도록이 기본 포트에 대 한 중요 하지 않습니다.
해결 되지 않은 svritem.cpp에서 더 많은 오류가 있습니다. 그들은 "진짜" 오류 ? 이전 오류에 의해 발생 하는 그냥 오류。
\hiersvr\svrview.cpp(325): 오류 C2660: 'CopyToClipboard': 함수 2 매개 변수를 받지 않습니다
COleServerItem::CopyToClipboard 는 더 이상 'bIncludeNative' 플래그를 지원합니다. 네이티브 데이터 (서버 항목의 Serialize 함수에 의해 쓰여진 데이터)는 첫 번째 매개 변수를 제거 하는 그래서 항상 복사 됩니다. 또한, CopyToClipboard 는 FALSE를 반환 하는 대신 오류가 발생 하는 경우 예외가 throw 됩니다. Cserverview::oneditcopy에 대 한 코드를 다음과 같이 변경
void CServerView::OnEditCopy()
{
nbsp;? 경우 (m_pSelectedNode = = NULL)
?????? AfxThrowNotSupportedException();
??????
?? 시도
?? {
?????? m_pSelectedNode-> CopyToClipboard(TRUE);
?? }
?? CATCH_ALL(e)
?? {
?????? AfxMessageBox ("클립보드에 복사 하지 못했습니다.");
?? }
?? END_CATCH_ALL}
HIERSVR의 MFC 2.0 버전의 편집에서 발생 하는 OCLIENT의 동일한 버전을 거기 보다 더 많은 오류 했다, 비록 거기 실제로 적은 변경。
이 시점에서 HIERSVR 컴파일 및 링크 되며 다음 구현할 제자리에서 편집 기능을 하지 않고 OLE 서버로 작동。
"비주얼 편집" 추가
"비주얼 편집" (또는 현재 위치에서 활성화)이 서버 응용 프로그램을 추가 하려면 거기에 몇 가지 당신이 알아서 해야 합니다.:
메뉴 리소스 만들기 쉽습니다. Visual c + + 실행, 메뉴 리소스 IDR_HIERSVRTYPE IDR_HIERSVRTYPE_SRVR_IP 라는 메뉴 리소스를 복사 합니다. 편집 및 도움말 메뉴 팝업만 왼쪽 메뉴를 수정 합니다. 편집 및 도움말 메뉴 사이 메뉴에 두 개의 구분 기호를 추가 (처럼 보이게 한다: 편집 | | 도움말)입니다. 이러한 구분은 무엇을 의미 하 고 서버 및 컨테이너 메뉴 병합 방법에 자세한 내용은 "메뉴 및 리소스: 메뉴 병합" OLE 2 수업에서 을 참조 하십시오.
하위 집합 도구 모음에 대 한 비트맵 "서버" 옵션을 체크 한 신선한 생성 AppWizard 응용 프로그램에서 복사 하 여 쉽게 만들 수 있습니다. 다음 Visual c + +가이 비트맵을 가져올 수 있습니다. 비트맵 IDR_HIERSVRTYPE_SRVR_IP ID를 제공 해야 합니다.
서버 지원 뿐만 아니라 생성 AppWizard 응용 프로그램에서 COleIPFrameWnd 에서 파생 된 클래스를 복사할 수 있습니다. 두 파일, IPFRAME를 복사 합니다.CPP 및 IPFRAME입니다.H를 프로젝트에 추가 합니다. LoadBitmap 호출 IDR_HIERSVRTYPE_SRVR_IP, 이전 단계에서 만든 비트맵 가리키는 있는지 확인 하십시오.
이제는 모든 새로운 리소스 및 클래스를 만든 프레임 워크 들에 대해 알고 (그리고 알고 이제이 응용 프로그램에서 내부 편집 지원)는 필요한 코드를 추가 합니다. InitInstance 함수에서 호출 하는 SetServerInfo 를 몇 가지 더 많은 매개 변수를 추가 하 여 이루어집니다.:
pDocTemplate-gt;SetServerInfo (IDR_HIERSVRTYPE_SRVR_EMB,
?? IDR_HIERSVRTYPE_SRVR_IP, RUNTIME_CLASS(CInPlaceFrame))(&G)
그것은 지금 위치에서 실행할 준비가 또한 위치에서 활성화를 지 원하는 모든 컨테이너에. 하지만, 거기에 하나의 사소한 버그 코드에 여전히 숨어 있다. HIERSVR는 마우스 오른쪽 단추를 누를 때 표시 되는 상황에 맞는 메뉴를 지원 합니다. 이 메뉴는 HIERSVR를 완전히 열고 하지만 포함-제자리에서 편집할 때 작동 하지 않습니다 때 작동 합니다. 이유는이 한 줄의 코드에서 Cserverview::onrbuttondown에 고정할 수 있습니다.
pMenu-gt;TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
?? point.x, point.y, AfxGetApp()-> m_pMainWnd)
통지에 대 한 참조가 AfxGetApp ()-gt; m_pMainWnd. 서버가 원위치 활성화 때 주 창 하고있다 m_pMainWnd 설정 되어 하지만 보통 보이지 않습니다. 또한,이 창 MDI 프레임 창 서버를 완전히 열고 나타난다 또는 독립 실행형 실행 하는 응용 프로그램의 메인 윈도우를 말합니다. 활성 프레임 창을 참조 하지 않습니다-때 현재 위치에서 활성화는 COleIPFrameWnd에서 파생 된 프레임 창. 제자리에서 편집 하 고,이 버전의 MFC AfxGetMainWnd새 함수를 추가 하는 경우에 올바른 활성 창을 얻으려면. 대신이 함수를 사용 해야 합니다 일반적으로, AfxGetApp()-> m_pMainWnd. 이 코드를 다음과 같이 변경 해야:
pMenu-gt;TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
?? point.x, point.y, AfxGetMainWnd(&))
이제 OLE 서버를 최소 기능 위치에서 활성화에 사용할 수 있다. 하지만 아직도 많은 기능 MFC/은에서 사용할 수 없었던 MFC/OLE 2와 함께 사용할 수 있습니다. 구현 하 고 있는 기능에 더 많은 아이디어에 대 한 HIERSVR 샘플을 참조 하십시오. HIERSVR를 구현 하는 기능 중 일부는 다음과 같습니다.:
MFC 3.0에서 HIERSVR 샘플은 또한 자사의 서버 항목에 대 한 약간 다른 디자인을 사용합니다. 이 메모리를 절약 하는 데 도움이 하 고 귀하의 링크를 좀 더 유연 하 게 만드는. HIERSVR의 2.0 버전과 트리의 각 노드에 한 COleServerItem. COleServerItem 이러한 노드의 각각에 대 한 엄격 하 게 필요한 것 보다 조금 더 많은 오버 헤드를 운반 하지만 COleServerItem 각 활성 링크에 대 한 필요 합니다. 하지만 대부분의 부분을 들어, 어떤 주어진된 시간에 매우 몇 가지 활성 링크 있습니다. 이 보다 효율적으로 만들려고,이 버전의 MFC에서 HIERSVR COleServerItem에서 노드를 분리 합니다. 그것은 CServerNode 및 CServerItem 클래스 있다. CServerItem ( COleServerItem에서 파생)만 필요에 따라 만들어집니다. 컨테이너 (또는 컨테이너)는 특정 노드에 특정 링크를 사용 하 여 중지, 일단은 Cservernode와 관련 된 CServerItem 개체가 삭제 됩니다. 이 디자인은 더 효율적이 고 더 유연 합니다. 유연성의 여러 선택 링크를 다룰 때 온다. HIERSVR의 두 가지 버전 중 어느 다중 선택을 지원 하지만 추가 (와 같은 선택 하려면 링크를 지원 하기 위해) 훨씬 더 쉽게 될 것 이라고 HIERSVR COleServerItem 원시 데이터에서 분리 된 이후 MFC 3.0 버전。
기술 노트 번호로 |nbsp; 카테고리별 기술 노트(&N)