• 追加された行はこの色です。
  • 削除された行はこの色です。
#title(.NETプログラミング研究 第47号)

#navi(.NET プログラミング研究)

#contents

*.NETプログラミング研究 第47号 [#we300dd9]

**.NET質問箱 [#jf18bb3f]

「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、Q&A形式にまとめて紹介します。

-[[どぼん!のプログラミング掲示板>http://dobon.net/vb/bbs.html]]

***通常使うプリンタの取得と設定 [#m12448a4]

#column(注意){{
この記事の最新版は「[[通常使うプリンタを取得、設定する>http://dobon.net/vb/dotnet/graphics/defaultprinter.html]]」で公開しています。
}}

''【質問】''

「通常使うプリンタ」に指定されているプリンタ名はどのように取得できますか?また、「通常使うプリンタ」を設定するにはどのようにすればいいですか?

''【回答】''

「通常使うプリンタ」に指定されているプリンタ名は、新しくPrintDocumentオブジェクトを作成し、そのPrinterSettings.PrinterNameプロパティを調べれば分かります。次のような感じです。

#code(vbnet){{
'PrintDocumentの作成
Dim pd As New System.Drawing.Printing.PrintDocument
'プリンタ名の取得
Dim defaultPrinterName As String = pd.PrinterSettings.PrinterName
'結果を表示
Console.WriteLine("通常使用するプリンタは " +
    defaultPrinterName + " です。")
}}

#code(csharp){{
//PrintDocumentの作成
System.Drawing.Printing.PrintDocument pd =
    new System.Drawing.Printing.PrintDocument();
//プリンタ名の取得
string defaultPrinterName = pd.PrinterSettings.PrinterName;
//結果を表示
Console.WriteLine(
    "通常使用するプリンタは " + defaultPrinterName + " です。");
}}

また、WMIを使用してプリンタを列挙し、デフォルトのプリンタを調べることもできます。これは次のようなコードになります。(参照に"System.Management"が追加する必要があります。)

-[[Win32_Printer>http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_printer.asp]]

#code(vbnet){{
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
    'デフォルトのプリンタか調べる
    'mo("Default")はWindows NT 4.0, 2000で使用できません
    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
}}

#code(csharp){{
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)
{
    //デフォルトのプリンタか調べる
    //mo["Default"]はWindows NT 4.0, 2000で使用できません
    if ((((uint) mo["Attributes"]) & 4) == 4)
    {
        Console.WriteLine(
            "通常使用するプリンタは " + mo["Name"] + " です。");
        break;
    }
}
}}

次に「通常使うプリンタ」を設定する方法を紹介します。

まず、WMIを使用した方法を紹介します。ただしこの方法はWindows XP以上でしか使用できません。先ほどの例と同様、参照に"System.Management"を追加する必要があります。

#code(vbnet){{
'/ <summary>
'/ 「通常使用するプリンタ」に設定する
'/ </summary>
'/ <param name="printerName">プリンタ名</param>
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
}}

#code(csharp){{
/// <summary>
/// 「通常使用するプリンタ」に設定する
/// </summary>
/// <param name="printerName">プリンタ名</param>
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メソッドを呼び出すには、次のようにします。

-[[SetDefaultPrinter メソッド>http://www.microsoft.com/japan/msdn/library/ja/script56/html/wsmthsetdefaultprinter.asp]]

#code(vbnet){{
'/ <summary>
'/ 「通常使用するプリンタ」に設定する
'/ </summary>
'/ <param name="printerName">プリンタ名</param>
Public Sub SetDefaultPrinter(ByVal printerName As String)
    'WshNetworkオブジェクトを作成する
    Dim t As Type = Type.GetTypeFromProgID("WScript.Network")
    Dim wshNetwork As Object = Activator.CreateInstance(t)
    'SetDefaultPrinterメソッドを呼び出す
    t.InvokeMember("SetDefaultPrinter", _
        System.Reflection.BindingFlags.InvokeMethod, _
        Nothing, wshNetwork, New Object() {printerName})
End Sub
}}

#code(csharp){{
/// <summary>
/// 「通常使用するプリンタ」に設定する
/// </summary>
/// <param name="printerName">プリンタ名</param>
public void SetDefaultPrinter(string printerName)
{
    //WshNetworkオブジェクトを作成する
    Type t = Type.GetTypeFromProgID("WScript.Network");
    object wshNetwork = Activator.CreateInstance(t);
    //SetDefaultPrinterメソッドを呼び出す
    t.InvokeMember("SetDefaultPrinter",
        System.Reflection.BindingFlags.InvokeMethod,
        null, wshNetwork, new object[] {printerName});
}
}}

上記で紹介した方法以外に通常使うプリンタを取得するためにGetDefaultPrinter、GetProfileString、GetPrinter関数を、通常使うプリンタを設定するためにSetDefaultPrinter、WriteProfileString、SetPrinter関数をP/Invokeにより呼び出すという方法もあります。これらの関数に関しては、次のリンク先をご覧ください。

