Author Topic: Child Processes in QB64 using WinAPI  (Read 4204 times)

0 Members and 1 Guest are viewing this topic.

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Child Processes in QB64 using WinAPI
« on: May 06, 2021, 04:25:05 pm »
This code below uses WinAPI to read in a file called "loremipsum.txt" and sends it to the child and is then routed back to the parent through the STD handles. The below code is based on an example from the MSDN here.

Parent code:
Code: QB64: [Select]
  1. _Title "Parent Process"
  2.  
  3. Type SECURITY_ATTRIBUTES
  4.     As Long nLength
  5.     $If 64BIT Then
  6.         As Long padding
  7.     $End If
  8.     As _Offset lpSecurityDescriptor
  9.     As Long bInheritHandle
  10.     $If 64BIT Then
  11.         As Long padding2
  12.     $End If
  13.  
  14. Type STARTUPINFO
  15.     As Long cb
  16.     $If 64BIT Then
  17.         As Long padding
  18.     $End If
  19.     As _Offset lpReserved, lpDesktop, lpTitle
  20.     As Long dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags
  21.     As Integer wShowWindow, cbReserved2
  22.     $If 64BIT Then
  23.         As Long padding2
  24.     $End If
  25.     As _Offset lpReserved2, hStdInput, hStdOutput, hStdError
  26.  
  27. Type PROCESS_INFORMATION
  28.     As _Offset hProcess, hThread
  29.     As Long dwProcessId
  30.     $If 64BIT Then
  31.         As Long padding
  32.     $End If
  33.  
  34. Const STARTF_USESTDHANDLES = &H00000100
  35. Const CREATE_NO_WINDOW = &H8000000
  36. Const HANDLE_FLAG_INHERIT = &H00000001
  37. Const BUFSIZE = 4096
  38. Const STD_OUTPUT_HANDLE = -11
  39. Const GENERIC_READ = &H80000000
  40. Const OPEN_EXISTING = 3
  41. Const FILE_ATTRIBUTE_READONLY = 1
  42. Const INVALID_HANDLE_VALUE = -1
  43.  
  44.     Function CreatePipe%% (ByVal hReadPipe As _Offset, Byval hWritePipe As _Offset, Byval lpPipeAttributes As _Offset, Byval nSize As Long)
  45.     Function CreateProcess%% Alias CreateProcessA (ByVal lpApplicationName As _Offset, Byval lpCommandLine As _Offset, Byval lpProcessAttributes As _Offset, Byval lpThreadAttributes As _Offset, Byval bInheritHandles As Integer, Byval dwCreationFlags As Long, Byval lpEnvironment As _Offset, Byval lpCurrentDirectory As _Offset, Byval lpStartupInfor As _Offset, Byval lpProcessInformation As _Offset)
  46.     Function HandleClose%% Alias "CloseHandle" (ByVal hObject As _Offset)
  47.     Function ReadFile%% (ByVal hFile As _Offset, Byval lpBuffer As _Offset, Byval nNumberOfBytesToRead As Long, Byval lpNumberOfBytesRead As _Offset, Byval lpOverlapped As _Offset)
  48.     Function WriteFile%% (ByVal hFile As _Offset, Byval lpBuffer As _Offset, Byval nNumberOfBytesToWrite As Long, Byval lpNumberOfBytesWritten As _Offset, Byval lpOverlapped As _Offset)
  49.     Function GetStdHandle%& (ByVal nStdHandle As Long)
  50.     Function SetHandleInformation%% (ByVal hObject As _Offset, Byval dwMask As Long, Byval dwFlags As Long)
  51.     Function CreateFile%& Alias "CreateFileA" (lpFileName As String, Byval dwDesiredAccess As Long, Byval dwSharedMode As Long, Byval lpSecurityAttributes As _Offset, Byval dwCreationDisposition As Long, Byval dwFlagsAndAttributes As Long, Byval hTemplateFile As _Offset)
  52.     Function GetLastError& ()
  53.  
  54. Dim Shared As _Offset hChildStd_IN_Rd, hChildStd_IN_Wr, hChildStd_OUT_Rd, hChildStd_OUT_Wr, hInputFile
  55.  
  56. Dim As SECURITY_ATTRIBUTES saAttr
  57.  
  58. Print "->Start of parent execution."
  59.  
  60. saAttr.nLength = Len(saAttr)
  61. saAttr.bInheritHandle = 1
  62. saAttr.lpSecurityDescriptor = 0
  63.  
  64. If CreatePipe(_Offset(hChildStd_OUT_Rd), _Offset(hChildStd_OUT_Wr), _Offset(saAttr), 0) = 0 Then
  65.     ErrorExit "StdoutRd CreatePipe"
  66.  
  67. If SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) = 0 Then
  68.     ErrorExit "Stdout SetHandleInformation"
  69.  
  70. If CreatePipe(_Offset(hChildStd_IN_Rd), _Offset(hChildStd_IN_Wr), _Offset(saAttr), 0) = 0 Then
  71.     ErrorExit "Stdin CreatePipe"
  72.  
  73. If SetHandleInformation(hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) = 0 Then
  74.     ErrorExit "Stdin SetHandleInformation"
  75.  
  76. CreateChildProcess
  77.  
  78. WriteToPipe
  79. Print "Contents of loremipsum.txt written to child STDIN pipe"
  80. Print "Contents of child process STDOUT:"
  81. ReadFromPipe
  82. Print "->End of parent execution"
  83.  
  84.  
  85. Sub CreateChildProcess ()
  86.     Dim As String szCmdline: szCmdline = "Child Process" + Chr$(0)
  87.     Dim As PROCESS_INFORMATION piProcInfo
  88.     Dim As STARTUPINFO siStartInfo
  89.     Dim As _Byte bSuccess
  90.  
  91.     siStartInfo.cb = Len(siStartInfo)
  92.     siStartInfo.hStdError = hChildStd_OUT_Wr
  93.     siStartInfo.hStdOutput = hChildStd_OUT_Wr
  94.     siStartInfo.hStdInput = hChildStd_IN_Rd
  95.     siStartInfo.dwFlags = STARTF_USESTDHANDLES
  96.  
  97.     bSuccess = CreateProcess(0, _Offset(szCmdline), 0, 0, 1, CREATE_NO_WINDOW, 0, 0, _Offset(siStartInfo), _Offset(piProcInfo))
  98.     If bSuccess = 0 Then
  99.         ErrorExit "CreateProcess"
  100.     Else
  101.         Dim As _Byte closeh
  102.         closeh = HandleClose(piProcInfo.hProcess)
  103.         closeh = HandleClose(piProcInfo.hThread)
  104.         closeh = HandleClose(hChildStd_OUT_Wr)
  105.         closeh = HandleClose(hChildStd_IN_Rd)
  106.     End If
  107.  
  108. Sub WriteToPipe ()
  109.     Dim As Long dwRead, dwWritten
  110.     Dim As String * BUFSIZE chBuf
  111.     Dim As _Byte bSuccess
  112.  
  113.     Dim As _Offset hInputFile: hInputFile = CreateFile("loremipsum.txt" + Chr$(0), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, 0)
  114.     Do
  115.         bSuccess = ReadFile(hInputFile, _Offset(chBuf), BUFSIZE, _Offset(dwRead), 0)
  116.         If bSuccess = 0 Or dwRead = 0 Then Exit Do
  117.         bSuccess = WriteFile(hChildStd_IN_Wr, _Offset(chBuf), dwRead, _Offset(dwWritten), 0)
  118.         If bSuccess = 0 Then Exit Do
  119.     Loop
  120.     If hInputFile = INVALID_HANDLE_VALUE Then
  121.         ErrorExit "CreateFile"
  122.     End If
  123.  
  124.     If HandleClose(hChildStd_IN_Wr) = 0 Then
  125.         ErrorExit "StdInWr CloseHandle"
  126.     End If
  127.     Dim As _Byte closeh: closeh = HandleClose(hInputFile)
  128.  
  129. Sub ReadFromPipe ()
  130.     Dim As Long dwRead, dwWritten
  131.     Dim As String * BUFSIZE chBuf
  132.     Dim As _Byte bSuccess
  133.     Dim As _Offset hParentStdOut: hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
  134.     Do
  135.         bSuccess = ReadFile(hChildStd_OUT_Rd, _Offset(chBuf), BUFSIZE, _Offset(dwRead), 0)
  136.         If bSuccess = 0 Or dwRead = 0 Then Exit Do
  137.         Print Mid$(chBuf, 1, dwRead)
  138.     Loop
  139.  
  140. Sub ErrorExit (Reason As String)
  141.     Print Reason, GetLastError
  142.     End

