.NETプログラミング研究 第47号 †
.NET質問箱 †
「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、Q&A形式にまとめて紹介します。
通常使うプリンタの取得と設定 †
【質問】
「通常使うプリンタ」に指定されているプリンタ名はどのように取得できますか?また、「通常使うプリンタ」を設定するにはどのようにすればいいですか?
【回答】
「通常使うプリンタ」に指定されているプリンタ名は、新しくPrintDocumentオブジェクトを作成し、そのPrinterSettings.PrinterNameプロパティを調べれば分かります。次のような感じです。
1
2
3
4
5
6
7
| | Dim pd As New System.Drawing.Printing.PrintDocument
Dim defaultPrinterName As String = pd.PrinterSettings.PrinterName
Console.WriteLine("通常使用するプリンタは " +
defaultPrinterName + " です。")
|
1
2
3
4
5
6
7
8
| | System.Drawing.Printing.PrintDocument pd =
new System.Drawing.Printing.PrintDocument();
string defaultPrinterName = pd.PrinterSettings.PrinterName;
Console.WriteLine(
"通常使用するプリンタは " + defaultPrinterName + " です。");
|
また、WMIを使用してプリンタを列挙し、デフォルトのプリンタを調べることもできます。これは次のようなコードになります。(参照に"System.Management"が追加する必要があります。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| | Dim mos As New System.Management.ManagementObjectSearcher( _
"Select * from Win32_Printer")
Dim moc As System.Management.ManagementObjectCollection = _
mos.Get()
Dim mo As System.Management.ManagementObject
For Each mo In moc
Dim attr As Long = Convert.ToInt64(mo("Attributes"))
If (attr And 4) = 4 Then
Console.WriteLine(("通常使用するプリンタは " + _
mo("Name").ToString() + " です。"))
Exit For
End If
Next mo
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| | System.Management.ManagementObjectSearcher mos =
new System.Management.ManagementObjectSearcher(
"Select * from Win32_Printer");
System.Management.ManagementObjectCollection moc =
mos.Get();
foreach(System.Management.ManagementObject mo in moc)
{
if ((((uint) mo["Attributes"]) & 4) == 4)
{
Console.WriteLine(
"通常使用するプリンタは " + mo["Name"] + " です。");
break;
}
}
|
次に「通常使うプリンタ」を設定する方法を紹介します。
まず、WMIを使用した方法を紹介します。ただしこの方法はWindows XP以上でしか使用できません。先ほどの例と同様、参照に"System.Management"を追加する必要があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| | Public Sub SetDefaultPrinter(ByVal printerName As String)
Dim mos As New System.Management.ManagementObjectSearcher( _
"Select * from Win32_Printer")
Dim moc As System.Management.ManagementObjectCollection = _
mos.Get()
Dim mo As System.Management.ManagementObject
For Each mo In moc
If CStr(mo("Name")) = printerName Then
Dim mbo As System.Management.ManagementBaseObject = _
mo.InvokeMethod("SetDefaultPrinter", Nothing, Nothing)
Dim ret As Long = Convert.ToInt64(mbo("returnValue"))
If ret <> 0 Then
Throw New Exception("失敗しました。")
End If
End If
Next mo
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| | public void SetDefaultPrinter(string printerName)
{
System.Management.ManagementObjectSearcher mos =
new System.Management.ManagementObjectSearcher(
"Select * from Win32_Printer");
System.Management.ManagementObjectCollection moc =
mos.Get();
foreach(System.Management.ManagementObject mo in moc)
{
if (((string) mo["Name"]) == printerName)
{
System.Management.ManagementBaseObject mbo =
mo.InvokeMethod("SetDefaultPrinter", null, null);
if (((uint) mbo["returnValue"]) != 0)
throw new Exception("失敗しました。");
}
}
}
|
また、Windows Scripting HostのWshNetworkオブジェクトのSetDefaultPrinterメソッドを使う方法もあります。WshNetworkオブジェクトのSetDefaultPrinterメソッドを呼び出すには、次のようにします。
1
2
3
4
5
6
7
8
9
10
11
12
13
| | Public Sub SetDefaultPrinter(ByVal printerName As String)
Dim t As Type = Type.GetTypeFromProgID("WScript.Network")
Dim wshNetwork As Object = Activator.CreateInstance(t)
t.InvokeMember("SetDefaultPrinter", _
System.Reflection.BindingFlags.InvokeMethod, _
Nothing, wshNetwork, New Object() {printerName})
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| | public void SetDefaultPrinter(string printerName)
{
Type t = Type.GetTypeFromProgID("WScript.Network");
object wshNetwork = Activator.CreateInstance(t);
t.InvokeMember("SetDefaultPrinter",
System.Reflection.BindingFlags.InvokeMethod,
null, wshNetwork, new object[] {printerName});
}
|
上記で紹介した方法以外に通常使うプリンタを取得するためにGetDefaultPrinter、GetProfileString、GetPrinter関数を、通常使うプリンタを設定するためにSetDefaultPrinter、WriteProfileString、SetPrinter関数をP/Invokeにより呼び出すという方法もあります。これらの関数に関しては、次のリンク先をご覧ください。
GetPrinter関数に関しては、次の紹介する「プリンタのポートと状態の取得」でその使用法を紹介しています(ただしこの方法で通常使うプリンタが取得できるのは、Windows95、98、Meのみです)。
○この記事の基になった掲示板のスレッド
プリンタのポート、状態の取得 †
【質問】
プリンタのポートや現在の状態に関する情報を取得したいのですが、どうすればいいの?
【回答】
「通常使うプリンタの取得と設定」と同じように、WMIのWin32_Printerを使ってこれらの情報を取得できます。ポートはPortNameプロパティ、状態はPrinterStatusプロパティで取得できます。
また、P/InvokeでGetPrinter関数を呼び出すことにより、プリンタの情報を取得することもできます。ここではこの方法を紹介します。
下にプリンタの情報をPRINTER_INFO_2構造体で取得するためのメソッド(GetPrinterInfoメソッド)のサンプルを示します。
なおこのコードを書くために次の記事を参考にしました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
| | <DllImport("winspool.drv", EntryPoint:="OpenPrinterW", _
CharSet:=CharSet.Auto, SetLastError:=True, ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function OpenPrinter( _
ByVal pPrinterName As String, ByRef hPrinter As IntPtr, _
ByVal pDefault As IntPtr) As Boolean
End Function
<DllImport("winspool.drv", CharSet:=CharSet.Auto, _
SetLastError:=True, ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function ClosePrinter( _
ByVal hPrinter As IntPtr) As Boolean
End Function
<DllImport("winspool.drv", EntryPoint:="GetPrinterW", _
CharSet:=CharSet.Auto, SetLastError:=True, ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function GetPrinter( _
ByVal hPrinter As IntPtr, ByVal dwLevel As Integer, _
ByVal pPrinter As IntPtr, ByVal cbBuf As Integer, _
ByRef pcbNeeded As Integer) As Boolean
End Function
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Public Structure PRINTER_INFO_2
Public pServerName As String
Public pPrinterName As String
Public pShareName As String
Public pPortName As String
Public pDriverName As String
Public pComment As String
Public pLocation As String
Public pDevMode As IntPtr
Public pSepFile As String
Public pPrintProcessor As String
Public pDatatype As String
Public pParameters As String
Public pSecurityDescriptor As IntPtr
Public Attributes As System.UInt32
Public Priority As System.UInt32
Public DefaultPriority As System.UInt32
Public StartTime As System.UInt32
Public UntilTime As System.UInt32
Public Status As System.UInt32
Public cJobs As System.UInt32
Public AveragePPM As System.UInt32
End Structure
Public Shared Function GetPrinterInfo( _
ByVal printerName As String) As PRINTER_INFO_2
Dim hPrinter As IntPtr
If Not OpenPrinter(printerName, hPrinter, IntPtr.Zero) Then
Throw New Win32Exception(Marshal.GetLastWin32Error())
End If
Dim pPrinterInfo As IntPtr = IntPtr.Zero
Try
Dim needed As Integer
GetPrinter(hPrinter, 2, IntPtr.Zero, 0, needed)
If needed <= 0 Then
Throw New Exception("失敗しました。")
End If
pPrinterInfo = Marshal.AllocHGlobal(needed)
Dim temp As Integer
If Not GetPrinter(hPrinter, 2, pPrinterInfo, needed, temp) Then
Throw New Win32Exception(Marshal.GetLastWin32Error())
End If
Dim printerInfo As PRINTER_INFO_2 = _
CType(Marshal.PtrToStructure( _
pPrinterInfo, GetType(PRINTER_INFO_2)), PRINTER_INFO_2)
Return printerInfo
Finally
ClosePrinter(hPrinter)
Marshal.FreeHGlobal(pPrinterInfo)
End Try
End Function
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
| | [DllImport("winspool.drv", EntryPoint="OpenPrinterW",
CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)]
private static extern bool OpenPrinter(string pPrinterName,
out IntPtr hPrinter, IntPtr pDefault);
[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true,
ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
private static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.drv", EntryPoint="GetPrinterW",
CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)]
private static extern bool GetPrinter(IntPtr hPrinter, int dwLevel,
IntPtr pPrinter, int cbBuf, out int pcbNeeded);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct PRINTER_INFO_2
{
public string pServerName;
public string pPrinterName;
public string pShareName;
public string pPortName;
public string pDriverName;
public string pComment;
public string pLocation;
public IntPtr pDevMode;
public string pSepFile;
public string pPrintProcessor;
public string pDatatype;
public string pParameters;
public IntPtr pSecurityDescriptor;
public uint Attributes;
public uint Priority;
public uint DefaultPriority;
public uint StartTime;
public uint UntilTime;
public uint Status;
public uint cJobs;
public uint AveragePPM;
}
public static PRINTER_INFO_2 GetPrinterInfo(string printerName)
{
IntPtr hPrinter;
if (!OpenPrinter(printerName, out hPrinter, IntPtr.Zero))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
IntPtr pPrinterInfo = IntPtr.Zero;
try
{
int needed;
GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out needed);
if (needed <= 0)
throw new Exception("失敗しました。");
pPrinterInfo = Marshal.AllocHGlobal(needed);
int temp;
if (!GetPrinter(hPrinter, 2, pPrinterInfo, needed, out temp))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
PRINTER_INFO_2 printerInfo =
(PRINTER_INFO_2) Marshal.PtrToStructure(pPrinterInfo,
typeof(PRINTER_INFO_2));
return printerInfo;
}
finally
{
ClosePrinter(hPrinter);
Marshal.FreeHGlobal(pPrinterInfo);
}
}
|
GetPrinterInfoメソッドを使ってプリンタのポートと状態を取得する例を以下に示します。
1
2
3
4
5
6
7
8
| | Dim printerName As String = "PRINTER NAME"
Dim pinfo As PRINTER_INFO_2 = GetPrinterInfo(printerName)
Console.WriteLine(("Port:" + pinfo.pPortName))
Console.WriteLine(("Status:" + pinfo.Status.ToString()))
|
1
2
3
4
5
6
7
8
| | string printerName = "PRINTER NAME";
PRINTER_INFO_2 pinfo = GetPrinterInfo(printerName);
Console.WriteLine("Port:" + pinfo.pPortName);
Console.WriteLine("Status:" + pinfo.Status.ToString());
|
○この記事の基になった掲示板のスレッド
エクスプローラへファイルをドラッグ&ドロップする †
【質問】
ListBoxコントロールのリストにファイル名を追加し、リストからエクスプローラにドラッグ&ドロップすることによりファイルをコピーできるようにしたいのですが、どうすればできますか?
【回答】
エクスプローラへのドラッグ&ドロップも通常のドラッグ&ドロップ同様、Control.DoDragDropメソッドで可能です。(通常のドラッグ&ドロップの方法に関しては、下記リンク先をご覧ください。)
ただしDoDragDropメソッドを呼び出す時、データ形式にDataFormats.FileDropを、データにドロップ&ドラッグしたいファイルのフルパスのString型配列(ただのString型ではないことに注意してください)を指定して作成したDataObjectオブジェクトを使用します。
以下にリストボックスからエクスプローラへのファイルのドラッグ&ドロップを可能にするサンプルを示します。このサンプルでは、フォーム(Form1)にリストボックス(ListBox1)が配置されている必要があります。フォームのLoadイベントハンドラでリストボックスにファイル名を表示し、リストボックスのMouseDownイベントハンドラでドラッグを開始するようにしています。ここでは一つのファイルしかドラッグ&ドロップできませんが、String型配列に複数のファイルのパスを指定すれば、複数のファイルのドラッグ&ドロップが可能です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| | Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As EventArgs) Handles MyBase.Load
AddHandler ListBox1.MouseDown, AddressOf ListBox1_MouseDown
ListBox1.Items.AddRange( _
System.IO.Directory.GetFiles("C:\", "*.*"))
End Sub
Private Sub ListBox1_MouseDown(ByVal sender As Object, _
ByVal e As MouseEventArgs)
Dim lbx As ListBox = CType(sender, ListBox)
Dim itemIndex As Integer = lbx.IndexFromPoint(e.X, e.Y)
If itemIndex < 0 Then
Return
End If
Dim files As String() = {CStr(lbx.Items(itemIndex))}
Dim dataObj As New DataObject(DataFormats.FileDrop, files)
Dim dde As DragDropEffects = _
lbx.DoDragDrop(dataObj, DragDropEffects.Copy)
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| | private void Form1_Load(object sender, System.EventArgs e)
{
ListBox1.MouseDown += new MouseEventHandler(ListBox1_MouseDown);
ListBox1.Items.AddRange(
System.IO.Directory.GetFiles("C:\\", "*.*"));
}
//ListBox1のMouseDownイベントハンドラ
private void ListBox1_MouseDown(object sender, MouseEventArgs e)
{
ListBox lbx = (ListBox) sender;
//ドラッグするファイル名を取得する
int itemIndex = lbx.IndexFromPoint(e.X, e.Y);
if (itemIndex < 0) return;
string[] files = {(string) lbx.Items[itemIndex]};
//DataObjectを作成する
DataObject dataObj = new DataObject(DataFormats.FileDrop, files);
//ドラッグを開始する
DragDropEffects dde =
lbx.DoDragDrop(dataObj, DragDropEffects.Copy);
}
|
なお、ファイルを移動するためにDoDragDropメソッドでDragDropEffects.Moveを使用した場合、DoDragDropメソッドが返すDragDropEffectsの値はNoneになるようです。ファイルを移動したかを調べるには、DoDragDropメソッドが終了してからFile.Existsメソッドを使ってドラッグしたファイルが削除されたか調べるなどの方法が考えられます。(詳しくは、下の「エクスプローラへのドラッグ&ドロップで移動された場合の判定について」のリンク先をご覧ください。)
○この記事の基になった掲示板のスレッド
ツールバーに画像が表示されない †
【質問】
Visual Studio .NETのフォームデザイナでImageListを使ってツールバーのボタンに画像を表示しているのですが、画像が表示されません。一体どうなっているのでしょうか?
【回答】
Application.EnableVisualStylesメソッドを使用している時は、これが原因でしょう。この場合は、EnableVisualStylesメソッドのすぐ後でApplication.DoEventsメソッドを呼び出すか、EnableVisualStylesメソッドを使わずにマニフェストファイルを使用することにより解決できます。これらの方法に関しては、次のリンク先をご覧ください。
さらに、EnableVisualStylesメソッドを使用していなくてもツールバーに画像が表示されないというバグもあるようで、これに関しては次のページで紹介されています。
この記事によるとこの問題の解決法は、コードエディタで該当するフォームクラスのInitializeComponentメソッド内にあるToolBar.Buttons.Addrangeメソッドを呼び出している箇所を、ツールバーのボタンの設定の後ろに移動させるということのようです。しかしデザイナで変更を加えるたびにコードが書き換わってしまうため、そのたびにAddrangeを移動させなければならないということです。
ツールバーに画像が表示されなくて困っている方は一度おためしを。
○この記事の基になった掲示板のスレッド
コメント †