C#からマルチストリームを扱う方法
「ファイルに関する概要情報の取得」で紹介した、マルチストリームをC#から扱う方法について解説します。 マルチストリームに関しては「ファイルに関する概要情報の取得」のほうで簡単に解説しているのでそちらを 参考にしてください。
ファイルハンドルはCreateFile()で取得する
通常C#からファイルを扱う場合にはFileStreamクラスを使用します。しかし、このFileStreamクラスは マルチストリームをサポートしていません。従って、下記に示すような書式を用いてマルチストリームを 扱うことはできないようです。
(例 test.txtというファイルのInformationというストリームの場合)
test.txt:Information
そこでCreateFileというAPI関数を用いてファイルハンドルを取得し、そのハンドルを元にFileStreamを 生成する方法が考えられます。実は、FileSystemObjectをはじめとするマルチストリームをサポートする クラス等は内部でCreateFileを使用しています。では、簡単なサンプルプログラムを以下に示します。 (C:\test.txtが存在することを前提としています。)
using System.Runtime.InteropServices;
using System.IO;
namespace MultiStreamSample
{
class Class1
{
#region Win32 API宣言
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
string lpFileName, // ファイル名
DesiredAccess dwDesiredAccess, // アクセスモード
ShareMode dwShareMode, // 共有モード
int lpSecurityAttributes, // セキュリティ記述子
CreationDisposition dwCreationDisposition, // 作成方法
FlagsAndAttributes dwFlagsAndAttributes, // ファイル属性
IntPtr hTemplateFile // テンプレートファイルのハンドル
);
#region 列挙体
private enum DesiredAccess : uint
{
GENERIC_READ = 0x80000000,
GENERIC_WRITE = 0x40000000,
GENERIC_EXECUTE = 0x20000000
}
private enum ShareMode : uint
{
FILE_SHARE_READ = 0x00000001,
FILE_SHARE_WRITE = 0x00000002,
FILE_SHARE_DELETE = 0x00000004
}
private enum CreationDisposition : uint
{
CREATE_NEW = 1,
CREATE_ALWAYS = 2,
OPEN_EXISTING = 3,
OPEN_ALWAYS = 4,
TRUNCATE_EXISTING = 5
}
private enum FlagsAndAttributes : uint
{
FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
FILE_ATTRIBUTE_ENCRYPTED = 0x00004000,
FILE_ATTRIBUTE_HIDDEN = 0x00000002,
FILE_ATTRIBUTE_NORMAL = 0x00000080,
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
FILE_ATTRIBUTE_OFFLINE = 0x00001000,
FILE_ATTRIBUTE_READONLY = 0x00000001,
FILE_ATTRIBUTE_SYSTEM = 0x00000004,
FILE_ATTRIBUTE_TEMPORARY = 0x00000100
}
#endregion
#endregion
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("書き込みの開始");
IntPtr fileHandle = CreateFile(@"C:\test.txt:config",
DesiredAccess.GENERIC_WRITE,
ShareMode.FILE_SHARE_READ, 0,
CreationDisposition.OPEN_ALWAYS,
FlagsAndAttributes.FILE_ATTRIBUTE_NORMAL,
IntPtr.Zero);
using(StreamWriter sw = new StreamWriter(new FileStream(fileHandle,
FileAccess.Write)))
{
sw.WriteLine("書き込みテスト用文字列");
}
Console.WriteLine("書き込み完了" + Environment.NewLine);
Console.WriteLine("読み込み開始");
fileHandle = CreateFile(@"C:\test.txt:config",
DesiredAccess.GENERIC_READ,
ShareMode.FILE_SHARE_READ, 0,
CreationDisposition.OPEN_ALWAYS,
FlagsAndAttributes.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
using(StreamReader sr = new StreamReader(new FileStream(fileHandle,
FileAccess.Read)))
{
Console.WriteLine("---------------------");
Console.WriteLine(sr.ReadToEnd());
Console.WriteLine("---------------------");
}
Console.WriteLine("読み込み終了");
}
}
}
さて、これを利用してアプリケーションの設定をストリームに保存してみようと思います。 この方法はNTFSをサポートするOSでしか機能しませんが、初期化ファイルのように 誤って削除、改変される等の心配がなくなります。また、実行ファイルと一体なので移動やコピー のときにも便利でしょう。では、簡単なサンプルです。
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
#endregion
namespace SerializeOnNTFSStream
{
/// <summary>
/// Form1
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
/// <summary>
/// 保存するデータを格納しておく
/// </summary>
[Serializable]
public class SettingData
{
public Point FormLocation;
public Size FormSize;
public SettingData(Point location, Size size)
{
FormLocation = location;
FormSize = size;
}
}
private System.ComponentModel.Container components = null;
#region Win32 API宣言
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
string lpFileName, // ファイル名
DesiredAccess dwDesiredAccess, // アクセスモード
ShareMode dwShareMode, // 共有モード
int lpSecurityAttributes, // セキュリティ記述子
CreationDisposition dwCreationDisposition, // 作成方法
FlagsAndAttributes dwFlagsAndAttributes, // ファイル属性
IntPtr hTemplateFile // テンプレートファイルのハンドル
);
#region 列挙体
private enum DesiredAccess : uint
{
GENERIC_READ = 0x80000000,
GENERIC_WRITE = 0x40000000,
GENERIC_EXECUTE = 0x20000000
}
private enum ShareMode : uint
{
FILE_SHARE_READ = 0x00000001,
FILE_SHARE_WRITE = 0x00000002,
FILE_SHARE_DELETE = 0x00000004
}
private enum CreationDisposition : uint
{
CREATE_NEW = 1,
CREATE_ALWAYS = 2,
OPEN_EXISTING = 3,
OPEN_ALWAYS = 4,
TRUNCATE_EXISTING = 5
}
private enum FlagsAndAttributes : uint
{
FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
FILE_ATTRIBUTE_ENCRYPTED = 0x00004000,
FILE_ATTRIBUTE_HIDDEN = 0x00000002,
FILE_ATTRIBUTE_NORMAL = 0x00000080,
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
FILE_ATTRIBUTE_OFFLINE = 0x00001000,
FILE_ATTRIBUTE_READONLY = 0x00000001,
FILE_ATTRIBUTE_SYSTEM = 0x00000004,
FILE_ATTRIBUTE_TEMPORARY = 0x00000100
}
#endregion
#endregion
#region コンストラクタ
public Form1()
{
InitializeComponent();
}
#endregion
#region Dispose 処理
/// <summary>
/// 使用されているリソースに後処理を実行します。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#endregion
#region Windows フォーム デザイナで生成されたコード
/// <summary>
/// デザイナ サポートに必要なメソッドです。このメソッドの内容を
/// コード エディタで変更しないでください。
/// </summary>
private void InitializeComponent()
{
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.Closed += new System.EventHandler(this.Form1_Closed);
}
#endregion
#region メインエントリポイント
/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
#endregion
private void Form1_Load(object sender, System.EventArgs e)
{
IFormatter formatter = new BinaryFormatter();
IntPtr fileHandle = CreateFile(Application.ExecutablePath + ":setting",
DesiredAccess.GENERIC_READ,
ShareMode.FILE_SHARE_READ, 0,
CreationDisposition.OPEN_EXISTING,
FlagsAndAttributes.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if(fileHandle.ToInt32() != -1)
{
using(Stream s = new FileStream(fileHandle, FileAccess.Read))
{
SettingData setting = (SettingData)formatter.Deserialize(s);
this.Location = setting.FormLocation;
this.Size = setting.FormSize;
}
}
}
private void Form1_Closed(object sender, System.EventArgs e)
{
IFormatter formatter = new BinaryFormatter();
IntPtr fileHandle = CreateFile(Application.ExecutablePath + ":setting",
DesiredAccess.GENERIC_WRITE,
ShareMode.FILE_SHARE_READ, 0,
CreationDisposition.OPEN_ALWAYS,
FlagsAndAttributes.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if(fileHandle.ToInt32() != -1)
{
using(Stream s = new FileStream(fileHandle, FileAccess.Write))
{
SettingData setting = new SettingData(this.Location, this.Size);
formatter.Serialize(s, setting);
}
}
}
}
}
フォームの位置とサイズをシリアル化を使用して保存しています。動作を確認するときは、Visual Studio等のIDEから 開始(実行)してはいけません。これは自分のファイル自身に書き込んでいるためです。従って、実行ファイルを 単独で実行してください。(エクスプローラー等からアイコンをダブルクリック)