DOBON.NET DOBON.NETプログラミング掲示板過去ログ

API使用のプリンタ設定変更についてHelp!(C#)

環境/言語:[WindowsXP Visual C# .NET Framework2.0]
分類:[.NET]

こんにちは 初めて投稿します。初心者です。
現在プリンタの設定をプログラムから変更したいと思っています。
目的は、ユーザごとに使用しているプリンタの機種が違うという環境で、
エクセルファイルを印刷する際に、任意のシートで片面印刷と両面印刷を切り替えるというものです。

下記のソースについて、
アカウントがadministratorの場合は設定の変更が成功しますが、制限付アカウントの場合は「アクセスが拒否されました」のエラーで失敗してしまいます。
どなたか助けて頂ければ嬉しいです!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace WindowsFormsApplication1
{
class Class1
{
}
public class PrinterSettings
{
#region "Private Variables"
private IntPtr hPrinter = new System.IntPtr();
private PRINTER_DEFAULTS PrinterValues = new PRINTER_DEFAULTS();
private PRINTER_INFO_2 pinfo = new PRINTER_INFO_2();
private DEVMODE dm;
private IntPtr ptrDM;
private IntPtr ptrPrinterInfo;
private int sizeOfDevMode = 0;
private int lastError;
private int nBytesNeeded;
private long nRet;
private int intError;
private System.Int32 nJunk;
private IntPtr yDevModeData;

#endregion
#region "Win API Def"
[DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern Int32 GetLastError();
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesA", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);
[DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true,
CharSet = CharSet.Ansi, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
private static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel,
IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded);

[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA",
SetLastError = true, CharSet = CharSet.Ansi,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool
OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter,
out IntPtr hPrinter, ref PRINTER_DEFAULTS pd);
[DllImport("winspool.drv", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr
pPrinter, int Command);
#endregion
#region "Data structure"
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_DEFAULTS
{
public int pDatatype;
public int pDevMode;
public int DesiredAccess;
}

[StructLayout(LayoutKind.Sequential)]
private struct PRINTER_INFO_2
{
[MarshalAs(UnmanagedType.LPStr)]
public string pServerName;
[MarshalAs(UnmanagedType.LPStr)]
public string pPrinterName;
[MarshalAs(UnmanagedType.LPStr)]
public string pShareName;
[MarshalAs(UnmanagedType.LPStr)]
public string pPortName;
[MarshalAs(UnmanagedType.LPStr)]
public string pDriverName;
[MarshalAs(UnmanagedType.LPStr)]
public string pComment;
[MarshalAs(UnmanagedType.LPStr)]
public string pLocation;
public IntPtr pDevMode;
[MarshalAs(UnmanagedType.LPStr)]
public string pSepFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pPrintProcessor;
[MarshalAs(UnmanagedType.LPStr)]
public string pDatatype;
[MarshalAs(UnmanagedType.LPStr)]
public string pParameters;
public IntPtr pSecurityDescriptor;
public Int32 Attributes;
public Int32 Priority;
public Int32 DefaultPriority;
public Int32 StartTime;
public Int32 UntilTime;
public Int32 Status;
public Int32 cJobs;
public Int32 AveragePPM;
}
private const short CCDEVICENAME = 32;
private const short CCFORMNAME = 32;
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
public string dmFormName;
public short dmUnusedPadding;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
}
#endregion

続く

#region "Constants"
private const int DM_DUPLEX = 0x1000;
private const int DM_IN_BUFFER = 8;
private const int DM_OUT_BUFFER = 2;
private const int PRINTER_ACCESS_ADMINISTER = 0x4;
private const int PRINTER_ACCESS_USE = 0x8;
private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
private const int PRINTER_ALL_ACCESS =
(STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER
| PRINTER_ACCESS_USE);
#endregion
#region "Function to change printer settings"
public bool ChangePrinterSetting(string PrinterName, System.Drawing.Printing.PrinterSettings PS)
{

if (((int)PS.Duplex < 1) || ((int)PS.Duplex > 3))
{
throw new ArgumentOutOfRangeException("nDuplexSetting",
"nDuplexSetting is incorrect.");
}
else
{
dm = this.GetPrinterSettings(PrinterName);

dm.dmDuplex = (short)PS.Duplex;
Marshal.StructureToPtr(dm, yDevModeData, true);
pinfo.pDevMode = yDevModeData;
pinfo.pSecurityDescriptor = IntPtr.Zero;

Marshal.StructureToPtr(pinfo, ptrPrinterInfo, true);
lastError = Marshal.GetLastWin32Error();
nRet = Convert.ToInt16(SetPrinter(hPrinter, 2, ptrPrinterInfo, 0));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());

}
if (hPrinter != IntPtr.Zero)
ClosePrinter(hPrinter);
return Convert.ToBoolean(nRet);
}
}
private DEVMODE GetPrinterSettings(string PrinterName)
{
DEVMODE dm;
const int PRINTER_ACCESS_ADMINISTER = 0x4;
const int PRINTER_ACCESS_USE = 0x8;
const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);


PrinterValues.pDatatype =0;
PrinterValues.pDevMode = 0 ;
PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;
nRet = Convert.ToInt32(OpenPrinter(PrinterName,
out hPrinter, ref PrinterValues));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out nBytesNeeded);
if (nBytesNeeded <= 0)
{
throw new System.Exception("Unable to allocate memory");
}
else
{

ptrPrinterInfo = Marshal.AllocCoTaskMem(nBytesNeeded);
ptrPrinterInfo = Marshal.AllocHGlobal(nBytesNeeded);
nRet = Convert.ToInt32(GetPrinter(hPrinter, 2,
ptrPrinterInfo, nBytesNeeded, out nJunk));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
pinfo = (PRINTER_INFO_2)Marshal.PtrToStructure(ptrPrinterInfo,
typeof(PRINTER_INFO_2));
IntPtr Temp = new IntPtr();
if (pinfo.pDevMode == IntPtr.Zero)
{
IntPtr ptrZero = IntPtr.Zero;
sizeOfDevMode = DocumentProperties(IntPtr.Zero, hPrinter,
PrinterName, ptrZero, ref ptrZero, 0);

ptrDM = Marshal.AllocCoTaskMem(sizeOfDevMode);
int i ;
i = DocumentProperties(IntPtr.Zero, hPrinter, PrinterName, ptrDM,
ref ptrZero, DM_OUT_BUFFER);
if ((i < 0) || (ptrDM == IntPtr.Zero))
{
throw new System.Exception("Cannot get DEVMODE data");
}
pinfo.pDevMode = ptrDM;
}
intError = DocumentProperties(IntPtr.Zero, hPrinter,
PrinterName, IntPtr.Zero , ref Temp , 0);
yDevModeData= Marshal.AllocHGlobal(intError);
intError = DocumentProperties(IntPtr.Zero, hPrinter,
PrinterName, yDevModeData , ref Temp , 2);
dm = (DEVMODE)Marshal.PtrToStructure(yDevModeData, typeof(DEVMODE));
if ((nRet == 0) || (hPrinter == IntPtr.Zero))
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return dm;
}
#endregion
}
}
}



