Vista x64でPrintDlgを使用しても機能せず、32ビットおよびXPで正常に動作する
-
08-07-2019 - |
質問
従来のプリンター「セットアップ」を使用したアプリがあります。まだ使用しているコード PrintDlg
カスタムテンプレートを使用して、方向と用紙サイズ/ソースとともに、さまざまな種類の印刷タスク(レポートや図面など)に使用するプリンターをユーザーが選択できるようにします。
XPおよび32ビットVistaでは動作しますが、Vista x64では CommDlgExtendedError()
を介して CDERR_MEMLOCKFAILURE
を取得します。 PRINTDLG
構造体の最低限の入力のみで実行しようとしましたが、パラメーターに PD_PRINTSETUP
または PD_RETURNDEFAULT
が含まれている場合、そのエラー。
プリンタの選択/ページ設定が PageSetupDlg
および PrintDlgEx
、かなりの量のコードを変更したり、ユーザーに印刷とプリンターの設定を提示する方法を完全に変更したりしない限り、明らかな簡単な移行はありません。
64ビットVistaでこの問題を見つけた人はいますか?また、回避策を見つけましたか?
注:
他の制約のため、アプリケーションは管理者として実行されます
解決
Microsoftフォーラムで関連する投稿を見つけました: Vista x64 、UAC昇格プロセスからDocumentPropertiesが失敗します
非管理者として実行されているPrintDlgが機能することをサンプルプログラムで確認しました。
他のヒント
Quickenコミュニティフォーラムに投稿を見つけました: Vista 64 Quicken 2008の印刷問題の解決、および関連するFAQ:印刷できない場合、または「プリンターとの通信エラー」を受信した場合はどうなりますか?およびエミュレーションプリンターを使用することをお勧めします。
アプリに印刷を追加しているときに、この問題に遭遇しました。私はPrintDialogクラスを使用していましたが、32ビットアプリとしてコンパイルされていればうまく機能しますが、64ビットモードでコンパイルされた場合でもポップアップしません。エラーメッセージも何もありません。 ShowDialogの呼び出しはすぐに戻ります。 (64ビットVistaを実行していることに注意してください。)
PrintDlgを使用しようとしましたが、同じ問題があります。私はオンラインで見たところ、64ビットVistaを持っているすべての人がこれを見ているわけではないようですが、同様の問題を抱えている人がたくさんいます。とにかく、私は最終的に独自のバージョンのPrintDialog(オンラインのコードから借用)を書くことにしましたが、これは少しトリッキーでした(オンラインコードの一部にバグがあったため)。ここにソリューションを投稿します。
注:私のバージョンでは、「印刷範囲」、「コピー」、「ファイルに印刷」など、ダイアログからいくつかの項目が削除されています。これは簡単に追加できるはずですが、私のアプリはそれらを必要としませんでした。また、" Type:"が何であるかわからなかったフィールドが表示されていたため、同様に除外しました。
ダイアログは次のようになります。
代替テキストhttp://www.geocities.com/deancooper2000/PrintDialog64.jpg
そして、ここに私のコードがあります(再作成するのは非常に簡単なはずなので、デザイナーのコードは省略しました):
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Printing;
using System.Printing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Zemetrics.Diagnostics;
namespace Utils
{
/// <summary>
/// The PrintDialog64 class replaces the standard PrintDialog with one that works in Vista x64
/// </summary>
public partial class PrintDialog64 : Form
{
#region Private members
[DllImport("winspool.drv", EntryPoint="DocumentPropertiesW")]
private static extern int DocumentProperties(IntPtr hWnd,IntPtr hPrinter,[MarshalAs(UnmanagedType.LPWStr)] string pDeviceName,IntPtr pDevMode,IntPtr devModeIn,int fMode);
[DllImport("winspool.drv")] private static extern int OpenPrinter(string pPrinterName,out IntPtr hPrinter,IntPtr pDefault);
[DllImport("winspool.drv")] private static extern int ClosePrinter(IntPtr phPrinter);
[DllImport("kernel32.dll")] private static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll")] private static extern int GlobalUnlock(IntPtr hMem);
[DllImport("kernel32.dll")] private static extern int GlobalFree(IntPtr hMem);
private const int DM_PROMPT = 4;
private const int DM_OUT_BUFFER = 2;
private const int DM_IN_BUFFER = 8;
private List<PrinterItem> printers;
private string printerName;
private string originalName;
private IntPtr hDevMode = IntPtr.Zero;
#endregion
/// <summary>
/// Gets or sets the printer that prints the document
/// </summary>
public PrinterSettings PrinterSettings { get; set; }
/// <summary>
/// Gets or sets a value indicating the PrintDocument used to obtain PrinterSettings.
/// </summary>
public PrintDocument Document { get; set; }
/// <summary>
/// Constructs a replacement for the standard PrintDialog with one that works in Vista x64
/// </summary>
public PrintDialog64()
{
InitializeComponent();
}
#region PrinterItem class
/// <summary>
/// The PrinterItem class holds a reference to a PrintQueue and allows us to sort a list based on printer name
/// </summary>
private class PrinterItem : IComparable<PrinterItem>
{
#region Private members
private PrinterItem() {}
#endregion
/// <summary>
/// Construct a PrinterItem by supplying a reference to the printer's PrintQueue class
/// </summary>
///
/// \param[in] printer Reference to PrintQueue class for this printer
public PrinterItem(PrintQueue printer)
{
Printer = printer;
}
/// <summary>
/// Reference to PrintQueue class for this printer
/// </summary>
public PrintQueue Printer { get; set; }
/// <summary>
/// The string for this class is simply the FullName of the printer
/// </summary>
public override string ToString()
{
return Printer.FullName;
}
#region IComparable<PrinterItem> Members
/// <summary>
/// Implements IComparable interface to allow sorting of PrinterItem classes (based on printer name)
/// </summary>
///
/// \param[in] other The other PrinterItem class that we are to compare this one to
public int CompareTo(PrinterItem other)
{
return other.Printer.FullName.CompareTo(this.Printer.FullName);
}
#endregion
}
#endregion
private List<PrinterItem> GetPrinters()
{
List<PrinterItem> printers = new List<PrinterItem>();
EnumeratedPrintQueueTypes[] Queue_types = {EnumeratedPrintQueueTypes.Local,EnumeratedPrintQueueTypes.Connections};
try {
using (LocalPrintServer server = new LocalPrintServer())
foreach (PrintQueue printer in server.GetPrintQueues(Queue_types))
printers.Add(new PrinterItem(printer));
} catch {}
printers.Sort();
return printers;
}
private void PrintDialog64_Shown(object sender, EventArgs e)
{
originalName = Document.PrinterSettings.PrinterName;
printers = GetPrinters();
int index=0, i=0;
foreach(PrinterItem printer in printers) {
nameComboBox.Items.Add(printer.ToString());
if (printer.ToString() == originalName) index = i;
i++;
}
nameComboBox.SelectedIndex = index;
}
private void nameComboBox_Leave(object sender, EventArgs e)
{
string text = nameComboBox.Text;
foreach(Object field in nameComboBox.Items)
if (((string) field).ToLower().StartsWith(text.ToLower())) nameComboBox.SelectedItem = field;
if (nameComboBox.SelectedIndex < 0)
nameComboBox.SelectedIndex = 0;
}
private void nameComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
PrintQueue printer = printers[nameComboBox.SelectedIndex].Printer;
if (hDevMode!=IntPtr.Zero) GlobalFree(hDevMode);
PrinterSettings.PrinterName = printerName = printer.FullName;
hDevMode = PrinterSettings.GetHdevmode(Document.DefaultPageSettings);
statusValue .Text = printer.QueueStatus.ToString()=="None" ? "Ready" : printer.QueueStatus.ToString();
whereValue .Text = printer.Location=="" ? printer.QueuePort.Name : printer.Location;
commentValue.Text = printer.Comment;
}
private void propertiesButton_Click(object sender, EventArgs e)
{
IntPtr handle;
OpenPrinter(printerName, out handle, IntPtr.Zero);
IntPtr pDevMode = GlobalLock( hDevMode );
DocumentProperties(this.Handle, handle, printerName, pDevMode, pDevMode, DM_IN_BUFFER | DM_PROMPT | DM_OUT_BUFFER);
GlobalUnlock( hDevMode );
PrinterSettings.SetHdevmode( hDevMode );
PrinterSettings.DefaultPageSettings.SetHdevmode( hDevMode );
ClosePrinter(handle);
}
private void pageDefaultsButton_Click(object sender, EventArgs e)
{
PageSetupDialog setup = new PageSetupDialog();
setup.PageSettings = Document.DefaultPageSettings;
if (setup.ShowDialog() == DialogResult.OK) {
if (hDevMode!=IntPtr.Zero) GlobalFree(hDevMode);
hDevMode = PrinterSettings.GetHdevmode( Document.DefaultPageSettings = setup.PageSettings );
}
}
private void okButton_Click(object sender, EventArgs e)
{
if (hDevMode!=IntPtr.Zero) GlobalFree(hDevMode);
}
private void cancelButton_Click(object sender, EventArgs e)
{
if (hDevMode!=IntPtr.Zero) GlobalFree(hDevMode);
PrinterSettings.PrinterName = originalName;
}
}
}