MetaFile 사용 및 클립보드에 복사하기
1. 메타파일이란?
메타파일이란 그래픽 함수를 저장하여, 나중에 플레이를 해볼 수 있는 일종의 녹음기와 같다. 한번 저장된 메타파일은, 여러 번 플레이 하여 똑같은 그림을 반복해서 그릴 수 있다. 메타파일을 플레이하는 것은 실제 그래픽 함수를 호출하여 그림을 그리는 것보다 빠르게 동작한다고 한다. 또한, 메타파일은 WMF(Windows MetaFile), EMF(Enhanced MetaFile) 확장자를 가진 파일로 디스크에 저장할 수 있다.
2. 메타파일 사용하기
메타파일에는 다음과 같은 두 종류가 있다. 이중 '확장 메타파일'을 가지고 예를 들어 보겠다
▶ 윈도우 메타파일(Window Metafile)
▶ 확장 메타파일(Enhanced Metafile)
2.1 확장 메타파일 생성
확장 메타파일을 사용할 때는 CMetaFileDC의 인스턴스를 생성한 후 CreateEnhanced 함수를 호출한다.
자세한 사항은 MSDN 참고.
CMetaFileDC m_metaDC;
char szDesc[] = "Enhanced Metafile Application";
CRect rc(0, 0, 10000, 10000);
m_metaDC.CreateEnhanced(NULL, NULL, rc, szDesc);
2.2 확장 메타파일에 그래픽 함수 저장
CMetaFileDC는 CDC클래스에서 상속받은 클래스이기 때문에 CDC의 모든 함수를 다 호출 할 수 있다.
예를 들어, 메타파일에 선을 그리는 함수를 저장하라면 다음과 같이 한다.
m_metaDC.MoveTo(100, 100);
m_metaDC.LintTo(200, 200);
2.3 확장 메타파일 플레이
확장 메타파일에 원하는 그래픽 함수들을 다 저장했으면 CloseEnhanced 함수를 호출한다. 이 함수는 HENHMETAFILE이라는 확장 메타파일의 핸들을 반환하는데, 이것을 저장해 두었다가 확장 메타파일을 플레이 할 때 사용한다.
HENHMETAFILE hMF = m_metaDC.CloseEnhanced();
이제, hMF를 이용하여 CDC의 멤버 함수인 PlayMetaFile 함수를 호출하면 메타파일을 플레이 할 수 있다.
CRect rect;
GetClientRect(&rect);
pDC->PlayMetaFile(hMF, rect);
확장 메타파일은 저장된 그림의 크기가 프로그램이나 디바이스에 독립적이기 때문에 얼마만한 크기로 출력할지를 인자로 넘겨야 한다. 위와 같이하면 클라이언트 영역 전체 크기로 메타파일이 출력된다.
2.4 확장 메타파일 지우기
확장 메타파일을 다 썼으면 ::DeleteEnhMetaFile 함수를 이용하여 메모리에서 해제시켜 준다.
::DeleteEnhMetaFile(hMF);
3. 확장 메타파일 소스 예제
// 확장 메타파일 만들기 -----------------------------------------------
void CMyMetaView::OnInitialUpdate()
{
CView::OnInitialUpdate();
char szDesc[] = "Enhanced Metafile Application";
CRect rc(0, 0, 10000, 10000);
// 확장 메타파일 생성
m_metaDC.CreateEnhanced(NULL, NULL, rc, szDesc);
// MM_HIMETRIC 맵핑모드를 이용하여 그래픽 함수 레코딩
m_metaDC.SetMapMode(MM_HIMETRIC);
CBrush brush, *pOldBrush;
brush.CreateSolidBrush(RGB(255, 255, 0));
pOldBrush = (CBrush*)m_metaDC.SelectObject(&brush);
m_metaDC.Ellipse(0, 0, 10000, -10000);
m_metaDC.Ellipse(0, 0, 5000, -5000);
CFont font, *pOldFont;
font.CreateFont(2000, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Book Antiqua");
pOldFont = (CFont*)m_metaDC.SelectObject(&font);
m_metaDC.SetBkMode(TRANSPARENT);
m_metaDC.SetTextAlign(TA_CENTER | TA_BOTTOM);
m_metaDC.TextOut(5000, -5000, "Visol");
m_metaDC.SelectObject(pOldBrush);
m_metaDC.SelectObject(pOldFont);
// 레코딩 완료
m_hMF = m_metaDC.CloseEnhanced();
}
// 확장 메타파일 플레이 --------------------------------------
void CMyMetaView::OnDraw(CDC* pDC)
{
CMyMetaDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CRect rcClient, rcRef, rcDest;
GetClientRect(&rcClient);
rcRef.left = rcRef.top = 1;
rcRef.right = rcClient.right;
rcRef.bottom = rcClient.bottom;
rcDest.CopyRect(rcRef);
pDC->PlayMetaFile(m_hMF, rcDest);
}
// 확장 메타파일 지우기 -------------------------------------
CMyMetaView::~CMyMetaView()
{
::DeleteEnhMetaFile(m_hMF);
}
4. 클립보드에 복사하기
생성한 확장 메타파일을 클립보드를 이용하여 다른 응용 프로그램에 붙여 넣기하여 사용할 수 있다.
참고로, 클립보드에 데이터를 넣고 빼는 과정은 다음과 같다.
[넣기] 복사할 데이터를 준비 -> Open -> Empty -> Set -> Close
[빼기] 원하는 포맷이 있는지 검사 -> Open -> Get -> Close
4.1 COleDataSource 사용하기
COleDataSource 클래스로 OLE 클립보드 기능을 이용할 수 있는데, 주의할 점이 클립보드에 복사된 데이터의
소유권은 전적으로 클립보드에게 있기 때문에 해제하거나 Lock하는 동작은 할 필요도 없고 해서도 안된다.
메모리의 해제는 다음번에 다른 윈도우에 의해 클립보드가 비워질 때 해제된다.
그리고 COleDataSource를 사용시 클립보드를 Close할 필요도 없고 메소드도 존재하지 않는다.
아마 내부적으로 자동처리를 해주는 모양이다.
아래는 MSDN에서 설명하는 COleDataSource의 사용예제이다.
void CMyView::OnEditCopy()
{
// Create an OLE data source on the heap
COleDataSource* pData = new COleDataSource;
// ...
// Get the currently selected data
// ...
// For the appropriate data formats...
pData->CacheData( CF_??, hData );
// ...
// The Clipboard now owns the allocated memory
// and will delete this data object
// when new data is put on the Clipboard
pData->SetClipboard();
}
5. 클립보드에 복사 예제 코드
void CMyMetaView::OnEditCopy()
{
COleDataSource* pOleData;
tagSTGMEDIUM* data;
pOleData = new COleDataSource;
data = new tagSTGMEDIUM;
data->tymed = TYMED_ENHMF;
data->hEnhMetaFile = m_hMF;
pOleData->CacheData(CF_ENHMETAFILE, data);
pOleData->FlushClipboard();
pOleData->SetClipboard();
delete data;
}