//////////テスト用ボタン付フォーム↓
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
//プリンタ名の取得
System.Drawing.Printing.PrintDocument pd =
new System.Drawing.Printing.PrintDocument();
//両面印刷設定
System.Drawing.Printing.PrinterSettings setting = new System.Drawing.Printing.PrinterSettings();
setting.Duplex = System.Drawing.Printing.Duplex.Vertical;


PrinterSettings ps = new PrinterSettings();
ps.ChangePrinterSetting(pd.PrinterSettings.PrinterName,setting);
}

private void button2_Click(object sender, EventArgs e)
{
//プリンタ名の取得
System.Drawing.Printing.PrintDocument pd =
new System.Drawing.Printing.PrintDocument();
//両面印刷設定
System.Drawing.Printing.PrinterSettings setting = new System.Drawing.Printing.PrinterSettings();
setting.Duplex = System.Drawing.Printing.Duplex.Simplex;


PrinterSettings ps = new PrinterSettings();
ps.ChangePrinterSetting(pd.PrinterSettings.PrinterName, setting);
}
}
}

終わり
初心者の割には面倒なコードを書いているようですが、これは貴方が書いたコードですか?
それで、どの場所でエラーになっているのですか?
■No29303に返信(zamaさんの記事)
> 初心者の割には面倒なコードを書いているようですが、これは貴方が書いたコードですか?
> それで、どの場所でエラーになっているのですか?

質問のコードは、ネットの情報に少しだけ手を加えたものです。
エラーの位置については、

private DEVMODE GetPrinterSettings(string PrinterName)
{
DEVMODE dm;
const int PRINTER_ACCESS_ADMINISTER = 0x4;
const int PRINTER_ACCESS_USE = 0x8;
const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);


PrinterValues.pDatatype =0;
PrinterValues.pDevMode = 0 ;
PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;
nRet = Convert.ToInt32(OpenPrinter(PrinterName,
out hPrinter, ref PrinterValues));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}

