Windows 10, CLISP 2.49, FFI.
I have used the built-in FFI to start a windows loop and a basic windproc callback. The initial windows message WM_PAINT is fine. In some tests, SetWindowPos or minimizing/maximizing the window, all of which call WM_PAINT, are also fine.
But when I, the user, grab the window edge to resize the window, it crashes. There is no lisp error. I've attempted to attach to CLISP via Visual Studio, but there is no windows exception either.
I've added (room) and (ext:gc) to check memory issues. I'm very suspicious that room reports "Bytes available until next GC: 6,510" being pretty low just before the program crashes. Multiple WM_PAINT calls will succeed, but if "bytes available" is low, there's a good (but not 100%) chance of a crash.
; Test Crash
;
; Win32 linkages at top.
; My Win32 windproc and message loop at bottom.
;
(ffi:def-c-enum eWin32Constants
(WS_OVERLAPPED #x00000000)
(WS_VISIBLE #x10000000)
(WS_CAPTION #x00C00000)
(WS_SYSMENU #x00080000)
(WS_THICKFRAME #x00040000)
(WM_PAINT 15 ) ; #x000f
)
;
; Win32 Structs
;
(ffi:def-c-type ATOM FFI:UINT16)
(ffi:def-c-type BOOL FFI:INT)
(ffi:def-c-type DWORD FFI:UINT32)
(ffi:def-c-type HANDLE FFI:c-pointer)
(ffi:def-c-type HBRUSH HANDLE)
(ffi:def-c-type HCURSOR HANDLE)
(ffi:def-c-type HDC HANDLE)
(ffi:def-c-type HICON HANDLE)
(ffi:def-c-type HINSTANCE HANDLE)
(ffi:def-c-type HMENU HANDLE)
(ffi:def-c-type HWND HANDLE)
(ffi:def-c-type LPARAM FFI:LONG)
(ffi:def-c-type LPVOID FFI:c-pointer)
(ffi:def-c-type LRESULT FFI:LONG)
(ffi:def-c-type WPARAM FFI:UINT32)
(ffi:def-c-struct POINT
(X ffi:long)
(Y ffi:long))
(FFI:def-c-struct RECT
(LEFT FFI:LONG)
(TOP FFI:LONG)
(RIGHT FFI:LONG)
(BOTTOM FFI:LONG)
)
(ffi:def-c-struct MSG
(hwnd HWND)
(message FFI:UINT)
(wparam WPARAM)
(lparam LPARAM)
(time dword)
(pt POINT)
(lprivate dword))
(FFI:def-c-struct PAINTSTRUCT
(HDC HDC)
(FERASE BOOL )
(RCPAINT RECT )
(FRESTORE BOOL )
(FINCUPDATE BOOL )
(RGBRESERVED FFI:UINT8)
)
(ffi:def-c-type WINDPROC (ffi:c-function
(:ARGUMENTS
(hwnd HWND :in)
(uMsg FFI:UINT32)
(wParam WPARAM)
(lParam LPARAM))
(:RETURN-TYPE FFI:UINT32)
(:LANGUAGE :stdc)))
(FFI:def-c-struct WNDCLASSA
(STYLE FFI:UINT32)
(LPFNWNDPROC WINDPROC)
(CBCLSEXTRA FFI:INT)
(CBWNDEXTRA FFI:INT)
(HINSTANCE HINSTANCE)
(HICON HICON)
(HCURSOR HCURSOR)
(HBRBACKGROUND HBRUSH)
(LPSZMENUNAME FFI:C-STRING)
(LPSZCLASSNAME FFI:C-STRING)
)
;
; Win32 Functions
;
(ffi:def-call-out RegisterClassA (:library "user32.dll")
(:name "RegisterClassA")
(:arguments (lpWndClass (FFI:c-ptr WNDCLASSA) :in)) ;HACK:; WNDCLASSA
(:return-type ATOM))
(defun RegisterClass (_name _style _wnd_proc)
(let* ( (wndclass (make-WNDCLASSA :STYLE _STYLE :|LPFNWNDPROC| _WND_PROC :|LPSZCLASSNAME| _NAME
:|CBCLSEXTRA| 0 :|CBWNDEXTRA| 0 :|HINSTANCE| NIL :|HICON| NIL
:|HCURSOR| NIL :|HBRBACKGROUND| NIL :|LPSZMENUNAME| NIL))
(registration (RegisterClassA wndclass)))
))
(ffi:def-call-out CreateWindowExA (:library "user32.dll")
(:name "CreateWindowExA")
(:arguments
(dwExStyle dword)
(lpClassName FFI:c-string)
(lpWindowName FFI:c-string)
(dwStyle dword)
(X FFI:int)
(Y FFI:int)
(nWidth FFI:int)
(nHeight FFI:int)
(hWndParent HWND)
(hMenu HMENU)
(hInstance HINSTANCE)
(lpParam LPVOID)
)
(:return-type HWND))
(ffi:def-call-out DefWindowProcA (:library "user32.dll")
(:name "DefWindowProcA")
(:arguments
(hWnd HWND :in)
(Msg ffi:uint32 :in)
(wParam WPARAM :in)
(lParam LPARAM :in))
(:return-type LRESULT))
(ffi:def-call-out GetMessageA (:library "user32.dll")
(:name "GetMessageA")
(:arguments
(LPMSG (ffi:c-ptr MSG) :out :alloca)
(HWND HWND :in)
(WMSGFILTERMIN FFI:UINT :in)
(WMSGFILTERMAX FFI:UINT :in))
(:return-type BOOL))
(ffi:def-call-out TranslateMessage (:library "user32.dll")
(:name "TranslateMessage")
(:arguments
(LPMSG (ffi:c-ptr MSG) :in-out))
(:return-type BOOL))
(ffi:def-call-out DispatchMessageA (:library "user32.dll")
(:name "DispatchMessageA")
(:arguments
(LPMSG (ffi:c-ptr MSG) :in-out))
(:return-type BOOL))
(ffi:def-call-out BeginPaint (:library "user32.dll")
(:name "BeginPaint")
(:arguments (HWND HWND :in)
(ps (ffi:c-ptr PAINTSTRUCT) :out :alloca))
(:return-type (ffi:c-pointer HDC)))
(ffi:def-call-out EndPaint (:library "user32.dll")
(:name "EndPaint")
(:arguments (HWND HWND :in)
(ps (ffi:c-ptr PAINTSTRUCT) :in))
(:return-type BOOL))
;
; My Win32 App Code
;
(FFI:DEF-CALL-IN MyWindowProc (:ARGUMENTS (handle UINT WPARAM LPARAM))
(:RETURN-TYPE dword)
(:LANGUAGE :stdc))
(defun MyWindowProc( hWnd uMsg wParam lParam)
(block defproc
(cond
((= uMsg WM_PAINT )
(format t "WM_PAINT~%")
(multiple-value-bind (dc ps)
(BeginPaint hWnd )
(EndPaint hWnd ps)
; Do nothing, but this clears the dirty flag.
)
(room)
(dotimes (j 2) (dotimes (i 40) (format t "*")) (FORMAT T "~%"))
)
(t
(return-from defproc (DefWindowProcA hWnd uMsg wParam lParam)))
)
; default return
0
)
)
(RegisterClass "LispGameWindow" 0 #'MyWindowProc) ;(logior CS_HREDRAW CS_VREDRAW CS_OWNDC)
(let ((*myhwnd* (CreateWindowExA
0 "LispGameWindow" "MyGameWindow"
(logior WS_OVERLAPPED WS_VISIBLE WS_CAPTION WS_SYSMENU WS_THICKFRAME)
100 100 655 415
NIL NIL NIL NIL)))
; Main message loop:
(loop
(multiple-value-bind (ret msg)
(GetMessageA *myhwnd* 0 0 )
(when (<= ret 0)
(return (jMSG-wparam msg)))
(TranslateMessage msg)
(DispatchMessageA msg)
)
;(ext:gc)
)
)
Output:
WM_PAINT
Number of garbage collections: 0
Bytes freed by GC: 0
Time spent in GC: 0.0 sec
Bytes permanently allocated: 92,960
Bytes currently in use: 2,714,832
Bytes available until next GC: 40,198
****************************************
****************************************
WM_PAINT
Number of garbage collections: 0
Bytes freed by GC: 0
Time spent in GC: 0.0 sec
Bytes permanently allocated: 92,960
Bytes currently in use: 2,726,060
Bytes available until next GC: 28,970
****************************************
****************************************
WM_PAINT
Number of garbage collections: 0
Bytes freed by GC: 0
Time spent in GC: 0.0 sec
Bytes permanently allocated: 92,960
Bytes currently in use: 2,737,292
Bytes available until next GC: 17,738
****************************************
****************************************
WM_PAINT
Number of garbage collections: 0
Bytes freed by GC: 0
Time spent in GC: 0.0 sec
Bytes permanently allocated: 92,960
Bytes currently in use: 2,748,520
Bytes available until next GC: 6,510
************
^^ Broken off for real at the point of the crash.
It is not windows functions that crash, but simple lisp commands like (dotimes ... (dotimes ... )) or (format t "a lot of text")
I am not certain I'm allocating/storing my FFI windows variables correctly.
The Cookbook http://cl-cookbook.sourceforge.net/win32.html has an example "Appendix A: "Hello, Lisp" Program #1" which is much more aggressive about manually allocating win32 strings and structures. I don't know if that's necessary in FFI as opposed to FLI, and I've failed in my own attempts to manually allocate the MSG buffer and pass it between the three windows functions.
Are the WM_PAINT messages sent by Windows in the same thread that executes the main message loop?
If yes, then it's likely a bug in CLISP. If you can reproduce it also with the current prerelease 2.49.92 (available from https://alpha.gnu.org/gnu/clisp/), it's worth submitting a bug report at https://gitlab.com/gnu-clisp/clisp/-/issues .
If no, then there's currently no way to make this work with CLISP; I'd then recommend SBCL instead. The reason is that multithreading in CLISP is not ready for prime-time, while SBCL supports multiple threads well.
Related
I am curious as to how to initialize a PROPVARIANT structure of VT_VARIANT | VT_VECTOR type. An existing model I am aware of is the HeadingPairs property of the DocumentSummaryInformation property set:
The HeadingPairs property is stored as a vector of variants, in repeating pairs of VT_LPSTR (or VT_LPWSTR) and VT_I4 values.
I am aware of the various Init functions, such as the InitPropVariantFromStringAsVector function to create a VT_VECTOR | VT_LPWSTR propvariant, but, as far as I know, there is no function to initialize a PROPVARIANT structure of VT_VARIANT | VT_VECTOR type.
Any help or suggestions would be appreciated.
Thank you.
You are correct that there is no ready-made Win32 function to initialize a PROPVARIANT with a vector of PROPVARIANT values. You are just going to have to initialize it manually, ie by allocating an array of PROPVARIANTs and then assigning a pointer to that array to the PROPVARIANT::capropvar.pElems field, and the array's length to the PROPVARIANT::capropvar.cElems field, eg:
int count = ...;
PROPVARIANT *arr = (PROPVARIANT*) CoTaskMemAlloc(count * sizeof(PROPVARIANT));
// initialize array values as needed...
PROPVARIANT pv;
PropVariantInit(&pv);
pv.vt = VT_VECTOR | VT_VARIANT;
pv.capropvar.pElems = arr;
pv.capropvar.cElems = count;
// use pv as needed...
PropVariantClear(&pv);
Here's a prototype based on Remy's solution written in AutoHotkey:
VariantVector := [{vt:0x001F, val:"hello world"}, {vt:0x0003, val:3}]
VarSetCapacity(PropVariant, 8 + 2 * A_PtrSize, 0)
InitPropVariatFromVariantAsVector(PropVariant, VariantVector)
; Do something here...
PropVariantClear(PropVariant)
return
InitPropVariatFromVariantAsVector(ByRef PropVariant, VariantVector) {
static VT_VARIANT := 0x000C
static VT_VECTOR := 0x1000
static Size := 8 + 2 * A_PtrSize
NumPut(VT_VARIANT | VT_VECTOR, PropVariant, 0, "UShort")
NumPut(VariantVector.Count(), PropVariant, 8, "UInt")
ptr := DllCall("Ole32\CoTaskMemAlloc", "UPtr", Size * VariantVector.Count(), "Ptr")
NumPut(ptr, PropVariant, 8 + A_PtrSize, "Ptr")
for Each, Variant in VariantVector {
Offset := Size * (Each - 1)
NumPut(Variant.vt, ptr + 0, Offset, "UShort")
Switch Variant.vt
{
Case 0x0003: NumPut(Variant.val, ptr + 0, Offset + 8, "Int") ; VT_I4
Case 0x001E: DllCall("Shlwapi\SHStrDup", "AStr", Variant.val, "Ptr", ptr + Offset + 8, "Int") ; VT_LPSTR
Case 0x001F: DllCall("Shlwapi\SHStrDup", "WStr", Variant.val, "Ptr", ptr + Offset + 8, "Int") ; VT_LPWSTR
Default: throw Exception(A_ThisFunc ": Unsupported Variant Type.", -1, Format("0x{:04X}", Variant.vt))
}
}
}
PropVariantClear(ByRef PropVariant) {
return DllCall("Ole32\PropVariantClear", "Ptr", &PropVariant, "Int")
}
There is a bug in an application (IntelliJ IDEA) where it fails to honour focus follows mouse (X-mouse). When I move the mouse on top of IntelliJ, it flashes the window. I figure I could capture the flashing and just force it to have focus. Tried this using AHK. It almost worked (got the RegisterShellHookWindow to work and it would run the call back message), but I couldn't give the window focus using SetFocus, presumably because I was in a different process space? I've tried some other ways to give the window focus, but all seem to raise the window, which isn't acceptable.
Is there some manner which I could force a window to capture focus in a different process space or is this not possible due to some security restrictions?
Here is the AHK code (with debugging statements) if anyone is interested (though it doesn't have to be written in AHK):
; Register shell hook to detect flashing windows.
DllCall("RegisterShellHookWindow", "Ptr", A_ScriptHwnd)
OnMessage(DllCall("RegisterWindowMessage", "Str", "SHELLHOOK"), "ShellEvent")
FHex( int, pad=0 ) { ; Function by [VxE]. Formats an integer (decimals are truncated) as hex.
; "Pad" may be the minimum number of digits that should appear on the right of the "0x".
Static hx := "0123456789ABCDEF"
If !( 0 < int |= 0 )
Return !int ? "0x0" : "-" FHex( -int, pad )
s := 1 + Floor( Ln( int ) / Ln( 16 ) )
h := SubStr( "0x0000000000000000", 1, pad := pad < s ? s + 2 : pad < 16 ? pad + 2 : 18 )
u := A_IsUnicode = 1
Loop % s
NumPut( *( &hx + ( ( int & 15 ) << u ) ), h, pad - A_Index << u, "UChar" ), int >>= 4
Return h
}
FlashWindowEx(hWnd := 0, dwFlags := 0, uCount := 0, dwTimeout := 0) {
Static A64 := (A_PtrSize = 8 ? 4 : 0) ; alignment for pointers in 64-bit environment
Static cbSize := 4 + A64 + A_PtrSize + 4 + 4 + 4 + A64
VarSetCapacity(FLASHWINFO, cbSize, 0) ; FLASHWINFO structure
Addr := &FLASHWINFO
Addr := NumPut(cbSize, Addr + 0, 0, "UInt")
Addr := NumPut(hWnd, Addr + 0, A64, "Ptr")
Addr := NumPut(dwFlags, Addr + 0, 0, "UInt")
Addr := NumPut(uCount, Addr + 0, 0, "UInt")
Addr := NumPut(dwTimeout, Addr + 0, 0, "Uint")
Return DllCall("User32.dll\FlashWindowEx", "Ptr", &FLASHWINFO, "UInt")
}
SetFocus(HWND) {
HPREV := HWND
While (HPREV := DllCall("User32.dll\GetWindow", "Ptr", HPREV, "UInt", 3, "UPtr")) ; GW_HWNDPREV = 3
If DllCall("User32.dll\IsWindowVisible", "Ptr", HPREV, "UInt")
Break
WinActivate, ahk_id %HWND%
If (HPREV)
DllCall("User32.dll\SetWindowPos", "Ptr", HWND, "Ptr", HPREV, "Int", 0, "Int", 0, "Int", 0, "Int", 0, "UInt", 0x0B)
}
ShellEvent(wParam, lParam) {
_wParam := FHex(wParam)
_lParam := FHex(lParam)
X := 0
Y := 0
MouseGetPos, X, Y
ToolTip % "1: (" . X . ", " . Y . ") Trying to activate " . _wParam . ", " . _lParam . ".", (X+1), (Y+1),1
if (wParam = 0x8006) ; HSHELL_FLASH
{ ; lParam contains the ID of the window which flashed:
FlashWindowEx(lParam)
;WinActivate, ahk_id %lParam%
;SetFocus(lParam)
DLLCall("User32.dll\SetFocus", "Ptr", lParam, "Ptr")
ToolTip % "2: (" . X . ", " . Y . ") Trying to activate " _wParam . ", " . _lParam . ".", (X+1), (Y+21),2
}
}
I'm trying to use Raylib (https://www.raylib.com/, https://github.com/raysan5/raylib) from Racket code via FFI. Here is the most simple example:
#lang racket
(require ffi/unsafe
ffi/unsafe/define)
; raylib shared object must be available for Racket
; for example, in Linux it must be in
; ~/.racket/<racket-version>/lib> or /usr/lib/racket
(define-ffi-definer define-raylib (ffi-lib "libraylib" #:global? #t))
(define-raylib BeginDrawing (_fun -> _void))
(define-raylib CloseWindow (_fun -> _void))
(define-raylib EndDrawing (_fun -> _void))
(define-raylib InitWindow (_fun _int _int _string -> _void))
(define-raylib SetTargetFPS (_fun _int -> _void))
(define-raylib WindowShouldClose (_fun -> _int))
(void InitWindow 640 480 "Test window")
(void SetTargetFPS 60)
(define (main-loop)
(BeginDrawing)
(EndDrawing)
(if (= (WindowShouldClose) 0)
(main-loop)
(CloseWindow)))
(main-loop)
But even this very simple example crashes with the message:
SIGSEGV MAPERR si_code 1 fault on addr (nil)
Aborted (core dumped)
Looks like it crashes when calling BeginDrawing() function. The code of this function is also very simple:
// Setup canvas (framebuffer) to start drawing
void BeginDrawing(void)
{
currentTime = GetTime(); // Number of elapsed seconds since InitTimer()
updateTime = currentTime - previousTime;
previousTime = currentTime;
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
rlMultMatrixf(MatrixToFloat(downscaleView)); // If downscale required, apply it here
}
Functions with rl prefix are OpenGL wrappers. May it be an OpenGL context issue?
I tried to call the same functions in Guile Scheme and in Guile all works great.
It looks like you're not actually calling either InitWindow or SetFPS.
Instead of (void InitWindow 640 480 "Test window"), try (InitWindow 640 480 "Test window").
I am trying to control the master volume. I am able to succesfully do that with this:
HWND mainhwnd = CreateWindow(szWindowClass, _T("window-noit-ext-profilist"), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, wcex.hInstance, NULL);
if (!mainhwnd) {
MessageBox(NULL, _T("Profilist: Call to CreateWindow failed!"), _T("window-noit-ext-profilist"), NULL);
return 1;
}
SendMessage(mainhwnd, WM_APPCOMMAND, (WPARAM)mainhwnd, (LPARAM)(APPCOMMAND_VOLUME_MUTE * 65536)); // mute
SendMessage(mainhwnd, WM_APPCOMMAND, (WPARAM)mainhwnd, (LPARAM)(APPCOMMAND_VOLUME_DOWN * 65536)); // vol down
SendMessage(mainhwnd, WM_APPCOMMAND, (WPARAM)mainhwnd, (LPARAM)(APPCOMMAND_VOLUME_UP * 65536)); // vol up
Why do I have to multiply by 65,536? The docs do not state this. IF I don't multiply, then it doesn't work.
For WM_APPCOMMAND, the lParam parameter packs three values in a single integer.
The lower 16bit word, dwKeys, indicates whether various virtual keys are down.
The higher 16bit word packs two fields: the highest 4 bits, uDevice, specifies the input device that is generating the input event. The lower 12 bits, cmd, contains the application command.
Multiplying by 65536 is same as bit shifting by 16 bits to the left (because 65536 = 0x10000 in hexadecimal). So, when you send the message with APPCOMMAND_VOLUME_UP * 65536, you are specifying the cmd is APPCOMMAND_VOLUME_UP, and the uDevice and dwKeys are both zero.
What i'm trying to do is write text to gzip.exe StdIn and redirect its StdOut to a file, all this using WINAPI functions and visual Foxpro 9.
The problem is that I always get "Stdin: Bad file descriptor" error and no output. What I expect to have is the file associated with StdOut filled with data and the file associated with StdErr empty. Instead I always endup with the file associated with StdOut empty and the file associated with StdErr filled with:
standard input: Bad file descriptor gzip:"
Here is my code:
*----------------------------------------------------------------
LOCAL cBuffer, nBytesRead, newBufSize, nBytesWritten
LOCAL hReadPipe, hWritePipe, res, cSecAttr, cFile, hFile, process_info, start_info, hProcess, hThread
*---------------------------------------------------
#DEFINE BUFF_SIZE 1024
#DEFINE WAIT_TIMEOUT 0x00000102
#DEFINE WAIT_INTERVAL 5000
#DEFINE INFINITE 0xFFFFFFFF
*---------------------------------------------------
#DEFINE HANDLE_FLAG_INHERIT 0x00000001
*---------------------------------------------------
#DEFINE FILE_APPEND_DATA 4
#DEFINE FILE_SHARE_READ 0x00000001
#DEFINE FILE_SHARE_WRITE 0x00000002
#DEFINE CREATE_ALWAYS 2
#DEFINE FILE_ATTRIBUTE_NORMAL 0x80
#DEFINE INVALID_HANDLE_VALUE -1
#DEFINE GENERIC_READ 0x80000000
#DEFINE GENERIC_WRITE 0x40000000
*---------------------------------------------------
#DEFINE STARTF_USESTDHANDLES 0x00000100
#DEFINE STARTF_USESHOWWINDOW 0x00000001
#DEFINE CREATE_NO_WINDOW 0x08000000
*---------------------------------------------------
DECLARE INTEGER WaitForSingleObject IN kernel32.DLL ;
INTEGER hHandle, ;
INTEGER dwMilliseconds
DECLARE INTEGER GetLastError IN kernel32.DLL
DECLARE INTEGER CloseHandle IN kernel32.DLL INTEGER hObject
DECLARE INTEGER CreatePipe IN kernel32;
INTEGER # hReadPipe, ;
INTEGER # hWritePipe, ;
STRING # lpPipeAttributes, ;
LONG nSize
DECLARE INTEGER SetHandleInformation IN Kernel32 ;
INTEGER hObject, ;
LONG dwMask, ;
LONG dwFlags
DECLARE INTEGER ReadFile IN kernel32 ;
INTEGER hFile, ;
STRING # lpBuffer, ;
INTEGER nNumberOfBytesToRead, ;
INTEGER # lpNumberOfBytesRead, ;
INTEGER lpOverlapped
DECLARE INTEGER WriteFile IN Kernel32 ;
INTEGER hFile, ;
STRING lpBuffer, ;
INTEGER nBt2Write, ;
INTEGER # lpBtWritten, ;
INTEGER lpOverlapped
DECLARE INTEGER CreateFile IN Kernel32.DLL ;
STRING lpFileName, ;
INTEGER dwDesiredAccess, ;
INTEGER dwShareMode, ;
STRING lpSecurityAttributes, ;
INTEGER dwCreationDisposition, ;
INTEGER dwFlagsAndAttributes, ;
INTEGER hTemplateFile
DECLARE INTEGER CreateProcess IN kernel32.DLL ;
INTEGER lpApplicationName, ;
STRING lpCommandLine, ;
INTEGER lpProcessAttributes, ;
INTEGER lpThreadAttributes, ;
INTEGER bInheritHandles, ;
INTEGER dwCreationFlags, ;
INTEGER lpEnvironment, ;
INTEGER lpCurrentDirectory, ;
STRING #lpStartupInfo, ;
STRING #lpProcessInformation
*-------------------------------------------------------
hReadPipe=0
hWritePipe=0
cSecAttr=BINTOC(12,'4RS')+BINTOC(0,'4RS')+BINTOC(1,'4RS')
res=CreatePipe(#hReadPipe,#hWritePipe,cSecAttr,BUFF_SIZE)
res=SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT,0)
res=SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT,0)
*-------------------------------------------------------
cSecAttr=BINTOC(12,'4RS')+BINTOC(0,'4RS')+BINTOC(1,'4RS')
cFile="d:\ggzz.sql.gz"
hFile=0
hFile=CreateFile(cFile,BITOR(GENERIC_READ,GENERIC_WRITE),BITOR(FILE_SHARE_READ,FILE_SHARE_WRITE),cSecAttr,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
*-------------------------------------------------------
cSecAttr=BINTOC(12,'4RS')+BINTOC(0,'4RS')+BINTOC(1,'4RS')
cFile="d:\gzip.err"
hGzipErr=0
hGzipErr=CreateFile(cFile,BITOR(GENERIC_READ,GENERIC_WRITE),BITOR(FILE_SHARE_READ,FILE_SHARE_WRITE),cSecAttr,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
*-------------------------------------------------------
cKmd="d:\gzip\bin\gzip.exe"
process_info = REPLICATE(CHR(0), 16)
start_info = BINTOC(68,'4RS') + REPLICATE(CHR(0), 64) start_info=LEFT(start_info,44)+BINTOC(BITOR(STARTF_USESTDHANDLES,STARTF_USESHOWWINDOW),'4RS')+REPLICATE(CHR(0), 20)
start_info=LEFT(start_info,56)+BINTOC(hReadPipe,'4RS')+BINTOC(hFile,'4RS')+BINTOC(hGzipErr,'4RS')
res = CreateProcess(0,cKmd, 0, 0, 1, 0, 0, 0, #start_info, #process_info)
hProcess=CTOBIN(SUBSTR(process_info, 1, 4),'4RS')
hThread=CTOBIN(SUBSTR(process_info, 5, 4),'4RS')
DIMENSION aaa(3)
aaa(1)="11111111111111111111111"
aaa(2)="222222222222222222222222"
aaa(3)="3333333333333333333333"
FOR i=1 TO ALEN(aaa)
cBuffer=aaa(i)
newBufsize=LEN(cBuffer)
nBytesWritten=0
res=WriteFile(hWritePipe,cBuffer,newBufsize,#nBytesWritten,0)
IF nBytesWritten=0
EXIT
ENDIF
ENDFOR
=WaitForSingleObject(hProcess,INFINITE)
res=CloseHandle(hFile)
res=CloseHandle(hThread)
res=CloseHandle(hProcess)
res=CloseHandle(hReadPipe)
res=CloseHandle(hWritePipe)
CLEAR DLLS "CreatePipe","GetLastError", "CreateFile", "CreateProcess", "CloseHandle","SetHandleInformation", ; "ReadFile", "WriteFile"