UWSCのDEF_DLLについて

DEF_DLLについて書きたくなった。
ちょっともやもやするんで、整理を兼ねて。


最重要「UWSCの配列はポインターであり、ポインターが必要なところ全てで渡せる」



全て同じ型の構造体は配列である

stuncloudさんの偉大な発見である。
ファイル時間を変更する #uwsc | たっぷす庵
全部WORDやDWORDの構造体は、配列のポインター定義にしてしまって良い。
{DWORD,DWORD} -> var DWORD[](2個の配列を渡す)

全て同じ型でなくても構造体は配列である

上を一歩進める。
例えば、WSADATA構造体

#define WSADESCRIPTION_LEN      256
#define WSASYS_STATUS_LEN       128
struct WSAData {
   WORD wVersion;
   WORD wHighVersion;
   char szDescription[WSADESCRIPTION_LEN+1];
   char szSystemStatus[WSASYSSTATUS_LEN+1];
   unsigned short iMaxSockets;
   unsigned short iMaxUdpDg;
   char FAR * lpVendorInfo;
};

最後の*lpVendorInfoは使わないので適当にサイズをあわせると、DWORDで良い。
すなわち、

{WORD,WORD,char[],char[],WORD,WORD,DWORD}

といったイメージ。(あくまでイメージ。多分配列はポインターにされてしまうので実際はダメっぽい)


ここで、それぞれのサイズを考える。

  2Byte WORD wVersion;
  2Byte WORD wHighVersion;
257Byte char szDescription[WSADESCRIPTION_LEN+1];
129Byte char szSystemStatus[WSASYSSTATUS_LEN+1];
  2Byte unsigned short iMaxSockets;
  2Byte unsigned short iMaxUdpDg;
  4Byte char FAR * lpVendorInfo;

szDescriptionとかszSystemStatusとかどうでも良いとすると、そこに奇数があるだけで他は偶数。
しかも奇数は二つ。
これって、2Byteの塊じゃない?
2+2+257+129+2+2+4=398Byte = 199個のWORD
ということで、WSADATA構造体 -> var WORD(199個の配列を渡す)
ただ、ポインターの指す先が大きくても怒られはしないので、var DWORD
(100個の配列を渡す)でもOK。
中身に応じて、単位を決めてください。


構造体は全てポインターで、そのサイズより大きくすることのみが重要ということ。

string/wstringもポインターである

確保した領域のポインターを渡している。
配列と同じ効果が見込めるが、「var」の動作が異なる。
数値の配列では0も扱えるが、string/wstringは0を終端とみなし、それ以上はコピーされない。

pchar/pbyte/string

pbyteというヘルプにない宣言がある模様。
でも多分、pcharと同じ。(だから書いてない模様)
pcharとstringはどちらもcharへのポインターですが、0を終端とみなすか否かの違いがある!
ということで、GetPrivateProfileStringAでキーをNULLにしたりセクションをNULLにすると、NULL区切りで文字列が取得できますが、これをbyte配列で受けてごにょごにょしなくて良いということです。
test.iniのセクション一覧ならこんな感じ

DEF_DLL GetPrivateProfileStringA(string,string,string,var pchar,DWORD,string): DWORD: kernel32
buf = FORMAT(CHR(0), 1024)	// バッファサイズは適当に
IFB GetPrivateProfileStringA(NULL,NULL,"ないっす",buf,LENGTH(buf),".\test.ini") THEN
	sections = SPLIT(buf, CHR(0), TRUE)
	FOR i = 0 TO LENGTH(sections) - 1
		MSGBOX(sections[i])
	NEXT
ENDIF

"ないっす"は、いらなきゃNULLで。
もうちょっと早く気づきたかったかなー。

多分UWSCは[]や{}をポインターにしてしまう

意外と呼ぶのが難しいuser32のAPI

HMONITOR MonitorFromPoint(POINT pt, DWORD dwFlags);

