Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending CTRL-S message to a window

Tags:

c#

pinvoke

I want to save a TextPad window using C# code; I can find out the handle of the window but not sure how to send CTRL - S to that window. I want to use P/Invoke API to achieve this. Also, that TextPad window will be inactive, because my application will be active at that time.

[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);

Send Ctrl+Up to a window

I looked at this discussion which is very similar to my problem. I understand logically that I have to do 4 send messages like below

  • KeyDown CTRL key
  • KeyDown S key
  • KeyUp S key
  • KeyUp CTRL key

I am not sure how to send the right parameters to the SendMessage. To close the window I use

SendMessage(hWnd, 0x0010, 0, 0);

I got this from MSDN library.

Can you please direct me to some link which tells me the hexadecimal for keys in the keyboard and explains what the last two parameters represent?

UPDATE - 1
Using spy++ I find these events generated with I press CTRL-S on Notepad window

1. WM_KEYDOWN nVirtKey:VK_Control, 
2. WM_KEYDOWN nVirtKey:'S' .. some other messates ..
3. WM_KEYUP nVirtKey:VK_Control. 
4. WM_KEYUP nVirtKey:'S'. 

UPDATE - 2


       private IntPtr startnotepad() {
            ProcessStartInfo psi = new ProcessStartInfo();
            psi.FileName = @"notepad.exe";
            String fileName = baseDirectory + textbox1.Text;
            psi.Arguments = @"""" + fileName + @"""";
            psi.WindowStyle = ProcessWindowStyle.Minimized;
            Process p = Process.Start(psi); 
            return p.MainWindowHandle;
        }

        private void SaveNotepad(IntPtr handle)
        {
            IntPtr handle = GetWindow(handle, 5); // get the keyboard focus handle
            // verified the handle with Spy ++.
            SendMessage(handle, 0x0100, 0x11, 0); // keydown ctrl
            SendMessage(handle, 0x0100, 0x53, 0); // keydown S
            //SendMessage(handle, 0x0101, 0x11, 0); --- I tried keeping "Keyup Ctrl" here as well.
            SendMessage(handle, 0x0101, 0x53, 0); // keyup s
            SendMessage(handle, 0x0101, 0x11, 0); // keyup ctrl            
        }

This code does not work (save part of it) even though I am able to see the WM_KEYDOWN - CTRL .. WM_KEYDOWN - 'S' .. KEYUP 'S' and KEYUP CTRL being sent to the right window in spy++. Can anyone comment what is wrong with this code? Or any suggestions if I am doing something really stupid.

UPDATE 3

I should be using PostMessage instead of SendMessage as @Hans suggested in his comments.


 [DllImport("user32.dll")]
 public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);  

        private void Save(IntPtr mainWindowHandle)
        {               
            IntPtr handle = GetWindow(mainWindowHandle, 5); // getChild window
            IntPtr CTRL_KEY = new IntPtr(0x11);
            uint KEY_DOWN = 0x0100;
            uint KEY_UP = 0x0101;
            IntPtr S_KEY = new IntPtr(0x53);

            //SetForegroundWindow(p.MainWindowHandle);
            PostMessage(handle, KEY_DOWN, CTRL_KEY, IntPtr.Zero);
            PostMessage(handle, KEY_DOWN, S_KEY, IntPtr.Zero);
            PostMessage(handle, KEY_UP, S_KEY, IntPtr.Zero);
            PostMessage(handle, KEY_UP, CTRL_KEY, IntPtr.Zero);                      
        } 

The above code instead of CTRL+S sends CTRL and S to Notepad window. So, I can see two "S" being thrown at Notepad. Looking at the real message generated when we press CTRL+S, I see that last parameter of my Postmessage is wrong. It should be something like (for KEY UP CTRL key "C01F0001")

Can you please tell me how to pass this hexadecimal as last parameter of postmessage?


