IronHSP (hsp3net) 内部仕様書

HSP3 .NET Framework 統合ランタイム — コンパイラ・ランタイム拡張仕様 — 最終更新: 2026-04-18

対象: IronHSP (OpenHSP 3.8beta1 ベース) / .NET Framework 4.8 / ツールセット v143 (VS2022)

本文書について: バニラ OpenHSP の SDK 仕様 (hspdll.txt, hsp3code.txt) に記載されていない IronHSP 固有の拡張仕様 を網羅します。バニラ HSP の仕様は上記文書を参照してください。
目次

1. アーキテクチャ概要

IronHSP (hsp3net) は OpenHSP 3.8beta1 をベースに、以下のレイヤーを追加した .NET Framework 統合ランタイムです。

┌─────────────────────────────────────────────────┐
│  HSP スクリプト (.hsp)                           │
├─────────────────────────────────────────────────┤
│  hspcmp (拡張コンパイラ)                         │
│   - #defstruct / #defunion / #field             │
│   - #defcbcom / #cbmethod                       │
│   - #cfuncst / #cfuncd / #cfuncf                │
│   - intptr 型 / Allman ブレース                  │
├─────────────────────────────────────────────────┤
│  .ax (拡張中間コード)                            │
├─────────────────────────────────────────────────┤
│  hsp3net ランタイム                              │
│  ┌───────────────────────────────────────────┐  │
│  │ コア (hsp3code / hsp3int)                 │  │
│  │  - NSTRUCT / MAP / WSTR / INT64 型        │  │
│  │  - dimmap / delmap / structdim 命令        │  │
│  │  - _struct_poke / _struct_peek             │  │
│  ├───────────────────────────────────────────┤  │
│  │ Win32 GUI / DLL 拡張 (hsp3ext_win)        │  │
│  │  - callfuncst / callfuncd / callfuncf     │  │
│  │  - COM コールバック (動的 thunk 生成)      │  │
│  │  - コールバック thunk (x86/x64)            │  │
│  ├───────────────────────────────────────────┤  │
│  │ .NET 連携レイヤー (C++/CLI)               │  │
│  │  - Hsp3Net (リフレクション)               │  │
│  │  - GlobalAccess (GCHandle 管理)           │  │
│  │  - HspForms (WinForms 統合)               │  │
│  │  - NETOBJ 変数型                          │  │
│  │  - HspCallbackProxy (デリゲートブリッジ)   │  │
│  └───────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘

バニラ HSP3 との主な違い

