DevCap 예제에서 프린터의 이름을 구할 때 잠시 DEVMODE 구조체를 참조하였는데 이 구조체에 대해 자세하게 알아 보자. 이 구조체는 프린터의 초기화 정보와 환경에 대한 정보를 가지는데 프린터에 관련된 모든 정보를 다 가지고 있는 아주 중요한 구조체이며 또한 프린터의 설정 상태를 프로그램이 강제로 바꾸는 수단으로 활용된다. 이 구조체를 정복해야 프린터를 마음대로 요리할 수 있다.
이 구조체는 여러 가지 방법으로 구할 수 있는데 EnumPrinters 함수로 프린터를 열거할 때 구해지는 PRINTER_INFO_2 구조체에도 포함되어 있으며 OpenPrinter 함수로 프린터의 핸들을 구할 때 같이 구할 수도 있다. 또한 인쇄 공통 대화상자 호출시 PRINTDLG 구조체의 hDevMode 멤버에서 이 구조체를 구할 수도 있고 GetPrinter, DocumentProperties 등과 같이 이 구조체를 다루는 전문적인 함수도 있다. 그렇다면 이 구조체가 과연 어떻게 정의되어 있는지 일단 보도록 하자.
typedef struct _devicemode { BCHAR dmDeviceName[CCHDEVICENAME]; WORD dmSpecVersion; WORD dmDriverVersion; WORD dmSize; WORD dmDriverExtra; DWORD dmFields; union { struct { short dmOrientation; short dmPaperSize; short dmPaperLength; short dmPaperWidth; }; POINTL dmPosition; }; short dmScale; short dmCopies; short dmDefaultSource; short dmPrintQuality; short dmColor; short dmDuplex; short dmYResolution; short dmTTOption; short dmCollate; |
BCHAR dmFormName[CCHFORMNAME]; WORD dmLogPixels; DWORD dmBitsPerPel; DWORD dmPelsWidth; DWORD dmPelsHeight; union { DWORD dmDisplayFlags; DWORD dmNup; } DWORD dmDisplayFrequency; #if(WINVER >= 0x0400) DWORD dmICMMethod; DWORD dmICMIntent; DWORD dmMediaType; DWORD dmDitherType; DWORD dmReserved1; DWORD dmReserved2; #if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400) DWORD dmPanningWidth; DWORD dmPanningHeight; #endif #endif /* WINVER >= 0x0400 */ } DEVMODE; |
보다시피 프린터라는 물건이 워낙 복잡하다 보니 그 초기화 정보의 양도 대단하다. 그러나 더 기가 막힌 것은 이 구조체가 여기서 끝나는 것이 아니라는 점이다. 이 구조체의 멤버는 대부분의 프린터나 장치에 공통적으로 적용되는 정보만 가지고 있으며 개별 프린터의 고유 정보를 추가로 더 가질 수 있다. 고유 정보는 이 구조체의 뒤쪽에 이어지는데 그 크기는 이 구조체보다 일반적으로 더 크며 프린터에 따라 고유하고 다양하다.
지금까지 만들어진 프린터의 종류가 수천가지는 더 될 것이고 지금 이 순간에도 새로운 프린터들이 만들어지고 있다. 도트 프린터부터, 잉크젯, 레이저, 페이지 프린터, 심지어 필름이나 섬유, 철판에 인쇄를 할 수 있는 것도 있고 플로터 방식의 프린터도 있다. 인쇄 기술은 날로 발전하고 있으며 앞으로 어떤 기상 천외한 방식의 프린터가 만들어질지 알 수가 없다. 그래서 DEVMODE를 처음 정의할 때 모든 프린터의 설정 정보를 포함할 수 있도록 만든다는 것 자체가 불가능하며 그래서 공통적으로 적용되는 속성만 포함시켜 놓고 나머지 고유 정보는 이 구조체 뒤쪽에 제조 업체가 덧붙일 수 있도록 되어 있는 것이다. 다음은 두가지 특이한 프린터의 기본 설정 화면이다.
HP 1050C는 캔트지 전지에도 인쇄할 수 있는 롤 프린터인데 이 프린터가 제공하는 자동 회전, 선 다듬기, 자동 절단기 사용 등의 옵션은 전혀 일반적이지 않다. 오른쪽은 많이 사용하는 인쇄 전용 소프트웨어인 FinePrinter라는 제품의 등록 정보 화면인데 테두리 설정, 작업 분리, 제본 여백 등은 일반적인 프린터에 있는 속성이 아니다. 프린터의 고유 정보는 아주 특수하며 일반화가 불가능하다는 것을 쉽게 이해할 수 있을 것이다. 이런 고유한 설정 정보는 프린터 제조 업체가 드라이버를 만들 때 포함시키게 되며 이 정보는 DEVMODE 뒤에 덧붙여짐으로써 프로그램에서 제어할 수 있다.
DEVMODE의 크기가 가변적이지만 다행스럽게도 우리는 DEVMODE에 포함되어 있는 멤버만을 다룰 뿐 프린터의 고유 정보에 관심을 가져야 할 경우가 거의 없다. 왜냐하면 이런 설정 상태는 사용자가 직접 제어판에서 제조 업체가 제공하는 설정 대화상자를 통해 원하는 바대로 수정하는 것이 원칙이며 프로그램이란 어떤 프린터를 선택하더라도 인쇄가 가능한, 즉 프린터에 독립적이어야 하기 때문이다. 그러나 만약 특정한 프린터를 위한 프로그램을 만든다면 이때는 물론 DEVMODE 뒤쪽의 정보도 같이 프로그래밍해야 할 것이다.
DEVMODE의 멤버 중 수직 주파수처럼 프린터에는 전혀 해당되지 않는 것도 있고 프로그램에서 직접 바꾸어야 할 필요가 없는 것들도 많이 있다. 다음은 이 구조체 중 자주 사용되는 몇가지만을 정리한 것이다.
멤버 |
설명 |
dmDeviceName |
프린터의 이름이며 문자열로 되어 있다. 이 이름은 시스템이 프린터를 구분하는 일종의 명칭으로 사용된다. |
dmSize |
이 구조체의 크기이며 버전 확인에 사용된다. sizeof(DEVMODE)와 동일한 크기를 가진다. |
dmDriverExtra |
드라이버의 고유 데이터 크기이다. DEVMODE 구조체 다음에 이 크기만큼의 데이터가 더 있다. |
dmFields |
DEVMODE 구조체로부터 초기값을 설정할 때 어떤 멤버가 초기화에 사용될 것인지를 지정한다. 예를 들어 용지의 방향을 바꾸고 싶다면 이 필드에 DM_ORIENTATION을 주고 용지 종류를 바꾸고 싶다면 DM_PAPERSIZE를 주면 된다. |
dmOrientation |
용지의 방향을 나타낸다. 세로 인쇄일 때는 DMORIENT_PORTRAIT가 되며 가로 인쇄일 때는 DMORIENT_LANDSCAPE가 된다. |
dmPaperSize |
용지의 종류를 나타낸다. DMPAPER_LETTER, DMPAPER_A4 같은 상수가 정의되어 있다. 커스텀 용지를 사용할 때는 이 멤버가 0이 되며 다음 두 멤버가 용지의 크기를 지정한다. |
dmPaperLength |
용지의 길이를 1/10밀리미터 단위로 지정한다. |
dmPaperWidth |
용지의 폭을 1/10밀리미터 단위로 지정한다. |
dmScale |
확대 비율을 지정하는 백분율이다. |
dmCopies |
인쇄 매수를 지정한다. |
dmDuplex |
양면 인쇄 방식을 지정한다. |
각 멤버가 가질 수 있는 값의 종류나 나머지 멤버에 대해서는 레퍼런스를 참고하기 바라되 위 표에 있는 것만 이해해도 99% 충분할 것이다. 그럼 이제 프린터로부터 DEVMODE를 구해 출력하는 예제를 만들어 보자. 소스는 다음과 같다.
void DisplayDevMode2()
{
char szPrinter[128];
DWORD dwPrinter=128;
HANDLE hPrinter;
DEVMODE *dm;
LONG ret;
HDC hdc;
int y=2;
char Mes[256];
hdc=GetDC(hWndMain);
if (MyGetDefaultPrinter(szPrinter) == FALSE) {
return;
}
OpenPrinter(szPrinter,&hPrinter,NULL);
ret=DocumentProperties(hWndMain, hPrinter, szPrinter, NULL, NULL, 0);
dm=(DEVMODE *)malloc(ret);
DocumentProperties(hWndMain, hPrinter, szPrinter, dm, NULL, DM_OUT_BUFFER);
// 초기화 정보 구함
wsprintf(Mes,"기본 프린터 이름=%s",dm->dmDeviceName);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
wsprintf(Mes,"드라이버 버전=%d", dm->dmDriverVersion);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
wsprintf(Mes,"공용 데이터의 크기=%d, 드라이버 고유 데이터의 크기=%d",
dm->dmSize, dm->dmDriverExtra);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
wsprintf(Mes,"용지의 종류=%d, 가로=%d, 세로=%d, 용지의 방향=%s",
dm->dmPaperSize, dm->dmPaperWidth, dm->dmPaperLength,
dm->dmOrientation == DMORIENT_PORTRAIT ? "세로":"가로");
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
wsprintf(Mes,"X해상도=%d, Y해상도=%d", dm->dmPrintQuality, dm->dmYResolution);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
free(dm);
ClosePrinter(hPrinter);
ReleaseDC(hWndMain,hdc);
}
void DisplayDevMode1()
{
PRINTDLG pd;
HDC hPrtdc;
DEVMODE *dm;
char szPrinter[128];
char Mes[256];
HDC hdc;
int y=2;
hdc=GetDC(hWndMain);
// 프린터에 관한 정보를 구하고 DC를 만든다.
memset(&pd,0,sizeof(PRINTDLG));
pd.lStructSize=sizeof(PRINTDLG);
pd.Flags=PD_RETURNDC;
pd.hwndOwner=hWndMain;
pd.nFromPage=1;
pd.nToPage=1;
pd.nMinPage=1;
pd.nMaxPage=1;
pd.nCopies=1;
PrintDlg(&pd);
hPrtdc=pd.hDC;
if (hPrtdc == NULL)
return;
// DEVMODE의 번지를 구한다.
dm=(DEVMODE *)GlobalLock(pd.hDevMode);
lstrcpy(szPrinter,(LPCTSTR)dm->dmDeviceName);
// 초기화 정보 구함
wsprintf(Mes,"선택한 프린터 이름=%s",dm->dmDeviceName);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
wsprintf(Mes,"드라이버 버전=%d", dm->dmDriverVersion);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
wsprintf(Mes,"공용 데이터의 크기=%d, 드라이버 고유 데이터의 크기=%d",
dm->dmSize, dm->dmDriverExtra);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
wsprintf(Mes,"용지의 종류=%d, 가로=%d, 세로=%d, 용지의 방향=%s",
dm->dmPaperSize, dm->dmPaperWidth, dm->dmPaperLength,
dm->dmOrientation == DMORIENT_PORTRAIT ? "세로":"가로");
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
wsprintf(Mes,"X해상도=%d, Y해상도=%d", dm->dmPrintQuality, dm->dmYResolution);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
GlobalUnlock(pd.hDevMode);
ReleaseDC(hWndMain,hdc);
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
char Mes[]="왼쪽 버튼=프린터 선택, 오른쪽 버튼=기본 프린터의 정보";
switch(iMessage) {
case WM_LBUTTONDOWN:
InvalidateRect(hWnd,NULL,TRUE);
UpdateWindow(hWnd);
DisplayDevMode1();
return 0;
case WM_RBUTTONDOWN:
InvalidateRect(hWnd,NULL,TRUE);
UpdateWindow(hWnd);
DisplayDevMode2();
return 0;
case WM_PAINT:
hdc=BeginPaint(hWnd, &ps);
TextOut(hdc,10,10,Mes,lstrlen(Mes));
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
DEVMODE를 구하는 방법은 여러 가지가 있지만 현실적으로 두 가지 방법만 알아 두면 될 것 같다. 첫번째 방법은 인쇄 공통 대화상자로 사용자가 선택한 프린터에 대한 설정 상태를 조사하는 방법이며 예제에서는 마우스 왼쪽 버튼을 누를 때 호출되는 DisplayDevMode1함수에서 이 방법을 보여주고 있다. 인쇄 대화상자 호출 후 PRINTDLG 구조체의 hDevMode 멤버를 읽으면 이 구조체를 구할 수 있다. 단 DEVMODE가 가변적인 크기를 가지기 때문에 hDevMode 멤버 자체가 이 구조체는 아니며 이 구조체를 포함하는 메모리의 핸들이므로 GlobalLock으로 포인터를 얻어야만 액세스할 수 있다. 일단 포인터를 얻으면 멤버는 자유롭게 읽을 수 있다. 실행 결과는 다음과 같다.
몇가지 간단한 정보들을 조사해 보았는데 결과를 보다시피 고유 데이터가 560바이트나 된다. 이 데이터의 포맷이나 정의에 대해서는 제조 업체가 제공하는 매뉴얼을 참조해야 알 수 있는데 구조체 형식으로 되어 있을 것이다. PrintDlg 함수는 사용자가 선택한 프린터의 초기화 정보를 조사하여 그 크기만큼 메모리를 할당하여 핸들을 hDevMode멤버에 대입해 준다. 따라서 이 함수를 호출한 후 인쇄가 완료되면 hDevMode를 해제해 주어야 하는데 같은 이유로 hDevNames도 같이 해제해 주어야 한다.
DEVMODE를 구하는 두번째 방법은 인쇄 대화상자를 통하지 않고 곧바로 기본 프린터를 조사했을 때이다. 이때는 PRINTDLG 구조체를 참조할 수 없으므로 직접 이 구조체를 구해야 한다. 프린터 이름 또는 핸들로부터 이 구조체를 구하는 함수가 몇가지 있는데 다음 함수가 가장 일반적이다.
LONG DocumentProperties(HWND hWnd, HANDLE hPrinter, LPTSTR pDeviceName, PDEVMODE pDevModeOutput, PDEVMODE pDevModeInput, DWORD fMode);
이 함수는 DEVMODE를 조사할 수도 있고 변경할 수도 있기 때문에 설정 정보를 바꿀 때 아주 편리하다. 이 함수의 마지막 인수인 fMode에 DM_IN_PROMPT 플래그가 지정되면 프린터 설정 대화상자를 보여주는데 이 대화상자를 위해 부모 윈도우 핸들과 대화상자의 캡션이 필요하다. 첫번째 인수와 세번째 인수가 각각 부모 윈도우의 핸들과 대화상자의 캡션이며 두번째 인수가 조사대상 프린터의 핸들이다.
네번째 인수와 다섯번째 인수가 DEVMODE 구조체의 포인터인데 각각 출력용, 입력용이다. 프린터로부터 정보를 조사하고자 할 때는 네번째의 출력용 인수를 전달하고 정보를 변경할 때는 다섯번째의 입력용 인수를 전달하면 된다. 마지막 인수 fMode는 이 함수가 어떤 동작을 할지를 결정하는데 만약 이 값이 0이면 이 함수는 DEVMODE를 조사, 설정하는 대신 DEVMODE 구조체의 크기를 조사해 준다.
모드 |
설명 |
DM_IN_BUFFER |
pDevModeInput으로 전달된 DEVMODE로 프린터의 설정 상태를 변경한다. 이때 어떤 정보가 변경될 것인가는 dmFields값에 지정된 플래그를 따른다. |
DM_IN_PROMPT |
프린터 설정 대화상자를 보여주고 이 대화상자에서 사용자가 설정한 정보대로 프린터 설정을 변경한다. |
DM_OUT_BUFFER |
프린터의 설정 상태를 조사하여 pDevModeOutput 구조체로 복사한다. |
이 예제에서는 프린터의 설정 상태를 변경하지는 않고 조사하기만 하는데 다음 세 줄로 이 작업을 수행한다.
ret=DocumentProperties(hWndMain, hPrinter, szPrinter, NULL, NULL, 0);
dm=(DEVMODE *)malloc(ret);
DocumentProperties(hWndMain, hPrinter, szPrinter, dm, NULL, DM_OUT_BUFFER);
먼저 fMode를 0으로 전달하여 DEVMODE구조체의 크기를 조사한다. 이 구조체에는 프린터의 고유 정보가 포함되므로 미리 DEVMODE의 크기를 알 수 없기 때문에 이런 식으로 크기를 먼저 조사해야 한다. 그리고 조사된 크기만큼 메모리를 할당하고 다시 한번 이 함수를 호출하였다. fMode에 DM_OUT_BUFFER를 전달하여 정보를 조사하기만 하므로 네번째 pDevModeOutput인수만 전달하면 되며 다섯번째 입력용 인수는 NULL로 주어도 상관없다. 이제 dm에는 조사된 설정 정보가 복사되었으며 이 구조체값 중 관심있는 값을 보면 된다. 출력 결과는 앞에서 보인 결과와 동일하다.
//////////////////////////////////////////////////////////////////////////////////////////////
HDC m_hPrtdc;
DOCINFO m_doc;
PRINTDLG m_pd;
HWND pHwnd 인수로는 this->m_hWnd 이걸 넣어 주시면 되구요..
BOOL CPrint::OpenPrint(HWND pHwnd,bool bIsPrintDlg,int nXSize,int nYSize,int nMaxPage,bool bIsHorizon,int nFstyle,int nFsize,const char* strFName)
{
if(bIsPrintDlg == false)
{
// 기본 프린트 다이얼로그 없이 출력
char szPrinter[80];
char *szDevice, *szDriver, *szOutput;
GetProfileString("windows","device",",,,",szPrinter,80);
szDevice = strtok(szPrinter,",");
szDriver = strtok(NULL, ", ");
szOutput = strtok(NULL, ", ");
m_hPrtdc=CreateDC(szDriver,szDevice,szOutput,NULL);
if(m_hPrtdc==NULL) return FALSE;
m_pd.nFromPage = 1;
m_pd.nToPage = nMaxPage; // 인쇄 끝 Page를 입력 받는 값의 초기값을 지정
m_pd.nMinPage = 1;
m_pd.nMaxPage = nMaxPage; // 최대 몇 page까지 인쇄 할것인가를 지정 (인쇄 끝 Page를 적용)
m_pd.nCopies = 1;
}
else
{
// 프린터에 관한 정보를 구하고 DC를 만듬
memset(&m_pd,0,sizeof(PRINTDLG));
m_pd.lStructSize = sizeof(PRINTDLG);
m_pd.Flags = PD_RETURNDC | PD_DISABLEPRINTTOFILE | PD_NOSELECTION | PD_HIDEPRINTTOFILE;
m_pd.hwndOwner = pHwnd;
m_pd.hDevMode = m_pd.hDevMode;
m_pd.hDevNames = NULL;
m_pd.nFromPage = 1;
m_pd.nToPage = nMaxPage; // 인쇄 끝 Page를 입력 받는 값의 초기값을 지정
m_pd.nMinPage = 1;
m_pd.nMaxPage = nMaxPage; // 최대 몇 page까지 인쇄 할것인가를 지정 (인쇄 끝 Page를 적용)
m_pd.nCopies = 1;
PrintDlg(&m_pd);
m_hPrtdc = m_pd.hDC;
if(m_hPrtdc == NULL)
{
return FALSE;
}
}
// 인쇄 시작
m_doc.cbSize = sizeof(DOCINFO);
m_doc.lpszDocName = "Hanlim";
m_doc.lpszOutput = NULL;
m_doc.lpszDatatype = NULL;
m_doc.fwType = 0;
if(StartDoc(m_hPrtdc,&m_doc) <= 0)
{
DeleteDC(m_hPrtdc);
return FALSE;
}
AfxGetApp()->GetPrinterDeviceDefaults(&m_pd);
DEVMODE* devmode = (DEVMODE*)::GlobalLock(m_pd.hDevMode);
// Change the orientation : 수직인쇄=DMORIENT_PORTRAIT, 수평인쇄=DMORIENT_LANDSCAPE
if(bIsHorizon == true) devmode->dmOrientation = DMORIENT_LANDSCAPE;
else devmode->dmOrientation = DMORIENT_PORTRAIT;
if(nXSize != 0 && nYSize != 0)
{
// 용지 Size 설정
devmode->dmPaperSize = DMPAPER_USER;
devmode->dmPaperWidth = nXSize; // 단위 = mm임. 예) A4 Size = 21Cm 일때 2100으로 설정 해야함.
devmode->dmPaperLength = nYSize; //
devmode->dmFields = devmode->dmFields|DMPAPER_USER|DM_PAPERLENGTH|DM_PAPERWIDTH;
}
::ResetDC(m_hPrtdc, devmode);
::GlobalUnlock(m_pd.hDevMode);
m_nXpage = GetDeviceCaps(m_hPrtdc,HORZRES); // 가로 최대 size
m_nYpage = GetDeviceCaps(m_hPrtdc,VERTRES); // 세로 최대 size
m_nDpiX = GetDeviceCaps(m_hPrtdc,LOGPIXELSX);
m_nDpiY = GetDeviceCaps(m_hPrtdc,LOGPIXELSY);
if(m_pd.Flags & PD_PAGENUMS)
{
m_nFirstPage = m_pd.nFromPage;
m_nFinalPage = m_pd.nToPage;
}
else
{
m_nFirstPage = m_pd.nMinPage;
m_nFinalPage = m_pd.nMaxPage;
}
return TRUE;
}
///////////
비트맴을 불러오는 방식은 검색해보시면 많이 나와있습니다..
다이얼로그 에있는 그림이라면.. 이미 불러와서 출력 하셨을테니.. 별 무리 없이 하실수 있으실 겁니다...
int CPrintData::DrawBitmap(HBITMAP hBit,int nX, int nY, int nScale)
{
HDC MemDC;
HBITMAP OldBitmap;
int bx,by;
BITMAP bit;
MemDC = CreateCompatibleDC(m_Print.m_hPrtdc);
OldBitmap = (HBITMAP)SelectObject(MemDC, hBit);
GetObject(hBit, sizeof(BITMAP), &bit);
bx = bit.bmWidth;
by = bit.bmHeight;
// BitBlt(m_Print.m_hPrtdc,0,0,bx,by,MemDC,0,0,SRCCOPY);
if(nX != -1 && nY != -1)
{
SetStretchBltMode(m_Print.m_hPrtdc,BLACKONWHITE);
StretchBlt(m_Print.m_hPrtdc,nX,nY,bx*nScale,by*nScale,MemDC,0,0,bx,by,SRCCOPY);
}
SelectObject(MemDC, OldBitmap);
DeleteDC(MemDC);
if(nY == -1) return by*nScale;
else return bx*nScale;
}
/////////////////////////////////////////////////////////////////////////////////
인쇄 관련 오류
=============================================================================
조치코드 1.
CPreviewViewEx 의 서브클래스 CMyPreview 작성
-> 헤더파일
class CMyPreview : public CPreviewViewEx
{
...
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnDestroy();
DECLARE_MESSAGE_MAP()
protected:
void ModifyStyleToMainFrame( BOOL bEnable );
};
-> cpp 파일
/*--------------------------------------------------------------------------------
미리보기 생성;
인쇄미리보기뷰는 OnInitialUpdate()가 호출이 안된다. OnCreate() 에서 처리
--------------------------------------------------------------------------------*/
int CMyPreview::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CPreviewViewEx::OnCreate(lpCreateStruct) == -1)
return -1;
//미리보기창 띄워졌다고 알림
((CMyApp*)AfxGetApp())->OpenPreView();
//닫기버튼 비활성화
ModifyStyleToMainFrame( FALSE );
return 0;
}
/*--------------------------------------------------------------------------------
미리보기 종료
--------------------------------------------------------------------------------*/
void CMyPreview::OnDestroy()
{
CPreviewViewEx::OnDestroy();
CMyApp* pApp = (CMyApp*)AfxGetApp();
//인쇄미리보기창 종료됐다고 알림
pApp->ClosePreView();
//인쇄미리보기창이 종료가 됐으니...X버튼 활성화
if( 0 == pApp->IsPreViewCount() ) //mdi 프로그램이므로 미리보기가 여러개 열렸을수 있다.
ModifyStyleToMainFrame( TRUE );
}
/*----------------------------------------------------------------------
X버튼 활성,비활성화
----------------------------------------------------------------------*/
void CMyPreview::ModifyStyleToMainFrame( BOOL bEnable )
{
CWnd* pMainWnd = AfxGetMainWnd();
CFrameWnd* pParent = STATIC_DOWNCAST(CFrameWnd, pMainWnd);
ASSERT_VALID(pParent);
// MainFrame의 현재 설정된 스타일을 얻는다.
DWORD nStyle = ::GetClassLong( pParent->GetSafeHwnd(), GCL_STYLE );
if( bEnable )
nStyle &= ~CS_NOCLOSE;
else
nStyle |= CS_NOCLOSE; //이 속성을 추가하면 ALT+F4도 작동이 안된다.
//변경된 스타일 적용
::SetClassLong( pParent->GetSafeHwnd(), GCL_STYLE, nStyle );
}
=============================================================================
조치코드 2.
App 클래스내 메뉴->끝내기 ON_UPDATE_COMMAND_UI 핸들러 작성
-> 헤더파일
class CMyApp : public CWinAppEx
{
...
public:
inline void OpenPreView() { InterlockedIncrement(&m_nPreViewCount); }
inline void ClosePreView() { InterlockedDecrement(&m_nPreViewCount); }
inline LONG IsPreViewCount() { return m_nPreViewCount; }
public:
afx_msg void OnUpdateAppExit(CCmdUI *pCmdUI);
DECLARE_MESSAGE_MAP()
private:
LONG volatile m_nPreViewCount; //현재 띄워진 인쇄미리보기창의 갯수
};
-> cpp 파일
BEGIN_MESSAGE_MAP(CMyApp, CWinAppEx)
ON_UPDATE_COMMAND_UI(ID_APP_EXIT, &CMyApp::OnUpdateAppExit)
END_MESSAGE_MAP()
CMyApp::CMyApp()
{
m_nPreViewCount = 0;
}
void CMyApp::OnUpdateAppExit(CCmdUI *pCmdUI)
{
//인쇄 미리보기가 띄워져 있으면 프로그램 종료메뉴 비활성
if( 0 == IsPreViewCount() )
pCmdUI->Enable( TRUE );
else
pCmdUI->Enable( FALSE );
}
=============================================================================
조치코드 3.
미리보기뷰를 띄울때 서브클래스로 구현한 클래스가 생성되도록 설정
-> 헤더파일
class CMyView : public CFormView
{
...
protected:
afx_msg void OnFilePrintPreview();
DECLARE_MESSAGE_MAP()
};
-> Cpp 파일
BEGIN_MESSAGE_MAP(CISMManagerView, CFormView)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CMyView::OnFilePrintPreview)
END_MESSAGE_MAP()
void CMyView::OnFilePrintPreview()
{
// AFXPrintPreview(this);
CPrintPreviewState *pState= new CPrintPreviewState;
if (!DoPrintPreview(IDD_AFXBAR_RES_PRINT_PREVIEW, this, RUNTIME_CLASS(CMyPreview), pState))
{
TRACE0("Error: OnFilePrintPreview failed.\n");
AfxMessageBox(AFX_IDP_COMMAND_FAILURE);
delete pState; // preview failed to initialize, delete State now
}
}
'C & C++ > C & C++' 카테고리의 다른 글
[공통] 메모리 누수 추적 (0) | 2011.04.28 |
---|---|
CFileDialog::GetFolderPath() 에러 대처법 (0) | 2011.04.28 |
[Tip] 모니터 정보 조사 (0) | 2011.04.28 |
메시지맵(Message Map) 을 사용하지 않고 메시지 처리하는 방법 (0) | 2011.04.28 |
인터넷 바로가기 만들기 (0) | 2011.04.27 |
댓글