このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
もう 1 つ構造体が残っている。
GetItemData メソッドに出てくる、SHCOLUMNDATA だ。
以前も MarshalAsAttribute という属性が出てきたが、
今日は、さらにこの属性が活躍する。
typedef struct {
ULONG dwFlags;
DWORD dwFileAttributes;
ULONG dwReserved;
WCHAR *pwszExt;
WCHAR wszFile[MAX_PATH];
} SHCOLUMNDATA, *LPSHCOLUMNDATA;
今までと同じく shlobj.h を参照し、
パッキングサイズを調べてみると 8 だった。
また、定数も調べておこう。
#define SHCDF_UPDATEITEM 0x00000001
FILE_ATTRIBUTE_XXX 定数は、winnt.h に記載がある。
#define FILE_ATTRIBUTE_READONLY 0x00000001
#define FILE_ATTRIBUTE_HIDDEN 0x00000002
#define FILE_ATTRIBUTE_SYSTEM 0x00000004
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
#define FILE_ATTRIBUTE_DEVICE 0x00000040
#define FILE_ATTRIBUTE_NORMAL 0x00000080
#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200
#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
#define FILE_ATTRIBUTE_COMPRESSED 0x00000800
#define FILE_ATTRIBUTE_OFFLINE 0x00001000
#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000
役者はそろった。C# に変換しよう。
[StructLayout(LayoutKind.Sequential,
Pack=8, CharSet=CharSet.Unicode)]
public struct SHCOLUMNDATA {
public SHCDF dwFlags;
public FILE_ATTRIBUTE dwFileAttributes;
public uint dwReserved;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszExt;
[MarshalAs(UnmanagedType.ByValTStr,
SizeConst=Constants.MAX_PATH)]
public string wszFile;
}
さて、最初の 3 つのフィールドは今までどおりだが、
pwszExt と wszFile は少し考えないといけない。
pwszExt は、文字のポインタだ。
C 言語における一般的な文字列であり、
実際の文字列は構造体に含まれない。
いわば、参照型の文字列だ。
それに対して、wszFile は、
今までに何度か出てきた、
構造体の一部として含まれる文字列配列だ。
いわば、値型の文字配列だ。
上記のように、C 言語では、文字列の格納方法や
バッファの取り方などのメモリレイアウトに
気をつける必要があるのだが、
.NET では、MarshalAs を使って文字列の扱いを明示すれば、
このようなレイアウトの差を吸収し、
単純な String として扱うことができる。
pwszExt に指定した、UnmanagedType.LPWStr は、
L P WStr で、Unicode 文字列へのポインタを意味する。
wszFile は、前と同じ UnmanagedType.ByValTStr だ。
構造体に含まれる値型のように文字列を取り扱ってくれる。
ByValTStr の場合は、必ずバッファのサイズ(要素数)を
SizeConst フィールドで指定する必要がある。
このように MarshalAs を指定しておけば、
後は .NET のコンパイラとライブラリが、
自動的に文字列を裏で変換してくれるようになる。
プログラマからは、pwszExt も wszFile も、
単なる String として使えるのだ。
これのおかげで、文字列処理が非常に楽になるのである。
さて、残りの列挙体も定義しておこう。
// 補足情報
[Flags]
public enum SHCDF : uint {
UPDATEITEM = 0x00000001,
}
現在は、UPDATEITEM しか定義されていないが、
名前からしてフラグなので、Flags をつけておく。
これは、IColumnProvider.cs に入れておこう。
// ファイル属性フラグ
public enum FILE_ATTRIBUTE : uint {
READONLY = 0x00000001,
HIDDEN = 0x00000002,
SYSTEM = 0x00000004,
DIRECTORY = 0x00000010,
ARCHIVE = 0x00000020,
DEVICE = 0x00000040,
NORMAL = 0x00000080,
TEMPORARY = 0x00000100,
SPARSE_FILE = 0x00000200,
REPARSE_POINT = 0x00000400,
COMPRESSED = 0x00000800,
OFFLINE = 0x00001000,
NOT_CONTENT_INDEXED = 0x00002000,
ENCRYPTED = 0x00004000,
}
これらは、ファイルの属性であり、
ファイル系の API や COM インタフェースで使われる、
一般的な定数群なので、Constants.cs に入れておこう。
よし、これでやっと、IColumnProvider の定義が終わった。
シェルインタフェースには、様々な型が関係しているので
結構手間がかかるのだ。
このように COM のインタフェースや API を
.NET で使う場合、このような面倒な手順が必要になる。
このような作業を軽減するために、
タイプライブラリインポータがあるのだが、
肝心のタイプライブラリがない場合は、かえって面倒になる。
ここまでの作業でうんざりしている人も多いはずだが、
ここで、ちょっとしたサイトを紹介しよう。
P/Invoke とは、プラットフォーム呼び出しのことで、
.NET から API を呼ぶための機構のことだ。
ここには、.NET で API や COM を利用するための記述が
すごい数掲載されている。
いちいちマニュアルを見ながら手で書かなくても、
有名な API ならここを調べれば一発で記述がコピーできる。
Visual Basic .NET 用と、C# 用の二つが載っているので、
Visual Basic ユーザでも利用できる。
例えば、ShellExecute ならこのとおり。
http://pinvoke.net/default.aspx/shell32/ShellExecute.html
このサイトは Wiki でできており、
有志の手によってどんどん定義が追加されている。
間違った情報もすぐに誰かが修正するので信頼性も高い。
(俺は幾つか訂正をしたことがある)
サイト自体は英語だが、ソースコードなら問題にならない。
うまく利用すれば、API 記述の効率があがるので便利である。
さて、明日は実際にカラムを作ってみよう。