2011-08-27T22:46:21の更新内容

programming/tips/eject_cdrom/index.wiki.txt

current previous
1,658 1,159
~
${smdncms:title,CD-ROM・リムーバブルメディアを取り出す}
${smdncms:title,CD-ROMをイジェクトする}
~
${smdncms:keywords,CD-ROM,リムーバブル,メディア,取り出し,イジェクト,トレイ,開閉,C#,VB.NET,.NET,DeviceIoControl,IOCTL}
${smdncms:keywords,CD-ROM,イジェクト,.NET,DeviceIoControl,IOCTL}
~
${smdncms:tags,lang/c#,lang/vb,api/win32,plat/win}
${smdncms:tags,lang/vb,plat/win}
~
${smdncms:document_versions,codelang=cs,codelang=vb}
.NETではCD-ROMをイジェクトするようなクラス・メソッドが無いので、APIを使用して実現する。 具体的にはDeviceIoControlを使用してデバイスに直接制御信号を送る。
 

        

        
~
.NETの&msdn(netfx,type,System.IO.DriveInfo){DriveInfo};クラスにはCD-ROMドライブの開閉やリムーバブルメディアの取り外しを行うためのメソッドが無いので、APIを使用して実現する。 具体的にはDeviceIoControlを使用してデバイスに直接制御信号を送る。 送信する命令を変えることでドライブのトレイ開閉や、メディアがセットされているかどうかを調べることもできる。
ここで紹介している例では、システムに存在するすべてのCD-ROMドライブに対してイジェクト命令を送っている。 送信する命令を変えれば、開いているディスクトレイを閉じることも出来る。 また、このコードは参考資料をVB.NETに訳した程度なので、詳しい解説・情報が欲しい場合は下記の参考資料を閲覧のこと。
+

          
+
-関連するページ
+
--[[programming/tips/md5_cdrom]]
+
--[[works/tools/junk/CDRomMD5Checker]]
+
--[[works/tools/junk/CDPlayer]] CD-ROMの取り出しにMCIを使った例
+

          
+
#googleadunit
+

          
+
*実装
+
以下のコードでは、次の四つのメソッドを用意して、DriveInfoクラスの拡張メソッドとして呼び出せるようにしている。
+

          
+
:Eject|メディアの取り外す、またはドライブのトレイを開く
+
:Load|メディアをセットする、またはドライブのトレイを閉じる
+
:IsTrayOpened|ドライブのトレイが開いているかどうかを返す
+
:HasMedia|ドライブにメディアがセットされているかどうかを返す (&msdn(netfx,member,System.IO.DriveInfo.IsReady){DriveInfo.IsReady};と同等)
+

          
+
なお、このコードはNT系のWindowsのみを対象としていて、9x系では動作しない。
 

        

        
-
このコードはNT系のWindowsのみで動作する(9x系のWindowsでの動作は考慮していない)。
 
-参考資料
-参考資料
~
--[[ハウツー解説: Windows NT]、[Windows 2000 および Windows XP でリムーバブル メディアを取り出し:http://support.microsoft.com/kb/165721/]]
--[[#35897 VB初心者友の会 - Q&A掲示板 - 過去ログ - RE#35893:リムバブルディスクのEjectについて:http://littlegray.minidns.net/vbtomo/log/vbqanda/lg-bbs_vbqanda_log_35878-35929.html#35897]]
-
--[[VBでMO等のリムーバブルディスクの取り外しを行うには?:http://homepage1.nifty.com/MADIA/vb/vb_bbs/200212_02120061.html]]
 
--[[DiskをEjectする:http://www.bea.hi-ho.ne.jp/my2project/vb/01/010206.htm]]
--[[DiskをEjectする:http://www.bea.hi-ho.ne.jp/my2project/vb/01/010206.htm]]
 

        

        
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.ComponentModel;
+
using System.IO;
+
using System.Runtime.InteropServices;
+

          
+
class Sample {
+
  public static void Main()
+
  {
+
    foreach (var d in DriveInfo.GetDrives()) {
+
      Console.WriteLine("{0,-10} {1}", d.Name, d.DriveType);
+
    }
+

          
+
    Console.Write("テストするドライブを指定してください: ");
+

          
+
    var drive = new DriveInfo(Console.ReadLine().TrimEnd());
+

          
+
    if (drive.IsTrayOpened()) {
+
      Console.WriteLine("トレイを閉じます");
+

          
+
      drive.Load();
+

          
+
      Console.WriteLine("トレイを閉じました");
+
    }
+
    else {
+
      Console.WriteLine("トレイは閉じています");
+
    }
+

          
+
    Console.WriteLine("メディアをチェックします");
+

          
+
    if (drive.HasMedia()) {
+
      Console.WriteLine("メディアがセットされています");
+
    }
+
    else {
+
      Console.WriteLine("メディアはセットされていません");
+
    }
+

          
+
    Console.WriteLine("トレイを開きます");
+

          
+
    drive.Eject();
+

          
+
    Console.WriteLine("トレイを開きました");
+
  }
+
}
+

          
+
/// <summary>DriveInfoクラスにトレイの開閉を行う拡張メソッドを追加するクラス</summary>
+
static class DriveInfoEjectLoadExtensions {
+
  /// <summary>ドライブにメディアがセットされているかどうかを返す</summary>
+
  public static bool HasMedia(this DriveInfo drive)
+
  {
+
    using (var volume = new Volume(drive, false)) {
+
      return IOControl.IOCtl(volume.Handle, IOControl.IOCTL_STORAGE_CHECK_VERIFY);
+
    }
+
  }
+

          
+
  /// <summary>ドライブのトレイが開いているかどうかを返す</summary>
+
  public unsafe static bool IsTrayOpened(this DriveInfo drive)
+
  {
+
    using (var volume = new Volume(drive, true)) {
+
      /*
+
       * http://www.eggheadcafe.com/conversation.aspx?messageid=33820121&threadid=33794406
+
       * http://forum.sources.ru/index.php?showtopic=225102
+
       */
+

          
+
      const int dataLength = 8;
+
      byte* data = stackalloc byte[dataLength];
+
      SCSI_PASS_THROUGH_DIRECT* sptd = stackalloc SCSI_PASS_THROUGH_DIRECT[1];
+

          
+
      var size = Marshal.SizeOf(typeof(SCSI_PASS_THROUGH_DIRECT));
+

          
+
      sptd[0].Length = (ushort)size;
+
      sptd[0].PathId = 0;
+
      sptd[0].TargetId = 0;
+
      sptd[0].CdbLength = 12;
+
      sptd[0].DataIn = IOControl.SCSI_IOCTL_DATA_IN;
+
      sptd[0].DataTransferLength = dataLength;
+
      sptd[0].TimeOutValue = 5;
+
      sptd[0].DataBuffer = (void*)data;
+
      sptd[0].Cdb[0] = 0xbd; // mechanism status
+
      sptd[0].Cdb[9] = 8; // timeout value
+

          
+
      uint bytesReturned;
+

          
+
      if (!IOControl.DeviceIoControl(volume.Handle, IOControl.IOCTL_SCSI_PASS_THROUGH_DIRECT, (void*)sptd, (uint)size, (void*)sptd, (uint)size, out bytesReturned, IntPtr.Zero))
+
        throw new Win32Exception(Marshal.GetLastWin32Error());
+

          
+
      return ((data[1] & 0x10) == 0x10);
+
    }
+
  }
+

          
+
  /// <summary>メディアをセットする、またはドライブのトレイを閉じる</summary>
+
  public static void Load(this DriveInfo drive)
+
  {
+
    using (var volume = new Volume(drive, false)) {
+
      if (!IOControl.IOCtl(volume.Handle, IOControl.IOCTL_STORAGE_LOAD_MEDIA))
+
        throw new Win32Exception(Marshal.GetLastWin32Error());
+
    }
+
  }
+

          
+
  /// <summary>メディアの取り外す、またはドライブのトレイを開く</summary>
+
  public static void Eject(this DriveInfo drive)
+
  {
+
    using (var volume = new Volume(drive, false)) {
+
      /*
+
       * http://support.microsoft.com/kb/165721/
+
       */
+

          
+
      const int maxRetry = 20; // デバイスロックの最大試行回数
+
      var locked = false;
+

          
+
      for (var t = 0; t < maxRetry; t++) {
+
        if (IOControl.IOCtl(volume.Handle, IOControl.FSCTL_LOCK_VOLUME)) {
+
          locked = true;
+
          break;
+
        }
+
        else {
+
          System.Threading.Thread.Sleep(1000); // ロックできなければ1秒後に再試行
+
        }
+
      }
+

          
+
      if (!locked)
+
        throw new IOException("デバイスがビジー状態です");
+

          
+
      if (!IOControl.IOCtl(volume.Handle, IOControl.FSCTL_DISMOUNT_VOLUME))
+
        throw new Win32Exception(Marshal.GetLastWin32Error());
+

          
+
      unsafe {
+
        uint bytesReturned;
+
        PREVENT_MEDIA_REMOVAL* inBuffer = stackalloc PREVENT_MEDIA_REMOVAL[1];
+

          
+
        inBuffer[0].PreventMediaRemoval = 0; // FALSE
+

          
+
        if (!IOControl.DeviceIoControl(volume.Handle, IOControl.IOCTL_STORAGE_MEDIA_REMOVAL, (void*)inBuffer, (uint)Marshal.SizeOf(typeof(PREVENT_MEDIA_REMOVAL)), (void*)IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero))
+
          throw new Win32Exception(Marshal.GetLastWin32Error());
+
      }
+

          
+
      if (!IOControl.IOCtl(volume.Handle, IOControl.IOCTL_STORAGE_EJECT_MEDIA))
+
        throw new Win32Exception(Marshal.GetLastWin32Error());
+
    }
+
  }
+

          
+
  // DeviceIoControl呼び出し関連のクラス
+
  private static class IOControl {
+
    [DllImport("kernel32", SetLastError = true)]
+
    public static unsafe extern bool DeviceIoControl(IntPtr hDevice,
+
                                                     uint dwIoControlCode,
+
                                                     void* lpInBuffer,
+
                                                     uint nInBufferSize,
+
                                                     void* lpOutBuffer,
+
                                                     uint nOutBufferSize,
+
                                                     out uint lpBytesReturned,
+
                                                     IntPtr lpOverlapped);
+

          
+
    public const uint FILE_ANY_ACCESS      = 0x00000000;
+
    public const uint FILE_SPECIAL_ACCESS  = FILE_ANY_ACCESS;
+
    public const uint FILE_READ_ACCESS     = 0x00000001;
+
    public const uint FILE_WRITE_ACCESS    = 0x00000002;
+

          
+
    public const byte SCSI_IOCTL_DATA_OUT         = 0;
+
    public const byte SCSI_IOCTL_DATA_IN          = 1;
+
    public const byte SCSI_IOCTL_DATA_UNSPECIFIED = 2;
+

          
+
    public const uint METHOD_BUFFERED    = 0;
+
    public const uint METHOD_IN_DIRECT   = 1;
+
    public const uint METHOD_OUT_DIRECT  = 2;
+
    public const uint METHOD_NEITHER     = 3;
+

          
+
    public const uint FILE_DEVICE_CONTROLLER    = 4;
+
    public const uint FILE_DEVICE_FILE_SYSTEM   = 9;
+
    public const uint FILE_DEVICE_MASS_STORAGE  = 45;
+

          
+
    public static readonly uint IOCTL_SCSI_BASE                   = FILE_DEVICE_CONTROLLER;
+
    public static readonly uint IOCTL_SCSI_PASS_THROUGH_DIRECT    = CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS);
+

          
+
    public static readonly uint FSCTL_BASE                        = FILE_DEVICE_FILE_SYSTEM;
+
    public static readonly uint FSCTL_LOCK_VOLUME                 = CTL_CODE(FSCTL_BASE, 6, METHOD_BUFFERED, FILE_ANY_ACCESS);
+
    public static readonly uint FSCTL_DISMOUNT_VOLUME             = CTL_CODE(FSCTL_BASE, 8, METHOD_BUFFERED, FILE_ANY_ACCESS);
+

          
+
    public static readonly uint IOCTL_STORAGE_BASE                = FILE_DEVICE_MASS_STORAGE;
+
    public static readonly uint IOCTL_STORAGE_CHECK_VERIFY        = CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS);
+
    public static readonly uint IOCTL_STORAGE_MEDIA_REMOVAL       = CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS);
+
    public static readonly uint IOCTL_STORAGE_EJECT_MEDIA         = CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS);
+
    public static readonly uint IOCTL_STORAGE_LOAD_MEDIA          = CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_READ_ACCESS);
+

          
+
    private static uint CTL_CODE(uint t, uint f, uint m, uint a)
+
    {
+
      return (uint)((t << 16) | (a << 14) | (f << 2) | m);
+
    }
+

          
+
    public unsafe static bool IOCtl(IntPtr hDevice, uint dwIoControlCode)
+
    {
+
      uint bytesReturned;
+

          
+
      return DeviceIoControl(hDevice, dwIoControlCode, (void*)IntPtr.Zero, 0, (void*)IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
+
    }
+
  }
+

          
+
  [StructLayout(LayoutKind.Sequential)]
+
  private unsafe struct SCSI_PASS_THROUGH_DIRECT {
+
    public ushort Length;
+
    public byte   ScsiStatus;
+
    public byte   PathId;
+
    public byte   TargetId;
+
    public byte   Lun;
+
    public byte   CdbLength;
+
    public byte   SenseInfoLength;
+
    public byte   DataIn;
+
    public uint   DataTransferLength;
+
    public uint   TimeOutValue;
+
    public void*  DataBuffer;
+
    public uint   SenseInfoOffset;
+
    public fixed byte Cdb[16];
+
  }
+

          
+
  [StructLayout(LayoutKind.Sequential)]
+
  private struct PREVENT_MEDIA_REMOVAL {
+
    public byte PreventMediaRemoval;
+
  }
+

          
+
  /// <summary>ボリュームのハンドルとCreateFile/CloseHandleをラップするクラス</summary>
+
  private class Volume : IDisposable {
+
    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
+
    private static extern IntPtr CreateFile(string lpFIleName,
+
                                            uint dwDesiredAccess,
+
                                            uint dwShareMode,
+
                                            IntPtr lpSecurityAttributes,
+
                                            uint dwCreationDisposition,
+
                                            uint dwFlagsAndAttributes,
+
                                            IntPtr hTemplateFile);
+

          
+
    [DllImport("kernel32", SetLastError = true)]
+
    private static extern bool CloseHandle(IntPtr hObject);
+

          
+
    private const uint GENERIC_READ = 0x80000000;
+
    private const uint GENERIC_WRITE = 0x40000000;
+
    private const uint FILE_SHARE_READ = 0x00000001;
+
    private const uint FILE_SHARE_WRITE = 0x00000002;
+
    private const uint OPEN_EXISTING = 3;
+

          
+
    private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
+

          
+
    public IntPtr Handle {
+
      get; private set;
+
    }
+

          
+
    public Volume(DriveInfo drive, bool accessWrite)
+
    {
+
      uint accessFlags;
+

          
+
      if (drive.DriveType == DriveType.CDRom)
+
        accessFlags = GENERIC_READ;
+
      else if (drive.DriveType == DriveType.Removable)
+
        accessFlags = GENERIC_READ | GENERIC_WRITE;
+
      else
+
        throw new NotSupportedException("リムーバブルドライブではありません");
+

          
+
      if (accessWrite)
+
        accessFlags |= GENERIC_WRITE;
+

          
+
      this.Handle = CreateFile(string.Format(@"\\.\{0}:", drive.Name[0]),
+
                               accessFlags,
+
                               FILE_SHARE_READ | FILE_SHARE_WRITE,
+
                               IntPtr.Zero,
+
                               OPEN_EXISTING,
+
                               0,
+
                               IntPtr.Zero);
+

          
+
      if (this.Handle == INVALID_HANDLE_VALUE)
+
        throw new Win32Exception(Marshal.GetLastWin32Error());
+
    }
+

          
+
    public void Dispose()
+
    {
+
      if (Handle != INVALID_HANDLE_VALUE) {
+
        CloseHandle(Handle); // ignore any errors
+
        Handle = INVALID_HANDLE_VALUE;
+
      }
+
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
 
#code(vb){{
#code(vb){{
 
Imports System
Imports System
+
Imports System.ComponentModel
 
Imports System.IO
Imports System.IO
+
Imports System.Runtime.CompilerServices
 
Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices
 

        

        
~
Class Sample
Class EjectCDRom
~
  Shared Sub Main()

          
~
    For Each d As DriveInfo In DriveInfo.GetDrives()
    <DllImport("kernel32.dll")> _
~
      Console.WriteLine("{0,-10} {1}", d.Name, d.DriveType)
    Private Shared Function CreateFile( _
~
    Next
    ByVal lpFileName As String, _
-
    ByVal dwDesiredAccess As Integer, _
-
    ByVal dwShareMode As Integer, _
-
    ByVal lpSecurityAttributes As IntPtr, _
-
    ByVal dwCreationDisposition As Integer, _
-
    ByVal dwFLagsAndAttributes As Integer, _
-
    ByVal hTemplateFile As IntPtr _
-
    ) As IntPtr
-
    End Function
-

          
-
    <DllImport("kernel32.dll")> _
-
    Private Shared Function CloseHandle( _
-
        ByVal hObject As IntPtr _
-
    ) As Boolean
-
    End Function
-

          
-
    <DllImport("kernel32.dll")> _
-
    Private Shared Function DeviceIoControl(ByVal hDevice As IntPtr, _
-
         ByVal dwIoControlCode As Integer, _
-
         ByVal lpInBuffer As IntPtr, _
-
         ByVal nInBufferSize As Integer, _
-
         ByVal lpOutBuffer As IntPtr, _
-
         ByVal nOutBufferSize As Integer, _
-
         ByRef lpBytesReturned As Integer, _
-
         ByVal lpOverlapped As IntPtr) As Boolean
-
    End Function
-

          
-
    <DllImport("kernel32.dll")> _
-
    Private Shared Function DeviceIoControl(ByVal hDevice As IntPtr, _
-
         ByVal dwIoControlCode As Integer, _
-
         ByVal lpInBuffer As Byte(), _
-
         ByVal nInBufferSize As Integer, _
-
         ByVal lpOutBuffer As IntPtr, _
-
         ByVal nOutBufferSize As Integer, _
-
         ByRef lpBytesReturned As Integer, _
-
         ByVal lpOverlapped As IntPtr) As Boolean
-
    End Function
-

          
-
    <DllImport("kernel32.dll")> _
-
    Private Shared Function GetDriveType(ByVal nDrive As String) As Integer
-
    End Function
-

          
-
    Private Shared ReadOnly INVALID_HANDLE_VALUE As IntPtr = New IntPtr(-1)
-

          
-
    Private Const GENERIC_READ As Integer = &H80000000
-
    Private Const OPEN_EXISTING As Integer = 3
-

          
-
    Private Const FILE_SHARE_READ As Integer = &H1
-

          
-
    Private Const FSCTL_LOCK_VOLUME As Integer = &H90018
-
    Private Const FSCTL_DISMOUNT_VOLUME As Integer = &H90020
-

          
-
    Private Const IOCTL_STORAGE_EJECT_MEDIA As Integer = &H2D4808
-
    Private Const IOCTL_STORAGE_LOAD_MEDIA As Integer = &H2D480C
-
    Private Const IOCTL_STORAGE_MEDIA_REMOVAL As Integer = &H2D4804
-

          
-
    Private Const DRIVE_UNKNOWN As Integer = 0
-
    Private Const DRIVE_NO_ROOT_DIR As Integer = 1
-
    Private Const DRIVE_REMOVABLE As Integer = 2
-
    Private Const DRIVE_FIXED As Integer = 3
-
    Private Const DRIVE_REMOTE As Integer = 4
-
    Private Const DRIVE_CDROM As Integer = 5
-
    Private Const DRIVE_RAMDISK As Integer = 6
-

          
-
    Public Shared Sub Main()
 

        

        
~
    Console.Write("テストするドライブを指定してください: ")
        ' システムに存在するすべての論理ドライブを列挙
-
        For Each drive As String In Environment.GetLogicalDrives()
 

        

        
~
    Dim drive As New DriveInfo(Console.ReadLine().TrimEnd())
            ' ドライブの種類を取得
-
            Dim driveType As Integer = GetDriveType(drive)
 

        

        
~
    If drive.IsTrayOpened() Then
            ' CD-ROMドライブの場合
~
      Console.WriteLine("トレイを閉じます")
            If driveType = DRIVE_CDROM Then
 

        

        
~
      drive.Load()
                ' ドライブレターから最後の"\"を取り除く
-
                drive = drive.Substring(0, 2)
 

        

        
~
      Console.WriteLine("トレイを閉じました")
                Eject(drive)
+
    Else
+
      Console.WriteLine("トレイは閉じています")
+
    End If
 

        

        
~
    Console.WriteLine("メディアをチェックします")
            End If
 

        

        
~
    If drive.HasMedia() Then
        Next
+
      Console.WriteLine("メディアがセットされています")
+
    Else
+
      Console.WriteLine("メディアはセットされていません")
+
    End If
 

        

        
~
    Console.WriteLine("トレイを開きます")
    End Sub
-

          
-
    Private Shared Sub Eject(ByVal drive As String)
-

          
-
        Console.WriteLine("ドライブ{0}のメディアを排出", drive)
 

        

        
~
    drive.Eject()
        ' ドライブを開く
-
        Dim hDrive As IntPtr = CreateFile(String.Format("\\.\{0}", drive), GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero)
 

        

        
~
    Console.WriteLine("トレイを開きました")
        If INVALID_HANDLE_VALUE.Equals(hDrive) Then Throw New IOException("CreateFile failed")
~
  End Sub

          
~
End Class
        ' ドライブをロックする(失敗した場合は25ミリ秒おきに最大20回リトライする)
-
        For i As Integer = 0 To 19
 

        

        
~
''' <summary>DriveInfoクラスにトレイの開閉を行う拡張メソッドを追加するクラス</summary>
            If DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, 0, IntPtr.Zero) Then
+
Module DriveInfoEjectLoadExtensions
+
  ''' <summary>ドライブにメディアがセットされているかどうかを返す</summary>
+
  <Extension()> _
+
  Public Function HasMedia(ByVal drive As DriveInfo) As Boolean
+
    Using volume As New Volume(drive, False)
+
      Return IOControl.IOCtl(volume.Handle, IOControl.IOCTL_STORAGE_CHECK_VERIFY)
+
    End Using
+
  End Function
+

          
+
  ''' <summary>ドライブのトレイが開いているかどうかを返す</summary>
+
  <Extension()> _
+
  Public Function IsTrayOpened(ByVal drive As DriveInfo) As Boolean
+
    Using volume As New Volume(drive, True)
+
      '
+
      ' http://www.eggheadcafe.com/conversation.aspx?messageid=33820121&threadid=33794406
+
      ' http://forum.sources.ru/index.php?showtopic=225102
+
      '
+
      Const dataLength As Integer = 8
+
      Dim sptd As New SCSI_PASS_THROUGH_DIRECT()
+
      Dim size As Integer = Marshal.SizeOf(GetType(SCSI_PASS_THROUGH_DIRECT))
+
      Dim bytesReturned As UInteger
+

          
+
      Try
+
        sptd.Length = CUShort(size)
+
        sptd.PathId = 0
+
        sptd.TargetId = 0
+
        sptd.CdbLength = 12
+
        sptd.DataIn = IOControl.SCSI_IOCTL_DATA_IN
+
        sptd.DataTransferLength = dataLength
+
        sptd.TimeOutValue = 5
+
        sptd.DataBuffer = Marshal.AllocCoTaskMem(dataLength)
+
        sptd.Cdb = New Byte(15) {}
+
        sptd.Cdb(0) = &hBD ' mechanism status
+
        sptd.Cdb(9) = 8 ' timeout value
+

          
+
        If Not IOControl.DeviceIoControl(volume.Handle, IOControl.IOCTL_SCSI_PASS_THROUGH_DIRECT, sptd, CUInt(size), sptd, CUInt(size), bytesReturned, IntPtr.Zero) Then
+
          Throw New Win32Exception(Marshal.GetLastWin32Error())
+
        End If
+

          
+
        Return (Marshal.ReadByte(sptd.DataBuffer, 1) And &h10) = &h10
+
      Finally
+
        If sptd.DataBuffer <> IntPtr.Zero Then Marshal.FreeCoTaskMem(sptd.DataBuffer)
+
      End Try
+
    End Using
+
  End Function
+

          
+
  ''' <summary>メディアをセットする、またはドライブのトレイを閉じる</summary>
+
  <Extension()> _
+
  Public Sub Load(ByVal drive As DriveInfo)
+
    Using volume As New Volume(drive, True)
+
      If Not IOControl.IOCtl(volume.Handle, IOControl.IOCTL_STORAGE_LOAD_MEDIA) Then
+
        Throw New Win32Exception(Marshal.GetLastWin32Error())
+
      End If
+
    End Using
+
  End Sub
+

          
+
  ''' <summary>メディアの取り外す、またはドライブのトレイを開く</summary>
+
  <Extension()> _
+
  Public Sub Eject(ByVal drive As DriveInfo)
+
    Using volume As New Volume(drive, True)
+
      '
+
      ' http://support.microsoft.com/kb/165721/
+
      '
+
      Const maxRetry As Integer = 20 ' デバイスロックの最大試行回数
+
      Dim locked As Boolean = False
+

          
+
      For t As Integer = 0 To maxRetry - 1
+
        If IOControl.IOCtl(volume.Handle, IOControl.FSCTL_LOCK_VOLUME) Then
+
          locked = True
+
          Exit For
+
        Else
+
          System.Threading.Thread.Sleep(1000) ' ロックできなければ1秒後に再試行
+
        End If
+
      Next
+

          
+
      If Not locked Then Throw New IOException("デバイスがビジー状態です")
+

          
+
      If Not IOControl.IOCtl(volume.Handle, IOControl.FSCTL_DISMOUNT_VOLUME) Then
+
        Throw New Win32Exception(Marshal.GetLastWin32Error())
+
      End If
+

          
+
      Dim bytesReturned As UInteger
+
      Dim pmr As PREVENT_MEDIA_REMOVAL
+

          
+
      pmr.PreventMediaRemoval = 0 ' FALSE
+

          
+
      If Not IOControl.DeviceIoControl(volume.Handle, IOControl.IOCTL_STORAGE_MEDIA_REMOVAL, pmr, CUInt(Marshal.SizeOf(GetType(PREVENT_MEDIA_REMOVAL))), IntPtr.Zero, CUInt(0), bytesReturned, IntPtr.Zero) Then
+
        Throw New Win32Exception(Marshal.GetLastWin32Error())
+
      End If
+

          
+
      If Not IOControl.IOCtl(volume.Handle, IOControl.IOCTL_STORAGE_EJECT_MEDIA) Then
+
        Throw New Win32Exception(Marshal.GetLastWin32Error())
+
      End If
+
    End Using
+
  End Sub
+

          
+
  ' DeviceIoControl呼び出し関連のクラス
+
  Private Class IOControl
+
    <DllImport("kernel32", SetLastError := True)> _
+
    Public Shared Overloads Function DeviceIoControl(ByVal hDevice As IntPtr, _
+
                                                     ByVal dwIoControlCode As UInteger, _
+
                                                     ByVal lpInBuffer As IntPtr, _
+
                                                     ByVal nInBufferSize As UInteger, _
+
                                                     ByVal lpOutBuffer As IntPtr, _
+
                                                     ByVal nOutBufferSize As UInteger, _
+
                                                     <Out> ByRef lpBytesReturned As UInteger, _
+
                                                     ByVal lpOverlapped As IntPtr) As Boolean
+
    End Function
 

        

        
~
    <DllImport("kernel32", SetLastError := True)> _
                ' ドライブをアンマウントする
~
    Public Shared Overloads Function DeviceIoControl(ByVal hDevice As IntPtr, _
                If DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, 0, IntPtr.Zero) Then
+
                                                     ByVal dwIoControlCode As UInteger, _
+
                                                     ByRef lpInBuffer As SCSI_PASS_THROUGH_DIRECT, _
+
                                                     ByVal nInBufferSize As UInteger, _
+
                                                     ByRef lpOutBuffer As SCSI_PASS_THROUGH_DIRECT, _
+
                                                     ByVal nOutBufferSize As UInteger, _
+
                                                     <Out> ByRef lpBytesReturned As UInteger, _
+
                                                     ByVal lpOverlapped As IntPtr) As Boolean
+
    End Function
 

        

        
~
    <DllImport("kernel32", SetLastError := True)> _
                    ' ドライブに対するイジェクト命令を有効にする
~
    Public Shared Overloads Function DeviceIoControl(ByVal hDevice As IntPtr, _
                    Dim preventMediaRemoval(0) As Byte
+
                                                     ByVal dwIoControlCode As UInteger, _
+
                                                     ByRef lpInBuffer As PREVENT_MEDIA_REMOVAL, _
+
                                                     ByVal nInBufferSize As UInteger, _
+
                                                     ByVal lpOutBuffer As IntPtr, _
+
                                                     ByVal nOutBufferSize As UInteger, _
+
                                                     <Out> ByRef lpBytesReturned As UInteger, _
+
                                                     ByVal lpOverlapped As IntPtr) As Boolean
+
    End Function
 

        

        
~
    Public Const FILE_ANY_ACCESS As UInteger= &h00000000
                    preventMediaRemoval(0) = 0
+
    Public Const FILE_SPECIAL_ACCESS As UInteger= FILE_ANY_ACCESS
+
    Public Const FILE_READ_ACCESS As UInteger = &h00000001
+
    Public Const FILE_WRITE_ACCESS As UInteger = &h00000002
+

          
+
    Public Const SCSI_IOCTL_DATA_OUT As Byte = 0
+
    Public Const SCSI_IOCTL_DATA_IN As Byte = 1
+
    Public Const SCSI_IOCTL_DATA_UNSPECIFIED As Byte = 2
+

          
+
    Public Const METHOD_BUFFERED As UInteger = 0
+
    Public Const METHOD_IN_DIRECT As UInteger = 1
+
    Public Const METHOD_OUT_DIRECT As UInteger = 2
+
    Public Const METHOD_NEITHER As UInteger = 3
+

          
+
    Public Const FILE_DEVICE_CONTROLLER As UInteger= 4
+
    Public Const FILE_DEVICE_FILE_SYSTEM As UInteger= 9
+
    Public Const FILE_DEVICE_MASS_STORAGE As UInteger= 45
+

          
+
    Public Shared ReadOnly IOCTL_SCSI_BASE                   As UInteger = FILE_DEVICE_CONTROLLER
+
    Public Shared ReadOnly IOCTL_SCSI_PASS_THROUGH_DIRECT    As UInteger = CTL_CODE(IOCTL_SCSI_BASE, &h0405, METHOD_BUFFERED, FILE_READ_ACCESS Or FILE_WRITE_ACCESS)
+

          
+
    Public Shared ReadOnly FSCTL_BASE                        As UInteger = FILE_DEVICE_FILE_SYSTEM
+
    Public Shared ReadOnly FSCTL_LOCK_VOLUME                 As UInteger = CTL_CODE(FSCTL_BASE, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
    Public Shared ReadOnly FSCTL_DISMOUNT_VOLUME             As UInteger = CTL_CODE(FSCTL_BASE, 8, METHOD_BUFFERED, FILE_ANY_ACCESS)
+

          
+
    Public Shared ReadOnly IOCTL_STORAGE_BASE                As UInteger = FILE_DEVICE_MASS_STORAGE
+
    Public Shared ReadOnly IOCTL_STORAGE_CHECK_VERIFY        As UInteger = CTL_CODE(IOCTL_STORAGE_BASE, &h0200, METHOD_BUFFERED, FILE_READ_ACCESS)
+
    Public Shared ReadOnly IOCTL_STORAGE_MEDIA_REMOVAL       As UInteger = CTL_CODE(IOCTL_STORAGE_BASE, &h0201, METHOD_BUFFERED, FILE_READ_ACCESS)
+
    Public Shared ReadOnly IOCTL_STORAGE_EJECT_MEDIA         As UInteger = CTL_CODE(IOCTL_STORAGE_BASE, &h0202, METHOD_BUFFERED, FILE_READ_ACCESS)
+
    Public Shared ReadOnly IOCTL_STORAGE_LOAD_MEDIA          As UInteger = CTL_CODE(IOCTL_STORAGE_BASE, &h0203, METHOD_BUFFERED, FILE_READ_ACCESS)
 

        

        
~
    Private Shared Function CTL_CODE(ByVal t As UInteger, ByVal f As UInteger, ByVal m As UInteger, ByVal a As UInteger) As UInteger
                    If DeviceIoControl(hDrive, IOCTL_STORAGE_MEDIA_REMOVAL, preventMediaRemoval, preventMediaRemoval.Length, IntPtr.Zero, 0, 0, IntPtr.Zero) Then
+
      Return CUInt(t << 16 Or a << 14 Or f << 2 Or m)
+
    End Function
 

        

        
~
    Public Shared Function IOCtl(ByVal hDevice As IntPtr, ByVal dwIoControlCode As UInteger) As Boolean
                        ' CD-ROMを排出する
~
      Dim bytesReturned As UInteger
                        ' IOCTL_STORAGE_EJECT_MEDIAの代わりにIOCTL_STORAGE_LOAD_MEDIAを指定すれば、
-
                        ' 開いているディスクトレイを閉じることが出来る
-
                        DeviceIoControl(hDrive, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, 0, IntPtr.Zero)
 

        

        
~
      Return DeviceIoControl(hDevice, dwIoControlCode, IntPtr.Zero, CUInt(0), IntPtr.Zero, CUInt(0), bytesReturned, IntPtr.Zero)
                        Console.WriteLine("排出成功")
+
    End Function
+
  End Class
 

        

        
~
  <StructLayout(LayoutKind.Sequential)> _
                        Exit For
+
  Private Structure SCSI_PASS_THROUGH_DIRECT
+
    Public Length As UShort
+
    Public ScsiStatus As Byte
+
    Public PathId As Byte
+
    Public TargetId As Byte
+
    Public Lun As Byte
+
    Public CdbLength As Byte
+
    Public SenseInfoLength As Byte
+
    Public DataIn As Byte
+
    Public DataTransferLength As UInteger
+
    Public TimeOutValue As UInteger
+
    'Public DataBuffer As Byte()
+
    Public DataBuffer As IntPtr
+
    Public SenseInfoOffset As UInteger
+
    <MarshalAs(UnmanagedType.ByValArray, SizeConst := 16)> Public Cdb As Byte()
+
  End Structure
+

          
+
  <StructLayout(LayoutKind.Sequential)> _
+
  Private Structure PREVENT_MEDIA_REMOVAL
+
    Public PreventMediaRemoval As Byte
+
  End Structure
+

          
+
  ''' <summary>ボリュームのハンドルとCreateFile/CloseHandleをラップするクラス</summary>
+
  Private Class Volume
+
    Implements IDisposable
+

          
+
    <DllImport("kernel32", SetLastError := True, CharSet := CharSet.Auto)> _
+
    Private Shared Function CreateFile(ByVal lpFIleName As String, _
+
                                       ByVal dwDesiredAccess As UInteger, _
+
                                       ByVal dwShareMode As UInteger, _
+
                                       ByVal lpSecurityAttributes As IntPtr, _
+
                                       ByVal dwCreationDisposition As UInteger, _
+
                                       ByVal dwFlagsAndAttributes As UInteger, _
+
                                       ByVal hTemplateFile As IntPtr) As IntPtr
+
    End Function
 

        

        
~
    <DllImport("kernel32", SetLastError := True)> _
                    End If
+
    Private Shared Function CloseHandle(ByVal hObject As IntPtr) As Boolean
+
    End Function
 

        

        
~
    Private Const GENERIC_READ As UInteger = &h80000000UI
                End If
+
    Private Const GENERIC_WRITE As UInteger = &h40000000UI
+
    Private Const FILE_SHARE_READ As UInteger = &h00000001UI
+
    Private Const FILE_SHARE_WRITE As UInteger = &h00000002UI
+
    Private Const OPEN_EXISTING As UInteger = 3
 

        

        
~
    Private Shared ReadOnly INVALID_HANDLE_VALUE As IntPtr = New IntPtr(-1)
            Else
 

        

        
~
    Private _handle As IntPtr
                ' 25ミリ秒待機
-
                System.Threading.Thread.Sleep(25)
 

        

        
~
    Public ReadOnly Property Handle As IntPtr
            End If
+
      Get
+
        Return _handle
+
      End Get
+
    End Property
+

          
+
    Public Sub New(ByVal drive As DriveInfo, ByVal accessWrite As Boolean)
+
      Dim accessFlags As UInteger
+

          
+
      If drive.DriveType = DriveType.CDRom Then
+
        accessFlags = GENERIC_READ
+
      Else If drive.DriveType = DriveType.Removable Then
+
        accessFlags = GENERIC_READ Or GENERIC_WRITE
+
      Else
+
        Throw New NotSupportedException("リムーバブルドライブではありません")
+
      End If
+

          
+
      If accessWrite Then accessFlags = accessFlags Or GENERIC_WRITE
+

          
+
      _handle = CreateFile(String.Format("\\.\{0}:", drive.Name(0)), _
+
                           accessFlags, _
+
                           FILE_SHARE_READ Or FILE_SHARE_WRITE, _
+
                           IntPtr.Zero, _
+
                           OPEN_EXISTING, _
+
                           0, _
+
                           IntPtr.Zero)
 

        

        
~
      If _handle = INVALID_HANDLE_VALUE Then Throw New Win32Exception(Marshal.GetLastWin32Error())
        Next
+
    End Sub
 

        

        
+
    Public Sub Dispose() Implements IDisposable.Dispose
+
      If _handle <> INVALID_HANDLE_VALUE Then
+
        CloseHandle(_handle) ' ignore any errors
+
        _handle = INVALID_HANDLE_VALUE
+
      End If
 
    End Sub
    End Sub
+
  End Class
+
End Module
+
}}
+
#tabpage-end
 

        

        
~
*動作例
End Class
+
Q:\に接続されている光学ディスクドライブでの例。 テストに使ったのはIODATA製ブルーレイドライブ、BRD-SH10B。
+
#prompt{{
+
E:\temp>eject.exe
+
A:\        Removable
+
C:\        Fixed
+
D:\        Removable
+
E:\        Fixed
+
F:\        Fixed
+
Q:\        CDRom
+
R:\        CDRom
+
テストするドライブを指定してください: q:\
+
トレイを閉じます
+
トレイを閉じました
+
メディアをチェックします
+
メディアがセットされています
+
トレイを開きます
+
トレイを開きました
+
}}
+

          
+
D:\に接続されているUSBメモリでの例。 テストに使ったのはSandisk製のもの。 Ejectメソッドを実行すると、「ハードウェアの安全な取り外し」を行った場合と同等の動作となる。
+
#prompt{{
+
E:\temp>eject.exe
+
A:\        Removable
+
C:\        Fixed
+
D:\        Removable
+
E:\        Fixed
+
F:\        Fixed
+
Q:\        CDRom
+
R:\        CDRom
+
テストするドライブを指定してください: d:\
+
トレイは閉じています
+
メディアをチェックします
+
メディアがセットされています
+
トレイを開きます
+
トレイを開きました
 
}}
}}
 

        

        

programming/tips/setdeskwallpaper/index.wiki.txt

current previous
1,274 1,189
 
${smdncms:title,壁紙を変更する}
${smdncms:title,壁紙を変更する}
~
${smdncms:keywords,C#,VB.NET,SystemParametersInfo,SPI_SETDESKWALLPAPER,WallpaperStyle,TileWallpaper}
${smdncms:keywords,C#,VB.NET,SystemParametersInfo,SPI_SETDESKWALLPAPER}
 
${smdncms:tags,plat/win,api/win32,lang/c#,lang/vb}
${smdncms:tags,plat/win,api/win32,lang/c#,lang/vb}
 
${smdncms:document_versions,codelang=cs,codelang=vb}
${smdncms:document_versions,codelang=cs,codelang=vb}
 

        

        
~
-関連するページ
壁紙を変更するAPI関数を使った処理をラップしたクラス。 ファイル名に指定するファイルはビットマップファイルである必要がある。
+
--[[programming/tips/setdeskwallpaper_multidisplay]]
+
--[[programming/tips/displayresolutionchanged]]
+
--[[programming/tips/imageformatandcodec]]
+
--[[programming/tips/rotate_image_by_exif_info]]
+

          
+
SystemParametersInfoにSPI_SETDESKWALLPAPERを指定して呼び出すことで、指定した画像ファイルを壁紙として設定することができる。
+

          
+
また、レジストリのHKEY_CURRENT_USER\Control Panel\Desktop\にある次のキーの値を変えることで、壁紙の表示方法も変更することができる。 キーWallpaperStyleとTileWallpaperの値と、表示方法の組み合わせは次のとおり。
+

          
+
|*WallpaperStyle, TileWallpaperと壁紙の表示方法の対応
+
|~WallpaperStyle|~TileWallpaper|~表示方法|~備考|h
+
|0|0|中央に表示||
+
|0|1|並べて表示||
+
|2|0|{{
+
拡大して表示/画面に合わせて伸縮
+
(アスペクト比を維持せず、画面全体の大きさにあわせて拡大)
+
}}||
+
|6|0|{{
+
ページ縦幅に合わせる
+
(アスペクト比を維持し、画面全体の大きさにあわせて拡大、画像が画面に収まるよう余白が入る)
+
}}|Windows 7以降|
+
|10|0|{{
+
ページ横幅に合わせる
+
(アスペクト比を維持し、画面全体の大きさにあわせて拡大、画面に収まらない部分ははみ出す)
+
}}|Windows 7以降|
 

        

        
~
以下のコードは、壁紙を変更する処理をラップしたクラスと、それを使ってさまざまな表示方法を試すサンプル。
マルチディスプレイ環境での壁紙の変更については、[[programming/tips/setdeskwallpaper_multidisplay]]を参照のこと。
 

        

        
 
#googleadunit
#googleadunit
 

        

        
 
#tabpage(C#)
#tabpage(C#)
 
#code(cs){{
#code(cs){{
 
using System;
using System;
+
using System.ComponentModel;
 
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
+
using System.Threading;
 
using Microsoft.Win32;
using Microsoft.Win32;
 

        

        
~
class Sample {
//-------------------------------------------------------------------------------------
~
  public static void Main()
// 名前 : WallpaperChanger
~
  {
// 概要 : 壁紙の配置方法を設定するための列挙型
~
    const string file = @"wallpaper.bmp";
// 割り当てられている数値はそのまま使うので変更しないこと
~

          
//-------------------------------------------------------------------------------------
~
    foreach (var style in new[] {
public enum WallpaperLocation : int
~
      WallpaperStyle.Center,
{
~
      WallpaperStyle.Tile,
    Center = 0,  // 中央に表示
~
      WallpaperStyle.Stretch,
    Fill = 1,    // 全体に並べて表示
~
      WallpaperStyle.ResizeFit,
    Stretch = 2, // 拡大して表示
~
      WallpaperStyle.ResizeFill,
}
+
    }) {
+
      Wallpaper.SetWallpaper(file, style);
 

        

        
~
      Thread.Sleep(2500);
//-------------------------------------------------------------------------------------
~
    }
// 名前 : WallpaperChanger
-
// 概要 : 壁紙を設定するメソッドを含むクラス
-
//-------------------------------------------------------------------------------------
-
public class WallpaperChanger
-
{
-
    // システムに関するパラメータを取得/設定するAPI関数
-
    [DllImport("user32")] 
-
    private extern static int SystemParametersInfo
-
        (int uAction,
-
        int uParam,
-
        [MarshalAs(UnmanagedType.LPStr)] string lpvParam,
-
        int fuWinIni);
-
        
-
    // SystemParametersInfoの呼び出し時に使用する定数
-
    private const int SPI_SETDESKWALLPAPER  = 20;  // デスクトップの壁紙を設定
-
    private const int SPIF_UPDATEINIFILE    = 0x1; // 更新する
-
    private const int SPIF_SENDWININICHANGE = 0x2; // 全てのアプリケーションに通知して更新する
-

          
-
    //-------------------------------------------------------------------------------------
-
    // 名前 : SetWallpaper()
-
    // 概要 : bitmapFileで指定されたファイルを壁紙に設定する
-
    //-------------------------------------------------------------------------------------
-
    public static void SetWallpaper( string bitmapFile, WallpaperLocation style )
-
    {
-

          
-
        // レジストリの値を変更
-

          
-
        RegistryKey regkeyDesktop;
-

          
-
        // HKEY_CURRENT_USER\Control Panel\Desktopを開く
-
        regkeyDesktop = Registry.CurrentUser.OpenSubKey( "Control Panel\\Desktop", true );
-

          
-
        if ( regkeyDesktop != null ) 
-
        {
-

          
-
            // TileWallpaperを 0 にすると中央に表示、1 にすると全体に並べて表示される
-
            // また、Wallpaperstyleを 2 にすると拡大して表示される。
-

          
-
            // さらに、レジストリに書き込むときは文字として書き込まなければならない
-
            if ( style == WallpaperLocation.Stretch ) 
-
            {
-
                regkeyDesktop.SetValue( "TileWallpaper", (0).ToString());
-
                regkeyDesktop.SetValue( "Wallpaperstyle", (2).ToString());
-
            }
-
            else
-
            {
-
                regkeyDesktop.SetValue( "TileWallpaper", style.ToString());
-
                regkeyDesktop.SetValue( "Wallpaperstyle", (0).ToString());
-
            }
 

        

        
~
    Wallpaper.UnsetWallpaper();
            // 閉じる
~
  }
            regkeyDesktop.Close();
+
}
 

        

        
~
/// <summary>壁紙の表示スタイル</summary>
        }
+
class WallpaperStyle {
+
  /// <summary>中央に表示</summary>
+
  public static readonly WallpaperStyle Center = new WallpaperStyle(0, 0);
+

          
+
  /// <summary>並べて表示</summary>
+
  public static readonly WallpaperStyle Tile = new WallpaperStyle(0, 1);
+

          
+
  /// <summary>拡大して表示 (画面に合わせて伸縮)</summary>
+
  public static readonly WallpaperStyle Stretch = new WallpaperStyle(2, 0);
+

          
+
  /// <summary>リサイズして表示 (ページ縦幅に合わせる)</summary><remarks>Windows 7以降のみ</remarks>
+
  public static readonly WallpaperStyle ResizeFit = new WallpaperStyle(6, 0);
+

          
+
  /// <summary>リサイズして全体に表示 (ページ横幅に合わせる)</summary><remarks>Windows 7以降のみ</remarks>
+
  public static readonly WallpaperStyle ResizeFill = new WallpaperStyle(10, 0);
+

          
+
  internal readonly int _WallpaperStyle;
+
  internal readonly int _TileWallpaper;
+

          
+
  private WallpaperStyle(int wallpaperStyle, int tileWallpaper)
+
  {
+
    _WallpaperStyle = wallpaperStyle;
+
    _TileWallpaper = tileWallpaper;
+
  }
+
}
 

        

        
~
/// <summary>壁紙の設定を行うためのクラス</summary>
        // 壁紙となるファイルを指定し、更新する
~
static class Wallpaper {
        SystemParametersInfo( SPI_SETDESKWALLPAPER, 0, bitmapFile, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE );
+
  /// <summary>壁紙を設定する</summary>
+
  /// <param name="file">壁紙として設定する画像ファイル</praram>
+
  public static void SetWallpaper(string file, WallpaperStyle style)
+
  {
+
    if (string.IsNullOrEmpty(file))
+
      throw new ArgumentException("ファイル名が空", "file");
+
    if (style == null)
+
      throw new ArgumentNullException("style");
+

          
+
    // HKEY_CURRENT_USER\Control Panel\Desktopを開く
+
    using (var regkeyDesktop = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true)) {
+
      if (regkeyDesktop == null)
+
        throw new Win32Exception(Marshal.GetLastWin32Error());
+

          
+
      // TileWallpaperとWallpaperStyleを設定
+
      regkeyDesktop.SetValue("TileWallpaper", style._TileWallpaper.ToString());
+
      regkeyDesktop.SetValue("WallpaperStyle", style._WallpaperStyle.ToString());
+

          
+
      // 「並べて表示」、「拡大して表示」などの原点も変えたい場合は、この値を0以外に変更する
+
      regkeyDesktop.SetValue("WallpaperOriginX", "0");
+
      regkeyDesktop.SetValue("WallpaperOriginY", "0");
+

          
+
      // Wallpaperの値をセットすることでも壁紙を設定できるが、
+
      // SystemParametersInfoを呼び出さないと、壁紙を設定しても即座には反映されない
+
      //regkeyDesktop.SetValue("Wallpaper", file);
 
    }
    }
 

        

        
~
    SetDeskWallpaper(file);
    //-------------------------------------------------------------------------------------
~
  }
    // 名前 : RemoveWallpaper()
~

          
    // 概要 : 壁紙の削除する
~
  /// <summary>壁紙をはがす</summary>
    //-------------------------------------------------------------------------------------
~
  public static void UnsetWallpaper()
    public static void RemoveWallpaper()
~
  {
    {
~
    // ファイル名に"\0"を指定すると、
        // ファイル名に"\0"を渡すことで壁紙をはがすことができる
~
    // 壁紙をはがす(背景色のみの状態にする)ことができる
        SystemParametersInfo( SPI_SETDESKWALLPAPER, 0, "\0", SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE );
~
    SetDeskWallpaper("\0");
    }
+
  }
+

          
+
  [DllImport("user32.dll", SetLastError = true)]
+
  private static extern bool SystemParametersInfo(int uAction,
+
                                                  int uParam,
+
                                                  string lpvParam,
+
                                                  int fuWinIini);
+

          
+
  private static void SetDeskWallpaper(string file)
+
  {
+
    const int SPI_SETDESKWALLPAPER  = 0x0014; // デスクトップの壁紙を設定
+
    const int SPIF_UPDATEINIFILE    = 0x0001; // 設定を更新する
+
    const int SPIF_SENDWININICHANGE = 0x0002; // 設定の更新を全てのアプリケーションに通知(WM_SETTIMGCHANGE)する
+

          
+
    var flags = (Environment.OSVersion.Platform == PlatformID.Win32NT)
+
      ? SPIF_SENDWININICHANGE
+
      : SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE;
+

          
+
    // 壁紙となるファイルを設定する
+
    // なお、XP以前の場合はBMP形式のみが指定可能、Vista/7では加えてJPEG形式も指定可能
+
    if (!SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, file, flags))
+
      throw new Win32Exception(Marshal.GetLastWin32Error());
+
  }
 
}
}
 
}}
}}
 
#tabpage(VB)
#tabpage(VB)
 
#code(vb){{
#code(vb){{
+
Imports System
+
Imports System.ComponentModel
 
Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices
+
Imports System.Threading
 
Imports Microsoft.Win32
Imports Microsoft.Win32
 

        

        
~
Class Sample
'-------------------------------------------------------------------------------------
~
  Public Shared Sub Main()
' 名前 : WallpaperChanger
~
    Const file As String = "wallpaper.bmp"
' 概要 : 壁紙の配置方法を設定するための列挙型
~

          
'        割り当てられている数値はそのまま使うので変更しないこと
~
    For Each style As WallpaperStyle In New WallpaperStyle() { _
'-------------------------------------------------------------------------------------
~
      WallpaperStyle.Center, _
Public Enum WallpaperLocation As Integer
~
      WallpaperStyle.Tile, _
    Center = 0  '中央に表示
~
      WallpaperStyle.Stretch, _
    Fill = 1    '全体に並べて表示
~
      WallpaperStyle.ResizeFit, _
    Stretch = 2 '拡大して表示
~
      WallpaperStyle.ResizeFill _
End Enum
~
    }

          
~
      Wallpaper.SetWallpaper(file, style)
'-------------------------------------------------------------------------------------
-
' 名前 : WallpaperChanger
-
' 概要 : 壁紙を設定するメソッドを含むクラス
-
'-------------------------------------------------------------------------------------
-
Public Class WallpaperChanger
-

          
-
    ' システムに関するパラメータを取得/設定するAPI関数
-
    <DllImport("user32")> _
-
    Private Shared Function SystemParametersInfo _
-
        (ByVal uAction As Integer, _
-
        ByVal uParam As Integer, _
-
        <MarshalAs(UnmanagedType.LPStr)> ByVal lpvParam As String, _
-
        ByVal fuWinIni As Integer) _
-
        As Integer
-
    End Function
-

          
-
    ' SystemParametersInfoの呼び出し時に使用する定数
-
    Private Const SPI_SETDESKWALLPAPER As Integer = 20      'デスクトップの壁紙を設定
-
    Private Const SPIF_UPDATEINIFILE As Integer = &H1      '更新する
-
    Private Const SPIF_SENDWININICHANGE As Integer = &H2      '全てのアプリケーションに通知して更新する
-

          
-
    '-------------------------------------------------------------------------------------
-
    ' 名前 : SetWallpaper()
-
    ' 概要 : bitmapFileで指定されたファイルを壁紙に設定する
-
    '-------------------------------------------------------------------------------------
-
    Public Shared Sub SetWallpaper(ByVal bitmapFile As String, ByVal style As WallpaperLocation)
-

          
-
        ' レジストリの値を変更
-

          
-
        Dim regkeyDesktop As RegistryKey
-

          
-
        ' HKEY_CURRENT_USER\Control Panel\Desktopを開く
-
        regkeyDesktop = Registry.CurrentUser.OpenSubKey("Control Panel\Desktop", True)
-

          
-
        If Not regkeyDesktop Is Nothing Then
-

          
-
            ' TileWallpaperを 0 にすると中央に表示、1 にすると全体に並べて表示される
-
            ' また、Wallpaperstyleを 2 にすると拡大して表示される。
-

          
-
            ' さらに、レジストリに書き込むときは文字として書き込まなければならない
-
            If style = WallpaperLocation.Stretch Then
-
                regkeyDesktop.SetValue("TileWallpaper", CInt(0).ToString())
-
                regkeyDesktop.SetValue("Wallpaperstyle", CInt(2).ToString())
-
            Else
-
                regkeyDesktop.SetValue("TileWallpaper", CInt(style).ToString())
-
                regkeyDesktop.SetValue("Wallpaperstyle", CInt(0).ToString())
-
            End If
-

          
-
            ' 閉じる
-
            regkeyDesktop.Close()
-

          
-
        End If
-

          
-
        ' 壁紙となるファイルを指定し、更新する
-
        SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, bitmapFile, SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE)
