0

i'm currently trying to implement a procedure called "GetDosOutput" in Delphi 5/ Delphi 10. In this procedure I want to get the DOS output from a worklist query (via DCMTK toolbox) and display it in a memo. The problem is that just the study instance UID isnt transfered/displayed correctly in the memo. It gets cut of right behind the end and several lines after it get also cut.

I have the following code and the following results from DOS, where everything is displayed correctly, and the memo:

// get output from cmd.exe and display on memo
procedure TForm4.OnReadPipeOutput(Sender: THandle);
var
  BytesRead: DWORD;
  BufferPtr: PAnsiChar; // Pointer to a dynamic array
  BufferSize: Integer; // Size of the dynamic array
  TempStr: AnsiString; // Temporary variable to hold the converted string
  WasOK: Boolean;
begin
  BufferSize := 1024; // Initial size of the dynamic array
  GetMem(BufferPtr, BufferSize); // Allocate memory for the dynamic array
  try
    repeat
      WasOK := ReadFile(THandle(Sender), BufferPtr^, BufferSize, BytesRead, nil);
      if BytesRead > 0 then
      begin
        SetString(TempStr, PAnsiChar(BufferPtr), BytesRead); // Convert the buffer to a string
        //ShowMessage(BufferPtr);
        Memo1.Lines.Add(TempStr); // Append the output to the memo
      end;
    until not WasOK or (BytesRead = 0);
  finally
    FreeMem(BufferPtr); // Free the memory used by the dynamic array
  end;
end;

// send command to cmd.exe, get output from cmd.exe and display on memo
procedure TForm4.GetDosOutput(Command:String);
var
  Security: TSecurityAttributes;
  ReadPipe, WritePipe: THandle;
  ProcessInfo: TProcessInformation;
  StartupInfo: TStartupInfo;
  Handle: Boolean;
begin
  memo1.Clear;
  if command = '' then begin
    Exit;
  end;
  Security.nLength := SizeOf(TSecurityAttributes);
  Security.bInheritHandle := True;
  Security.lpSecurityDescriptor := nil;
  if not CreatePipe(ReadPipe, WritePipe, @Security, 0) then begin
    Exit;
  end;
  try
    FillChar(StartupInfo, SizeOf(StartupInfo), 0);
    StartupInfo.cb := SizeOf(StartupInfo);
    StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
    StartupInfo.wShowWindow := SW_HIDE;
    StartupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
    StartupInfo.hStdOutput := WritePipe;
    StartupInfo.hStdError := WritePipe;

    Handle := CreateProcess(nil, PChar('cmd.exe /c ' + Command), nil, nil, True, CREATE_NEW_CONSOLE, nil, nil, StartupInfo, ProcessInfo);
    CloseHandle(WritePipe);
    if Handle then begin
      try
        // get output from cmd.exe and display on memo
        OnReadPipeOutput(ReadPipe);
        WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
      finally
        CloseHandle(ProcessInfo.hThread);
        CloseHandle(ProcessInfo.hProcess);
      end;
    end;

  finally
    CloseHandle(ReadPipe);
  end;
end;

// query a worklist from HIS
procedure TForm4.WorklistQuery();
var
  Command:String;
  AET,IP,Port,WlQPath: String;
begin
  edtCmd.Text := '';

  AET := Trim(edtAET.Text)+' ';
  IP := Trim(edtIP.Text)+' ';
  Port := Trim(edtPort.Text)+' ';

  WlQPath := '"'+ Trim(edtWlQFilepath.Text) +'"';   //Worklist path

  Command := 'cd /d ' + dcmtkPath +'&&findscu -v --call ' + AET +  IP +  Port +  WlQPath;

  edtCmd.Text := Command;
  GetDosOutput(Command);
end;

Memo: enter image description here

The result should be like the DOS Output:(https://i.stack.imgur.com/lJMCx.png)

I tried a lot e.g. to reproduce the error with the other attributes like patient ID but just the study instance UID with the RV "UI" gives problems.

I appreciate some help (:

Edit: I tried "Memo.SelStart ... etc" from the comment section with following result: 2

Kind regards

Robert
  • 3
  • 3
  • How do you generate the worklist output? And how does the captured string from the pipe look like? – Markus Sabin Aug 30 '23 at 13:41
  • The worklists are given as example .txt data by dcmtk and via a prompt i can convert them to .wl (dicom format) also with dcmtk. I use the binay packages from dcmtk for everything except dos to memo. Do you mean with captured string the memo? – Robert Aug 30 '23 at 13:47
  • 1
    You are not handling the pipe inheritance correctly. Have you read [Creating a Child Process with Redirected Input and Output](https://learn.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output) yet? – Remy Lebeau Aug 30 '23 at 14:33
  • 1
    Also, you are piping arbitrary data to the Memo and not parsing out line breaks etc, so you shouldn't use `Lines.Add()` as that won't preserve the original formatting of the data. Consider just appending new data to the end of the Memo, eg: `Memo1.SelStart := Memo1.GetTextLen; Memo1.SelLength := 0; Memo1.SelText := TempStr;` Otherwise just buffer the data and split out only complete lines to add to the Memo – Remy Lebeau Aug 30 '23 at 14:33
  • Also, have a look at [Programmatically controlling which handles are inherited by new processes in Win32](https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873) and related articles – Remy Lebeau Aug 30 '23 at 14:41
  • @RemyLebeau Thank you so much for your input. The problem with the buffer is that even in the buffer it nearly always cuts of behind the UID. I tried also "Memo1.SelStart ..." etc. but it just moved the uncomplete part of the next lines up behind the UID. I posted a new pic at the end of the original post. I will read both of the mentioned articles, but i´m newish to programming especially delphi and c++ and so it will take some time to understand it. – Robert Aug 31 '23 at 06:17

1 Answers1

2

I tried a lot e.g. to reproduce the error with the other attributes like patient ID but just the study instance UID with the RV "UI" gives problems.

In DICOM, odd-length data element values have to be padded to an even length. For UI (Unique Identifier) values, the padding character is a zero byte (0x00). Since the UI value in your sample (see referenced screenshot) has an odd length, your parsing code might have a problem with the trailing zero byte.

One solution would be to remove the padding before parsing. The easiest way to do this is to add the option +dc (short version of --enable-correction) to your findscu call. By default, option -dc (--disable-correction) is used. See documentation of this tool.

J. Riesmeier
  • 1,641
  • 10
  • 14
  • Oh thank you so much that solved it. I read the documentation and noticed the "+dc" before, but for god knows why didnt try it... This answer relieved a workday of pain. – Robert Aug 31 '23 at 09:07
  • Now if I search for a specific worklist with a worklist mask just including specific Study Instance UID, dcmtk doesn´t filter for it and all worklists get retrieved. Can´t I query just with the Study Instance UID? in the DOS the mask has clearly the UID value in it and nothing else. – Robert Aug 31 '23 at 09:26
  • Filtering is done by the SCP (Modality Worklist Server) and not by the SCU (i.e. findscu). According to DICOM Part 4 Table K.6-1, Study Instance UID (0020,000D)​ is an optional Matching Key. Whether the SCP supports this Matching Key or not should be documented in the corresponding DICOM Conformance Statement. – J. Riesmeier Aug 31 '23 at 09:31
  • Again, thank you so much! – Robert Aug 31 '23 at 09:52