MFC對話方塊使用CPrintDialog實現列印,指定印表機、後臺列印
阿新 • • 發佈:2019-01-29
對話方塊列印,網上一搜一大堆,基本分2類:
A類: CPrintDialog.DoModal,然後在模態對話方塊裡選印表機、列印配置;
B類:GetPrinterDeviceDefaults,呼叫預設印表機。
我的工作內容是理解以上2類後,再根據MSDN,實現MDF對話方塊後臺指定印表機列印。
廢話不多說,上菜~
功能:基於對話方塊的MFC列印(非文件檢視結構),指定印表機,後臺列印(不彈出對話方塊)
思路:
1、列舉印表機,並選擇其中一個;
2、CPrintDialog例項指定到選中的印表機;
3、CPrintDialog後臺列印
具體實現:
1、變數(控制元件)。在對話方塊上新增一個combobox(IDC_COMBO1,對應變數m_cboPrint)、一個edit(IDC_EDIT1),edit允許回車,多行(程式碼就不貼了,知道MFC應該就懂);
2、在OnInitDialog裡列舉印表機裝置,如果報函式未定義,加入標頭檔案#include <winspool.h>
需要呼叫兩次EnumPrinters函式,第一次的到結構體的大小,第二次得到印表機列表
// TODO: 在此新增額外的初始化程式碼 DWORD dwNeeded; DWORD dwReturn; DWORD dwFlag = PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL; EnumPrinters(dwFlag, NULL, 4, NULL, 0, &dwNeeded, &dwReturn); PRINTER_INFO_4* p4; p4 = new PRINTER_INFO_4[dwNeeded]; EnumPrinters(dwFlag, NULL, 4, (PBYTE)p4, dwNeeded, &dwNeeded, &dwReturn); for (int i = 0; i<(int)dwReturn; i++) this->m_cboPrint.AddString(p4[i].pPrinterName); delete []p4;
3、操作對話方塊,在IDC_EDIT1裡輸入列印的內容,在IDC_COMBO1裡選中印表機;
4、列印(我是用OK按鈕列印的,大家隨便)
// TODO: 在此新增控制元件通知處理程式程式碼 // CDialogEx::OnOK(); this->UpdateData(); CString strMessage; CString strPrintDevice; this->GetDlgItem(IDC_EDIT1)->GetWindowTextW(strMessage); strMessage += _T("\r\n"); //新增結尾,方便後面迴圈讀取列印資料 this->GetDlgItem(IDC_COMBO1)->GetWindowTextW(strPrintDevice); DWORD dwFlag = PD_ALLPAGES | PD_NOPAGENUMS | PD_USEDEVMODECOPIES | PD_HIDEPRINTTOFILE; //列印配置介面的按鈕可用性,因為後臺列印,其實這個配置沒什麼意義 CPrintDialog pPrintdlg(FALSE, dwFlag, this); //CPrintDialog例項化,因為MFC的列印裝置無關性,可以理解為這就是一臺印表機 HGLOBAL hDevMode = NULL; HGLOBAL hDevNames = NULL; if (GetPrinterDevice(strPrintDevice.GetBuffer(0), &hDevNames, &hDevMode)) //獲得指定印表機的配置、名字 AfxGetApp()->SelectPrinter(hDevNames, hDevMode); else AfxMessageBox(_T("Failed to select custom printer")); strPrintDevice.ReleaseBuffer(); pPrintdlg.m_pd.hDevMode = hDevMode; //讓pPrintdlg使用我們指定的印表機 pPrintdlg.m_pd.hDevNames = hDevNames; CDC dc; dc.Attach(pPrintdlg.CreatePrinterDC()); //後臺列印建立法,如果需要彈出列印對話方塊,請用DoModal DOCINFO di; //下面的內容網上很多,就不解釋了 di.cbSize = sizeof(DOCINFO); di.lpszDocName = _T("有驅列印測試"); di.lpszDatatype = NULL; di.lpszOutput = NULL; di.fwType = 0; dc.StartDocW(&di); dc.StartPage(); dc.SetMapMode(MM_TEXT); CRect recPrint(0, 0, dc.GetDeviceCaps(LOGPIXELSX), dc.GetDeviceCaps(LOGPIXELSY)); dc.DPtoLP(&recPrint); dc.SetWindowOrg(0, 0); CFont newFont; VERIFY(newFont.CreatePointFont(120, _T("宋體"), &dc)); CFont* oldFont = dc.SelectObject(&newFont); dc.SetTextAlign(TA_TOP | TA_LEFT); CString strPrint; int nIndex = 0; int x = 50; int y = 50; CSize textSize; textSize = dc.GetTextExtent(_T("00"), 2); //根據當前字型的寬、高,後面以此高度為行高 while ((nIndex = strMessage.Find(_T("\r\n"))) > -1) //將IDC_EDIT1編輯框中內容列印,支援換行,一次換行等於'\r\n',所以在開頭strMessage += _T("\r\n") { strPrint = strMessage.Left(nIndex); strMessage = strMessage.Mid(nIndex+2); dc.TextOutW(x, y, strPrint); y += textSize.cy; //下移一行,行高為字型高度 } dc.SelectObject(oldFont); newFont.DeleteObject(); dc.EndPage(); dc.EndDoc(); DeleteDC(dc.Detach());
關於GetPrinterDevice,來自微軟的一篇文章,點我跳轉
程式碼也貼出來
BOOL CMFCApplication2Dlg::GetPrinterDevice(LPTSTR pszPrinterName, HGLOBAL* phDevNames, HGLOBAL* phDevMode)
{
// if NULL is passed, then assume we are setting app object's
// devmode and devnames
if (phDevMode == NULL || phDevNames == NULL)
return FALSE;
// Open printer
HANDLE hPrinter;
if (OpenPrinter(pszPrinterName, &hPrinter, NULL) == FALSE)
return FALSE;
// obtain PRINTER_INFO_2 structure and close printer
DWORD dwBytesReturned, dwBytesNeeded;
GetPrinter(hPrinter, 2, NULL, 0, &dwBytesNeeded);
PRINTER_INFO_2* p2 = (PRINTER_INFO_2*)GlobalAlloc(GPTR,
dwBytesNeeded);
if (GetPrinter(hPrinter, 2, (LPBYTE)p2, dwBytesNeeded,
&dwBytesReturned) == 0) {
GlobalFree(p2);
ClosePrinter(hPrinter);
return FALSE;
}
ClosePrinter(hPrinter);
// Allocate a global handle for DEVMODE
HGLOBAL hDevMode = GlobalAlloc(GHND, sizeof(*p2->pDevMode) +
p2->pDevMode->dmDriverExtra);
ASSERT(hDevMode);
DEVMODE* pDevMode = (DEVMODE*)GlobalLock(hDevMode);
ASSERT(pDevMode);
// copy DEVMODE data from PRINTER_INFO_2::pDevMode
memcpy(pDevMode, p2->pDevMode, sizeof(*p2->pDevMode) +
p2->pDevMode->dmDriverExtra);
GlobalUnlock(hDevMode);
// Compute size of DEVNAMES structure from PRINTER_INFO_2's data
DWORD drvNameLen = lstrlen(p2->pDriverName)+1; // driver name
DWORD ptrNameLen = lstrlen(p2->pPrinterName)+1; // printer name
DWORD porNameLen = lstrlen(p2->pPortName)+1; // port name
// Allocate a global handle big enough to hold DEVNAMES.
HGLOBAL hDevNames = GlobalAlloc(GHND,
sizeof(DEVNAMES) +
(drvNameLen + ptrNameLen + porNameLen)*sizeof(TCHAR));
ASSERT(hDevNames);
DEVNAMES* pDevNames = (DEVNAMES*)GlobalLock(hDevNames);
ASSERT(pDevNames);
// Copy the DEVNAMES information from PRINTER_INFO_2
// tcOffset = TCHAR Offset into structure
int tcOffset = sizeof(DEVNAMES)/sizeof(TCHAR);
ASSERT(sizeof(DEVNAMES) == tcOffset*sizeof(TCHAR));
pDevNames->wDriverOffset = tcOffset;
memcpy((LPTSTR)pDevNames + tcOffset, p2->pDriverName,
drvNameLen*sizeof(TCHAR));
tcOffset += drvNameLen;
pDevNames->wDeviceOffset = tcOffset;
memcpy((LPTSTR)pDevNames + tcOffset, p2->pPrinterName,
ptrNameLen*sizeof(TCHAR));
tcOffset += ptrNameLen;
pDevNames->wOutputOffset = tcOffset;
memcpy((LPTSTR)pDevNames + tcOffset, p2->pPortName,
porNameLen*sizeof(TCHAR));
pDevNames->wDefault = 0;
GlobalUnlock(hDevNames);
GlobalFree(p2); // free PRINTER_INFO_2
// set the new hDevMode and hDevNames
*phDevMode = hDevMode;
*phDevNames = hDevNames;
return TRUE;
}
基本上是完整程式碼了,如果有記憶體錯誤,請聯絡我