-

          
-
    End Sub
-

          
-
    '-------------------------------------------------------------------------------------
-
    ' 名前 : RemoveWallpaper()
-
    ' 概要 : 壁紙の削除する
-
    '-------------------------------------------------------------------------------------
-
    Public Shared Sub RemoveWallpaper()
 

        

        
~
      Thread.Sleep(2500)
        ' ファイル名にvbNullCharを渡すことで壁紙をはがすことができる
~
    Next
        SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, vbNullChar, SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE)
 

        

        
~
    Wallpaper.UnsetWallpaper()
    End Sub
+
  End Sub
+
End Class
 

        

        
+
''' <summary>壁紙の表示スタイル</summary>
+
Class WallpaperStyle
+
  ''' <summary>中央に表示</summary>
+
  Public Shared ReadOnly Center As New WallpaperStyle(0, 0)
+

          
+
  ''' <summary>並べて表示</summary>
+
  Public Shared ReadOnly Tile As New WallpaperStyle(0, 1)
+

          
+
  ''' <summary>拡大して表示 (画面に合わせて伸縮)</summary>
+
  Public Shared ReadOnly Stretch As New WallpaperStyle(2, 0)
+

          
+
  ''' <summary>リサイズして表示 (ページ縦幅に合わせる)</summary><remarks>Windows 7以降のみ</remarks>
+
  Public Shared ReadOnly ResizeFit As New WallpaperStyle(6, 0)
+

          
+
  ''' <summary>リサイズして全体に表示 (ページ横幅に合わせる)</summary><remarks>Windows 7以降のみ</remarks>
