我需要使用c#包装器与命令行Fortran app进行通信。 Fortran进程使用以下c#代码启动。
var process = new Process();
process.StartInfo = new ProcessStartInfo(pathToFortranExe)
{
WorkingDirectory = directory,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
...
//listen for prompts from the Fortran program
//and send replies using standardInput as follows
process.StandardInput.WriteLine(data);
当Fortran程序在命令行上等待用户输入时,我可以使用上面的代码成功发送消息。
现在问题就在于此。 Fortran程序使用长时间运行的分析循环,可以通过发送Esq
或Q
等密钥来中断。我被告知这个中断功能是使用英特尔Fortran命令PEEKCHARQQ在Fortran代码中实现的。当我尝试使用StandardInput从c#触发这些键时,Fortran程序会忽略它们。要发送这些中断信号,我使用:
char key = 'q'
process.StandardInput.Write(key);
//Note that StandardInput.AutoFlush==true
我也通过pinvoke尝试了SendMessage,但到目前为止还没有运气:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const UInt32 WM_KEYDOWN = 0x0100;
private const UInt32 WM_KEYUP = 0x0101;
public static void SendKey(Process process, char key)
{
var keyCode = (IntPtr)key;
var hWnd = process.Handle;
SendMessage(hWnd, WM_KEYDOWN, keyCode, IntPtr.Zero);
SendMessage(hWnd, WM_KEYUP, keyCode, IntPtr.Zero);
}
所以问题是:是否还有其他方法可以将键放入键盘缓冲区,以便在Fortran进程中拾取PEEKCHARQQ?或者我在这里可能遗失的任何其他内容?
更新1:
我也尝试过WriteConsoleInput,但我认为我没有正确的句柄:
var keyCode = (short)key;
var hWnd = process.Handle;
INPUT_RECORD[] lpBuffer = new INPUT_RECORD[1];
lpBuffer[0].KeyEvent.wVirtualKeyCode = keyCode;
int nLength = lpBuffer.Length;
int lpNumberOfEventsWritten;
if (!WriteConsoleInput(
hWnd,
lpBuffer,
nLength,
out lpNumberOfEventsWritten))
{
//this results error code 6: Invalid handle
Console.WriteLine("Error: {0}", GetLastError());
}
答案 0 :(得分:1)
您可以使用带有WriteConsoleInput API的CONIN$
设备句柄写入子进程的控制台输入缓冲区。
以下两个Fortran程序演示。子进程的程序:
PROGRAM peek_a_boo
USE IFCORE
IMPLICIT NONE
CHARACTER(*), PARAMETER :: fmt = "('Press any key!')"
PRINT fmt
DO WHILE (.NOT. PEEKCHARQQ())
CALL SLEEPQQ(2000)
PRINT fmt
END DO
END PROGRAM peek_a_boo
显示Windows API调用的程序。你可以将它翻译成漂浮在船上的任何语言。
PROGRAM peek_parent
USE IFWIN
IMPLICIT NONE
INTEGER(BOOL) :: api_result
INTEGER(HANDLE) :: console_input
TYPE(T_STARTUPINFO) :: startup_info
TYPE(T_PROCESS_INFORMATION) :: process_info
TYPE(T_INPUT_RECORD) :: input_record
INTEGER(DWORD) :: events_written
INTEGER(DWORD) :: wait_event
! Create the child process.
CALL ZeroMemory(LOC(startup_info), SIZEOF(startup_info))
startup_info%cb = SIZEOF(startup_info)
api_result = CreateProcess( &
'peek_a_boo.exe', &
NULL, & ! command line.
NULL, & ! process security attributes.
NULL, & ! thread security attributes.
.FALSE., & ! inherit handles.
0_DWORD, &
NULL, & ! environment.
NULL, & ! current directory.
startup_info, &
process_info )
IF (api_result == 0) THEN
ERROR STOP 'Couldn''t start it :('
END IF
api_result = CloseHandle(process_info%hThread)
! Let the child run for a bit.
CALL SLEEPQQ(5000)
! Get a handle to our console input buffer.
console_input = CreateFile( &
'CONIN$', &
GENERIC_WRITE, &
IANY([FILE_SHARE_READ, FILE_SHARE_WRITE]), &
NULL, & ! security attrs
OPEN_EXISTING, &
0_DWORD, & ! attributes
NULL ) ! template file
IF (console_input == 0) THEN
ERROR STOP 'Couldn''t open it :('
END IF
! Poke something into the buffer.
input_record%EventType = KEY_EVENT
input_record%KeyEvent%bKeyDown = .TRUE.
input_record%KeyEvent%wRepeatCount = 1
input_record%KeyEvent%wVirtualKeyCode = INT(Z'51', WORD)
input_record%KeyEvent%wVirtualScanCode = INT(Z'51', WORD)
input_record%KeyEvent%AsciiChar = 'Q'
input_record%KeyEvent%dwControlKeyState = 0
api_result = WriteConsoleInput( &
console_input, &
input_record, &
1, &
LOC(events_written) )
! Wait for the child to terminate.
wait_event = WaitForSingleObject(process_info%hProcess, INFINITE)
api_result = CloseHandle(console_input)
api_result = CloseHandle(process_info%hProcess)
END PROGRAM peek_parent