Child code:
Code: QB64: [Select]
  1. _Title "Child Process"
  2.  
  3. Const BUFSIZE = 4096
  4. Const STD_OUTPUT_HANDLE = -11
  5. Const STD_INPUT_HANDLE = -10
  6. Const STD_ERROR_HANDLE = -12
  7. Const INVALID_HANDLE_VALUE = -1
  8.  
  9.     Function ReadFile%% (ByVal hFile As _Offset, Byval lpBuffer As _Offset, Byval nNumberOfBytesToRead As Long, Byval lpNumberOfBytesRead As _Offset, Byval lpOverlapped As _Offset)
  10.     Function GetStdHandle%& (ByVal nStdHandle As Long)
  11.     Sub ExitProcess (ByVal uExitCode As _Unsigned Long)
  12.  
  13. Dim As String * BUFSIZE chBuf
  14. Dim As Long dwRead, dwWritten
  15. Dim As _Offset hStdin, hStdout
  16. Dim As _Byte bSuccess
  17.  
  18. hStdin = GetStdHandle(STD_INPUT_HANDLE)
  19.  
  20. If hStdout = INVALID_HANDLE_VALUE Then
  21.     ExitProcess 1
  22.  
  23. Print " ** This is a message from the child process. **"
  24.  
  25.     bSuccess = ReadFile(hStdin, _Offset(chBuf), BUFSIZE, _Offset(dwRead), 0)
  26.     Print Mid$(chBuf, 1, dwRead);
  27.     If bSuccess = 0 Or dwRead = 0 Then
  28.         Exit Do
  29.     End If