上記の一番下  lastError = Marshal.GetLastWin32Error(); の部分で
「アクセスが拒否されました」となります。よろしくお願いします。
> 質問のコードは、ネットの情報に少しだけ手を加えたものです。
> エラーの位置については、

ネットの情報とありますが、そこには管理者権限についてコメントはありませんでしたか?
また、可能であれば、そのサイトのURLを載せてもらっていいですか?
>アカウントがadministratorの場合は設定の変更が成功しますが、制限付アカウントの場合は「アクセスが拒否されました」のエラーで失敗してしまいます。

ソースちゃんと見てないですがこの部分でもう答え出てないですかね?

制限付きアカウントでプリンターのプロパティを変更する権限がないか
もしくはプリンターを使用することができないかだと思われます

プログラムからではなく実際にプリンターのプロパティをそのユーザーから
変更できるか確認してみてはいかがでしょうか?

そのユーザーのまま実行したいということであれば偽装が必要になります

http://support.microsoft.com/kb/306158/ja
の「コード内で特定のユーザーを偽装する 」の部分をご参考ください
■No29300に返信(Ayaneさんの記事)
> こんにちは 初めて投稿します。初心者です。
> 現在プリンタの設定をプログラムから変更したいと思っています。
> 目的は、ユーザごとに使用しているプリンタの機種が違うという環境で、
> エクセルファイルを印刷する際に、任意のシートで片面印刷と両面印刷を切り替えるというものです。
>
> 下記のソースについて、
> アカウントがadministratorの場合は設定の変更が成功しますが、制限付アカウントの場合は「アクセスが拒否されました」のエラーで失敗してしまいます。
> どなたか助けて頂ければ嬉しいです!

定数PRINTER_ALL_ACCESSで管理者以外をつっぱねているからだと思います。まずは以下を見てください。
http://msdn.microsoft.com/ja-jp/library/cc410486.aspx

こちらにXPがないのでXP制限アカウントでの再現はできませんが、win7の標準アカウントでためしたところ、Ayaneさん同様にアクセス拒否されました。そこでOpenPrinter関数のアクセス値を変更することで標準アカウントでもアクセスすることができるようになりました。
たぶんこれなんじゃないかと思います。

それとこのプログラムこのままでそちらでは動くのでしょうか?こちらではメモリの書込み違反などが起きたりしたのでメモリリークがないかもう一度確認したほうがいいかもしれません。

以上です。
2011/10/29(Sat) 10:45:03 編集(投稿者)

上記訂正です。

申し訳ありません、標準アカウントでもアクセス不可でした。よく見るとしっかりエラーキャッチしてました。上のアクセス可能発言は無視しちゃってください。では。
(厳密にはアクセス値変更でOpenPrinterはできるもののSetPrinterでlasterror = 5(アクセス拒否)が返ります。Level2で弾かれてるのだと考えられます。)

> ネットの情報とありますが、そこには管理者権限についてコメントはありませんでしたか?
> また、可能であれば、そのサイトのURLを載せてもらっていいですか?

管理者権限についてのコメントは無いようです...
コード掲載のサイトは以下です。よろしくお願いいたします。
http://www.codeproject.com/KB/dotnet/NET_Printer_Library.aspx
> ソースちゃんと見てないですがこの部分でもう答え出てないですかね?
>
> 制限付きアカウントでプリンターのプロパティを変更する権限がないか
> もしくはプリンターを使用することができないかだと思われます
>
> プログラムからではなく実際にプリンターのプロパティをそのユーザーから
> 変更できるか確認してみてはいかがでしょうか?
>
> そのユーザーのまま実行したいということであれば偽装が必要になります
>
> http://support.microsoft.com/kb/306158/ja
> の「コード内で特定のユーザーを偽装する 」の部分をご参考ください

目的のアカウントから手動であればプリンタのプロパティを変更することはできるのです。だからプログラムからでも可能であると思ったのですが、知識が乏しくうまくいかない状況です
■No29313に返信(Ayaneさんの記事)

> 管理者権限についてのコメントは無いようです...
> コード掲載のサイトは以下です。よろしくお願いいたします。
> http://www.codeproject.com/KB/dotnet/NET_Printer_Library.aspx

codeprojectですね。
ネットワークプリンタについての「Access Denied」(いわゆる、「アクセスが拒否されました」)に関するコメントが
「Comments and Discussions」にありますね。
よく読んではいませんが、何か関連があるかもしれません。

DOBON.NET | プログラミング道 | プログラミング掲示板