I pulled this code from one of my programs. I does work but I may have removed some things that referred to other parts of my program and wouldn't work for you. The key is you have to keep asking windows if the called code is done. Just ask if you have any questions or something doesn't work.
cmdLine := 'your command goes here'.
startUpInfo := OSStartupinfo new.
startUpInfo
dwFlags: 1; "1 - startf_useshowwindow"
wShowWindow: 0. "0 - do not show window"
processInfo := OSProcessInformation new.
os := OSCall new.
startedOk := os
createProcess: nil
lpszCommandLine: cmdLine
lpsaProcess: 0
lpsaThread: 0
fInheritHandles: false
fdwCreate: 0
lpvEnvironment: 0
lpszCurDir: nil
lpsiStartInfo: startUpInfo
lppiProcInfo: processInfo.
startedOk ifTrue: [
[ | exitCodeBytes exitCode cnt exitCodeMsg |
cnt := 0.
exitCodeBytes := ByteArray new: 4.
[
processInfo hProcess getExitCodeProcess: exitCodeBytes.
exitCode := exitCodeBytes int32At: 0.
(exitCode = StillActive) & (cnt < timeLimit).
] whileTrue: [
processInfo hProcess waitForSingleObject: 1000.
cnt := cnt + 1.
].
(exitCode = StillActive) ifTrue: [processInfo hProcess terminateProcess: 666].
processInfo hProcess getExitCodeProcess: exitCodeBytes.
exitCode := exitCodeBytes int32At: 0.
processInfo hProcess closeHandle.
processInfo hThread closeHandle.
exitCodeMsg := 'Command exit code: %1 %2' bindWith: exitCode printString with: ((exitCode = 666) ifTrue: ['(TimedOut - Stopped)'] ifFalse: ['']).
self logVeryImptTcpRequest: exitCodeMsg with: cmdLineForLog forConnection: commandData.
self logCmdResult: 'CmdLog.txt'.
self logCmdResult: 'ErrLog.txt'.
commandRunning := false.
] forkReadyAt: Processor userBackgroundPriority named: 'RunUserCommand'.