And the text file for the test:
 

And a screenshot of how it should look:
 
Screenshot 2021-05-06 162758.png
« Last Edit: May 06, 2021, 04:42:17 pm by SpriggsySpriggs »
Shuwatch!

Offline justsomeguy

  • Newbie
  • Posts: 47
    • View Profile
Re: Child Processes in QB64 using WinAPI
« Reply #1 on: May 06, 2021, 06:07:25 pm »
@SpriggsySpriggs This is very well written code.

I see some tricks that I was unaware of, like ' $If 64BIT Then ... $End If' and using 'len' to find the size of a structure.

This appears to be similar to the 'fork' that you mentioned on one of my posts. Is the intent to use the child processes for extra processing and concurrency?

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Child Processes in QB64 using WinAPI
« Reply #2 on: May 06, 2021, 06:15:44 pm »
@justsomeguy In a sense, yes. These two executables run simultaneously. One could adjust some logic and make them talk back and forth to each other constantly. This is similar to Fellippe's InForm UIEditor. His program uses two executables, launches the child from the parent, and uses TCP/IP communication to send and receive between them.
Shuwatch!

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Child Processes in QB64 using WinAPI
« Reply #3 on: May 07, 2021, 03:59:07 am »
Hi Spriggsy
Cool example!
Thanks to share.
Programming isn't difficult, only it's  consuming time and coffee

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Child Processes in QB64 using WinAPI
« Reply #4 on: May 07, 2021, 04:11:38 am »
@SpriggsySpriggs
sorry but I find a bug!
I think so because I got this result that is quiet different from your result!
see here
 
IloveQB64.jpg


Programming isn't difficult, only it's  consuming time and coffee

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Child Processes in QB64 using WinAPI
« Reply #5 on: May 07, 2021, 07:15:44 am »
@TempodiBasic Glad to see you got it working!
Shuwatch!