클립보드를 사용 하 여
이 섹션에는 다음 작업에 대한 코드 샘플이 있습니다.
잘라내기, 복사 및 붙여넣기 명령 구현
이 섹션에서는 표준 잘라내기, 복사 및 붙여넣기 명령이 애플리케이션에서 구현되는 방법을 설명합니다. 이 섹션의 예제에서는 이러한 메서드를 사용하여 등록된 클립보드 형식, 형식 및 형식을 사용하여 데이터를 클립보드에 CF_OWNERDISPLAY
배치합니다 CF_TEXT
. 등록된 형식은 레이블이라고 하는 사각형 또는 타원 텍스트 창을 나타내는 데 사용됩니다.
데이터 선택
정보를 클립보드에 복사하려면 먼저 복사하거나 잘라낼 특정 정보를 선택해야 합니다. 애플리케이션은 사용자가 문서 내에서 정보를 선택할 수 있는 수단과 선택한 데이터를 나타내는 일종의 시각적 피드백을 제공해야 합니다.
편집 메뉴 만들기
애플리케이션은 편집 메뉴 명령에 대한 표준 키보드 가속기가 포함된 가속기 테이블을 로드해야 합니다.
TranslateAccelerator
가속기가 적용되려면 함수를 애플리케이션의 메시지 루프에 추가해야 합니다. 키보드 가속기에 대한 자세한 내용은 키보드 가속기를 참조하세요.
WM_INITMENUPOPUP
메시지 처리
사용자가 특정 시간에 모든 클립보드 명령을 사용할 수 있는 것은 아닙니다. 애플리케이션은 WM_INITMENUPOPUP
사용 가능한 명령에 메뉴 항목을 사용하도록 설정하고 사용할 수 없는 명령을 사용하지 않도록 설정하기 위해 메시지를 처리해야 합니다.
다음은 Label이라는 애플리케이션의 경우입니다 WM_INITMENUPOPUP
.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
함수는 InitMenu
다음과 같이 정의됩니다.
void WINAPI InitMenu(HMENU hmenu)
{
int cMenuItems = GetMenuItemCount(hmenu);
int nPos;
UINT id;
UINT fuFlags;
PLABELBOX pbox = (hwndSelected == NULL) ? NULL :
(PLABELBOX) GetWindowLong(hwndSelected, 0);
for (nPos = 0; nPos < cMenuItems; nPos++)
{
id = GetMenuItemID(hmenu, nPos);
switch (id)
{
case IDM_CUT:
case IDM_COPY:
case IDM_DELETE:
if (pbox == NULL || !pbox->fSelected)
fuFlags = MF_BYCOMMAND | MF_GRAYED;
else if (pbox->fEdit)
fuFlags = (id != IDM_DELETE && pbox->ichSel
== pbox->ichCaret) ?
MF_BYCOMMAND | MF_GRAYED :
MF_BYCOMMAND | MF_ENABLED;
else
fuFlags = MF_BYCOMMAND | MF_ENABLED;
EnableMenuItem(hmenu, id, fuFlags);
break;
case IDM_PASTE:
if (pbox != NULL && pbox->fEdit)
EnableMenuItem(hmenu, id,
IsClipboardFormatAvailable(CF_TEXT) ?
MF_BYCOMMAND | MF_ENABLED :
MF_BYCOMMAND | MF_GRAYED
);
else
EnableMenuItem(hmenu, id,
IsClipboardFormatAvailable(
uLabelFormat) ?
MF_BYCOMMAND | MF_ENABLED :
MF_BYCOMMAND | MF_GRAYED
);
}
}
}
WM_COMMAND
메시지 처리
메뉴 명령을 처리하려면 애플리케이션의 기본 창 프로시저에 사례를 추가 WM_COMMAND
합니다.
WM_COMMAND
다음은 레이블 애플리케이션의 창 프로시저에 대한 사례입니다.
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_CUT:
if (EditCopy())
EditDelete();
break;
case IDM_COPY:
EditCopy();
break;
case IDM_PASTE:
EditPaste();
break;
case IDM_DELETE:
EditDelete();
break;
case IDM_EXIT:
DestroyWindow(hwnd);
}
break;
복사 및 잘라내기 명령을 수행하기 위해 창 프로시저는 애플리케이션 정의 EditCopy
함수를 호출합니다. 자세한 내용은 클립보드에 정보 복사를 참조하세요.
붙여넣기 명령을 수행하기 위해 창 프로시저는 애플리케이션 정의 EditPaste
함수를 호출합니다. 함수에 EditPaste
대한 자세한 내용은 클립보드에서 정보 붙여넣기를 참조하세요.
클립보드에 정보 복사
레이블 애플리케이션에서 애플리케이션 정의 EditCopy 함수는 현재 선택 항목을 클립보드에 복사합니다. 이 함수는 다음을 수행합니다.
- 함수를 호출하여 클립보드를
OpenClipboard
엽니다. - 함수를 호출하여 클립보드를
EmptyClipboard
비웁니다. - 애플리케이션이
SetClipboardData
제공하는 각 클립보드 형식에 대해 함수를 한 번 호출합니다. - 함수를 호출하여 클립보드를 닫습니다
CloseClipboard
.
현재 선택 영역에 따라 EditCopy 함수는 텍스트 범위를 복사하거나 전체 레이블을 나타내는 애플리케이션 정의 구조를 복사합니다. 라는 LABELBOX
구조체는 다음과 같이 정의됩니다.
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
RECT rcText; // coordinates of rectangle containing text
BOOL fSelected; // TRUE if the label is selected
BOOL fEdit; // TRUE if text is selected
int nType; // rectangular or elliptical
int ichCaret; // caret position
int ichSel; // with ichCaret, delimits selection
int nXCaret; // window position corresponding to ichCaret
int nXSel; // window position corresponding to ichSel
int cchLabel; // length of text in atchLabel
TCHAR atchLabel[CCH_MAXLABEL];
} LABELBOX, *PLABELBOX;
다음은 함수입니다 EditCopy
.
BOOL WINAPI EditCopy(VOID)
{
PLABELBOX pbox;
LPTSTR lptstrCopy;
HGLOBAL hglbCopy;
int ich1, ich2, cch;
if (hwndSelected == NULL)
return FALSE;
// Open the clipboard, and empty it.
if (!OpenClipboard(hwndMain))
return FALSE;
EmptyClipboard();
// Get a pointer to the structure for the selected label.
pbox = (PLABELBOX) GetWindowLong(hwndSelected, 0);
// If text is selected, copy it using the CF_TEXT format.
if (pbox->fEdit)
{
if (pbox->ichSel == pbox->ichCaret) // zero length
{
CloseClipboard(); // selection
return FALSE;
}
if (pbox->ichSel < pbox->ichCaret)
{
ich1 = pbox->ichSel;
ich2 = pbox->ichCaret;
}
else
{
ich1 = pbox->ichCaret;
ich2 = pbox->ichSel;
}
cch = ich2 - ich1;
// Allocate a global memory object for the text.
hglbCopy = GlobalAlloc(GMEM_MOVEABLE,
(cch + 1) * sizeof(TCHAR));
if (hglbCopy == NULL)
{
CloseClipboard();
return FALSE;
}
// Lock the handle and copy the text to the buffer.
lptstrCopy = GlobalLock(hglbCopy);
memcpy(lptstrCopy, &pbox->atchLabel[ich1],
cch * sizeof(TCHAR));
lptstrCopy[cch] = (TCHAR) 0; // null character
GlobalUnlock(hglbCopy);
// Place the handle on the clipboard.
SetClipboardData(CF_TEXT, hglbCopy);
}
// If no text is selected, the label as a whole is copied.
else
{
// Save a copy of the selected label as a local memory
// object. This copy is used to render data on request.
// It is freed in response to the WM_DESTROYCLIPBOARD
// message.
pboxLocalClip = (PLABELBOX) LocalAlloc(
LMEM_FIXED,
sizeof(LABELBOX)
);
if (pboxLocalClip == NULL)
{
CloseClipboard();
return FALSE;
}
memcpy(pboxLocalClip, pbox, sizeof(LABELBOX));
pboxLocalClip->fSelected = FALSE;
pboxLocalClip->fEdit = FALSE;
// Place a registered clipboard format, the owner-display
// format, and the CF_TEXT format on the clipboard using
// delayed rendering.
SetClipboardData(uLabelFormat, NULL);
SetClipboardData(CF_OWNERDISPLAY, NULL);
SetClipboardData(CF_TEXT, NULL);
}
// Close the clipboard.
CloseClipboard();
return TRUE;
}
클립보드에서 정보 붙여넣기
Label 애플리케이션에서 애플리케이션 정의 EditPaste
함수는 클립보드의 콘텐츠를 붙여넣습니다. 이 함수는 다음을 수행합니다.
- 함수를 호출하여 클립보드를
OpenClipboard
엽니다. - 검색할 사용 가능한 클립보드 형식을 결정합니다.
- 함수를 호출하여 선택한 형식의 데이터에 대한 핸들을 검색합니다
GetClipboardData
. - 문서의 데이터 복사본을 삽입합니다. 에서
GetClipboardData
반환된 핸들은 여전히 클립보드에서 소유하므로 애플리케이션에서 해제하거나 잠가서는 안 됩니다. - 함수를 호출하여 클립보드를 닫습니다
CloseClipboard
.
레이블을 선택하고 삽입 지점을 포함하는 경우 EditPaste 함수는 삽입 지점에 클립보드의 텍스트를 삽입합니다. 선택 영역이 없거나 레이블이 선택된 경우 함수는 클립보드의 애플리케이션 정의 LABELBOX
구조를 사용하여 새 레이블을 만듭니다. 구조체 LABELBOX
는 등록된 클립보드 형식을 사용하여 클립보드에 배치됩니다.
라는 LABELBOX
구조체는 다음과 같이 정의됩니다.
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
RECT rcText; // coordinates of rectangle containing text
BOOL fSelected; // TRUE if the label is selected
BOOL fEdit; // TRUE if text is selected
int nType; // rectangular or elliptical
int ichCaret; // caret position
int ichSel; // with ichCaret, delimits selection
int nXCaret; // window position corresponding to ichCaret
int nXSel; // window position corresponding to ichSel
int cchLabel; // length of text in atchLabel
TCHAR atchLabel[CCH_MAXLABEL];
} LABELBOX, *PLABELBOX;
다음은 함수입니다 EditPaste
.
VOID WINAPI EditPaste(VOID)
{
PLABELBOX pbox;
HGLOBAL hglb;
LPTSTR lptstr;
PLABELBOX pboxCopy;
int cx, cy;
HWND hwnd;
pbox = hwndSelected == NULL ? NULL :
(PLABELBOX) GetWindowLong(hwndSelected, 0);
// If the application is in edit mode,
// get the clipboard text.
if (pbox != NULL && pbox->fEdit)
{
if (!IsClipboardFormatAvailable(CF_TEXT))
return;
if (!OpenClipboard(hwndMain))
return;
hglb = GetClipboardData(CF_TEXT);
if (hglb != NULL)
{
lptstr = GlobalLock(hglb);
if (lptstr != NULL)
{
// Call the application-defined ReplaceSelection
// function to insert the text and repaint the
// window.
ReplaceSelection(hwndSelected, pbox, lptstr);
GlobalUnlock(hglb);
}
}
CloseClipboard();
return;
}
// If the application is not in edit mode,
// create a label window.
if (!IsClipboardFormatAvailable(uLabelFormat))
return;
if (!OpenClipboard(hwndMain))
return;
hglb = GetClipboardData(uLabelFormat);
if (hglb != NULL)
{
pboxCopy = GlobalLock(hglb);
if (pboxCopy != NULL)
{
cx = pboxCopy->rcText.right + CX_MARGIN;
cy = pboxCopy->rcText.top * 2 + cyText;
hwnd = CreateWindowEx(
WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT,
atchClassChild, NULL, WS_CHILD, 0, 0, cx, cy,
hwndMain, NULL, hinst, NULL
);
if (hwnd != NULL)
{
pbox = (PLABELBOX) GetWindowLong(hwnd, 0);
memcpy(pbox, pboxCopy, sizeof(LABELBOX));
ShowWindow(hwnd, SW_SHOWNORMAL);
SetFocus(hwnd);
}
GlobalUnlock(hglb);
}
}
CloseClipboard();
}
클립보드 형식 등록
클립보드 형식을 등록하려면 다음과 같이 애플리케이션의 instance 초기화 함수에 함수에 대한 호출 RegisterClipboardFormat
을 추가합니다.
// Register a clipboard format.
// We assume that atchTemp can contain the format name and
// a null-terminator, otherwise it is truncated.
//
LoadString(hinstCurrent, IDS_FORMATNAME, atchTemp,
sizeof(atchTemp)/sizeof(TCHAR));
uLabelFormat = RegisterClipboardFormat(atchTemp);
if (uLabelFormat == 0)
return FALSE;
WM_RENDERFORMAT
및 WM_RENDERALLFORMATS
메시지 처리
창이 함수에 핸들 SetClipboardData
을 NULL
전달하는 경우 요청 시 및 WM_RENDERALLFORMATS
메시지를 처리 WM_RENDERFORMAT
하여 데이터를 렌더링해야 합니다.
창에서 특정 형식 렌더링이 지연되고 다른 애플리케이션이 해당 형식의 WM_RENDERFORMAT
데이터를 요청하는 경우 메시지가 창으로 전송됩니다. 또한 창에서 하나 이상의 형식 렌더링이 지연되고 창이 제거될 WM_RENDERALLFORMATS
때 이러한 형식 중 일부가 렌더링되지 않은 상태로 유지되는 경우 삭제하기 전에 창으로 메시지가 전송됩니다.
클립보드 형식을 렌더링하려면 창 프로시저에서 함수를 사용하여 SetClipboardData
비데이NULL
터 핸들을 클립보드에 배치해야 합니다. 창 프로시저가 메시지에 대한 응답으로 WM_RENDERFORMAT
형식을 렌더링하는 경우 를 호출 SetClipboardData
하기 전에 클립보드를 열면 안 됩니다. 그러나 메시지에 대한 응답으로 WM_RENDERALLFORMATS
하나 이상의 형식을 렌더링하는 경우 클립보드를 열고 를 호출SetClipboardData
하기 전에 창이 여전히 클립보드를 소유하고 있음을 검사 반환하기 전에 클립보드를 닫아야 합니다.
Label 애플리케이션은 다음과 같이 및 WM_RENDERALLFORMATS
메시지를 처리 WM_RENDERFORMAT
합니다.
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;
case WM_RENDERALLFORMATS:
if (OpenClipboard(hwnd))
{
if (GetClipboardOwner() == hwnd)
{
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
}
CloseClipboard();
}
break;
두 경우 모두 창 프로시저는 다음과 같이 정의된 애플리케이션 정의 RenderFormat
함수를 호출합니다.
라는 LABELBOX
구조체는 다음과 같이 정의됩니다.
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
RECT rcText; // coordinates of rectangle containing text
BOOL fSelected; // TRUE if the label is selected
BOOL fEdit; // TRUE if text is selected
int nType; // rectangular or elliptical
int ichCaret; // caret position
int ichSel; // with ichCaret, delimits selection
int nXCaret; // window position corresponding to ichCaret
int nXSel; // window position corresponding to ichSel
int cchLabel; // length of text in atchLabel
TCHAR atchLabel[CCH_MAXLABEL];
} LABELBOX, *PLABELBOX;
void WINAPI RenderFormat(UINT uFormat)
{
HGLOBAL hglb;
PLABELBOX pbox;
LPTSTR lptstr;
int cch;
if (pboxLocalClip == NULL)
return;
if (uFormat == CF_TEXT)
{
// Allocate a buffer for the text.
cch = pboxLocalClip->cchLabel;
hglb = GlobalAlloc(GMEM_MOVEABLE,
(cch + 1) * sizeof(TCHAR));
if (hglb == NULL)
return;
// Copy the text from pboxLocalClip.
lptstr = GlobalLock(hglb);
memcpy(lptstr, pboxLocalClip->atchLabel,
cch * sizeof(TCHAR));
lptstr[cch] = (TCHAR) 0;
GlobalUnlock(hglb);
// Place the handle on the clipboard.
SetClipboardData(CF_TEXT, hglb);
}
else if (uFormat == uLabelFormat)
{
hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(LABELBOX));
if (hglb == NULL)
return;
pbox = GlobalLock(hglb);
memcpy(pbox, pboxLocalClip, sizeof(LABELBOX));
GlobalUnlock(hglb);
SetClipboardData(uLabelFormat, hglb);
}
}
WM_DESTROYCLIPBOARD
메시지 처리
창은 지연된 렌더링을 지원하기 위해 따로 설정한 리소스를 해제하기 위해 메시지를 처리 WM_DESTROYCLIPBOARD
할 수 있습니다. 예를 들어 레이블 애플리케이션은 레이블을 클립보드에 복사할 때 로컬 메모리 개체를 할당합니다. 그런 다음 다음과 같이 메시지에 대한 응답으로 이 개체를 WM_DESTROYCLIPBOARD
해제합니다.
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Owner-Display 클립보드 형식 사용
창이 클립보드 형식을 사용하여 클립보드에 정보를 배치하는 CF_OWNERDISPLAY
경우 다음을 수행해야 합니다.
- 메시지를 처리합니다
WM_PAINTCLIPBOARD
. 이 메시지는 클립보드 뷰어 창의 일부를 다시 그려야 하는 경우 클립보드 소유자에게 전송됩니다. - 메시지를 처리합니다
WM_SIZECLIPBOARD
. 클립보드 뷰어 창의 크기가 조정되었거나 콘텐츠가 변경되면 이 메시지가 클립보드 소유자에게 전송됩니다. 일반적으로 창은 클립보드 뷰어 창의 스크롤 위치와 범위를 설정하여 이 메시지에 응답합니다. 이 메시지에 대한 응답으로 Label 애플리케이션은 클립보드 뷰어 창에 대한 구조도 업데이트합니다SIZE
. -
WM_HSCROLLCLIPBOARD
및WM_VSCROLLCLIPBOARD
메시지를 처리합니다. 이러한 메시지는 클립보드 뷰어 창에서 스크롤 막대 이벤트가 발생할 때 클립보드 소유자에게 전송됩니다. - 메시지를 처리합니다
WM_ASKCBFORMATNAME
. 클립보드 뷰어 창은 소유자 표시 형식의 이름을 검색하기 위해 이 메시지를 애플리케이션에 보냅니다.
레이블 애플리케이션의 창 프로시저는 다음과 같이 이러한 메시지를 처리합니다.
LRESULT CALLBACK MainWindowProc(hwnd, msg, wParam, lParam)
HWND hwnd;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
static RECT rcViewer;
RECT rc;
LPRECT lprc;
LPPAINTSTRUCT lpps;
switch (msg)
{
//
// Handle other messages.
//
case WM_PAINTCLIPBOARD:
// Determine the dimensions of the label.
SetRect(&rc, 0, 0,
pboxLocalClip->rcText.right + CX_MARGIN,
pboxLocalClip->rcText.top * 2 + cyText
);
// Center the image in the clipboard viewer window.
if (rc.right < rcViewer.right)
{
rc.left = (rcViewer.right - rc.right) / 2;
rc.right += rc.left;
}
if (rc.bottom < rcViewer.bottom)
{
rc.top = (rcViewer.bottom - rc.bottom) / 2;
rc.bottom += rc.top;
}
// Paint the image, using the specified PAINTSTRUCT
// structure, by calling the application-defined
// PaintLabel function.
lpps = (LPPAINTSTRUCT) GlobalLock((HGLOBAL) lParam);
PaintLabel(lpps, pboxLocalClip, &rc);
GlobalUnlock((HGLOBAL) lParam);
break;
case WM_SIZECLIPBOARD:
// Save the dimensions of the window in a static
// RECT structure.
lprc = (LPRECT) GlobalLock((HGLOBAL) lParam);
memcpy(&rcViewer, lprc, sizeof(RECT));
GlobalUnlock((HGLOBAL) lParam);
// Set the scroll ranges to zero (thus eliminating
// the need to process the WM_HSCROLLCLIPBOARD and
// WM_VSCROLLCLIPBOARD messages).
SetScrollRange((HWND) wParam, SB_HORZ, 0, 0, TRUE);
SetScrollRange((HWND) wParam, SB_VERT, 0, 0, TRUE);
break;
case WM_ASKCBFORMATNAME:
LoadString(hinst, IDS_OWNERDISPLAY,
(LPSTR) lParam, wParam);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
클립보드 콘텐츠 모니터링
클립보드에 대한 변경 내용을 모니터링하는 세 가지 방법이 있습니다. 가장 오래된 방법은 클립보드 뷰어 창을 만드는 것입니다. Windows 2000은 클립보드 시퀀스 번호를 쿼리하는 기능을 추가했으며 Windows Vista는 클립보드 형식 수신기에 대한 지원을 추가했습니다. 클립보드 뷰어 창은 이전 버전의 Windows와 이전 버전과의 호환성을 위해 지원됩니다. 새 프로그램은 클립보드 형식 수신기 또는 클립보드 시퀀스 번호를 사용해야 합니다.
클립보드 시퀀스 번호 쿼리
클립보드의 내용이 변경될 때마다 클립보드 시퀀스 번호라고 하는 32비트 값이 증가합니다. 프로그램은 함수를 호출하여 현재 클립보드 시퀀스 번호를 검색할 GetClipboardSequenceNumber
수 있습니다. 에 대한 이전 호출 GetClipboardSequenceNumber
에서 반환된 값과 반환된 값을 비교하여 프로그램에서 클립보드 내용이 변경되었는지 여부를 확인할 수 있습니다. 이 메서드는 현재 클립보드 내용을 기반으로 결과를 캐시하는 프로그램에 더 적합하며 해당 캐시의 결과를 사용하기 전에 계산이 여전히 유효한지 여부를 알아야 합니다. 이는 알림 메서드가 아니며 폴링 루프에서 사용해서는 안 됩니다. 클립보드 콘텐츠가 변경될 때 알림을 받려면 클립보드 형식 수신기 또는 클립보드 뷰어를 사용합니다.
클립보드 형식 수신기 만들기
클립보드 형식 수신기는 클립보드의 내용이 변경되었을 때 알림을 받도록 등록된 창입니다. 프로그램이 클립보드 뷰어 체인을 제대로 유지 관리하지 못하거나 클립보드 뷰어 체인의 창이 메시지에 응답하지 않는 경우 이 메서드는 더 간단하게 구현하고 문제를 방지하므로 클립보드 뷰어 창을 만드는 데 권장됩니다.
창은 함수를 호출하여 클립보드 형식 수신기로 등록됩니다 AddClipboardFormatListener
. 클립보드의 내용이 변경되면 창에 메시지가 게시됩니다 WM_CLIPBOARDUPDATE
. 함수를 호출하여 창 자체의 등록을 취소할 때까지 등록은 RemoveClipboardFormatListener
유효한 상태로 유지됩니다.
클립보드 뷰어 창 만들기
클립보드 뷰어 창은 클립보드의 현재 콘텐츠를 표시하고 클립보드 콘텐츠가 변경되면 메시지를 받습니다. 클립보드 뷰어 창을 만들려면 애플리케이션에서 다음을 수행해야 합니다.
- 클립보드 뷰어 체인에 창을 추가합니다.
- 메시지를 처리합니다
WM_CHANGECBCHAIN
. - 메시지를 처리합니다
WM_DRAWCLIPBOARD
. - 클립보드 뷰어 체인이 제거되기 전에 창을 제거합니다.
클립보드 뷰어 체인에 창 추가
창은 함수를 호출 SetClipboardViewer
하여 클립보드 뷰어 체인에 자신을 추가합니다. 반환 값은 체인의 다음 창에 대한 핸들입니다. 창은 이 값을 추적해야 합니다(예: 라는 hwndNextViewer
정적 변수에 저장).
다음 예제에서는 메시지에 대한 응답으로 클립보드 뷰어 체인에 창을 추가합니다 WM_CREATE
.
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
다음 작업에 대한 코드 조각이 표시됩니다.
WM_CHANGECBCHAIN
메시지 처리
클립보드 뷰어 창은 WM_CHANGECBCHAIN
다른 창이 클립보드 뷰어 체인에서 자신을 제거할 때 메시지를 받습니다. 제거되는 창이 체인의 다음 창인 경우 메시지를 받는 창은 체인에서 다음 창의 연결을 해제해야 합니다. 그렇지 않으면 이 메시지를 체인의 다음 창으로 전달해야 합니다.
다음 예제에서는 메시지의 처리를 보여 있습니다 WM_CHANGECBCHAIN
.
case WM_CHANGECBCHAIN:
// If the next window is closing, repair the chain.
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;
// Otherwise, pass the message to the next link.
else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
클립보드 뷰어 체인에서 창 제거
클립보드 뷰어 체인에서 자신을 제거하려면 창에서 함수를 호출합니다 ChangeClipboardChain
. 다음 예제에서는 메시지에 대한 응답으로 클립보드 뷰어 체인에서 창을 제거합니다 WM_DESTROY
.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
WM_DRAWCLIPBOARD
메시지 처리
이 WM_DRAWCLIPBOARD
메시지는 클립보드 뷰어 창에 클립보드의 콘텐츠가 변경되었음을 알립니다. 창은 메시지를 처리할 때 다음을 WM_DRAWCLIPBOARD
수행해야 합니다.
- 표시할 사용 가능한 클립보드 형식을 결정합니다.
- 클립보드 데이터를 검색하고 창에 표시합니다. 또는 클립보드 형식이 인
CF_OWNERDISPLAY
경우 클립보드 소유자에게 메시지를 보냅니WM_PAINTCLIPBOARD
다. - 클립보드 뷰어 체인의 다음 창으로 메시지를 보냅니다.
메시지를 처리하는 WM_DRAWCLIPBOARD
예제는 클립보드 뷰어 예제의 예제 목록을 참조하세요.
클립보드 뷰어의 예
다음 예제에서는 간단한 클립보드 뷰어 애플리케이션을 보여줍니다.
HINSTANCE hinst;
UINT uFormat = (UINT)(-1);
BOOL fAuto = TRUE;
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
static HWND hwndNextViewer;
HDC hdc;
HDC hdcMem;
PAINTSTRUCT ps;
LPPAINTSTRUCT lpps;
RECT rc;
LPRECT lprc;
HGLOBAL hglb;
LPSTR lpstr;
HBITMAP hbm;
HENHMETAFILE hemf;
HWND hwndOwner;
switch (uMsg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Branch depending on the clipboard format.
switch (uFormat)
{
case CF_OWNERDISPLAY:
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_MOVEABLE,
sizeof(PAINTSTRUCT));
lpps = GlobalLock(hglb);
memcpy(lpps, &ps, sizeof(PAINTSTRUCT));
GlobalUnlock(hglb);
SendMessage(hwndOwner, WM_PAINTCLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);
GlobalFree(hglb);
break;
case CF_BITMAP:
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
if (OpenClipboard(hwnd))
{
hbm = (HBITMAP)
GetClipboardData(uFormat);
SelectObject(hdcMem, hbm);
GetClientRect(hwnd, &rc);
BitBlt(hdc, 0, 0, rc.right, rc.bottom,
hdcMem, 0, 0, SRCCOPY);
CloseClipboard();
}
DeleteDC(hdcMem);
}
break;
case CF_TEXT:
if (OpenClipboard(hwnd))
{
hglb = GetClipboardData(uFormat);
lpstr = GlobalLock(hglb);
GetClientRect(hwnd, &rc);
DrawText(hdc, lpstr, -1, &rc, DT_LEFT);
GlobalUnlock(hglb);
CloseClipboard();
}
break;
case CF_ENHMETAFILE:
if (OpenClipboard(hwnd))
{
hemf = GetClipboardData(uFormat);
GetClientRect(hwnd, &rc);
PlayEnhMetaFile(hdc, hemf, &rc);
CloseClipboard();
}
break;
case 0:
GetClientRect(hwnd, &rc);
DrawText(hdc, "The clipboard is empty.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
break;
default:
GetClientRect(hwnd, &rc);
DrawText(hdc, "Unable to display format.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
}
EndPaint(hwnd, &ps);
break;
case WM_SIZE:
if (uFormat == CF_OWNERDISPLAY)
{
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(RECT));
lprc = GlobalLock(hglb);
GetClientRect(hwnd, lprc);
GlobalUnlock(hglb);
SendMessage(hwndOwner, WM_SIZECLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);
GlobalFree(hglb);
}
break;
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
case WM_CHANGECBCHAIN:
// If the next window is closing, repair the chain.
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;
// Otherwise, pass the message to the next link.
else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
case WM_DRAWCLIPBOARD: // clipboard contents changed.
// Update the window by using Auto clipboard format.
SetAutoView(hwnd);
// Pass the message to the next window in clipboard
// viewer chain.
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
case WM_INITMENUPOPUP:
if (!HIWORD(lParam))
InitMenu(hwnd, (HMENU) wParam);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_EXIT:
DestroyWindow(hwnd);
break;
case IDM_AUTO:
SetAutoView(hwnd);
break;
default:
fAuto = FALSE;
uFormat = LOWORD(wParam);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return (LRESULT) NULL;
}
void WINAPI SetAutoView(HWND hwnd)
{
static UINT auPriorityList[] = {
CF_OWNERDISPLAY,
CF_TEXT,
CF_ENHMETAFILE,
CF_BITMAP
};
uFormat = GetPriorityClipboardFormat(auPriorityList, 4);
fAuto = TRUE;
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
void WINAPI InitMenu(HWND hwnd, HMENU hmenu)
{
UINT uFormat;
char szFormatName[80];
LPCSTR lpFormatName;
UINT fuFlags;
UINT idMenuItem;
// If a menu is not the display menu, no initialization is necessary.
if (GetMenuItemID(hmenu, 0) != IDM_AUTO)
return;
// Delete all menu items except the first.
while (GetMenuItemCount(hmenu) > 1)
DeleteMenu(hmenu, 1, MF_BYPOSITION);
// Check or uncheck the Auto menu item.
fuFlags = fAuto ? MF_BYCOMMAND | MF_CHECKED :
MF_BYCOMMAND | MF_UNCHECKED;
CheckMenuItem(hmenu, IDM_AUTO, fuFlags);
// If there are no clipboard formats, return.
if (CountClipboardFormats() == 0)
return;
// Open the clipboard.
if (!OpenClipboard(hwnd))
return;
// Add a separator and then a menu item for each format.
AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
uFormat = EnumClipboardFormats(0);
while (uFormat)
{
// Call an application-defined function to get the name
// of the clipboard format.
lpFormatName = GetPredefinedClipboardFormatName(uFormat);
// For registered formats, get the registered name.
if (lpFormatName == NULL)
{
// Note that, if the format name is larger than the
// buffer, it is truncated.
if (GetClipboardFormatName(uFormat, szFormatName,
sizeof(szFormatName)))
lpFormatName = szFormatName;
else
lpFormatName = "(unknown)";
}
// Add a menu item for the format. For displayable
// formats, use the format ID for the menu ID.
if (IsDisplayableFormat(uFormat))
{
fuFlags = MF_STRING;
idMenuItem = uFormat;
}
else
{
fuFlags = MF_STRING | MF_GRAYED;
idMenuItem = 0;
}
AppendMenu(hmenu, fuFlags, idMenuItem, lpFormatName);
uFormat = EnumClipboardFormats(uFormat);
}
CloseClipboard();
}
BOOL WINAPI IsDisplayableFormat(UINT uFormat)
{
switch (uFormat)
{
case CF_OWNERDISPLAY:
case CF_TEXT:
case CF_ENHMETAFILE:
case CF_BITMAP:
return TRUE;
}
return FALSE;
}