項目バニラ HSP3IronHSP (hsp3net)
変数型数最大 11 (FLAG 0〜10)最大 13 (FLAG 0〜12) +2
構造体TYPE_STRUCT (module 擬似)NSTRUCT (バイナリレイアウト保持) + #defstruct
連想配列なしMAP 型 (dimmap / map("key"))
DLL 戻り値int のみ (#cfunc)int / double / float / struct (#cfunc / #cfuncd / #cfuncf / #cfuncst)
COM コールバックなし#defcbcom で任意 COM インターフェース実装
.NET 連携なしC++/CLI 経由でリフレクション・WinForms
ポインタ型int (32bit固定)intptr (プラットフォーム幅)
文字列エンコードShift-JIS (CP_ACP)UTF-8 基盤 + WSTR (UTF-16)

2. コンパイラ (hspcmp) 拡張

2.1 構造体定義 (#defstruct / #defunion / #field)

Win32 API や DLL とのデータ交換で必要な C 構造体互換のバイナリレイアウト を HSP スクリプトから定義できます。

構文

#defstruct [global] 構造体名 [, pack=N]
    #field 型名 メンバ名 [配列サイズ]
    ...
    #defunion                    ; ネストされた共用体開始
        #field ...
    #endunion
#endstruct

#defunion 共用体名              ; トップレベル共用体
    #field ...
#endunion

対応するフィールド型

型名サイズC 相当備考
byte1unsigned char
short2short / WORD
int4int / DWORD / LONG
int648__int64 / LONGLONG
float4float
double8double
intptr4 or 8INT_PTR / size_t32bit=4, 64bit=8
bool4BOOL (Win32)4バイト整数
bool11bool (C++)1バイト
bool22VARIANT_BOOLCOM 用
char NNchar[N]固定長 ANSI 文字列
wchar NN*2wchar_t[N]固定長 UTF-16 文字列
構造体名可変struct 入れ子ネスト構造体

アライメント (pack)

デフォルトは pack=8 で MSVC の C 構造体 ABI に準拠します。各メンバのオフセットは min(フィールド自然アライメント, pack値) の倍数に揃えられます。構造体全体のサイズは最大メンバアライメントの倍数に切り上げられます。

注意: GCC のアライメント規則ではなく MSVC (Windows) の ABI に従います。Linux 向けの構造体定義をそのまま移植するとレイアウトが異なる場合があります。

使用例

#defstruct POINT
    #field int x
    #field int y
#endstruct

#defstruct RECT
    #field int left
    #field int top
    #field int right
    #field int bottom
#endstruct

; global で宣言すると #module 内からも参照可能
#defstruct global GUID, pack=1
    #field int   Data1
    #field short Data2
    #field short Data3
    #field byte  Data4 8
#endstruct

; 使い方
stdim pt, POINT        ; NSTRUCT 変数を確保
pt->x = 100
pt->y = 200
mes "x=" + pt->x

global キーワード

#defstruct global 名前 で宣言すると、構造体名がグローバルスコープに登録され、任意の #module 内から参照できます。省略するとファイルスコープです。

2.2 アロー演算子 (->) によるメンバアクセス

#defstruct で定義した構造体の NSTRUCT 変数に対して、C 言語風の -> 演算子でメンバを読み書きできます。

stdim rc, RECT
rc->left = 10
rc->top = 20
rc->right = 640
rc->bottom = 480
mes "width = " + (rc->right - rc->left)

内部展開

プリプロセッサフェーズで以下のように展開されます:

HSP コード展開結果
v->member (読み取り)_struct_peek(v, offset, type, size)
v->member = val (書き込み)_struct_poke v, offset, type, struct_size, member_size, val

ネストされた構造体チェーン a->b->c や配列インデックス arr(i)->member もサポートされています。

仕組み: -> はコンパイル時のテキスト展開であり、ランタイムコストはバイトオフセットによる直接メモリアクセスのみです。C のポインタ演算と同等の速度で動作します。

2.3 COM コールバック (#defcbcom / #cbmethod)

任意の COM インターフェースを HSP スクリプトから実装できます。IDropTarget, IBindStatusCallback 等の COM コールバックをスクリプト側で処理するために使用します。

構文

#defcbcom クラス名 インターフェース名
    #cbmethod vtableインデックス 戻り値型 [引数型, ...] *ラベル
    ...
#endcbcom

パラメータ

要素説明
クラス名HSP 側で使用するクラス識別名
インターフェース名#usecom で事前登録した COM インターフェース名
vtableインデックスIUnknown の 3 メソッド (0,1,2) の次から。3 以上 64 未満
戻り値型int, int64, intptr, double, float, str, wstr, comobj
引数型上記に加え sval<N> (構造体値渡し)
IUnknown の QueryInterface / AddRef / Release (vtable 0,1,2) はランタイムが自動実装します。ユーザーが定義する必要はありません。

使用例 (IDropTarget)

#usecom IDropTarget "{00000122-0000-0000-C000-000000000046}"

#defcbcom MyDropTarget IDropTarget
    ; vtable 3: DragEnter(pDataObj, grfKeyState, pt_lo, pt_hi, pdwEffect)
    #cbmethod 3 int comobj, int, int, int, intptr, *on_drag_enter
    ; vtable 4: DragOver
    #cbmethod 4 int int, int, int, intptr, *on_drag_over
    ; vtable 5: DragLeave
    #cbmethod 5 int *on_drag_leave
    ; vtable 6: Drop
    #cbmethod 6 int comobj, int, int, int, intptr, *on_drop
#endcbcom

; インスタンス化
newcomcb myobj, "MyDropTarget"
; myobj は COM オブジェクトとして Win32 API に渡せる

*on_drag_enter
    ; comprm(N) で引数取得
    key_state = comprm(1)
    comret 0  ; S_OK
    return

*on_drop
    comret 0  ; S_OK
    return

コールバック内で使える命令・関数

命令/関数説明
comprm(N)N 番目の引数を取得 (0 始まり)
comret 値戻り値を設定
comcbidx()現在呼び出されている vtable インデックス
comcbtag()newcomcb 時に設定した整数タグ
comcbtags()newcomcb 時に設定した文字列タグ
comcbis(var)現在の this ポインタが var と同じか判定
comcbthis()現在の this ポインタを intptr で取得

2.4 DLL 関数戻り値拡張 (#cfuncst / #cfuncd / #cfuncf)

バニラ HSP の #cfunc は int 戻り値しかサポートしていません。IronHSP では構造体・double・float 戻り値に対応しています。

ディレクティブ戻り値型内部オペコード備考
#cfuncintcallfunc (0x100)バニラ HSP と同じ
#cfuncddoublecallfuncd (0x107)IronHSP
#cfuncffloatcallfuncf (0x108)IronHSP float→double 昇格
#cfuncststruct (値渡し)callfuncst (0x10A)IronHSP 隠し第1引数にポインタ

#cfuncst の構文

; 新形式 (構造体名指定)
#cfuncst [global] 構造体名 関数名 "エントリポイント" [パラメータ型...]

; レガシー形式 (サイズ直指定)
#cfuncst [global] 関数名 "エントリポイント" サイズ [, パラメータ型...]

動作原理

C/C++ で構造体を値で返す関数は、実際には呼び出し元が確保したバッファへのポインタを隠し第1引数 (x64: RCX, x86: スタック先頭) として渡します。#cfuncst はこの 隠し引数挿入 を自動的に行います。

; C 側: POINT GetCursorPos2(void)
; 実際の ABI: void GetCursorPos2(POINT *__retval)

#defstruct POINT
    #field int x
    #field int y
#endstruct

#uselib "mylib.dll"
#cfuncst POINT GetCursorPos2 "GetCursorPos2"

; 呼び出し (戻り値は NSTRUCT 変数に自動格納)
pt = GetCursorPos2()
mes pt->x + ", " + pt->y
#cfuncst の戻り値は NSTRUCT 型 です。ランタイムが hsp_nstruct_pending_size にサイズヒントを設定し、代入先の変数が暗黙的に NSTRUCT として確保されます。

2.5 新規キーワード一覧

プログラム制御命令 (TYPE_PROGCMD = 15)

オペコードキーワード説明
0x21dim6464ビット整数 (INT64) 配列確保
0x22stdimNSTRUCT 変数確保
0x23_struct_poke構造体メンバ書き込み (内部用)
0x24wsdimUTF-16 ワイド文字列 (WSTR) 配列確保
0x25dimmap連想配列 (MAP) 確保

拡張命令 (TYPE_EXTCMD = 8)

オペコードキーワード説明
0x032setenv環境変数を設定
0x033delenv環境変数を削除
0x034delmapMAP エントリを削除
0x035mapclearMAP を全消去

拡張関数 (TYPE_INTFUNC = 13)

オペコードキーワード説明
0x17_struct_peek構造体メンバ読み取り (内部用)
0x200getenv環境変数を取得 (UTF-8)
0x201hasenv環境変数の存在確認
0x202getcmdargcコマンドライン引数の数
0x203getcmdargN 番目のコマンドライン引数
0x204getcmdargs名前付きコマンドライン引数
0x207hasmapMAP キーの存在確認
0x208mapcountMAP エントリ数
0x209mapkeyMAP キーをインデックスで取得

DLL コントロール (TYPE_DLLCTRL = 17)

オペコードキーワード説明
0x40_cb_class_beginCOM コールバッククラス開始 (内部)
0x41_cb_class_methodCOM コールバックメソッド定義 (内部)
0x42_cb_class_endCOM コールバッククラス終了 (内部)
0x43newcomcbCOM コールバックインスタンス作成
0x44comretコールバック戻り値設定
0x150comprmコールバック引数取得
0x151comcbidx現在の vtable インデックス
0x152comcbtag整数タグ取得
0x153comcbtags文字列タグ取得
0x154comcbisthis ポインタ比較
0x155comcbthisthis ポインタ (intptr)

2.6 intptr 型

intptr はプラットフォームのポインタ幅に合わせて 32bit 環境では 4 バイト、64bit 環境では 8 バイトとなる整数型です。

定義ファイル
MPTYPE_INTPTR-24hsp3struct.h:248
HSPLPTRlong (64bit) / int (32bit)hsp3struct.h:33-37
構造体メンバ型SMT_PTRtoken.h StructMemberType enum

#func, #cfunc, #comfunc 等のパラメータ型として intptr を指定できます。DLL の void*, HANDLE, HWND, LPARAM 等のポインタ引数に使用してください。

#uselib "user32.dll"
#cfunc FindWindowW "FindWindowW" wstr, wstr
#func SetWindowTextW "SetWindowTextW" intptr, wstr

hwnd = FindWindowW("Notepad", 0)
SetWindowTextW hwnd, "Hello"

2.7 Allman ブレーススタイル

バニラ HSP では if の後にブレースを同一行に書く必要がありましたが、IronHSP では 次の行にブレースを置く Allman スタイル もサポートしています。

; K&R スタイル (従来通り動作)
if a > 0 {
    mes "positive"
}

; Allman スタイル (IronHSP で追加)
if a > 0
{
    mes "positive"
}

実装: codegen.cpp の if 解析で、行末 { がなかった場合に次行の先頭を先読みして { を検出します。

3. ランタイム拡張

3.1 新規変数型

FLAG名前定義概要
8INT64hspvar_int6464ビット整数
9NETOBJhspvar_netobj.NET オブジェクト参照 (GCHandle)
10WSTRhspvar_wstrUTF-16 ワイド文字列
11NSTRUCThspvar_nstructネイティブ構造体 (バイナリレイアウト)
12MAPhspvar_map連想配列 (文字列→文字列)

バニラ HSP の変数型: 0=なし, 1=label, 2=str, 3=double, 4=int, 5=struct, 6=comobj (最大 FLAG=10)

3.2 NSTRUCT (ネイティブ構造体)

目的

Win32 API や C DLL とのデータ交換で必要な バイナリレイアウトを正確に保持 する変数型です。バニラ HSP の TYPE_STRUCT (モジュール変数) とは異なり、名前付きフィールドではなくバイトオフセットで直接メモリにアクセスします。

メモリレイアウト

PVal
  ├─ flag = HSPVAR_FLAG_NSTRUCT (11)
  ├─ len[0] = 要素サイズ (バイト数)   例: POINT なら 8
  ├─ len[1] = 配列サイズ             例: 10 個なら 10
  ├─ pt → calloc(要素サイズ * 配列サイズ) で確保した生バッファ
  └─ mode = HSPVAR_MODE_MALLOC

確保方法

; #defstruct で定義した構造体名を使用
stdim var, POINT          ; POINT サイズの NSTRUCT を 1 個確保
stdim arr, RECT, 10       ; RECT サイズの配列を 10 個確保

; cfuncst の戻り値は自動的に NSTRUCT になる
pt = GetSomePoint()       ; 暗黙確保 (hsp_nstruct_pending_size で通知)

サポート操作

_struct_poke のメンバ型コード

コードサイズ
0SMT_BYTE1
1SMT_SHORT2
2SMT_INT4
3SMT_INT648
4SMT_FLOAT4
5SMT_DOUBLE8
6SMT_PTR4 or 8 (intptr)
7SMT_BOOL4
8SMT_BOOL11
9SMT_BOOL22
10SMT_CHAR_ARRAY可変
11SMT_WCHAR_ARRAY可変 (MultiByteToWideChar で変換)

3.3 MAP (連想配列)

内部実装

// hspvar_map.cpp
struct MapData {
    std::unordered_map<std::string, std::string> entries;
    std::string current_key;    // ArrayObject の書き込みターゲットキー
    std::string read_buf;       // ArrayObjectRead の戻りバッファ
};

特徴

API

; 確保
dimmap m

; 書き込み
m("name") = "Alice"
m("age") = "30"

; 読み取り
mes m("name")              ; "Alice"

; 存在確認
if hasmap(m, "name") { mes "found" }

; エントリ数
mes "count = " + mapcount(m)

; キー一覧
repeat mapcount(m)
    mes mapkey(m, cnt)
loop

; 削除
delmap m, "name"

; 全消去
mapclear m
注意: MAP の値は全て文字列で格納されます。数値を格納しても文字列に変換されます。数値として取り出す場合は int(m("key")) 等の明示的な変換が必要です。

3.4 WSTR (UTF-16 ワイド文字列)

Win32 W 版 API に直接渡せる UTF-16 (Little Endian) 文字列型です。

wsdim ws, 260        ; 260 wchar_t 分のバッファ確保

#func / #cfunc のパラメータ型として wstr を指定すると、HSP 文字列 (UTF-8) から自動的に UTF-16 に変換されて渡されます。var で受け取った場合は変換されず、UTF-16 バイト列がそのまま格納されます。

W 版 API の入出力パターン:

3.5 INT64 (64ビット整数)

dim64 v, 10          ; INT64 配列を 10 要素確保

8バイト (64ビット) 整数配列です。Win32 API の LARGE_INTEGER, FILETIME 等のデータ格納に使用します。

3.6 新規命令・関数

Section 2.5 の一覧も参照してください。ここでは使い方を解説します。

環境変数操作

; 環境変数を設定
setenv "MY_KEY", "MY_VALUE"

; 環境変数を取得 (UTF-8)
val = getenv("PATH")
val2 = getenv("MISSING", "default")   ; デフォルト値指定

; 存在確認
if hasenv("MY_KEY") { mes "exists" }

; 削除
delenv "MY_KEY"

コマンドライン引数

; 引数の数
n = getcmdargc()

; N番目の引数 (0始まり, 0=実行ファイル名)
repeat n
    mes getcmdarg(cnt)
loop

; 名前付き引数: program.exe --port 8080 --name test
port = getcmdargs("--port", "3000")     ; デフォルト "3000"
name = getcmdargs("--name", "unknown")

内部では CommandLineToArgvW() + UTF-8 変換を使用。最初の呼び出し時に 1 回だけパースしてキャッシュします。

4. .NET Framework 連携

4.1 アーキテクチャ

.NET 連携は C++/CLI で実装されています。HSP ランタイム (ネイティブ C++) と .NET マネージドコード (C#/VB.NET) の間を C++/CLI がブリッジします。

HSP スクリプト
    │ mcall / newnet / callnet
    ▼
hsp3ext_win.cpp (ネイティブ C++)
    │ Hsp3Net::InvokeMethod() 等
    ▼
Hsp3Net.h / Hsp3Net.cpp (C++/CLI マネージドラッパー)
    │ System::Reflection
    ▼
.NET Framework (System.*, System.Windows.Forms, etc.)

主要クラス

クラスファイル役割
Hsp3NetHsp3Net.h/cppリフレクション経由でメソッド呼び出し・プロパティアクセス・配列操作
GlobalAccessHsp3Net.h:382-561GCHandle 管理。マネージドオブジェクトへのネイティブポインタの発行・追跡・解放
HspCallbackProxyHsp3Net.h:572-660HSP ラベルを .NET デリゲートにブリッジ
HspFormsHspForms.h/cppWinForms コントロール管理
HspFormsInteropHspFormsInterop.h/cppC リンケージでネイティブ→マネージド呼び出し

4.2 NETOBJ 変数型

NETOBJ (FLAG=9) は .NET オブジェクトへのネイティブポインタ (GCHandle) を格納する変数型です。

ライフサイクル

  1. newnet var, "System.Collections.ArrayList" で .NET インスタンス作成
  2. GlobalAccess::CreateNativePtr()GCHandle.Alloc() でマネージドオブジェクトをピン留め
  3. ネイティブポインタ (void*) を PVal に格納
  4. delnet varGlobalAccess::Free()GCHandle.Free()

スコープ管理

GlobalAccess は 2 つのセットでポインタを追跡します:

PushNativePtrCurrentStack() / PopNativePtrCurrentStack() で関数呼び出しスコープに連動した自動解放を行います。

型変換

方向変換内容
STR → NETOBJHsp3Net::CreateString()
INT → NETOBJHsp3Net::CreateInt32()
DOUBLE → NETOBJHsp3Net::CreateDouble()
NETOBJ → STRobj.ToString()
NETOBJ → INTConvert.ToInt32()
NETOBJ → DOUBLEConvert.ToDouble()

4.3 .NET 操作命令

オペコード命令構文説明
0x0dloadnetloadnet "名前", opt [, refs...]アセンブリ読み込み (下記参照)
0x0enewnetnewnet var, "クラス名" [, args...].NET インスタンス作成
0x0fdelnetdelnet var.NET オブジェクト解放
0x10netresnetres(var)メソッド呼び出し結果取得

loadnet のオプション

opt意味
0GAC 短縮名loadnet "System.Windows.Forms", 0
1GAC 完全名loadnet "System.Windows.Forms, Version=4.0.0.0, ...", 1
2ファイルパスloadnet "MyLib.dll", 2
3C# ソースコードloadnet cs_source, 3 [, "ref1.dll", ...]
4VB.NET ソースコードloadnet vb_source, 4 [, "ref1.dll", ...]

4.4 C# ソースコンパイル

loadnet の opt=3 で C# ソースコードを動的コンパイルして即座にロードできます。

cs = {"
using System;
public class MyMath {
    public static int Add(int a, int b) { return a + b; }
    public static string Greet(string name) {
        return "Hello, " + name + "!";
    }
}
"}
loadnet cs, 3

; 静的メソッド呼び出し
newnet dummy, "MyMath"
mcall dummy, "Add", result, 10, 20
mes result    ; 30

mcall dummy, "Greet", greeting, "HSP"
mes greeting  ; "Hello, HSP!"
参照アセンブリの追加: 第3引数以降にDLLパスを指定できます。
loadnet cs, 3, "System.Xml.dll", "System.Xml.Linq.dll"

4.5 WinForms 統合

hsp3net の GUI ランタイムは内部で System.Windows.Forms.Form をホストし、HSP のウィンドウと WinForms コントロールをハイブリッドで管理します。

HspFormsInterop が提供するネイティブ関数

関数説明
HspInterop_CreateButtonButton コントロール作成
HspInterop_CreateCheckBoxCheckBox 作成
HspInterop_CreateTextBoxTextBox 作成
HspInterop_CreateComboBoxComboBox 作成
HspInterop_CreateListBoxListBox 作成
HspInterop_IsManagedControlマネージドコントロールか判定
HspInterop_DestroyControlコントロール破棄
HspInterop_SetControlTextテキスト設定

上位レベルでは iron_forms.hsp が mcall 経由で DataGridView, PropertyGrid, TabControl, Anchor/Dock 等の高機能コントロールを提供します。

4.6 コールバック・デリゲート

HspCallbackProxy クラスが HSP ラベルを .NET デリゲートにブリッジします。

対応デリゲートパターン

パターンメソッド用途
EventHandlerHandleEvent(sender, e)ボタンクリック等のイベント
ActionHandleAction()引数なしコールバック
Func<T, bool>HandlePredicate(item)LINQ Where 等
Func<T, TResult>HandleSelector(item)LINQ Select 等

内部では HSP の code_callback() を呼び出し、HSP スクリプト上の *label にジャンプ → stat を戻り値として回収します。

5. COM コールバック実装

動的 Thunk 生成

COM インターフェースの vtable メソッドが呼ばれた際に HSP ラベルにジャンプするために、動的にマシンコードを生成 (JIT thunk) しています。

x64 版 Thunk

; 512 バイトのコードバッファ
; RCX, RDX, R8, R9 → args[0..3] に保存
; スタック引数 → args[4+] に保存
; R10/R11 をスクラッチレジスタとして使用
; hsp_cbcom_bridge() を呼び出し

x86 版 Thunk

; 256 バイトのコードバッファ
; スタック引数をコピー
; __cdecl 呼び出し規約
; hsp_cbcom_bridge() を呼び出し

VirtualAllocPAGE_EXECUTE_READWRITE メモリを確保し、マシンコードを書き込みます。vtable にこの thunk のアドレスを設定することで、COM メソッド呼び出しが HSP ラベルにディスパッチされます。

IUnknown 自動実装

6. 64ビット対応

コンパイル時定義

マクロ定義時用途
HSP6464ビットビルドランタイム全体で条件分岐
PTR64BIT64ビットポインタ構造体サイズ計算
PTR32BIT32ビットポインタ構造体サイズ計算

.ax ファイルフォーマットの差異

.ax (オブジェクトファイル) は 32bit アラインメントで保存されるため、64bit ランタイムではポインタフィールドの変換が必要です:

構造体ファイル上 (HED_*)メモリ上 (32bit)メモリ上 (64bit)
STRUCTDATint p_libptrvoid *proc (4byte)void *proc (8byte)
HPIDATint p_libptrvoid *libptr (4byte)void *libptr (8byte)
LIBDATint p_hlibvoid *hlib (4byte)void *hlib (8byte)

DLL 関数呼び出しの差異

項目x86x64
引数バッファ型int *prmINT_PTR *prm
double 戻り値call_extfunc_double_x86 (FPU ST(0))call_extfunc_double (XMM0)
float 戻り値call_extfunc_float_x86 (FPU ST(0))call_extfunc_float (XMM0)
COM ポインタcode_getdi() (32bit cast)code_getdi64() (64bit 保持)
Thunk サイズ256 バイト512 バイト (SEH 互換)

7. 環境変数・コマンドライン

バニラ HSP にはない環境変数・コマンドライン引数操作を追加しています。全て UTF-8 で処理されます。

環境変数

setenv "KEY", "VALUE"             ; 設定 (内部: MultiByteToWideChar + SetEnvironmentVariableW)
val = getenv("KEY")               ; 取得 (内部: GetEnvironmentVariableW + WideCharToMultiByte)
val = getenv("KEY", "default")    ; デフォルト値付き
if hasenv("KEY") { ... }          ; 存在確認
delenv "KEY"                      ; 削除

コマンドライン引数

n = getcmdargc()                  ; 引数数 (内部: CommandLineToArgvW)
arg0 = getcmdarg(0)               ; 実行ファイルパス
arg1 = getcmdarg(1)               ; 第1引数

; 名前付き引数: --key value 形式
port = getcmdargs("--port", "8080")

8. 注意事項・落とし穴

1. #define はケースインセンシティブ

HSP の #define は大文字・小文字を区別しません。#define LOG_DEBUG 0 を宣言すると、log_debug という関数名もマクロ展開されて壊れます。定数名と関数名の衝突に注意してください。

; NG: LOG_DEBUG が log_debug にもマッチする
#define LOG_DEBUG 0
log_debug "msg"    ; → 0 "msg" に展開されてエラー

; OK: プレフィックスで回避
#define LOGLV_DEBUG 0

2. #cfunc は関数形式でしか呼べない

#cfunc / #cfuncd / #cfuncf / #cfuncst で宣言した関数は 必ず関数形式 r = func(args) で呼び出してください。命令形式 func args はコンパイルエラーにならないことがありますが、正しく動作しません。

; NG
FindWindowW "Notepad", 0

; OK
hwnd = FindWindowW("Notepad", 0)

3. #module トップレベルコードは実行されない

#module#global 内のトップレベルに書いた dim / sdim 等の初期化コードは 実行されません。初期化は #deffunc 内で行うか、varname@modname 構文でモジュール変数に直接代入してください。

4. str パラメータに peek/strmid を直接使えない

#defcfuncstr パラメータは一時バッファを指しているため、peekstrmid を直接適用するとクラッシュすることがあります。ローカル変数にコピーしてから操作してください。

#defcfunc my_func str s
    local ss
    ss = s          ; ローカルにコピー
    c = peek(ss, 0) ; OK
    return c

5. W 版 API 出力バッファの扱い

W 版 API (GetTempPathW 等) の出力は UTF-16 です。var で受け取った後、WideCharToMultiByte(65001, ...) で UTF-8 に変換する必要があります。

また、W 版 API の出力を別の W 版 API の入力に渡す 場合、wstr ではなく var で渡してください。wstr は UTF-8→UTF-16 変換を行うため、既に UTF-16 のバッファを二重変換してしまいます。

6. return str は stat を変えない

HSP の return で文字列を返す場合、stat は変更されず refstr に格納されます。確実に stat を返したい場合は var 出力パラメータを使用してください。

; NG: stat は直前の API 戻り値のまま
#deffunc my_read str key
    return "value"   ; refstr に入る, stat は不定

; OK: var で明示出力
#deffunc my_read str key, var out_result
    out_result = "value"
    return 0         ; stat = 0

7. MAP はキー・値ともに文字列

MAP の内部は std::unordered_map<string, string> です。数値を格納すると文字列に変換されます。取り出し時に int()double() で明示変換が必要です。

8. NSTRUCT の暗黙確保

cfuncst 関数の戻り値を変数に代入すると、その変数は自動的に NSTRUCT 型になります。hsp_nstruct_pending_size グローバル変数を通じてサイズが通知され、代入時にクリアされます。一度確保された NSTRUCT 変数はそのまま再利用できます。

9. newcomcb のスコープ

newcomcb で作成した COM オブジェクトの寿命は AddRef/Release のリファレンスカウントで管理されます。HSP 変数がスコープを抜けても、COM 側で参照が残っていれば解放されません。リークに注意してください。

10. 64bit ビルドでの #comfunc 引数

64bit ビルドでは COM メソッドのポインタ引数に intptr を使用してください。int では上位 32bit が切り捨てられます。

9. ソースファイル一覧

コンパイラ (hspcmp/)

ファイル主要な責務
hspcmd.cppキーワード定義テーブル (全 TYPE のオペコード↔キーワード対応)
token.cppプリプロセッサ (PP_* 関数群): #defstruct パース、-> 展開、構造体検出、sval<N> 変換
token.hStructDef / StructMember / CbComClass 構造体、StructMemberType enum、変数→構造体ID マップ
codegen.cppコード生成 (GenerateCodePP_*): #defstruct/#defcbcom/#cbmethod のバイトコード出力、アライメント計算
errormsg.hエラーコード定義 (CGERROR_PP_* 系追加)

ランタイム (hsp3net/)

ファイル主要な責務
hspvar_core.h変数型 FLAG 定義 (MAX=13)、PVal 構造体
hspvar_core.cpp変数型登録 (NSTRUCT, MAP 追加)
hspvar_map.cpp/hMAP 型実装 (MapData, ArrayObject/Read)
hspvar_nstruct.cppNSTRUCT 型実装 (calloc, memcpy, pending_size)
hsp3struct.hMPTYPE_INTPTR 定義、STRUCTDAT/HPIDAT/LIBDAT の 32/64bit 分岐
hsp3code.cpp中間コード実行: dimmap/structdim/_struct_poke 命令処理
hsp3int.cpp拡張命令実行: setenv/delenv/delmap/mapclear + getenv/hasenv/getcmdarg* 関数

.NET 連携 (hsp3net/win32gui/)

ファイル主要な責務
Hsp3Net.hHsp3Net/GlobalAccess/HspCallbackProxy クラス定義 (C++/CLI)
Hsp3Net.cppリフレクション実装: メソッド呼び出し、C#/VB コンパイル、型変換
hspvar_netobj.cpp/hNETOBJ 変数型 (GCHandle ⇔ void*)
hsp3ext_win.cppDLL 拡張: callfunc*/newcomcb/comret + COM コールバック thunk 生成
HspForms.cpp/hWinForms コントロール管理
HspFormsInterop.cpp/hネイティブ→マネージド C リンケージブリッジ