2010-03-13 229 views
7

編輯:我的錯!我預料,實際上只有PrinterSettings的本地實例發生更改時,纔會將更改寫回默認的打印機設置。 - 下面的代碼似乎按預期工作如何顯示打印機屬性/首選項對話框並保存更改?

我想顯示給定打印機的自定義打印機屬性。我需要這個作爲我試圖編寫的自定義PrintDialog的一部分。

我可以在網上找到的大多數例子都顯示對話框,但用戶可能做出的任何更改都會丟失,這使得它無用。

實施例: http://www.codeproject.com/KB/system/PrinterPropertiesWindow.aspx

(關於網頁上面:我試圖改變代碼由BartJoy的建議(在頁面上),但沒有解決問題)

我還試圖將樣品和在pinvoke.net頁面上的建議,但它仍然不能正常工作:

http://www.pinvoke.net/default.aspx/winspool.documentproperties

從上面的網站,我認爲這個問題可能只有在64位Windows中的nd /或打印機名稱是否長於32個字符。

我不知道我應該嘗試接下來...我欣賞任何建議和意見!

編輯:這是我曾嘗試:

[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); 
} 
+0

而你怎麼樣設法得到chages救回來?此代碼確實會更改printerSettings,但這些更改不會保存爲默認打印機設置:( – Ando 2010-05-11 07:24:07

回答

3
  • 當你的應用程序啓動:
    • 有你分配之前詢問的DEVMODE結構的正確尺寸的打印機驅動程序它?
    • 您是否要求設備驅動程序在分配後使用默認設置初始化DEVMODE緩衝區?
  • 當你的應用程序彈出打印機的對話框:在fMode參數DocumentProperties
    • 你設置DM_IN_BUFFERDM_OUT_BUFFER標誌(除DM_IN_PROMPT)?
    • 您是否已將pDevModeInputpDevModeOutput指向您在應用程序啓動時初始化的DEVMODE緩衝區?
    • 都在DEVMODE緩衝的dmFields位正確設置您的通話DocumentProperties(... DM_IN_PROMPT ...)
    • 之前你保留DEVMODE緩衝區的內容在調用DocumentProperties(... DM_IN_PROMPT ...)之間?

參見:

+0

感謝您的輸入。我相信我會做這些事情。我已經更新了該問題幷包含了我嘗試過的代碼。 – 2010-03-17 00:13:37

+0

我的錯!我希望將這些更改寫回到默認的打印機設置,以便當我使用新的PrinterSettings()調用相同的方法時,它將反映過去的更改。 - 看起來它正在正常工作,因爲printerSettings已更新正確 – 2010-03-17 00:40:32

7

即使回答結束了工作的方式進入的問題,我覺得有以下提供了一個更好的答案原來的問題,

(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; 
} 
+0

@JeffRow我剛剛試過你在win8 64位機器上提供的代碼,並且DocumentProperties返回-1,因此當Marshal。AllocHGlobal(sizeNeeded)被調用,它拋出一個錯誤「內存不足以繼續執行程序」。這是有意義的,因爲sizeNeeded是-1。 – Thierry 2014-03-10 15:45:16

+1

儘管它處於早期階段,但這似乎是迄今爲止我發現的最穩定的代碼,可以解決我的問題。根據我以前的評論,唯一的小改變是,爲了獲得sizeNeeded,你需要改變API調用來使用IntPtr.Zero而不是0,即Dim sizeNeeded As Integer = DocumentProperties(Me.Handle,IntPtr.Zero ,printerSettings.PrinterName,IntPtr.Zero,pDevMode,0)。感謝分享! – Thierry 2014-03-10 16:04:49

7

如果你的目標的x86彙編和X64的機器上運行,從傑夫·羅的代碼將無法正常工作:分配devModeData時,DocumentPropreties總是會失敗,並返回-1 sizeNeeded,具有LastError碼13

爲了解決這個問題,無論是確保您指定AnyCPU或者只是改變調用DocumentPropreties到以下:

int sizeNeeded = DocumentProperties(pHandle, 
            IntPtr.Zero, 
            printerSettings.PrinterName, 
            IntPtr.Zero, // This solves it 
            pDevMode, 
            fMode); 

使用IntPtr.Zero,而不是一個適當的指針DEVMODE結構看起來不對,但DocumentProperties是第一次調用不會嘗試在該位置修改內存。調用返回的唯一數據是存儲代表打印驅動程序內部參數的設備模式數據所需的內存大小。

參考:

相關問題