|
Is there a way to save the printer & print options that a user selects from the CPrintDialog::DoModal() function into an ini file/registry entry? I want the user to be able to set up certain options when printing checks so that they won't have to change printers/trays each time they want to print a check.
Thanks,
Chad
|
|
|
|
|
Save the DEVMODE structure, device name, driver name, and port name from the m_pd member. You can then pass them to the CDC::CreateDC() function to create the printer DC that you use for printing.
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" mYkel - 21 Jun '04
Within you lies the power for good - Use it! Honoured as one of The Most Helpful Members of 2004
|
|
|
|
|
Ok so I have the following code to save the printer options:
CPrintDialog pd(TRUE);<br />
pd.DoModal();<br />
DEVMODE *dm=pd.GetDevMode();<br />
theApp.WriteProfileBinary("CheckPrinter", "DevMode", (LPBYTE)&dm, sizeof(dm));<br />
theApp.WriteProfileString("CheckPrinter", "DeviceName", (LPCTSTR) pd.GetDeviceName());<br />
theApp.WriteProfileString("CheckPrinter", "PortName", (LPCTSTR) pd.GetPortName());<br />
theApp.WriteProfileString("CheckPrinter", "DriverName", (LPCTSTR) pd.GetDriverName());
Then to read it back:
DEVMODE *hDevMode;<br />
UINT nl;<br />
CString csDeviceName, csPortName, csDriverName;<br />
theApp.GetProfileBinary("CheckPrinter", "DevMode", (LPBYTE*)&hDevMode, &nl);<br />
theApp.GetProfileString("CheckPrinter", "DeviceName", csDeviceName);<br />
theApp.GetProfileString("CheckPrinter", "PortName", csPortName);<br />
theApp.GetProfileString("CheckPrinter", "DriverName", csDriverName);<br />
<br />
CDC* pDC=new CDC();<br />
pDC->CreateDC(csDriverName, csDeviceName, csPortName, hDevMode);
Does this look right? I am not familar with the Get/WriteProfileBinary functions and it didn't look like it was retreiving the information correctly when I debugged it.
Also how would I get the same options back into a CPrintDialog object so that a user could change the options?
And finally where should I call the CDC::CreateDC() function from the view class so that the CPrintDialog dialog won't be displayed again? Currently I have OnPreparePrinting(), OnBeginPrinting(), OnEndPrinting(), and OnPrint() functions extended in my view class.
Thanks,
Chad
|
|
|
|
|
crowbarcberg wrote:
DEVMODE *dm=pd.GetDevMode();
theApp.WriteProfileBinary("CheckPrinter", "DevMode", (LPBYTE)&dm, sizeof(dm));
dm is a pointer to a DEVMODE structure, so sizeof(dm) returns the size of the pointer - 4 bytes. Use sizeof(DEVMODE) and it should work correctly.
Ryan "Punctuality is only a virtue for those who aren't smart enough to think of good excuses for being late" John Nichol "Point Of Impact"
|
|
|
|
|
That didn't seem to work either, when I try to retreive the DEVMODE structure via:
<br />
DEVMODE *hDevMode;<br />
UINT nl;<br />
theApp.GetProfileBinary("CheckPrinter", "DevMode", (LPBYTE*)&hDevMode, &nl);
the string values have garbage in them, and most of the other paramaters are 0, or rather large values. Any other ideas?
Thanks,
Chad
|
|
|
|
|
What is the return value of GetProfileBinary? what is the value in nl?
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" mYkel - 21 Jun '04
Within you lies the power for good - Use it! Honoured as one of The Most Helpful Members of 2004
|
|
|
|
|
crowbarcberg wrote:
DEVMODE *dm=pd.GetDevMode();
theApp.WriteProfileBinary("CheckPrinter", "DevMode", (LPBYTE)&dm, sizeof(dm));
dm is a pointer, so what you are saving here is the address of the DEVMODE structure, not the data in the DEVMODE structure.
theApp.WriteProfileBinary("CheckPrinter", "DevMode", (LPBYTE)dm, sizeof(DEVMODE));
crowbarcberg wrote:
Also how would I get the same options back into a CPrintDialog object so that a user could change the options?
That is the fun part. You have to rebuild the hDevMode and hDevNames members of the m_pd member of the CPrintDialog dialog. Remember that those are HGLOBAL handles to movable global memory objects, not pointers to a locally allocated memory block. Here is code that I use to do this:
LPDEVMODE pDM = pConfig->StringToDevMode(pConfig->DevMode);
int size = sizeof(DEVMODE);
HGLOBAL hDEVMODE = GlobalAlloc(GHND, size);
void *pV = GlobalLock(hDEVMODE);
memcpy(pV, pDM, size);
GlobalUnlock(pV);
delete pDM;
pDM = NULL;
size = sizeof(DEVNAMES);
size += (pConfig->Printer.GetLength() + 1) * sizeof(TCHAR);
size += (pConfig->Port.GetLength() + 1) * sizeof(TCHAR);
size += (pConfig->Driver.GetLength() + 1) * sizeof(TCHAR);
HGLOBAL hDEVNAMES = GlobalAlloc(GHND, size);
LPDEVNAMES pDN = (LPDEVNAMES)GlobalLock(hDEVNAMES);
pDN->wDefault = 0;
pDN->wDriverOffset = sizeof(DEVNAMES);
pDN->wDeviceOffset = (WORD)(pDN->wDriverOffset + pConfig->Driver.GetLength() + 1);
pDN->wOutputOffset = (WORD)(pDN->wDeviceOffset + pConfig->Printer.GetLength() + 1);
_tcsncpy((TCHAR *)((int)pDN + pDN->wDriverOffset), pConfig->Driver, pConfig->Driver.GetLength());
_tcsncpy((TCHAR *)((int)pDN + pDN->wDeviceOffset), pConfig->Printer, pConfig->Printer.GetLength());
_tcsncpy((TCHAR *)((int)pDN + pDN->wOutputOffset), pConfig->Port, pConfig->Port.GetLength());
GlobalUnlock(pDN);
CPrintSetupDialog dlg(this);
dlg.m_pd.hDevMode = hDEVMODE;
dlg.m_pd.hDevNames = hDEVNAMES;
if (dlg.DoModal() == IDOK)
{
pConfig->Printer = dlg.GetDeviceName();
pConfig->Driver = dlg.GetDriverName();
pConfig->Port = dlg.GetPortName();
m_EditSendTo.SetWindowText(pConfig->Printer);
pDM = dlg.GetDevMode();
pConfig->DevMode = pConfig->DevModeToString(pDM);
GlobalUnlock(pDM);
}
else
{
GlobalFree (hDEVMODE);
}
GlobalFree (hDEVNAMES);
crowbarcberg wrote:
And finally where should I call the CDC::CreateDC() function from the view class so that the CPrintDialog dialog won't be displayed again? Currently I have OnPreparePrinting(), OnBeginPrinting(), OnEndPrinting(), and OnPrint() functions extended in my view class.
The Print dialog is called from OnPreparePrinting(), so I think I would do it there. Have a look in MSDN[^] for more information. I have done this only in a dialog based app so I did not have the Doc-View framework at my disposal and have not had to worry about which function to override.
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" mYkel - 21 Jun '04
Within you lies the power for good - Use it! Honoured as one of The Most Helpful Members of 2004
|
|
|
|
|
You were right I was just saving the address of the DEVMODE structure, no wonder it was reading back in garbage.
In the second part what are the pConfig and m_EditSendTo variables? And is the CPrintSetupDialog just a class you extended from CPrintDialog?
I will take a look at the MSDN docs and let you know what works for calling CreateDC().
Thanks!
Chad
|
|
|
|
|
pConfig is a pointer to a class that stores all my configuration data, the printer stuff is just a small part it. m_EditSendTo is an edit control that displays the selected printer and CPrintSetupDialog is a subclass of CPrintDialog.
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" mYkel - 21 Jun '04
Within you lies the power for good - Use it! Honoured as one of The Most Helpful Members of 2004
|
|
|
|
|
Ok so here is the function that I have that is suppose to read values from the registry and setup the CPrintDialog with those values:
<br />
void CMainFrame::OnFileCheckPrintSetup() <br />
{<br />
DEVMODE *hDevMode;<br />
UINT nl;<br />
CString csDeviceName, csPortName, csDriverName;<br />
theApp.GetProfileBinary("CheckPrinter", "DevMode", (LPBYTE*)&hDevMode, &nl);<br />
theApp.GetProfileString("CheckPrinter", "DeviceName", csDeviceName);<br />
theApp.GetProfileString("CheckPrinter", "PortName", csPortName);<br />
theApp.GetProfileString("CheckPrinter", "DriverName", csDriverName);<br />
<br />
CPrintDialog dlg(TRUE);<br />
LPDEVMODE pDM;<br />
HGLOBAL hDEVNAMES;<br />
HGLOBAL hDEVMODE;<br />
BOOL bLoadedData=FALSE;<br />
<br />
if(!csDeviceName.IsEmpty()&&!csPortName.IsEmpty()&&!csDriverName.IsEmpty())<br />
{<br />
bLoadedData=TRUE;<br />
<br />
pDM = hDevMode;<br />
int size = sizeof(DEVMODE);<br />
hDEVMODE = GlobalAlloc(GHND, size);<br />
void *pV = GlobalLock(hDEVMODE);<br />
memcpy(pV, pDM, size);<br />
GlobalUnlock(pV);<br />
delete pDM;<br />
pDM = NULL;<br />
<br />
size = sizeof(DEVNAMES);<br />
size += (csDeviceName.GetLength() + 1) * sizeof(TCHAR);<br />
size += (csPortName.GetLength() + 1) * sizeof(TCHAR);<br />
size += (csDriverName.GetLength() + 1) * sizeof(TCHAR);<br />
<br />
hDEVNAMES = GlobalAlloc(GHND, size);<br />
LPDEVNAMES pDN = (LPDEVNAMES)GlobalLock(hDEVNAMES);<br />
pDN->wDefault = 0;<br />
pDN->wDriverOffset = sizeof(DEVNAMES);<br />
pDN->wDeviceOffset = (WORD)(pDN->wDriverOffset + csDriverName.GetLength() + 1);<br />
pDN->wOutputOffset = (WORD)(pDN->wDeviceOffset + csDeviceName.GetLength() + 1);<br />
_tcsncpy((TCHAR *)((int)pDN + pDN->wDriverOffset), csDriverName, csDriverName.GetLength());<br />
_tcsncpy((TCHAR *)((int)pDN + pDN->wDeviceOffset), csDeviceName, csDeviceName.GetLength());<br />
_tcsncpy((TCHAR *)((int)pDN + pDN->wOutputOffset), csPortName, csPortName.GetLength());<br />
GlobalUnlock(pDN);<br />
<br />
dlg.m_pd.hDevMode = hDEVMODE;<br />
dlg.m_pd.hDevNames = hDEVNAMES;<br />
}<br />
<br />
<br />
<br />
if(dlg.DoModal()==IDOK)<br />
{<br />
DEVMODE *dm=dlg.GetDevMode();<br />
theApp.WriteProfileBinary("CheckPrinter", "DevMode", (LPBYTE)dm, sizeof(DEVMODE));<br />
theApp.WriteProfileString("CheckPrinter", "DeviceName", (LPCTSTR) dlg.GetDeviceName());<br />
theApp.WriteProfileString("CheckPrinter", "PortName", (LPCTSTR) dlg.GetPortName());<br />
theApp.WriteProfileString("CheckPrinter", "DriverName", (LPCTSTR) dlg.GetDriverName());<br />
<br />
if(bLoadedData)<br />
GlobalUnlock(pDM);<br />
}<br />
else<br />
{<br />
if(bLoadedData)<br />
GlobalFree(hDEVMODE);<br />
}<br />
<br />
if(bLoadedData)<br />
GlobalFree(hDEVNAMES);<br />
}<br />
It reads/saves them fine, the problem is that it isn't correctly setting up the CPrintDialog with the read in options. Am I missing something?
Thanks,
Chad
|
|
|
|
|
Have a look at CWinApp::SelectPrinter , it may be better suited for you if you are using only one printer for all your documents (or have only one SDI document).
I have multiple documents that can be printed to different printers at different times with different formats, all without any user input, so I could not use CWinApp 's built in printer support.
There is one thing I did miss telling you is the DEVMODE extra data (dmDriverExtra member). If you do not correctly save that data you may be loosing some information.
Otherwise the code you posted looks fine. You will just have to step through your code in the debugger to see if there is anything going amiss.
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" mYkel - 21 Jun '04
Within you lies the power for good - Use it! Honoured as one of The Most Helpful Members of 2004
|
|
|
|
|
Actually the problem was reading in the strings with the GetProfileString() call I had:
theApp.GetProfileString("CheckPrinter", "DeviceName", csDeviceName);
which should have been:
csDeviceName=theApp.GetProfileString("CheckPrinter", "DeviceName", "");
so that was an easy fix.
Now the function is working fine, except when the program is closed. After I reopen the program I get an unhandled exception in COMDLG32.DLL (0xC0000005) Access Violation after the dlg.DoModal() call. When debugging the exception comes after executing: m_pd.hwndOwner = PreModal(); . Here is the code from the MFC file DLGPRNT.CPP:
<br />
int CPrintDialog::DoModal()<br />
{<br />
ASSERT_VALID(this);<br />
ASSERT(m_pd.Flags & PD_ENABLEPRINTHOOK);<br />
ASSERT(m_pd.Flags & PD_ENABLESETUPHOOK);<br />
ASSERT(m_pd.lpfnPrintHook != NULL);
ASSERT(m_pd.lpfnSetupHook != NULL);
<br />
m_pd.hwndOwner = PreModal();<br />
int nResult = ::PrintDlg(&m_pd);<br />
PostModal();<br />
return nResult ? nResult : IDCANCEL;<br />
}
I did check the dmDriverExtra member and it was saved correctly.
I also tried using the SelectPrinter function by just replacing:
dlg.m_pd.hDevMode = hDEVMODE;<br />
dlg.m_pd.hDevNames = hDEVNAMES;
with:
theApp.SelectPrinter(hDEVNAMES, hDEVMODE, FALSE);
while it didn't throw an error, it didn't populate the CPrintDialog with the correct printer/values. This application has a MDI, and what I am doing is an attempt to setup a different printer for checks, cash receipts, and reports with out user interaction (once they have been setup of course).
Thanks again,
Chad
|
|
|
|
|
OK so the problem was saving the dmDriverExtra param, which contains the number of bytes of private driver-data that follow the structure. So adding dm->dmDriverExtra=0; before saving the DEVMODE structure fixed it. Here is the completed function, included for completeness:
<br />
BOOL CMainFrame::SetupPrinter(CString csPrinter, CPrintDialog* pDlg)<br />
{<br />
DEVMODE *hDevMode=NULL;<br />
UINT nl;<br />
CString csDeviceName, csPortName, csDriverName;<br />
theApp.GetProfileBinary(csPrinter, "DevMode", (LPBYTE*)&hDevMode, &nl);<br />
csDeviceName=theApp.GetProfileString(csPrinter, "DeviceName", "");<br />
csPortName=theApp.GetProfileString(csPrinter, "PortName", "");<br />
csDriverName=theApp.GetProfileString(csPrinter, "DriverName", "");<br />
<br />
BOOL bLoadedDialog=TRUE;<br />
if(pDlg==NULL)<br />
{<br />
pDlg=new CPrintDialog(TRUE);<br />
bLoadedDialog=FALSE;<br />
}<br />
<br />
LPDEVMODE pDM;<br />
HGLOBAL hDEVNAMES;<br />
HGLOBAL hDEVMODE;<br />
BOOL bLoadedData=FALSE;<br />
BOOL bCancel=FALSE;<br />
<br />
if(!csDeviceName.IsEmpty()||!csPortName.IsEmpty()||!csDriverName.IsEmpty()||hDevMode!=NULL)<br />
{<br />
bLoadedData=TRUE;<br />
<br />
pDM = hDevMode;<br />
int size = sizeof(DEVMODE);<br />
hDEVMODE = GlobalAlloc(GHND, size);<br />
void *pV = GlobalLock(hDEVMODE);<br />
memcpy(pV, pDM, size);<br />
GlobalUnlock(pV);<br />
delete pDM;<br />
pDM = NULL;<br />
<br />
size = sizeof(DEVNAMES);<br />
size += (csDeviceName.GetLength() + 1) * sizeof(TCHAR);<br />
size += (csPortName.GetLength() + 1) * sizeof(TCHAR);<br />
size += (csDriverName.GetLength() + 1) * sizeof(TCHAR);<br />
<br />
hDEVNAMES = GlobalAlloc(GHND, size);<br />
LPDEVNAMES pDN = (LPDEVNAMES)GlobalLock(hDEVNAMES);<br />
pDN->wDefault = 0;<br />
pDN->wDriverOffset = sizeof(DEVNAMES);<br />
pDN->wDeviceOffset = (WORD)(pDN->wDriverOffset + csDriverName.GetLength() + 1);<br />
pDN->wOutputOffset = (WORD)(pDN->wDeviceOffset + csDeviceName.GetLength() + 1);<br />
_tcsncpy((TCHAR *)((int)pDN + pDN->wDriverOffset), csDriverName, csDriverName.GetLength());<br />
_tcsncpy((TCHAR *)((int)pDN + pDN->wDeviceOffset), csDeviceName, csDeviceName.GetLength());<br />
_tcsncpy((TCHAR *)((int)pDN + pDN->wOutputOffset), csPortName, csPortName.GetLength());<br />
GlobalUnlock(pDN);<br />
<br />
pDlg->m_pd.hDevMode = hDEVMODE;<br />
pDlg->m_pd.hDevNames = hDEVNAMES;<br />
}<br />
<br />
if(pDlg->DoModal()==IDOK)<br />
{<br />
DEVMODE *dm=pDlg->GetDevMode();<br />
dm->dmDriverExtra=0;
theApp.WriteProfileBinary(csPrinter, "DevMode", (LPBYTE)dm, sizeof(DEVMODE));<br />
theApp.WriteProfileString(csPrinter, "DeviceName", (LPCTSTR) pDlg->GetDeviceName());<br />
theApp.WriteProfileString(csPrinter, "PortName", (LPCTSTR) pDlg->GetPortName());<br />
theApp.WriteProfileString(csPrinter, "DriverName", (LPCTSTR) pDlg->GetDriverName());<br />
<br />
if(bLoadedData)<br />
GlobalUnlock(pDM);<br />
}<br />
else<br />
{<br />
bCancel=TRUE;<br />
if(bLoadedData)<br />
GlobalFree(hDEVMODE);<br />
}<br />
<br />
if(bLoadedData)<br />
GlobalFree(hDEVNAMES);<br />
<br />
if(!bLoadedDialog)<br />
delete pDlg;<br />
<br />
if(bCancel)<br />
return FALSE;<br />
else<br />
return TRUE;<br />
}<br />
|
|
|
|
|
Ok so now it's time to figure out how to use the data that I saved...
In the view's OnPreparePrinting() function how can I override the function so that it won't display the CPrintDialog unless the paramaters that I saved previously are missing? The problem is the CPrintInfo's m_pPD->m_pd.hDC var can't be NULL after the function is called.
Secondly to switch between printers can I just have a call like:
pDC->CreateDC(m_csCashDriverName, m_csCashDeviceName, m_csCashPortName, m_hCashDevMode);
in the OnDraw() function of my view class when I want to send the output to a different printer?
Thanks, Chad
|
|
|
|
|
In OnPreparePrinting, do not call DoPreparePrinting if you do not want the print dialog.
Read MSDN[^] to get a complete over view of printing in MFC.
This page is linked from the page I gave you earlier, be sure to read the entire section on printing, follow the links given.
Maybe also read the printing articles here on CP[^].
Just a suggestion, but sometimes the MFC framework does not fit the model you want to use, so rather than fight MFC, it may be better to roll your own printing model that better fits what you are trying to do.
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" mYkel - 21 Jun '04
Within you lies the power for good - Use it! Honoured as one of The Most Helpful Members of 2004
|
|
|
|
|
Quick question: How do I find out what Version #, Service Pack(s), and other versioning info for my Visual Studio 6.0?
When I click Help --> About Visual C++, I don't get anything useful.
Thanks.
Johnny
|
|
|
|
|
MS info about service packs is dreadful. Do you have VB installed? If so, it tells you on its splash screen which SP it has. In which case the chances are that VC++ will be at the same level.
However, a few years ago I remember there being a web page somewhere which listed various DLLs and their versions against the SP versions.
Kevin
|
|
|
|
|
Thanks. I looked in a few places (Add/Remove Programs, through Visual InterDev 6.0) nothing very useful. InterDev reported (SP5), but I didn't think I was that current so it makes me suspicious of this number tied into Visual Studio (VC++ 6.0).
Anyways, if I need to get current (establish a baseline for me and my fellow developers), I can get the latest Service Pack http://msdn.microsoft.com/vstudio/downloads/updates/sp/vs6/sp5/faq.aspx[^]
as it sez that SPs are cumulative and that the latest encompasses the fixes from the previous SPs.
Thanks!
Johnny
|
|
|
|
|
Well, I guess if all the constituent apps. were installed at the same time then V InterDev being at SP5 probably means they all are. However, if any indvidual apps. were reinstalled then you'd have to reapply the SP. BTW, are you aware that the latest SP is SP6? It was released about a year ago.
Kevin
|
|
|
|
|
See here:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\6.0\ServicePacks
"Ideas are a dime a dozen. People who put them into action are priceless." - Unknown
|
|
|
|
|
Hi everyone!
I'm trying to develop an application that uses a control just like the one used on Windows XP to test the hardware of microphone. Looks like a progressBar, that every time that you speaks on the microphone, it goes up and down showing something like the frequence of the wave.
Does anybody knows how can I implement this kind of control?
thanks,
cheers!
|
|
|
|
|
|
That's it!
thanks Ravi!
cheers!
|
|
|
|
|
Who can teach me how to show different view in the CSplitterWnd ?
I want to show different view in the second pane if I click the button in the
first pane.
|
|
|
|
|
There are several examples available here at CP and on the Web. Here's what I've implemented.
I have a CSplitterWndEx object that belongs to the frame class. In the left pane's handler (where the different view type is selected) I get a pointer to the frame's CSplitterWndEx object, and then call that object's ReplaceView() method. The ReplaceView() method looks like:
if ((GetPane(nRow, nCol)->IsKindOf(pViewClass)) == TRUE)
return;
CListView pActiveView = (CListView *) GetParentFrame()->GetActiveView();
if (NULL == pActiveView || GetPane(nRow, nCol) == pActiveView)
bSetActive = TRUE;
CDocument *pDoc = ((CListView *) GetPane(nRow, nCol))->GetDocument();
pDoc->m_bAutoDelete = FALSE;
((CListView *) GetPane(nRow, nCol))->DestroyWindow();
pDoc->m_bAutoDelete = TRUE;
CCreateContext context;
context.m_pNewViewClass = pViewClass;
context.m_pCurrentDoc = pDoc;
context.m_pNewDocTemplate = NULL;
context.m_pLastView = NULL;
context.m_pCurrentFrame = NULL;
CreateView(nRow, nCol, pViewClass, size, &context);
pNewView = (CListView *) GetPane(nRow, nCol);
if (TRUE == bSetActive)
GetParentFrame()->SetActiveView(pNewView);
RecalcLayout();
pDoc->UpdateAllViews(NULL); I would call this method with something like:
pMainFrame->GetSplitterWnd()->ReplaceView(0, 1, RUNTIME_CLASS(CMeetView), CSize(0, 0));
"Ideas are a dime a dozen. People who put them into action are priceless." - Unknown
|
|
|
|
|