對話框打印,網上一搜一大堆,基本分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;
}
基本上是完整代碼了,如果有內存錯誤,請聯繫我