2006 年 6 月 23 日 19 時 11 分

MarshalAs による文字列変換


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


もう 1 つ構造体が残っている。
GetItemData メソッドに出てくる、SHCOLUMNDATA だ。

以前も MarshalAsAttribute という属性が出てきたが、
今日は、さらにこの属性が活躍する。

定義を見てみよう。
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/shcolumndata.asp

    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 で使う場合、このような面倒な手順が必要になる。

このような作業を軽減するために、
タイプライブラリインポータがあるのだが、
肝心のタイプライブラリがない場合は、かえって面倒になる。

ここまでの作業でうんざりしている人も多いはずだが、
ここで、ちょっとしたサイトを紹介しよう。

 http://pinvoke.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 記述の効率があがるので便利である。


さて、明日は実際にカラムを作ってみよう。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.