Question

Below is the complete routine I'm using to send the key Ctrl + Shift + S to a PDF document. It should show the save dialog but fails to do so.

The procedure opens a pdf document residing in sFolder using GetFiles. There is only one pdf doc in sFolder.

As you can see from the commented out lines, I also tried the sndkey32 without success.

procedure TForm1.Button1Click(Sender: TObject);
var
  oBrowser: TBrowseForFolder;
  oList: TStringDynArray;
  sFile: string;
  sFolder: string;
  oShellExecuteInfo: TShellExecuteInfo;
begin
  oBrowser := TBrowseForFolder.Create(self);
  oBrowser.Execute;
  sFolder := oBrowser.Folder;
  oBrowser.Free;
  if DirectoryExists(sFolder) then begin
    oList := TDirectory.GetFiles(sFolder, '*.pdf', TSearchOption.soAllDirectories);
    if Length(oList) > 0 then begin
      for sFile in oList do begin
        FillChar(oShellExecuteInfo, SizeOf(oShellExecuteInfo), 0);
        oShellExecuteInfo.cbSize := SizeOf(TShellExecuteInfo);
        with oShellExecuteInfo do begin
          fMask := SEE_MASK_NOCLOSEPROCESS;
          Wnd := Application.Handle;
          lpFile := PChar(sFile);
          nShow := SW_SHOWNORMAL;
        end;
        if ShellExecuteEx(@oShellExecuteInfo) then begin
          ShowWindow(oShellExecuteInfo.Wnd, 1);
          SetForegroundWindow(oShellExecuteInfo.Wnd);
          Winapi.Windows.SetFocus(oShellExecuteInfo.Wnd);
          SendKey(Ord('s'), [ssCtrl, ssShift], False);
//        if sndkey32.AppActivate('adobe') then
//        sndkey32.SendKeys('^+S', False);
        end;
      end;
    end;
  end;
end;

procedure TForm1.SendKey(key: Word; const shift: TShiftState; specialkey: Boolean);
type
  TShiftKeyInfo = record
    shift: Byte;
    vkey: Byte;
  end;
  ByteSet = set of 0 .. 7;
const
  shiftkeys: array [1 .. 3] of TShiftKeyInfo = ((shift: Ord(ssCtrl); vkey: VK_CONTROL), (shift: Ord(ssShift); vkey: VK_SHIFT), (shift: Ord(ssAlt); vkey: VK_MENU));
var
  flag: DWORD;
  bShift: ByteSet absolute shift;
  j: Integer;
begin
  for j := 1 to 3 do begin
    if shiftkeys[j].shift in bShift then keybd_event(shiftkeys[j].vkey, MapVirtualKey(shiftkeys[j].vkey, 0), 0, 0);
  end;
  if specialkey then flag := KEYEVENTF_EXTENDEDKEY
  else flag := 0;
  keybd_event(key, MapVirtualKey(key, 0), flag, 0);
  flag := flag or KEYEVENTF_KEYUP;
  keybd_event(key, MapVirtualKey(key, 0), flag, 0);
  for j := 3 downto 1 do begin
    if shiftkeys[j].shift in bShift then keybd_event(shiftkeys[j].vkey, MapVirtualKey(shiftkeys[j].vkey, 0), KEYEVENTF_KEYUP, 0);
  end;
end;
Was it helpful?

Solution

The window oShellExecuteInfo.Wnd is a window in your Delphi process. You assign it as Application.Handle. You seem to be hoping that it will be the main window of the PDF viewer but that's not the case.

So you need to find the main window of the PDF viewer. That involves a call to EnumerateWindows to get all top level windows. Then, for each one, use GetWindowThreadProcessId to test whether or not the window is owned by the PDF viewer process.

Some other comments:

  1. You neglect error checking when calling API functions.
  2. You should use SendInput rather than keybd_event.
  3. You leak the process handle returned by ShellExecuteEx.
  4. It is possible that ShellExecuteEx does not return a process handle at all. That depends on how the file association is setup, and whether or not Acrobat was already running.
  5. You may need to wait until the new process has finished starting up before you send input.
  6. Your program seems to assume that the installed PDF viewer is Acrobat. What if it is not?
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top