从C#发送密钥以中断Fortran进程

时间:2015-07-23 08:50:13

标签: c# fortran interop intel-fortran

我需要使用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程序使用长时间运行的分析循环,可以通过发送EsqQ等密钥来中断。我被告知这个中断功能是使用英特尔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());
}

1 个答案:

答案 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