【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

EXEを作る (3)

id:n7shi:20110420の続きです。フィールドを1つずつ指定して書き込むのは面倒なので、リフレクションで自動化します。

Function writeFields%(image As Byte(), pos%, obj As Object)
    Dim t = obj.GetType
    For Each f In t.GetFields
        Dim v = f.GetValue(obj)
        If TypeOf v Is Byte Then
            image(pos) = CByte(v)
            pos += 1
        ElseIf TypeOf v Is UShort Then
            pos = write16(image, pos, CUShort(v))
        ElseIf TypeOf v Is Integer Then
            pos = write32(image, pos, CInt(v))
        ElseIf TypeOf v Is Byte() Then
            pos = writebin(image, pos, CType(v, Byte()))
        ElseIf TypeOf v Is UShort() Then
            pos = write16s(image, pos, CType(v, UShort()))
        ElseIf TypeOf v Is Integer() Then
            pos = write32s(image, pos, CType(v, Integer()))
        Else
            Throw New ArgumentException("不明な型: ", t.FullName)
        End If
    Next
    Return pos
End Function

obj.GetType.GetFieldsでobjのパブリックフィールドが取得できます。GetValueで値を取り出しながら、型ごとに処理を振り分けて書き込みます。これによりクラスはフィールドだけを定義すれば済みます。

Public Class IMAGE_DOS_HEADER
    Public e_magic, e_cblp, e_cp, e_crlc, e_cparhdr, e_minalloc, e_maxalloc,
           e_ss, e_sp, e_csum, e_ip, e_cs, e_lfarlc, e_ovno,
           e_res(3), e_oemid, e_oeminfo, e_res2(9) As UShort
    Public e_lfanew%
End Class

Public Class IMAGE_FILE_HEADER
    Public Machine, NumberOfSections As UShort
    Public TimeDateStamp, PointerToSymbolTable, NumberOfSymbols As Integer
    Public SizeOfOptionalHeader, Characteristics As UShort
End Class

書き込む側ではwriteFieldsを呼ぶだけです。

Dim dosh = New IMAGE_DOS_HEADER With {
    .e_magic = conv16("MZ"),
    .e_cblp = &H90,
    .e_cp = 3,
    .e_cparhdr = 4,
    .e_maxalloc = &HFFFF,
    .e_sp = &HB8,
    .e_lfarlc = &H40,
    .e_lfanew = &H80}
writeFields(image, 0, dosh)

Dim fh = New IMAGE_FILE_HEADER With {
    .Machine = &H14C,
    .NumberOfSections = 1,
    .SizeOfOptionalHeader = &HE0,
    .Characteristics = &H102}
writeFields(image, &H84, fh)

だいぶC++のコードに近付きました。Win32では構造体の中に構造体が入っています。次回はこれに対応します。