構造体のポインタを引数にとるWindows APIの呼び出し - C#
C#で構造体のポインタを引数にとるWindows APIを呼び出したいことがあります。
構造体のポインタを引数にとるWindows APIの呼び出し方法を紹介します。
今回は、システム時刻を取得するGetSystemTimeとシステム時刻を設定するSetSystemTime APIを呼び出します。
コード例
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;
using System.Runtime.InteropServices;
namespace GetSetSystemTimeDemo
{
public partial class FormMain : Form
{
// SYSTEM TIME
// SYSTEMTIME構造体
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
public Int16 wYear;
public Int16 wMonth;
public Int16 wDayOfWeek;
public Int16 wDay;
public Int16 wHour;
public Int16 wMinute;
public Int16 wSecond;
public Int16 wMilliseconds;
}
[DllImport("Kernel32.dll", EntryPoint = "GetSystemTime")]
static extern void GetSystemTime(IntPtr lpSystemTime);
[DllImport("Kernel32.dll", EntryPoint = "SetSystemTime")]
static extern bool SetSystemTime(IntPtr lpSystemTime);
public FormMain()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
SystemTime st = GetSystemTime();
textBox1.Text = string.Format("{0:d}-{1:d}-{2:d}", st.wYear, st.wMonth, st.wDay);
}
private void button3_Click(object sender, EventArgs e)
{
DateTime now = DateTime.Now;
SystemTime st = new SystemTime();
st.wDay = 1;
st.wMonth = 1;
st.wYear = 2011;
st.wHour = 0;
st.wMinute = 0;
st.wSecond = 0;
SetSystemTimeSimple(st);
}
public SystemTime GetSystemTime()
{
SystemTime sysTime = new SystemTime();
IntPtr sysTimePtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(sysTime));
GetSystemTime(sysTimePtr);
sysTime = (SystemTime)Marshal.PtrToStructure(sysTimePtr, sysTime.GetType());
Marshal.FreeCoTaskMem(sysTimePtr);
return sysTime;
}
public void SetSystemTimeSimple(SystemTime sysTime)
{
IntPtr sysTimePtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(sysTime));
Marshal.StructureToPtr(sysTime, sysTimePtr, false);
bool ret = SetSystemTime(sysTimePtr);
Marshal.FreeCoTaskMem(sysTimePtr);
}
}
}
解説
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
public Int16 wYear;
public Int16 wMonth;
public Int16 wDayOfWeek;
public Int16 wDay;
public Int16 wHour;
public Int16 wMinute;
public Int16 wSecond;
public Int16 wMilliseconds;
}
にてSystemTime構造体を定義しています。メンバが記述順にメモリに配置されるように [StructLayout(LayoutKind.Sequential)]属性を追加しておきます。
[DllImport("Kernel32.dll", EntryPoint = "GetSystemTime")]
static extern void GetSystemTime(IntPtr lpSystemTime);
[DllImport("Kernel32.dll", EntryPoint = "SetSystemTime")]
static extern bool SetSystemTime(IntPtr lpSystemTime);
にて、GetSystemTime, SetSystemTime APIを定義します。
public SystemTime GetSystemTime() メソッド
SystemTime sysTime = new SystemTime();
SystemTime構造体を宣言しnewによりメモリ確保します。
IntPtr sysTimePtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(sysTime));
GetSystemTime()APIに与えるポインタを宣言し、Marshal.AllocCoTaskMem()メソッドによりメモリを確保します。メモリ確保するサイズはSystemTime構造体と同じサイズにします。Marshal.SizeOf()メソッドでサイズを求めます。
GetSystemTime(sysTimePtr);
GetSystemTime() APIを呼び出します。
sysTime = (SystemTime)Marshal.PtrToStructure(sysTimePtr, sysTime.GetType());
sysTimePtrはIntPtr型のためそのままでは実体データにアクセスできません。Marshal.PrtToStructure()メソッドを用いてsysTimePtrのデータをSystemTime構造体のsysTimeにコピーします。
Marshal.FreeCoTaskMem(sysTimePtr);
Marshal.FreeCoTaskMem()メソッドを用い確保したメモリを開放します。
return sysTime;
sysTimeを戻り値として返します。
public SystemTime SetSystemTime() メソッド
IntPtr sysTimePtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(sysTime));
SetSystemTime()APIに与えるポインタを宣言し、Marshal.AllocCoTaskMem()メソッドによりメモリを確保します。メモリ確保するサイズはSystemTime構造体と同じサイズにします。Marshal.SizeOf()メソッドでサイズを求めます。
Marshal.StructureToPtr(sysTime, sysTimePtr, false);
Marshal.StructureToPtr()メソッドを用い、引数で与えられたsysTime SystemTime構造体のデータをメモリ確保したsysTimePtrにコピーします。
bool ret = SetSystemTime(sysTimePtr);
SetSystemTime()APIを呼び出します。
Marshal.FreeCoTaskMem(sysTimePtr);
メモリ確保したsysTimePtrを開放します。
※注意
Windows NT, Windows Vista, Windows 7 などのNT系のOSではSetSystemTime() APIを呼び出しただけではシステム時間の変更はできません。システム時間を変更するには、APIを呼び出す前にSE_SYSTEMTIME_NAME 特権を有効にする必要があります。特権の取得と有効化は
こちらの記事にて紹介しています。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用