typedef struct tagPOINT { 
  LONG x; 
  LONG y; 
} POINT, *PPOINT;

一発でDEF_DLLにできますか?


最初、どうしてもこう書く

DEF_DLL MonitorFromPoint({long,long},dword): dword: user32

ところがこれが期待動作しない。
{}はポインターにしてしまうのか?と推理。では配列は?

DEF_DLL MonitorFromPoint(long[],dword): dword: user32

これもダメ。
正解は

DEF_DLL MonitorFromPoint(long,long,dword): dword: user32


元の定義から考えると、MonitorFromPointの引数は、POINT=8ByteとDWORD=4Byteの12Byte。
{}やの場合、ポインター=4ByteとDWORD=4Byteの8Byteしか渡ってない模様。
正解定義は、12Byteが渡る。


{}やは無条件でポインターにするのはやめて欲しかったかな。
横着定義ができなくなるから。

全てはメモリー

符号がなくなってしまうのでマイナスが面倒になりますが、

DEF_DLL MonitorFromPoint(word,word,word,word,word,word): dword: user32

もOK。
これも12Byte渡りますから。
もちろん

DEF_DLL MonitorFromPoint(longlong,dword): dword: user32

もOK。(これまた合成が面倒)


varをつければポインターになる、は厳密には正しくない。
{}や[]もポインターにする効果があるが、それにvarをつけてもポインターポインターにはならない。
varはポインターでないものをポインターにして、関数リターン時に値コピーする、という機能。


引数として渡すサイズはいくつか。
どれがポインターで、ポインターの指す先に必要なサイズはいくつか。
それを意識していれば、DEF_DLLで苦戦はしない、、、といいな。

DLLモジュールについて

UWSCでDLLを扱う - じゅんじゅんのきまぐれ
モリーからDLLをロードするモジュールですが、Win32APIの呼び出し用としても便利ですよ!
上のMonitorFromPointなら以下のように呼び出せます。

Dll.CallEx(r, e, "MonitorFromPoint", "user32", 3, x, y, 0)

(rは呼び出し結果:成功(True)・失敗(False)。eはGetLastErrorのリターン)
ジャンプ前にスタックに積むメモリーがわかっているなら、自前で用意してDll.Callするのもあり。
Dll.CallExを使うなら、数値は4Byte、文字列はポインターになることに留意してください。
モリーを想定できるなら便利に使えるもの。
InterpreterにCALLしておくと、DLLが自在に呼べて便利です。
モリーの確保もDll.Allocでできますしね!
(小さいサイズのメモリーの場合、プロセスヒープから確保するように修正しました。
 メモリーを想像してたら、ページ単位でジャブジャブ使うのが嫌になったので。)

おまけ

もし要望するなら、DLL利用のもう1手段。
DLL_IMPステートメントをお願いしたい。
定義はほぼ同じだけど、{}や[]をポインターにしない。
varの能力は同じ。
string/wstringもポインターにしない、かな?
まあでもなんとかなるので、いらんか。
それより、EVALでDEF_DLLを通してくれる方が嬉しいかな。
(定義文字列を手動で書かなくてよくなるし、RtlMoveMemoryは変身させたい)


この辺の話は、アセンブリを書いてて気づいた。
API呼び出しって、引数渡す、とかいうイメージあるじゃないですか。
でも、アセンブリ的には正しくなくて、API呼び出しに引数はない。
ジャンプがあるだけ。
ただ、ジャンプ先に値を渡したいから、呼び出し規約に従って、スタックに引数相当を積んでジャンプとなっている。
ようは、APIの期待通りにスタックを積めば良いの。
DEF_DLLはジャンプする際にスタックにどう積むかの指示、ということ。


あと気づいたこと。
EVAL、CALLが通る。
ただし、既にCALL済みのもので、ファイルは読まない。
スクリプトを動的に実行するのは無理、ということ。
DOSCMDやEXECでUWSCを起動すれば、動的実行も可能だけど。