UWSCRの新機能：構造体

UWSCRでは`struct-endstruct`構文によりCライクな構造体を定義できるようになりました。

# 構造体定義

```php
// メンバ名: 型名を記述
// 型名に関してはdef_dllとほぼ同じ
// varまたはrefを付けるとその型のポインタになる
struct MyStruct
    foo: int
    bar: wchar[100]
    baz: var int
endstruct
```

# 構造体の初期化

`構造体名()`とすることで構造体を初期化します。

```php
struct MyStruct
    foo: int
endstruct

s = MyStruct()
```

# データの読み書き

`.メンバ名`でアクセスできます。

```php
s = MyStruct()
print s.foo // 0
s.foo = 100
print s.foo // 100
```

# 構造体のメソッド

構造体は共通のメソッドを持ちます。

## size()

構造体のサイズを返します。

```php
struct MyStruct
    foo: dword // 4
    bar: word  // 2
    baz: word  // 2
endstruct

s = MyStruct()
print s.size() // 8
// サイズはlength関数でも得られる
print length(s)        // 8
print length(MyStruct) // 8
```

:::note info
この値は単純にメンバサイズの合計というわけではありません。構造体サイズが必要な場合はこのメソッドを使用してください。
:::

:::note alert
`0.14.0`ではネストした構造体のサイズが正しく得られない場合があります。
:::

## address()

構造体のアドレス(ポインタ)を得ます。

```php
s = MyStruct()
ptr = s.address()
```


# ポインタから構造体の実体を得る

`構造体名(ポインタ)`とすることで構造体の実体を得られます。

例えば`WTSEnumerateSessionsExW`という関数は第四引数が`WTS_SESSION_INFO_1`構造体のポインタを受ける変数のポインタであったため`{}`が使えず、さらに`WTS_SESSION_INFO_1`は複数個存在する可能性があるためUWSCではこの関数から情報を得ることが困難でした。
UWSCRであれば予め`WTS_SESSION_INFO_1`を定義しておくことでポインタからその実体を得て値を参照することができます。

```php
def_dll WTSEnumerateSessionsExW(handle, var dword, dword, var pointer, var dword):bool:Wtsapi32
def_dll WTSFreeMemoryExW(dword, pointer, dword):bool:Wtsapi32

struct WTS_SESSION_INFO_1
    ExecEnvId   : dword
    State       : dword
    SessionId   : dword
    pSessionName: wstring
    pHostName   : wstring
    pUserName   : wstring
    pDomainName : wstring
    pFarmName   : wstring
endstruct
size = length(WTS_SESSION_INFO_1)

dim ptr, cnt, plevel = 1
// WTS_SESSION_INFO_1のポインタをptrで、個数をcntで受ける
if WTSEnumerateSessionsExW(0, plevel, 0, ptr, cnt) then
    for i = 0 to cnt - 1
        // WTS_SESSION_INFO_1構造体は複数個が連結している
        // 元のポインタ + インデックス * 構造体サイズ でそれぞれのポインタが得られる
        info = WTS_SESSION_INFO_1( ptr + i * size )
        print info.ExecEnvId
        print info.State
        print info.SessionId
        print info.pSessionName
        print info.pHostName
        print info.pUserName
        print info.pDomainName
        print info.pFarmName
        print
    next
    // WTS_SESSION_INFO_1構造体をすべて開放する
    WTSFreeMemoryExW(2, ptr, cnt)
endif
```


# LPARAMとしての構造体利用

Win32 APIではコールバック関数とのデータの橋渡しとしてLPARAMが用いられますが、そのデータとして構造体を利用できます。

```php:uwscr
// 利用するDLL関数を定義する
def_dll EnumDisplayMonitors(handle, pointer, callback(handle, handle, pointer, pointer):bool, pointer):bool:user32.dll
def_dll GetMonitorInfoW(handle, struct):bool:user32.dll
def_dll EnumDisplayDevicesW(wstring, dword, struct, dword):bool:user32.dll

// EnumDisplayMonitorsに呼び出されるコールバック関数
// TRUEを返すと続行し、FALSEを返すとその時点でEnumDisplayMonitorsを終了します
// lparamはEnumDisplayMonitorsに渡したUserData構造体のポインタです
function MonitorEnumProc(hmonitor, hdc, prect, lparam)
    // ポインタからUserData構造体を得る
    data = UserData(lparam)
    // モニタハンドルを配列に入れる
    data.handles[data.count] = hmonitor
    // カウントを進める
    data.count += 1
    if data.count == length(data.handles) then
        // 取得上限を超えたら終了する
        result = FALSE
    else
        // trueを返して次に進む
        result = TRUE
    endif
fend

// lparamとして渡される構造体
struct UserData
    // モニタハンドルを入れる配列
    // 今回は最大10個まで取得することとします
    handles: handle[10]
    // ハンドル数
    count  : uint
endstruct

// UserData構造体を初期化
data = UserData()
// 構造体アドレスをLPARAMとして渡す
lparam = data.address()

// callbackにはコールバック関数として呼ばれるユーザー定義関数を渡す
EnumDisplayMonitors(null, null, MonitorEnumProc, lparam)

for i = 0 to data.count - 1
    // 構造体からハンドル値を得ます
    handle = data.handles[i]

    // デバイス名を得る
    info = MONITORINFOEX()
    info.monitorInfo.cbSize = info.size() // ※1
    // info.monitorInfo.cbSize = 104
    GetMonitorInfoW(handle, info)
    print info.szDevice

    // モニタデバイスの詳細を得る
    device = DISPLAY_DEVICEW()
    device.cb = device.size()
    EnumDisplayDevicesW(info.szDevice, 0, device, 0)
    print device.DeviceName
    print device.DeviceString
next

// デバイス情報取得のための構造体定義

// MONITORINFO構造体で使用されるため定義する必要がある
struct RECT
    left  : int
    top   : int
    right : int
    bottom: int
endstruct

// MONITORINFOEX構造体で使われる
struct MONITORINFO
    cbSize   : uint
    rcMonitor: RECT // ※2
    rcWork   : RECT // ※2
    dwFlags  : uint
endstruct

// szDeviceにデバイス名が入る
struct MONITORINFOEX
    monitorInfo: MONITORINFO
    szDevice   : wchar[32]
endstruct

// MONITORINFOEXのszDeviceからEnumDisplayDevicesWで詳細を得るのに使う
struct DISPLAY_DEVICEW
    cb          : uint
    DeviceName  : wchar[32]
    DeviceString: wchar[128]
    StateFlags  : uint
    DeviceID    : wchar[128]
    DeviceKey   : wchar[128]
endstruct
```

:::note alert
#### `0.14.0`には以下の不具合があります

- コールバック呼び出し時に落ちる場合がある (`STATUS_HEAP_CORRUPTION`)
- ネストしている構造体のサイズ計算に誤りがある (サンプル内の※1)
- 多重ネストした場合に構造体が初期化されない (サンプル内の※2)
    サンプルでは触れていませんが、`print info.monitorInfo.rcMonitor`とするとエラーになります

いずれも`0.15.0`で修正されます。ただし`STATUS_HEAP_CORRUPTION`の件に関しては原因が特定できていないので暫定対策のみです。
:::