+
  Public Shared ReadOnly ResizeFill As New WallpaperStyle(10, 0)
+

          
+
  Friend ReadOnly _WallpaperStyle As Integer
+
  Friend ReadOnly _TileWallpaper As Integer
+

          
+
  Private Sub New(ByVal wallpaperStyle As Integer, ByVal tileWallpaper As Integer)
+
    _WallpaperStyle = wallpaperStyle
+
    _TileWallpaper = tileWallpaper
+
  End Sub
 
End Class
End Class
+

          
+
''' <summary>壁紙の設定を行うためのクラス</summary>
+
Module Wallpaper
+
  ''' <summary>壁紙を設定する</summary>
+
  ''' <param name="file">壁紙として設定する画像ファイル</praram>
+
  Public Sub SetWallpaper(ByVal file As String, ByVal style As WallpaperStyle)
+
    If String.IsNullOrEmpty(file) Then Throw New ArgumentException("ファイル名が空", "file")
+
    If style Is Nothing Then Throw New ArgumentNullException("style")
+

          
+
    ' HKEY_CURRENT_USER\Control Panel\Desktopを開く
+
    Using regkeyDesktop As RegistryKey = Registry.CurrentUser.OpenSubKey("Control Panel\Desktop", True)
+
      If regkeyDesktop Is Nothing Then Throw New Win32Exception(Marshal.GetLastWin32Error())
+

          
+
      ' TileWallpaperとWallpaperStyleを設定
+
      regkeyDesktop.SetValue("TileWallpaper", style._TileWallpaper.ToString())
+
      regkeyDesktop.SetValue("WallpaperStyle", style._WallpaperStyle.ToString())
+

          
+
      ' 「並べて表示」、「拡大して表示」などの原点も変えたい場合は、この値を0以外に変更する
+
      regkeyDesktop.SetValue("WallpaperOriginX", "0")
+
      regkeyDesktop.SetValue("WallpaperOriginY", "0")
+

          
+
      ' Wallpaperの値をセットすることでも壁紙を設定できるが、
+
      ' SystemParametersInfoを呼び出さないと、壁紙を設定しても即座には反映されない
+
      'regkeyDesktop.SetValue("Wallpaper", file)
+
    End Using
+

          
+
    SetDeskWallpaper(file)
+
  End Sub
+

          
+
  ''' <summary>壁紙をはがす</summary>