-[[[HOWTO] Windows の通常使うプリンタを取得および設定する方法>http://support.microsoft.com/kb/246772/ja]]

GetPrinter関数に関しては、次の紹介する「プリンタのポートと状態の取得」でその使用法を紹介しています(ただしこの方法で通常使うプリンタが取得できるのは、Windows95、98、Meのみです)。

○この記事の基になった掲示板のスレッド

-[[通常使うプリンタについて質問 | 投稿者(敬称略) PASA, 管理人>http://dobon.net/vb/bbs/log3-4/1950.html]]
-[[通常使うプリンタの取得 | 投稿者(敬称略) tab, 管理人>http://dobon.net/vb/bbs/log3-4/1992.html]]

***プリンタのポート、状態の取得 [#a17f7ee6]

#column(注意){{
この記事の最新版は「[[プリンタのポート、状態を取得する>http://dobon.net/vb/dotnet/graphics/printerport.html]]」で公開しています。
}}

''【質問】''

プリンタのポートや現在の状態に関する情報を取得したいのですが、どうすればいいの?

''【回答】''

「通常使うプリンタの取得と設定」と同じように、WMIのWin32_Printerを使ってこれらの情報を取得できます。ポートはPortNameプロパティ、状態はPrinterStatusプロパティで取得できます。

また、P/InvokeでGetPrinter関数を呼び出すことにより、プリンタの情報を取得することもできます。ここではこの方法を紹介します。

下にプリンタの情報をPRINTER_INFO_2構造体で取得するためのメソッド(GetPrinterInfoメソッド)のサンプルを示します。

-[[PRINTER_INFO_2>http://msdn.microsoft.com/library/en-us/gdi/prntspol_9otu.asp]]

なおこのコードを書くために次の記事を参考にしました。

-[[Newsgroups:microsoft.public.dotnet.framework.interop | Subject:Solution: Printer Port and Driver Information (PRINTER_INFO_2)>http://groups.google.co.jp/groups?hl=ja&lr=&inlang=ja&selm=8FE95F7B-C55D-4AB2-8E6D-3A3FE1E1812C%40microsoft.com]]
-[[The Code Project - Changing printer settings using C#>http://www.codeproject.com/dotnet/NET_Printer_Library.asp]]

#code(vbnet){{
<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

'/ <summary>
'/ プリンタの情報をPRINTER_INFO_2で取得する
'/ </summary>
'/ <param name="printerName">プリンタ名</param>
'/ <returns>プリンタの情報</returns>
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

        'PRINTER_INFO_2型にマーシャリングする
        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
}}

#code(csharp){{
[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;
}

/// <summary>
/// プリンタの情報をPRINTER_INFO_2で取得する
/// </summary>
/// <param name="printerName">プリンタ名</param>
/// <returns>プリンタの情報</returns>
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型にマーシャリングする
        PRINTER_INFO_2 printerInfo =
            (PRINTER_INFO_2) Marshal.PtrToStructure(pPrinterInfo,
            typeof(PRINTER_INFO_2));

        //結果を返す
        return printerInfo;
    }
    finally
    {
        //後始末をする
        ClosePrinter(hPrinter);
        Marshal.FreeHGlobal(pPrinterInfo);
    }
}
}}

GetPrinterInfoメソッドを使ってプリンタのポートと状態を取得する例を以下に示します。

#code(vbnet){{
'プリンタ名
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()))
}}

#code(csharp){{
//プリンタ名
string printerName = "PRINTER NAME";
//プリンタ情報を取得する
PRINTER_INFO_2 pinfo = GetPrinterInfo(printerName);
//ポートを表示する
Console.WriteLine("Port:" + pinfo.pPortName);
//状態を表示する
Console.WriteLine("Status:" + pinfo.Status.ToString());
}}

○この記事の基になった掲示板のスレッド

-[[プリンターポートの取得方法について | 投稿者(敬称略) パオ, 管理人>http://dobon.net/vb/bbs/log3-4/2062.html]]

***エクスプローラへファイルをドラッグ&ドロップする [#m993668d]

#column(注意){{
この記事の最新版は「[[エクスプローラへファイルをDrag&Dropする>http://dobon.net/vb/dotnet/control/droptoexplore.html]]」で公開しています。
}}

''【質問】''

ListBoxコントロールのリストにファイル名を追加し、リストからエクスプローラにドラッグ&ドロップすることによりファイルをコピーできるようにしたいのですが、どうすればできますか?

''【回答】''

エクスプローラへのドラッグ&ドロップも通常のドラッグ&ドロップ同様、Control.DoDragDropメソッドで可能です。(通常のドラッグ&ドロップの方法に関しては、下記リンク先をご覧ください。)

-[[DOBON.NET .NET Tips - ドラッグ&ドロップを行う>http://dobon.net/vb/dotnet/control/draganddrop.html]]

ただしDoDragDropメソッドを呼び出す時、データ形式にDataFormats.FileDropを、データにドロップ&ドラッグしたいファイルのフルパスのString型配列(ただのString型ではないことに注意してください)を指定して作成したDataObjectオブジェクトを使用します。

以下にリストボックスからエクスプローラへのファイルのドラッグ&ドロップを可能にするサンプルを示します。このサンプルでは、フォーム(Form1)にリストボックス(ListBox1)が配置されている必要があります。フォームのLoadイベントハンドラでリストボックスにファイル名を表示し、リストボックスのMouseDownイベントハンドラでドラッグを開始するようにしています。ここでは一つのファイルしかドラッグ&ドロップできませんが、String型配列に複数のファイルのパスを指定すれば、複数のファイルのドラッグ&ドロップが可能です。

#code(vbnet){{
'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As EventArgs) Handles MyBase.Load
    'MouseDownイベントハンドラを追加
    AddHandler ListBox1.MouseDown, AddressOf ListBox1_MouseDown
    'ListBox1に"C:\"にあるファイルを表示する
    ListBox1.Items.AddRange( _
        System.IO.Directory.GetFiles("C:\", "*.*"))
End Sub

'ListBox1のMouseDownイベントハンドラ
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))}
    'DataObjectを作成する
    Dim dataObj As New DataObject(DataFormats.FileDrop, files)

    'ドラッグを開始する
    Dim dde As DragDropEffects = _
        lbx.DoDragDrop(dataObj, DragDropEffects.Copy)
End Sub
}}

#code(csharp){{
//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    //MouseDownイベントハンドラを追加
    ListBox1.MouseDown += new MouseEventHandler(ListBox1_MouseDown);
    //ListBox1に"C:\"にあるファイルを表示する
    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メソッドを使ってドラッグしたファイルが削除されたか調べるなどの方法が考えられます。(詳しくは、下の「エクスプローラへのドラッグ&ドロップで移動された場合の判定について」のリンク先をご覧ください。)

○この記事の基になった掲示板のスレッド

-[[VB.Netで作成したアプリからエクスプローラへのドラッグ&ドロップの方法 | 投稿者(敬称略) L, 管理人>http://dobon.net/vb/bbs/log3-4/2064.html]]
-[[listBoxでエクスプローラーのドラッグ&ドロップの実現方法 | 投稿者(敬称略) brn, りょう, Sugi@ぱぱんぶぃびぃ, 管理人>http://dobon.net/vb/bbs/log3-6/3108.html]]
-[[エクスプローラへのドラッグ&ドロップで移動された場合の判定について | 投稿者(敬称略) Mike3, 深山>http://dobon.net/vb/bbs/log3-5/2450.html]]

***ツールバーに画像が表示されない [#uea10adc]

#column(注意){{
この記事の最新版は「[[ToolBarに画像が表示されない問題の解決法>http://dobon.net/vb/dotnet/control/tbnoimage.html]]」で公開しています。
}}

''【質問】''

Visual Studio .NETのフォームデザイナでImageListを使ってツールバーのボタンに画像を表示しているのですが、画像が表示されません。一体どうなっているのでしょうか?

''【回答】''

Application.EnableVisualStylesメソッドを使用している時は、これが原因でしょう。この場合は、EnableVisualStylesメソッドのすぐ後でApplication.DoEventsメソッドを呼び出すか、EnableVisualStylesメソッドを使わずにマニフェストファイルを使用することにより解決できます。これらの方法に関しては、次のリンク先をご覧ください。

-[[DOBON.NET .NET Tips - コントロールの外観をXPスタイルにする>http://dobon.net/vb/dotnet/control/xpstyle.html]]

さらに、EnableVisualStylesメソッドを使用していなくてもツールバーに画像が表示されないというバグもあるようで、これに関しては次のページで紹介されています。

-[[Newsgroups:microsoft.public.dotnet.languages.csharp | Subject:RE: C# toolbar image visible at design time, but vanishes at runtime.>http://groups.google.co.jp/groups?hl=ja&lr=&selm=kV%24mXFjOCHA.1716%40cpmsftngxa07]]

この記事によるとこの問題の解決法は、コードエディタで該当するフォームクラスのInitializeComponentメソッド内にあるToolBar.Buttons.Addrangeメソッドを呼び出している箇所を、ツールバーのボタンの設定の後ろに移動させるということのようです。しかしデザイナで変更を加えるたびにコードが書き換わってしまうため、そのたびにAddrangeを移動させなければならないということです。

ツールバーに画像が表示されなくて困っている方は一度おためしを。

○この記事の基になった掲示板のスレッド

-[[ツールバーのアイコンが表示されない | 投稿者(敬称略) MoMoMo, Sugi@ぱぱんぶぃびぃ, 管理人>http://dobon.net/vb/bbs/log3-4/2064.html]]

**コメント [#b1ee81cd]
#comment

//これより下は編集しないでください
#pageinfo([[:Category/.NET]],2004-12-09 (木) 06:00:00,DOBON!,2010-03-22 (月) 00:51:27,DOBON!)

[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]