0

I've been trying to resolve this for weeks now. I'm trying to create an app in Delphi that runs in the background and catches any hot key and executes Ctrl + c. So what I did is I catch the hot key for example ALT + right arrow then simulate Ctrl + c and throw the command to the current window.

What I noticed is that when I try sending commands to Firefox like below:

SendMessage(FireFoxHandle, WM_COPY, 0, 0);
sleep(250);
CopiedText := ClipBoard.AsText;

Copying was successful and I get the expected text. But once I used the same line of codes to other windows like chrome(chrome_widget_1) or notepad, I can't get anything. So what I tried is get the child window of the chrome and notepad and try to send the command to the child window.

  1. Got "Chrome Legacy Window" as child of "chrome_widget_1" window but when i try to select a text in the tab, simulate the Ctrl + c, still not working.

  2. Found an example here in stackoverflow for sending wm_copy to notepad's child window which is an edit like the code below:

ParentWindw := FindWindow('Notepad',nil);
if ParentWindow <> 0 then
begin
  ChildWindow := FindWindowEx(ParentWindow, 0, 'Edit', nil);
  SendMessage(ChildWindow, WM_COPY, 0, 0);
  sleep(250);
  CopiedText := ClipBoard.AsText;
end;

The code works but is there any dynamic way to determine the child window that i need to use for the wm_copy command? I'm asking not only for the notepad window but for all possible window that can be used.

Or is there anyway where I can copy the highlighted text in any window Programmatically in Delphi specifically in xe2?

I already researched already like sendinput, keyevents and tried them but no luck. I'm running out of option how to make it work.

Thanks for any help in advance.

  • No, there is no dynamic way. You need knowledge about each particular application. – Andreas Rejbrand Mar 24 '20 at 07:25
  • Nothing in the system guarantees that arbitrary windows will respond to that message – David Heffernan Mar 24 '20 at 07:46
  • Why are you trying to do that anyway? – Olivier Mar 24 '20 at 08:23
  • @Olivier Hi, cause i need to create an app that can simulate ctrl + c, copying the currently highlighted text in any window. – Francis Carillo Mar 24 '20 at 08:28
  • Still not clear why you need that. Would intercepting the standard Ctrl + c be acceptable? – Olivier Mar 24 '20 at 08:53
  • 1
    All the same, sending `WM_COPY` messages isn't going to do it. Perhaps you should ask about your root problem rather than your proposed solution (which cannot work). – David Heffernan Mar 24 '20 at 09:15
  • @FrancisCarillo "*cause i need to create an app that can simulate ctrl + c*" - then you should simulate CTRL-C itself, via [`SendInput()`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput), don't simulate the `WM_COPY` message that MAY OR MAY NOT result from pressing CTRL-C. And not all apps handle `WM_COPY`. – Remy Lebeau Mar 24 '20 at 16:28
  • @DavidHeffernan edited the question. Thanks. – Francis Carillo Mar 25 '20 at 00:40
  • @RemyLebeau already tried sendinput which I found from this link "https://stackoverflow.com/questions/17604833/how-to-simulate-ctrl-c-in-delphi" though David Heffernan said in that link that sendinput will only work for windown with input focus. Also tried this link "https://www.swissdelphicenter.ch/en/showcode.php?id=336" using keybd_event still not able to make it. Any other ideas? – Francis Carillo Mar 26 '20 at 08:39
  • @FrancisCarillo There is no single solution to this. Not all windows support `WM_COPY`, and `CTRL-C` only works for windows with input focus or keyboard hooks. You are kind of in a catch-22 situation. Regarding `WM_COPY`, instead of `FindWindow()`, you might try `GetForegroundWindow()`+`AttachInput()`+`GetActiveWindow()` to know which window to send the message to. And monitor the clipboard so you can detect whether it actually changes in reply to sending `WM_COPY`, if it doesn't then issue `CTRL-C` and check again. `GetClipboardSequenceNumber()` would suffice for that checking. – Remy Lebeau Mar 26 '20 at 18:52

2 Answers2

0

Ok. Found issue which is related to mine here Topic: [SOLVED] Simulate ctrl+c press in active application (Windows). One of the people there suggested to use "MouseAndKeyInput" which can be found and downloaded here BGRA Controls. Inside the download link will be found 5 files namely:

KeyInputIntf.pas
MouseAndKeyInput.pas
MouseInputIntf.pas
WinKeyInput.pas
WinMouseInput.pas

Added those files into my project and declare the "MouseAndKeyInput" to my project's implementation uses. Now in my procedure for simulating the ctr c:

Sleep(250);

KeyInput.Apply([ssCtrl]);
KeyInput.Press('C');
KeyInput.Unapply([ssCtrl]);

Sleep(250);

Memo1.Lines.Add(Clipboard.AsText);

And the text that appears in the memo are the text or lines that I highlighted from the current active application I've been working at the moment.

Was able to do it to google chrome tab and notepad and any other application.

Thanks for everyone who helped here!

0

The MouseAndKeyInput code you presented in your accepted answer is just a wrapper for SendInput() on Windows, which I told you in comments to use 3 days ago. You can (and should) get rid of those 5 .pas files completely and replace those 3 separate KeyInput method calls with 1 call to SendInput(). It can send all 4 key inputs (Ctrl down, C down, C up, Ctrl up) atomically at 1 time (which is important to ensure other events don't get inter-mixed with your events), eg:

var
  Inputs: array[0..3] of TInput;
begin
  ZeroMemory(@Inputs, SizeOf(Inputs));

  Sleep(250);

  Inputs[0].type := INPUT_KEYBOARD;
  Inputs[0].ki.wVk := VK_CONTROL;

  Inputs[1].type := INPUT_KEYBOARD;
  Inputs[1].ki.wVk := Ord('C');

  Inputs[2].type := INPUT_KEYBOARD;
  Inputs[2].ki.wVk := Ord('C');
  Inputs[2].ki.dwFlags := KEYEVENTF_KEYUP;

  Inputs[3].type := INPUT_KEYBOARD;
  Inputs[3].ki.wVk := VK_CONTROL;
  Inputs[3].ki.dwFlags := KEYEVENTF_KEYUP;

  SendInput(4, Inputs, SizeOf(TInput));

  Sleep(250);

  Memo1.Lines.Add(Clipboard.AsText);
end;

Calling SendInput() with its cInputs parameter set to 1 (as KeyInput and MouseInput do) is almost always a bug in practice, and should only be done in rare situations.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770