Delphi XE 6中还有另一个bug(可能是在添加Unicode支持的时候添加的)。
最初可以通过尝试调用以下命令来公开它:
procedure TForm1.Button1Click(Sender: TObject);
begin
Printer.Orientation := poLandscape; //use Vcl.Printers
end;
此操作失败,并显示隐藏错误
- 所选打印机不支持此操作 *
调试他们的代码
当您跟踪VCL时,问题归结为全局TPrinter
无法获得打印机的DEVMODE
结构。当它尝试从Vcl调用Windows DocumentProperties
函数时,此操作失败。打印机:
if DeviceMode = 0 then // alloc new device mode block if one was not passed in
begin
DeviceMode := GlobalAlloc(GHND,
DocumentProperties(0, FPrinterHandle, ADevice, nil, nil, 0));
//...snip...
end;
bufferSize := DocumentProperties(0, FPrinterHandle, ADevice, PDeviceMode(@dummyDevMode), PDeviceMode(@dummyDevMode), 0); //20160522 Borland forgot to check the result
奇怪的是,DocumentProperties
失败了:返回**-1**。这很奇怪,因为参数在概念上没有什么特别的错误。DocumentProperties
在失败时没有记录为SetLastError,但GetLastError始终返回:
50 -请求不受支持
代码审核
这里有一些非常糟糕的代码:
- 它调用
DocumentProperties
,但不检查返回值(* 如果失败,则返回小于零的值 *) DocumentProperties
返回 * 有符号 * 32位整数- 当
GlobalAlloc
采用无符号32位整数时下溢 DocumentProperties
失败,返回-1
- 在传递给
GlobalAlloc
时转换为$ffffffff
,然后尝试分配4 GB内存
但只在XE 6中失败
奇怪的是,同样的代码在 Delphi 7中也能工作。它不应该在支持Unicode的XE 6中失败。查看XE 6中Winapi.WinSpool
与DocumentProperties
的头转换:
function DocumentProperties( hWnd: HWND; hPrinter: THandle; pDeviceName: LPWSTR; const pDevModeOutput: TDeviceMode; var pDevModeInput: TDeviceMode; fMode: DWORD): Longint; stdcall; overload;
function DocumentProperties( hWnd: HWND; hPrinter: THandle; pDeviceName: LPWSTR; pDevModeOutput: PDeviceMode; pDevModeInput: PDeviceMode; fMode: DWORD): Longint; stdcall; overload;
function DocumentPropertiesA(hWnd: HWND; hPrinter: THandle; pDeviceName: LPSTR; const pDevModeOutput: TDeviceModeA; var pDevModeInput: TDeviceModeA; fMode: DWORD): Longint; stdcall; overload;
function DocumentPropertiesA(hWnd: HWND; hPrinter: THandle; pDeviceName: LPSTR; pDevModeOutput: PDeviceModeA; pDevModeInput: PDeviceModeA; fMode: DWORD): Longint; stdcall; overload;
function DocumentPropertiesW(hWnd: HWND; hPrinter: THandle; pDeviceName: LPWSTR; const pDevModeOutput: TDeviceModeW; var pDevModeInput: TDeviceModeW; fMode: DWORD): Longint; stdcall; overload;
function DocumentPropertiesW(hWnd: HWND; hPrinter: THandle; pDeviceName: LPWSTR; pDevModeOutput: PDeviceModeW; pDevModeInput: PDeviceModeW; fMode: DWORD): Longint; stdcall; overload;
他们在那里做了一些非常花哨的const-var
/typed-untyped
过载步法。
其中 Delphi 7具有更简单的:
function DocumentProperties( hWnd: HWND; hPrinter: THandle; pDeviceName: PChar; const pDevModeOutput: TDeviceMode; var pDevModeInput: TDeviceMode; fMode: DWORD): Longint; stdcall;
function DocumentPropertiesA(hWnd: HWND; hPrinter: THandle; pDeviceName: PAnsiChar; const pDevModeOutput: TDeviceModeA; var pDevModeInput: TDeviceModeA; fMode: DWORD): Longint; stdcall;
function DocumentPropertiesW(hWnd: HWND; hPrinter: THandle; pDeviceName: PWideChar; const pDevModeOutput: TDeviceModeW; var pDevModeInput: TDeviceModeW; fMode: DWORD): Longint; stdcall;
完成最小测试程序
这里已经过了午夜了你们中的一些人刚刚醒来。而我已经过了睡觉时间,伴随着一大堆的咒骂和脏话:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, Windows, WinSpool;
var
dwBufferLen: DWORD;
defaultPrinter: string;
ADevice: PChar; //Pointer to printer name
printerHandle: THandle;
devModeSize: Integer;
deviceMode: THandle;
begin
dwBufferLen := 1024;
SetLength(defaultPrinter, dwBufferLen);
GetDefaultPrinter(PChar(defaultPrinter), @dwBufferLen);
SetLength(defaultPrinter, dwBufferLen);
ADevice := PChar(defaultPrinter);
if not OpenPrinter(ADevice, {var}printerHandle, nil) then
raise Exception.Create('Error checking removed for expository purposes');
devModeSize := DocumentProperties(0, printerHandle, ADevice, nil, nil, 0);
if devModeSize < 0 then
begin
//DocumentProperties is documented to have failed if it returns a value less than zero.
//It's not documented to have also SetLastError; but we'll raise it anyway (Error code 50 - The request is not supported)
RaiseLastOSError;
Exit;
//It's a good thing we fail. Because the return value -1 is coerced into an unsigned $FFFFFFFF.
//Delphi then asks GlobalAlloc to try to allocate 4 GB of memory. *le sigh*
end;
deviceMode := GlobalAlloc(GHND, NativeUInt(devModeSize));
if deviceMode = 0 then
raise Exception.Create('It''s DocumentProperties above that fails. GlobalAlloc is just the victim of being asked to allocate 4GB of memory.');
end.
怎么走?
奖励聊天
- QC#6725:一个单独的打印相关bug,8年前关闭,现在仍然存在于VCL中。他们将无效参数传递给他们甚至不应该使用的Windows函数。该函数随后因参数无效而失败,导致代码回退到使用检查
win.ini
的16位Windows旧版兼容性函数。而不是首先使用正确的功能。
2条答案
按热度按时间ffvjumwh1#
我也有这个bug……它总是返回-1,但只有当我在IDE中调试时。窃听器突然出现了。我认为这是一个Windows更新或自动驱动程序更新。我没有改变任何具体的工作站设置。经过几个小时的测试和调试,我注意到一个解决问题的技巧:
查询“GetDriverInfos”似乎会发出某种重置,PrinterSystem开始工作。
我知道这很奇怪,它为我工作(使用柏林10.1)。我们以前在所有 Delphi 版本中都有这个bug,随机出现。
vngu2lb82#
我只是想发布一个“TL; www.example.com信息的“DR”pinvoke.net
我不得不更换
与
完整的代码是:
由于第一次使用
DocumentProperties()
的唯一目的是确定devmode的大小,因此将out参数设置为空指针没有问题。