+
  Public Sub UnsetWallpaper()
+
    ' ファイル名にvbNullCharを指定すると、
+
    ' 壁紙をはがす(背景色のみの状態にする)ことができる
+
    SetDeskWallpaper(vbNullChar)
+
  End Sub
+

          
+
  <DllImport("user32.dll", SetLastError := True)> _
+
  Private Function SystemParametersInfo(ByVal uAction As Integer, _
+
                                        ByVal uParam As Integer, _
+
                                        ByVal lpvParam As String, _
+
                                        ByVal fuWinIini As Integer) As Boolean
+
  End Function
+

          
+
  Private Sub SetDeskWallpaper(ByVal file As String)
+
    Const SPI_SETDESKWALLPAPER  As Integer = &h0014 ' デスクトップの壁紙を設定
+
    Const SPIF_UPDATEINIFILE    As Integer = &h0001 ' 設定を更新する
+
    Const SPIF_SENDWININICHANGE As Integer = &h0002 ' 設定の更新を全てのアプリケーションに通知(WM_SETTIMGCHANGE)する
+

          
+
    Dim flags As Integer = SPIF_SENDWININICHANGE
+

          
+
    If Environment.OSVersion.Platform <> PlatformID.Win32NT Then
+
      flags = flags Or SPIF_UPDATEINIFILE
+
    End If
+

          
+
    ' 壁紙となるファイルを設定する
+
    ' なお、XP以前の場合はBMP形式のみが指定可能、Vista/7では加えてJPEG形式も指定可能
+
    If Not SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, file, flags) Then
+
      Throw New Win32Exception(Marshal.GetLastWin32Error())
+
    End If
+
  End Sub
+
End Module
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
+
なお、マルチディスプレイ環境では、個々の画面に別々の壁紙を設定する方法がないので、[[programming/tips/setdeskwallpaper_multidisplay]]で解説しているような手段をとる必要がある。
+