// does not work
PostMessage(handle, KEY_UP, CTRL_KEY, new IntPtr(0xC01F0001);

Update 4 : I think we cannot send "hot key" messages to a minimized window. using Post Message.
This below code works with SendInput: but it will pop up the window, which kind a mar the purpose. Anyways, just wanted to update this thread.


 private void Save_Notepad(IntPtr mainWindowHandle) {
            //SetActiveWindow(mainWindowHandle);
            //SetFocus(GetWindow(mainWindowHandle, 5));
            ShowWindow(mainWindowHandle, 1); // show window 
            SetForegroundWindow(mainWindowHandle);
            //SetActiveWindow(mainWindowHandle);
            //SetFocus(GetWindow(mainWindowHandle, 5));
            //IntPtr returnvalue = SetFocus(mainWindowHandle);
            uint intReturn;
            INPUT structInput;
            structInput = new INPUT();
            structInput.type = 1;// keyboard input
            // key down
            structInput.ki.wScan = 0x1D;
            structInput.ki.time = 0;
            structInput.ki.dwFlags = 0;
            // key down Ctrl
            structInput.ki.wVk = 0x11;  //0x1D; //
            intReturn = SendInput(1, ref structInput, Marshal.SizeOf(new INPUT()));
            // key down S
            structInput.ki.wScan = 0x1F;
            structInput.ki.wVk = 0x53; //0x41;//0x53;  //0x1F;//
            intReturn = SendInput(1, ref structInput, Marshal.SizeOf(new INPUT()));
            // key up 
            structInput.ki.dwFlags = 0x0002; // key up
            // key up S
            intReturn = SendInput(1, ref structInput, Marshal.SizeOf(typeof(INPUT)));
            // key up CTRL
            structInput.ki.wVk = 0x11;  //0x1D; //
            intReturn = SendInput(1, ref structInput, Marshal.SizeOf(new INPUT()));
            //ShowWindow(mainWindowHandle, 2); // minimize it again 
        }

After CTRL + S I can minimize the window but then it will create a flash. Can anyone suggest if I can achieve this functionality without the "FLASH" (at least)?
UPDATE 5 : using WM_SYSCOMMAND


IntPtr handle = GetWindow(mainWindowHandle, 5);
// send Alt F message to Notepad.
// http://msdn.microsoft.com/en-us/library/ms646360(v=VS.85).aspx
// I can see this is working.
PostMessage(handle, 0x0112, 0xF100, 'f');  // send WM_SYSCOMMAND
// how to send s on this window ??
// I have tried things which I have learned so far. 

I just need to send S messages to file menu which will pop up as a result of WM_SYSCOMMAND. Can anyone point me to a documentation to how to do that?


PostMessage(handle, KEY_DOWN, S_KEY, 0x1F0001); // POST does not work
// tried with mainwindowhandle as well
PostMessage(handle, 0x111, 'S', 0); // WM_COMMAND does not work
PostMessage(handle, 0x0112, 0xF100, 's');  // WM_SYSCOMMAND does not work (and does not make sence either)

Final Update

I was not able to send ^S message to a minimized window. I am using sendkeys(^s) every 2-3 seconds when the focus is on an editor.

-- Karephul

like image 676
karephul Avatar asked Feb 28 '11 16:02

karephul


2 Answers

You cannot use SendMessage (or PostMessage, the correct one) to simulate the state of the modifiers keys, like CTRL. You must use SendInput().

A keystroke like Ctrl+S is not untypically translated into a WM_COMMAND message. Use the Spy++ tool to see what happens when you type Ctrl+S by hand. If you see WM_COMMAND then you're golden, you can use SendMessage() to send that message. This will of course only work on a specific process.

like image 191
Hans Passant Avatar answered Oct 03 '22 16:10

Hans Passant


I needed to do exactly what you need to do, call a Ctrl+S in some background window, after about a day of research there seem to be many ways of achieving this.

For instance to press Alt-F then a 'S' you can call:

PostMessage(handle, 0x0112, 0xF100, 0x0046);
PostMessage(handle, 0x0102, 0x0053, 0);

The Alt-F part works on background windows, while the subsequent 'S' does not.

Another way of doing this is with WM_COMMAND and WM_MENUSELECT, MF_MOUSESELECT:

IntPtr menu = GetMenu(handle);
IntPtr subMenu = GetSubMenu(menu, 0);//0 = first menu item
uint menuItemID = GetMenuItemID(subMenu, 2);//2 = second item in submenu
SendMessage(handle, 0x0111, 0x20000000 + menuItemID, menu);

Finally and somewhat ironically the simples solution is to call WM_COMMAND with the menuItemID:

PostMessage(handle, 0x0111, 0x0003, 0x0);

The 0x0003 (save in notepad) is determined by the app and it's menu structure, you can get this code by listening to WM_COMMAND in spy++ on the window while you either use the combo key combination or press the mouse button on the menu command.

It seems it's also possible to use WM_SYSKEYUP/DOWN and WM_MENUCOMMAND, also keys like Ctrl-C, Ctrl-V have constant defined values and can be passed as one character, but only to apps that listen to these hardcoded chars...

Thanks for the starting point.

like image 41
critic Avatar answered Oct 03 '22 16:10

critic