Как отобразить диалоговое окно свойств/настроек принтера и сохранить изменения?
EDIT: моя вина! Я ожидал, что изменения будут записаны обратно в настройки принтера по умолчанию, когда на самом деле изменяется только локальный экземпляр PrinterSettings. - Приведенный ниже код, похоже, работает по назначению
Я пытаюсь показать свойства принтера для данного принтера. Мне нужно это как часть пользовательского PrintDialog, который я пытаюсь написать.
большинство примеров, которые я могу найти в интернете, показывают диалог, но любые изменения пользователь может потерять, что делает его бесполезным.
пример: http://www.codeproject.com/KB/system/PrinterPropertiesWindow.aspx
(относительно выше страницы: я попытался изменить код, как предложил BartJoy (на странице), но это не исправило его)
Я также попробовал образец и предложения на pinvoke.net страница, но это все еще не работа:
http://www.pinvoke.net/default.aspx/winspool.documentproperties
из вышеперечисленных сайтов я предполагаю, что проблема может быть только в 64-битной версии Windows и/или если имя принтера длиннее 32 символов.
Я не знаю, что я должен попробовать следующий... Я ценю любые предложения и комментарии!
EDIT: вот что я пробовал:
[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesW", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPWStr)] string pDeviceName,
IntPtr pDevModeOutput, IntPtr pDevModeInput, 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")]
static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll")]
static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("kernel32.dll")]
static extern bool GlobalFree(IntPtr hMem);
private const int DM_PROMPT = 4;
private const int DM_OUT_BUFFER = 2;
private const int DM_IN_BUFFER = 8;
private void OpenPrinterPropertiesDialog()
{
var printerSettings = new System.Drawing.Printing.PrinterSettings();
var printerName = printerSettings.PrinterName;
IntPtr handle;
OpenPrinter(printerName, out handle, IntPtr.Zero);
IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings);
IntPtr pDevMode = GlobalLock(hDevMode);
int sizeNeeded = DocumentProperties(this.Handle, handle, printerName, pDevMode, pDevMode, 0);
IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded);
DocumentProperties(this.Handle, handle, printerName, devModeData, pDevMode, DM_IN_BUFFER | DM_PROMPT | DM_OUT_BUFFER);
ClosePrinter(handle);
GlobalUnlock(hDevMode);
printerSettings.SetHdevmode(devModeData);
printerSettings.DefaultPageSettings.SetHdevmode(devModeData);
GlobalFree(hDevMode);
Marshal.FreeHGlobal(devModeData);
}
Я попытался использовать метод OpenPrinter и ClosePrinter и передайте devModeData в качестве выходного параметра во втором вызове, поскольку мне показалось странным, что исходный код из pinvoke.net не делал этого. (но я признаю, что я не знаю, что я делаю-это просто проб и ошибок).
вот исходный код с сайта pinvoke:
private void OpenPrinterPropertiesDialog(PrinterSettings printerSettings)
{
IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings);
IntPtr pDevMode = GlobalLock(hDevMode);
int sizeNeeded = DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, pDevMode, pDevMode, 0);
IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded);
DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, IntPtr.Zero, pDevMode, 14);
GlobalUnlock(hDevMode);
printerSettings.SetHdevmode(devModeData);
printerSettings.DefaultPageSettings.SetHdevmode(devModeData);
GlobalFree(hDevMode);
Marshal.FreeHGlobal(devModeData);
}
4 ответов
- при запуске приложения:
- вы запросили драйвер принтера для правильного размера
DEVMODE
структура перед ее выделением? - вы попросили драйвер устройства инициализировать
DEVMODE
буфер с настройками по умолчанию после его выделения?
- вы запросили драйвер принтера для правильного размера
- когда ваше приложение открыло диалоговое окно принтера:
- ты
DM_IN_BUFFER
иDM_OUT_BUFFER
флаги (в дополнение кDM_IN_PROMPT
) в доDocumentProperties
? - вы указали как
pDevModeInput
иpDevModeOutput
доDEVMODE
буфер инициализируется при запуске приложения? - это
dmFields
битDEVMODE
буфер правильно установлен до вашего вызоваDocumentProperties(... DM_IN_PROMPT ...)
- вы сохраняете содержимое
DEVMODE
буфер между вызовамиDocumentProperties(... DM_IN_PROMPT ...)
?
- ты
посмотреть:
несмотря на то, что ответ в конечном итоге пробился в вопрос, я думаю, что следующее дает лучший ответ на исходный вопрос,
(1), потому что он явно не изменяет переданные PrinterSettings, если пользователь отменяет.
(2), потому что он возвращает DialogResult, который, вероятно, будет интересен вызывающему.
[DllImport("kernel32.dll")]
static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll")]
static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("kernel32.dll")]
static extern bool GlobalFree(IntPtr hMem);
[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesW", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPWStr)] string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);
private const int DM_PROMPT = 4;
private const int DM_OUT_BUFFER = 2;
private const int DM_IN_BUFFER = 8;
private DialogResult EditPrinterSettings(PrinterSettings printerSettings)
{
DialogResult myReturnValue = DialogResult.Cancel;
IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings);
IntPtr pDevMode = GlobalLock(hDevMode);
int sizeNeeded = DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, pDevMode, pDevMode, 0);
IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded);
long userChoice = DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, devModeData, pDevMode, DM_IN_BUFFER | DM_PROMPT | DM_OUT_BUFFER);
long IDOK = (long)DialogResult.OK;
if (userChoice == IDOK)
{
myReturnValue = DialogResult.OK;
printerSettings.SetHdevmode(devModeData);
printerSettings.DefaultPageSettings.SetHdevmode(devModeData);
}
GlobalUnlock(hDevMode);
GlobalFree(hDevMode);
Marshal.FreeHGlobal(devModeData);
return myReturnValue;
}
если вы нацелены на компиляцию x86 и запускаетесь с машины x64, код от Jeff Roe не будет работать: при выделении devModeData
, DocumentPropreties
всегда будет терпеть неудачу и возвращает sizeNeeded
-1, а с LastError
код 13.
чтобы решить проблему, либо убедитесь, что вы target любой ЦП или просто измените вызов DocumentPropreties
следующим образом:
int sizeNeeded = DocumentProperties(pHandle,
IntPtr.Zero,
printerSettings.PrinterName,
IntPtr.Zero, // This solves it
pDevMode,
fMode);
используя IntPtr.Zero
вместо правильного указателя на структуру DevMode выглядит неправильно, но этот первый вызов DocumentProperties не пытается изменить память в этом положении. Единственные данные, возвращаемые вызовом, - это размер памяти, необходимый для хранения данных режима устройства, представляющих внутренние параметры драйвера печати.
ссылки:
кроме того, если вы хотите сделать это с помощью классов WPF (PrintQueue, PrintTicket) эта страница указывает вам в правильном направлении:
http://social.msdn.microsoft.com/Forums/en/wpf/thread/0dc695c1-578d-4da5-8f68-b2a257846c02