Thread View: borland.public.cppbuilder.nativeapi
8 messages
8 total messages
Started by JD
Wed, 27 Jun 2007 13:02
More on Dialogs
Author: JD
Date: Wed, 27 Jun 2007 13:02
Date: Wed, 27 Jun 2007 13:02
28 lines
1953 bytes
1953 bytes
TPrinterSetupDialog and TPrintDialog will set the TPrinter's
PrinterIndex. Other dialogs such as PageSetupDlg, PrintDlg and
PrintDlgEx do not (duh ... they're win32 APIs) but they do
return a DEVMODE and DEVNAMES struct that one can use to get
the name of the printer but I have a problem with both structs.
The DEVMODE has a dmDeviceName member which is defined as:
BCHAR dmDeviceName[CCHDEVICENAME];
Specifies the the "friendly" name of the printer; for
example, "PCL/HP LaserJet" in the case of PCL/HP
LaserJet๏ฟฝ. This string is unique among device drivers.
Note that this name may be truncated to fit in the
dmDeviceName array.
and CCHDEVICENAME is 32. Similarly, the DEVNAMES related
member is also 32 bytes long.
I went back and looked at some old BCB5 code to verify that I
was recalling correctly that 32 bytes isn't enough for the
names listed in TPrinter's Printers and I found where I had
noted a change from saving 50 characters for the printer name
to 100 because TPrinter::Printers::IndexOf was failing.
So, How does one get the same names that TPrinter uses?
~ JD
Re: More on Dialogs
Author: Bob Gonder
Date: Wed, 27 Jun 2007 15:02
Date: Wed, 27 Jun 2007 15:02
23 lines
1581 bytes
1581 bytes
JD wrote: >TPrinterSetupDialog and TPrintDialog will set the TPrinter's >PrinterIndex. Other dialogs such as PageSetupDlg, PrintDlg and >PrintDlgEx do not (duh ... they're win32 APIs) but they do >So, How does one get the same names that TPrinter uses? I'm guessing this would be easiest... // IPrintDialogServices::GetCurrentPrinterName(pPrinterName, pcchSize) // Returns the printer name for the currently selected printer. http://msdn2.microsoft.com/en-us/library/ms646897.aspx Win2000 required There is a pName in each of these PRINTER_INFO_1 PRINTER_INFO_2 PRINTER_INFO_3 PRINTER_INFO_4 PRINTER_INFO_5 You could maybe use EnumPrinters() and strncmp()
Re: More on Dialogs
Author: Remy Lebeau (TeamB)
Date: Wed, 27 Jun 2007 16:37
Date: Wed, 27 Jun 2007 16:37
40 lines
2304 bytes
2304 bytes
"JD" <nospam@nospam.com> wrote in message
news:46825ff3@newsgroups.borland.com...
> TPrinterSetupDialog and TPrintDialog will set the TPrinter's
> PrinterIndex. Other dialogs such as PageSetupDlg, PrintDlg
> and PrintDlgEx do not (duh ... they're win32 APIs) but they
> do return a DEVMODE and DEVNAMES struct that one
> can use to get the name of the printer but I have a problem
> with both structs.
If you look at the source code for TPrinterSetupDialog and
TPrintDialog, you can see how they both use the DEVMODE and DEVNAMES
structures to set the PrinterIndex. Both dialogs call the
TPrinter::SetPrinter() method. For example (translated from the
dialog source code):
void __fastcall SetPrinter(HANDLE DeviceMode, HANDLE DeviceNames)
{
DEVNAMES *DevNames = (DEVNAMES*) GlobalLock(DeviceNames);
try
{
Printer()->SetPrinter(
((char*)DevNames) + DevNames->wDeviceOffset,
((char*)DevNames) + DevNames->wDriverOffset,
((char*)DevNames) + DevNames->wOutputOffset,
(int)DeviceMode);
}
__finally
{
GlobalUnlock(DeviceNames);
GlobalFree(DeviceNames);
// TPrinter takes ownership of the DeviceMode so do not
free it...
}
}
Gambit
Re: More on Dialogs
Author: JD
Date: Thu, 28 Jun 2007 08:06
Date: Thu, 28 Jun 2007 08:06
59 lines
3202 bytes
3202 bytes
"Remy Lebeau \(TeamB\)" <no.spam@no.spam.com> wrote:
>
> [...] Both dialogs call the TPrinter::SetPrinter() method.
> For example (translated from the dialog source code):
That is what I'm doing but that doesn't address my original
concern about the length of the printer name. It is my current
understanding that one must first set the PrinterIndex before
one calls SetPrinter.
Simple enough if one uses Printers::IndexOf method but that
will only work if the string is an exact match and the printer
names in Printers are not limited to 32 bytes. Now, the docs
for DEVNAMES says it's 32 bytes and it must match DEVMODE
exactly but I just found this in the docs for PRINTDLGEX:
Note that the dmDeviceName member of the DEVMODE structure
also specifies a printer name. However, dmDeviceName is
limited to 32 characters, and the wDeviceOffset name is
not.
Obviousely there's an error there and I assume that the error
is that dmDeviceName and the string pointed to by wDeviceOffset
need not match exactly so I can use wDeviceOffset with IndexOf.
I just don't have a printer installed that's longer than 32
bytes to test it. Is there a way for me to lengthen a printer's
name?
> // TPrinter takes ownership of the DeviceMode so do not free it...
Don't I need to dispose of the TPrinter's current DEVMODE? For
example:
if( PageSetupDlg(&psd) )
{
unsigned int hDevMode;
char ADevice[MAX_PATH], ADriver[MAX_PATH], APort[MAX_PATH];
Printer()->PrinterIndex = Printer()->Printers->IndexOf(
(char*)pDevNames + pDevNames->wDeviceOffset );
Printer()->GetPrinter( ADevice, ADriver, APort, hDevMode );
DEVNAMES *pDevNames = (DEVNAMES *) GlobalLock( psd.hDevNames );
Printer()->SetPrinter( (char*)pDevNames +
pDevNames->wDeviceOffset,
(char*)pDevNames +
pDevNames->wDriverOffset,
(char*)pDevNames +
pDevNames->wOutputOffset,
(int)pds.hDevMode);
GlobalFree( (void*)hDevMode );
GlobalFree( psd.hDevNames );
}
Thanks.
~ JD
Re: More on Dialogs
Author: JD
Date: Thu, 28 Jun 2007 08:07
Date: Thu, 28 Jun 2007 08:07
13 lines
1399 bytes
1399 bytes
Bob Gonder <notbg@notmindspring.invalid> wrote: > >I'm guessing this would be easiest... >// IPrintDialogServices::GetCurrentPrinterName(pPrinterName, pcchSize) >// Returns the printer name for the currently selected printer. I had found that and was considering it when I found some docs that contradicted the 32 byte limit so I was worried about nothing. See my reply to Gambit for more. Thanks. ~ JD
Re: More on Dialogs
Author: Remy Lebeau (TeamB)
Date: Thu, 28 Jun 2007 09:23
Date: Thu, 28 Jun 2007 09:23
106 lines
4814 bytes
4814 bytes
"JD" <nospam@nospam.com> wrote in message
news:46836c01@newsgroups.borland.com...
> It is my current understanding that one must first set the
> PrinterIndex before one calls SetPrinter.
Then your understanding has been misled. TPrinter::SetPrinter()
assigns the PrinterIndex (amongst other things) based on the
structures that are passed to it. The current TPrinter::PrinterIndex
value has no effect whatsoever on TPrinter::SetPrinter().
> Simple enough if one uses Printers::IndexOf method but
> that will only work if the string is an exact match
TPrinter::SetPrinter() performs a comparison of the original printer
information that is provided by the OS. It does not perform a
comparison of the string values that are stored (and potentially
reformatted) in the TPrinter::Printers list. On the other hand, the
TPrinter::Printers list is a plain TStringList, and thus its IndexOf()
method does an exact string match on the TStringList values only, not
the original printer information. They are not the same search
operation.
> Now, the docs for DEVNAMES says it's 32 bytes and it
> must match DEVMODE exactly but I just found this in the
> docs for PRINTDLGEX:
The VCL dialogs do not use PRINTDLGEX.
> Note that the dmDeviceName member of the DEVMODE structure
> also specifies a printer name. However, dmDeviceName is
> limited to 32 characters, and the wDeviceOffset name is
> not.
>
> Obviousely there's an error there
There is not. The dmDeviceName member will contain the truncated copy
of a name if the original is longer than 32 characters. Your original
post in this thread even quoted as much:
"Note that this name may be truncated to fit in the dmDeviceName
array."
The wDeviceOffset name, on the other hand, will be the full name.
> I assume that the error is that dmDeviceName and the string pointed
> to by wDeviceOffset need not match exactly
Yes, they do match - but only up to 32 characters. But TPrinter never
uses dmDeviceName to begin with, so it should never be limited to 32
characters.
> so I can use wDeviceOffset with IndexOf.
Only on NT-based systems. On Win9x-based systems, the
TPrinter::Printers list does NOT reflect the wDeviceOffset name by
itself. On those systems, TPrinter::SetPrinter() merges the device
and port names together into a single string, and then stores that
into the TPrinters::Printers list. That entire string is what
IndexOf() will be comparing. You are not taking that into account
when you use IndexOf(). If you really want to know the index of a
given printer name regardless of the OS version, then you would have
to ignore IndexOf() altogether and do your own substring scanning of
the list manually, ie:
#include <StrUtils.hpp>
AnsiString device = ((char*)pDevNames) + pDevNames->wDeviceOffset;
TStrings *list = Printer()->Printers;
int index = -1;
for(int i = 0; i < list->Count; ++i)
{
if( AnsiStartsText(device, list->Strings[i]) )
{
index = i;
break;
}
}
> I just don't have a printer installed that's longer than 32
> bytes to test it. Is there a way for me to lengthen a printer's
> name?
Have you tried simply renaming it in the Control Panel's Printers
applet? Depending on your OS version, it may or may not allow you to
do that directly.
> Don't I need to dispose of the TPrinter's current DEVMODE?
No. That is handled automatically inside of TPrinter::SetPrinter().
> Printer()->PrinterIndex = Printer()->Printers->IndexOf(
> (char*)pDevNames + pDevNames->wDeviceOffset );
Use SetPrinter() instead of assigning PrinterIndex manually. Let
TPrinter do all the work for you.
> GlobalFree( (void*)hDevMode );
DO NOT DO THAT!!!! You are freeing memory that TPrinter owns and uses
internally.
Gambit
Re: More on Dialogs
Author: JD
Date: Thu, 28 Jun 2007 21:38
Date: Thu, 28 Jun 2007 21:38
72 lines
3299 bytes
3299 bytes
"Remy Lebeau \(TeamB\)" <no.spam@no.spam.com> wrote:
>
>> It is my current understanding that one must first set the
>> PrinterIndex before one calls SetPrinter.
>
> Then your understanding has been misled.
From a post by Damon:
if( PageSetupDlg(&psd) )
{
HANDLE HMem;
char device[MAX_PATH], driver[MAX_PATH], port[MAX_PATH];
Printer()->GetPrinter(device, driver, port, (int)HMem);
Printer()->SetPrinter(device, driver, port, (int)psd.hDevMode);
// do some printing...
GlobalFree(HMem);
GlobalFree(psd.hDevNames);
}
and that wasn't working until I added setting the PrinterIndex.
> TPrinter::SetPrinter() assigns the PrinterIndex (amongst
> other things) based on the structures that are passed to
> it.
Thanks. It's working fine now and the TDialogs respect the
currently selected printer even if an API dialog selected it
but I'm still working on getting the API dialogs to respect
it (they open with the default printer).
>> [...] but I just found this in the docs for PRINTDLGEX:
>
> The VCL dialogs do not use PRINTDLGEX.
True, but they do use DEVNAMES.
>> Obviousely there's an error there
>
> There is not.
The docs for DEVNAMES wDeviceOffset says (in part):
[...] (maximum of 32 bytes including the null) [...]
This string must be identical to the dmDeviceName member
of the DEVMODE structure.
However, not only does PRINTDLGEX contradict that, so does
PAGESETUPDLG:
If both hDevNames and hDevMode have valid handles and the
printer name specified by the wDeviceOffset member of the
DEVNAMES structure is not the same as the name specified
by the dmDeviceName member of the DEVMODE structure, the
system uses the name specified by wDeviceOffset by default.
They can't both be correct (unless there are different rules
for input and output) but that's a moot point now. It just
added to the confusion.
>> Don't I need to dispose of the TPrinter's current DEVMODE?
>
> No. That is handled automatically inside of TPrinter::SetPrinter().
Good to know.
Thanks.
~ JD
Re: More on Dialogs
Author: Remy Lebeau (TeamB)
Date: Thu, 28 Jun 2007 23:07
Date: Thu, 28 Jun 2007 23:07
62 lines
3258 bytes
3258 bytes
"JD" <nospam@nospam.com> wrote in message
news:46842a5a@newsgroups.borland.com...
> Printer()->GetPrinter(device, driver, port, (int)HMem);
That is retreiving the current information for the printer specified
by the TPrinter::PrinterIndex property.
However, the last paramter is wrong. The HMem variable is declared as
a HANDLE. GetPrinter() expects a reference to an int instead. By
casting a HANDLE variable to an int like that, it is actually passing
a temporary variable to GetPrinter(). The compiler even warns as
much. So GetPrinter() is going to assign a value to that temporary
instead of to the original HMem variable. HMem will remain
uninitialized, which will be very bad when that variable is passed to
GlobalFree() later on.
> Printer()->SetPrinter(device, driver, port,
(int)psd.hDevMode);
That is telling TPrinter to use the same printer device that it was
already set to, rather than using the actual printer that was selected
in the dialog. So the code is basically just updating the
TPrinter::Capabilities property without updating the
TPrinter::PrinterIndex property.
> GlobalFree(HMem);
That is undefined behavior since HMem is uninitialized. However, even
if HMem had been filled in properly, that line would still be
problematic. It is freeing the DEVMODE that TPrinter::SetPrinter()
had already freed internally.
> that wasn't working until I added setting the PrinterIndex.
It wasn't working because it was using TPrinter incorrectly to begin
with. The code should look more like the following:
if( ::PageSetupDlg(&psd) )
{
DEVNAMES *pDevNames = (DEVNAMES*) GlobalLock(psd.hDevNames);
char *pDriver = ((char*)pDevNames) + pDevNames->wDriverOffset;
char *pDevice = ((char*)pDevNames) + pDevNames->wDeviceOffset;
char *pPort = ((char*)pDevNames) + pDevNames->wOutputOffset;
Printer()->SetPrinter(pDevice, pDriver, pPort,
(int)psd.hDevMode);
GlobalUnlock(psd.hDevNames);
GlobalFree(psd.hDevNames);
// do some printing...
}
> The docs for DEVNAMES wDeviceOffset says (in part):
You are looking at old documentation There is no size limitation
mentioned in the current documentation.
Gambit
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads