UWSCで名前付きパイプ
UWSCでプロセス間通信をやってみようと思い立ったので、やってみた。
メモリマップドファイル方式は、しろまささんのところにあるので、
http://siromasa.xxxxxxxx.jp/Sample/index.html
名前付きパイプにて。
これなら、別端末でも通信できますね。
まずはスクリプト
pipe.uws
OPTION EXPLICIT IFB GET_UWSC_NAME = "pipe.uws" THEN DIM ret, loop = TRUE IFB MSGBOX("Server?", BTN_YES or BTN_NO) = BTN_YES THEN ret = Pipe.Create() loop = (ret = 0) WHILE loop ret = Pipe.Wait() // INPUTかつThreadがAPI内でBlockすると、止まる模様。とりあえず一方向 ret = "" WHILE VARTYPE(ret) = VAR_BSTR Pipe.Log(ret) ret = Pipe.ReadString() WEND Pipe.Disconnect() loop = (MSGBOX("Wait?", BTN_YES or BTN_NO) = BTN_YES) WEND Pipe.Close() ELSE WHILE loop ret = Pipe.Connect() IFB ret = 0 THEN // INPUTかつThreadがAPI内でBlockすると、止まる模様。とりあえず一方向 ret = INPUT("Message to server") WHILE ret <> EMPTY Pipe.WriteString(ret, FALSE) ret = INPUT("Message to server") WEND Pipe.Close(FALSE) ENDIF loop = (MSGBOX("Connect?", BTN_YES or BTN_NO) = BTN_YES) WEND ENDIF ENDIF MODULE Pipe CONST DEF_NAME = "\\.\pipe\UwscNamedPipe" CONST DEF_TIMEOUT = 1000 HASHTBL _handleServer HASHTBL _handleClient DIM _log = TRUE // General API DEF_DLL CloseHandle(dword): bool: kernel32.dll DEF_DLL GetLastError(): dword: kernel32.dll // NamedPipe API DEF_DLL CreateNamedPipeW(wstring,dword,dword,dword,dword,dword,dword,dword): dword: kernel32.dll CONST PIPE_ACCESS_INBOUND = 1 CONST PIPE_ACCESS_OUTBOUND = 2 CONST PIPE_ACCESS_DUPLEX = 3 CONST PIPE_UNLIMITED_INSTANCES = 255 CONST INVALID_HANDLE_VALUE = $FFFFFFFF DEF_DLL ConnectNamedPipe(dword,dword): bool: kernel32.dll DEF_DLL DisconnectNamedPipe(dword): bool: kernel32.dll DEF_DLL PeekNamedPipe(dowrd,var byte[],dword,var dword,var dword,var dword): bool: kernel32.dll DEF_DLL TransactNamedPipe(dword,byte[],dword,var byte[],dword,var dword,dword): bool: kernel32.dll DEF_DLL WaitNamedPipeW(wstring,dword): bool: kernel32.dll // File API DEF_DLL CreateFileW(wstring,dword,dword,dword,dword,dword,dword): dword: kernel32.dll CONST GENERIC_READ = $80000000 CONST GENERIC_WRITE = $40000000 CONST GENERIC_EXECUTE = $20000000 CONST GENERIC_ALL = $10000000 CONST CREATE_NEW = 1 CONST CREATE_ALWAYS = 2 CONST OPEN_EXISTING = 3 CONST OPEN_ALWAYS = 4 CONST TRUNCATE_EXISTING = 5 DEF_DLL ReadFile(dword,var byte[],dword,var dword,dword): bool: kernel32.dll DEF_DLL WriteFile(dword,byte[],dword,var dword,dword): bool: kernel32.dll FUNCTION Create(name = DEF_NAME, timeout = DEF_TIMEOUT, dir = PIPE_ACCESS_DUPLEX, num = PIPE_UNLIMITED_INSTANCES) RESULT = 0 _handleServer[name] = CreateNamedPipeW(name, dir, 0, num, 0, 0, timeout, NULL) IFB _handleServer[name] = INVALID_HANDLE_VALUE THEN RESULT = GetLastError() Log("Err: CreateNamedPipe: " + RESULT) ENDIF FEND FUNCTION Wait(name = DEF_NAME) RESULT = 0 IFB !ConnectNamedPipe(_handleServer[name], NULL) THEN RESULT = GetLastError() Log("Err: ConnectNamedPipe: " + RESULT) ENDIF FEND FUNCTION Disconnect(name = DEF_NAME) RESULT = 0 IFB !DisconnectNamedPipe(_handleServer[name]) THEN RESULT = GetLastError() IF RESULT THEN Log("Err: DisconnectNamedPipe: " + RESULT) ENDIF FEND FUNCTION Close(server = TRUE, name = DEF_NAME) RESULT = 0 DIM handle IFB server THEN handle = _handleServer[name] ELSE handle = _handleClient[name] ENDIF IFB !CloseHandle(handle) THEN RESULT = GetLastError() Log("Err: CloseHandle: " + RESULT) ENDIF FEND FUNCTION Connect(name = DEF_NAME) RESULT = 0 IFB WaitNamedPipeW(name, 0) THEN _handleClient[name] = CreateFileW(name, GENERIC_ALL, 0, NULL, OPEN_EXISTING, 0, NULL) IFB _handleClient[name] = INVALID_HANDLE_VALUE THEN RESULT = GetLastError() Log("Err: CreateFile: " + RESULT) ENDIF ELSE RESULT = GetLastError() IF !RESULT THEN RESULT = -1 // TIMEOUT Log("Err: WaitNamedPipe: " + RESULT) ENDIF FEND FUNCTION ReadString(server = TRUE, name = DEF_NAME) DIM handle IFB server THEN handle = _handleServer[name] ELSE handle = _handleClient[name] ENDIF CONST SIZE = 1024 DIM buf[SIZE], ret // 本来ならバッファサイズ以上での続行処理が必要 IFB ReadFile(handle, buf, SIZE, ret, NULL) THEN RESIZE(buf, ret) RESULT = ConvertByte(buf) ELSE RESULT = GetLastError() IF RESULT THEN Log("Err: ReadFile: " + RESULT) ENDIF FEND FUNCTION WriteString(data, server = TRUE, name = DEF_NAME) DIM handle IFB server THEN handle = _handleServer[name] ELSE handle = _handleClient[name] ENDIF DIM len = LENGTH(data) * 2 DIM buf[len] IFB ConvertByte(buf, data) THEN DIM ret // 本来なら少量書込みでの続行処理が必要 IFB WriteFile(handle, buf, len, ret, NULL) and ret = len THEN ret = 0 ELSE ret = GetLastError() IF ret THEN Log("Err: WriteFile: " + ret) ENDIF RESULT = ret ELSE RESULT = -1 ENDIF FEND FUNCTION ConvertByte(var bdata[], data = EMPTY, dir = 0) // dir: 1(string->byte[]) 2(byte[]->string) IFB dir <> 1 and dir <> 2 THEN IFB data = NULL or data = EMPTY or LENGTH(data) = 0 THEN dir = 2 ELSE dir = 1 ENDIF ENDIF DIM ret = FALSE IFB dir = 1 THEN ret = ConvertByteFromString(bdata, data) ELSEIF dir = 2 THEN ret = ConvertByteToString(bdata) ENDIF RESULT = ret FEND FUNCTION ConvertByteFromString(var bdata[], data) DIM m = LENGTH(data), i DIM b, t FOR i = 1 TO m t = ASC(COPY(data, i, 1)) b = t MOD 256 bdata[(i - 1) * 2] = VARTYPE(b, VAR_BYTE) b = (t - b) / 256 bdata[(i - 1) * 2 + 1] = VARTYPE(b, VAR_BYTE) NEXT RESULT = TRUE FEND FUNCTION ConvertByteToString(bdata[]) DIM m = LENGTH(bdata) / 2 - 1, i DIM ret = "" FOR i = 0 TO m ret = ret + CHR(bdata[i*2] + bdata[i*2+1] * 256) NEXT RESULT = ret FEND FUNCTION Log(msg, t=0) DIM n = 0 IFB _log THEN n = GETTIME() * 1000 + G_TIME_ZZ DIM buf = G_TIME_NN2 + ":" + G_TIME_SS2 + "." + G_TIME_ZZ2 + " " + msg IF t > 0 THEN buf = buf + "(" + (n - t) + ")" PRINT buf ENDIF RESULT = n FEND ENDMODULE
注意点と今後の課題
スクリプト中にも書きましたが、スレッドがAPI中でブロックしている時に、
メインでINPUTを出すスクリプトでは、UWSCが無反応になりました。
スクリプトで交互に通信するのはOKだったのですが、、、。
ということで、とりあえず一方向にしています。
何が原因か不明なので、それ次第でもありますが、
場合によっては、Overlappedの助けを借りる必要がありそうです。
→DWORDの5個配列でよさげ(UWSCは32bitなので)
あと、なんとなくWin32 APIはANSI版を使ってましたが、
UWSCだとUnicodeが便利な気がしたので、今回はUnicode版にしました。