SetText of textbox in external app. Win32 API - c#

Using Winspector I've found out the ID of the child textbox I want to change is 114. Why isn't this code changing the text of the TextBox?
[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int Param, string s);
const int WM_SETTEXT = 0x000c;
private void SetTextt(IntPtr hWnd, string text)
{
IntPtr boxHwnd = GetDlgItem(hWnd, 114);
SendMessage(boxHwnd, WM_SETTEXT, 0, text);
}

The following is what I've used successfully for that purpose w/ my GetLastError error checking removed/disabled:
[DllImport("user32.dll", SetLastError = false)]
public static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, string lParam);
public const uint WM_SETTEXT = 0x000C;
private void InteropSetText(IntPtr iptrHWndDialog, int iControlID, string strTextToSet)
{
IntPtr iptrHWndControl = GetDlgItem(iptrHWndDialog, iControlID);
HandleRef hrefHWndTarget = new HandleRef(null, iptrHWndControl);
SendMessage(hrefHWndTarget, WM_SETTEXT, IntPtr.Zero, strTextToSet);
}
I've tested this code and it works, so if it fails for you, you need to be sure that you are using the right window handle (the handle of the Dialog box itself) and the right control ID. Also try something simple like editing the Find dialog in Notepad.
I can't comment yet in the post regarding using (char *) but it's not necessary. See the second C# overload in p/Invoke SendMessage. You can pass String or StringBuilder directly into SendMessage.
I additionally note that you say that your control ID is 114. Are you certain WinSpector gave you that value in base 10? Because you are feeding it to GetDlgItem as a base 10 number. I use Spy++ for this and it returns control IDs in base 16. In that case you would use:
IntPtr boxHwnd = GetDlgItem(hWnd, 0x0114);

Please convert your control id (obtained from spy ++) from Hexdecimal Number to Decimal Number and pass that value to the GetDlgItem function.With this
you will get the handle of Text box.This worked for me.
[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int Param, string s);
const int WM_SETTEXT = 0x000c;
private void SetTextt(IntPtr hWnd, string text)
{
IntPtr boxHwnd = GetDlgItem(hWnd, 114);
SendMessage(boxHwnd, WM_SETTEXT, 0, text);
}

Are you sure you are passing text right? SendMessage last param should be a pointer to char* containing text you want to set.
Look at my "crude hack" of setting text in
How to get selected cells from TDBGrid in Delphi 5
this is done in Delphi 5, where PChar is char* alias, and I simply cast it as int (Integer in Delphi).

You must make sure that "text" is allocated in the external app's memory space. You will not be able to allocate text in the caller app and pass it to another app as each of them will have their own private memory space.

Related

Russian characters in with SetWindowTextW in C#

I basically use this:
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetWindowTextW(IntPtr hWnd, string lpString);
SetWindowTextW(HWnd, "лфорфпылвоарпфлыьтвмлафывафыва")
to set the title of a window but the title ends up as:
ð╗Ðäð¥ÐÇÐäð┐Ðïð╗ð▓ð¥ð░ÐÇð┐Ðäð╗ÐïÐîÐéð▓ð╝ð╗ð░ÐäÐïð▓ð░ÐäÐïð▓ð░
I think that this has something to do with incorrect encoding.
Interestingly it seems to work if I type the string into a TextBox and send the property textbox.Text to the same function.
I get a similar string from an API so just typing it in and saving the output is not possible.
(I know the text in the code above is just random characters but the result is a similar mess with actual words)
You can directly use SendMessage to send a WM_SETTEXT message to a foreign Window:
From the Remarks section of SetWindowText:
If the target window is owned by the current process, SetWindowText
causes a WM_SETTEXT message to be sent to the specified window or
control. If the control is a list box control created with the
WS_CAPTION style, however, SetWindowText sets the text for the
control, not for the list box entries.
To set the text of a control in another process, send the WM_SETTEXT
message directly instead of calling SetWindowText.
Charset = CharSet.Auto is used to correctly marshal the string. The target operating system requirements are determined automatically (C# would mark it as ANSI otherwise).
See also: Charsets and marshaling.
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int SendMessage(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
const uint WM_SETTEXT = 0X000C;
IntPtr hWnd = [TheWindowHandle];
IntPtr russianPtr = Marshal.StringToHGlobalUni("лфорфпылвоарпфлыьтвмлафывафыва");
SendMessage(hWnd, WM_SETTEXT, IntPtr.Zero, russianPtr);
Marshal.FreeHGlobal(russianPtr);
Try this, with the L for literal prefixing.
SetWindowTextW(HWnd, L"лфорфпылвоарпфлыьтвмлафывафыва")

How do I load hex value from program settings or INI (custom class) in C# as an int?

I am using C# application to monitor another program, and simply click a button when inactivity is detected.
I am using Pinvoke and that works great. My issue is loading the button control ID from an INI which is in hex format. inif simply reads INI file for hex string ("000003F2").
//Load control id from INI file
public int m_button_id;
int m_button_id = Int32.Parse(inif.Read("MMonitor", "button_id"));
Now I need this to work with the following...
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr GetDlgItem(IntPtr hWnd, int nIDDlgItem);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
//Click Button
IntPtr hWndButton = GetDlgItem(m_handle, m_button_id);
int wParam = (BN_CLICKED << 16) | (m_button_id & 0xffff);
SendMessage(m_handle, WM_COMMAND, wParam, hWndButton);
m_button_id has to be in the format of hex not decimal. It should load this "000003F2" from INI and set it equal to m_button_id. I am having so many issues due to it being a string.
This works fine if I set manually...
public int m_button_id = 0x3F2;
Use Convert.ToInt32(hexString, 16)
or int.Parse(hexString, System.Globalization.NumberStyles.HexNumber)

How to use SendMessage to paste something to a different window

I am using the following SendMessage function to send/paste text to a different application.
But in that function I have to give the name of the window from the other application.
How can I change this to get the current active window and paste in the code there?
Code:
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPStr)] string lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
public const int WM_PASTE = 0x0302;
IntPtr windowHandle = FindWindow("NOTEPAD", null);
IntPtr editHandle = FindWindowEx(windowHandle, IntPtr.Zero, "EDIT", null);
string textToSendToFile = "Input here your text";
Clipboard.SetText("Test");
SendMessage((int)editHandle, WM_PASTE, 0, textToSendToFile);
I also got this but I do not really know how to combine this with the code above...
[DllImportAttribute("user32.dll", EntryPoint = "GetForegroundWindow")]
public static extern IntPtr GetForegroundWindow();
[DllImportAttribute("user32.dll", EntryPoint = "GetWindowThreadProcessId")]
public static extern uint GetWindowThreadProcessId([InAttribute()] IntPtr hWnd, IntPtr lpdwProcessId);
IntPtr hWndForegroundWindow = GetForegroundWindow();
uint activeThreadID = GetWindowThreadProcessId(hWndForegroundWindow, IntPtr.Zero);
The WM_PASTE message does not use the parameters. It's just an instruction to the recipient to take the contents of the clipboard and paste them. So if you wish the recipient to do anything, you'll need to populate the clipboard first.
If you don't wish to pollute the clipboard, and you should not since it belongs to the user, then you can send an EM_REPLACESEL message passing the text in lParam.
If you want to find the window which the user is currently working on, use GetForegroundWindow.
However, rather than faking low level messages, best of all would be to use the automation API.

Hooking extTextOut problems

I am working on a dll injection soft in c#, the injected dll is also in c# and i'am using pinvoke for certain system functions.
When using extTextOut i get the string scrambled and the lines get mixed together
What am i doing wrong?
I hooked extTextOut using EasyHook from codeplex.com like this:
try
{
CreateFileHook = LocalHook.Create(
LocalHook.GetProcAddress("gdi32.dll", "ExtTextOutW"),
new DExtTextOutW(ExtTextOutW_Hooked),
this);
CreateFileHook.ThreadACL.SetExclusiveACL(new Int32[1]);
}
and my extTextOut method is
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern bool ExtTextOutW(IntPtr hdc,
int X,
int Y,
uint fuOptions,
[In] ref RECT lprc,
string lpString,
uint cbCount,
[In] IntPtr lpDx);
static bool ExtTextOutW_Hooked(
IntPtr hdc,
int X,
int Y,
uint fuOptions,
[In] ref RECT lprc,
string lpString,
uint cbCount,
[In] IntPtr lpDx)
{
try
{
DemoInjection This = (DemoInjection)HookRuntimeInfo.Callback;
lock (This.Queue)
{
This.Queue.Push(lpString);
}
}
catch
{
}
return ExtTextOutW(
hdc,
X,
Y,
fuOptions,
ref lprc,
lpString,
cbCount,
lpDx
);
}
And another question if i may. How can i constantly monitor a window which is out of focus or minimized(using this approach it does not work properly)
Thanks a lot!
If I understand correctly what you meant by "string scrambled and the lines get mixed" than there are two issues that might help you:
The string could be output as Glyphs indices and not as Chars (therefore will appear as scrambled text).
You should refer only to the amount of chars supplied by cbCount param other chars in the string may be "garbage" chars.
Hope that helped.

C# - Sending messages to Google Chrome from C# application

I've been searching around, and I haven't found how I would do this from C#.
I was wanting to make it so I could tell Google Chrome to go Forward, Back, Open New Tab, Close Tab, Open New Window, and Close Window from my C# application.
I did something similar with WinAmp using
[DllImport("user32", EntryPoint = "SendMessageA")]
private static extern int SendMessage(int Hwnd, int wMsg, int wParam, int lParam);
and a a few others. But I don't know what message to send or how to find what window to pass it to, or anything.
So could someone show me how I would send those 6 commands to Chrome from C#? thanks
EDIT:
Ok, I'm getting voted down, so maybe I wasn't clear enough, or people are assuming I didn't try to figure this out on my own.
First off, I'm not very good with the whole DllImport stuff. I'm still learning how it all works.
I found how to do the same idea in winamp a few years ago, and I was looking at my code. I made it so I could skip a song, go back, play, pause, and stop winamp from my C# code. I started by importing:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow([MarshalAs(UnmanagedType.LPTStr)] string lpClassName, [MarshalAs(UnmanagedType.LPTStr)] string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SendMessageA(IntPtr hwnd, int wMsg, int wParam, uint lParam);
[DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern int GetWindowText(IntPtr hwnd, string lpString, int cch);
[DllImport("user32", EntryPoint = "FindWindowExA")]
private static extern int FindWindowEx(int hWnd1, int hWnd2, string lpsz1, string lpsz2);
[DllImport("user32", EntryPoint = "SendMessageA")]
private static extern int SendMessage(int Hwnd, int wMsg, int wParam, int lParam);
Then the code I found to use this used these constants for the messages I send.
const int WM_COMMAND = 0x111;
const int WA_NOTHING = 0;
const int WA_PREVTRACK = 40044;
const int WA_PLAY = 40045;
const int WA_PAUSE = 40046;
const int WA_STOP = 40047;
const int WA_NEXTTRACK = 40048;
const int WA_VOLUMEUP = 40058;
const int WA_VOLUMEDOWN = 40059;
const int WINAMP_FFWD5S = 40060;
const int WINAMP_REW5S = 40061;
I would get the hwnd (the program to send the message to) by:
IntPtr hwnd = FindWindow(m_windowName, null);
then I would send a message to that program:
SendMessageA(hwnd, WM_COMMAND, WA_STOP, WA_NOTHING);
I assume that I would do something very similar to this for Google Chrome. but I don't know what some of those values should be, and I googled around trying to find the answer, but I couldn't, which is why I asked here. So my question is how do I get the values for:
m_windowName and WM_COMMAND
and then, the values for the different commands, forward, back, new tab, close tab, new window, close window?
Start your research at http://dev.chromium.org/developers
EDIT: Sending a message to a window is only half of the work. The window has to respond to that message and act accordingly. If that window doesn't know about a message or doesn't care at all you have no chance to control it by sending window messages.
You're looking at an implementation detail on how you remote controlled Winamp. Sending messages is just one way to do it and it's the way the Winamp developers chose. Those messages you're using are user defined messages that have a specific meaning only to Winamp.
What you have to do in the first step is to find out if Chromium supports some kind of remote controlling and what those mechanisms are.
You can get the window name easily using Visual Studio's Spy++ and pressing CTRL+F, then finding chrome. I tried it and got
"Chrome_VistaFrame" for the out window. The actual window with the webpage in is "Chrome_RenderWidgetHostHWND".
As far as WM_COMMAND goes - you'll need to experiment. You'll obviously want to send button clicks (WM_MOUSEDOWN of the top off my head). As the back,forward buttons aren't their own windows, you'll need to figure out how to do this with simulating a mouse click at a certain x,y position so chrome knows what you're doing. Or you could send the keyboard shortcut equivalent for back/forward and so on.
An example I wrote a while ago does this with trillian and winamp: sending messages to windows via c# and winapi
There's also tools out there to macro out this kind of thing already, using a scripting language - autoit is one I've used: autoit.com
Ok, here's what I've got so far... I kinda know what I need to do, but it's just a matter of doing it now...
Here's the window from Spy++, I locked onto the Chrome_RenderWidgetHostHWND and clicked the Back button on my keyboard. Here's what I got:
So here's my assumptions, and I've been playing with this forever now, I just can't figure out the values.
IntPtr hWnd = FindWindow("Chrome_RenderWidgetHostHWND", null);
SendMessage(hWnd, WM_KEYDOWN, VK_BROWSER_BACK, 0);
SendMessage(hWnd, WM_KEYUP, VK_BROWSER_BACK, 0);
Now, I just don't know what I should make the WM_KEYDOWN/UP values or the VK_BROWSER_BACK/FORWARD values...
I tried this:
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int VK_BROWSER_BACK = 0x6A;
const int VK_BROWSER_FORWARD = 0x69;
The latter two values I got from the image I just showed, the ScanCodes for those two keys. I don't know if I did it right though. The former two values I got after searching google for the WM_KEYDOWN value, and someone used &H100 and &H101 for the two values. I've tried several other random ideas I've seen floating around. I just can't figure this out.
Oh, and here's the SendMessage method
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, uint lParam);
This is a great site for interop constants:
pinvoke
Another way of finding the values is to search koders.com, using C# as the language, for WM_KEYDOWN or the constant you're after:
Koders.com search
&H values look like that's from VB(6). pinvoke and koders both return results for VK_BROWSER_FORWARD,
private const UInt32 WM_KEYDOWN = 0x0100;
private const UInt32 WM_KEYUP = 0x0101;
public const ushort VK_BROWSER_BACK = 0xA6;
public const ushort VK_BROWSER_FORWARD = 0xA7;
public const ushort VK_BROWSER_REFRESH = 0xA8;
public const ushort VK_BROWSER_STOP = 0xA9;
public const ushort VK_BROWSER_SEARCH = 0xAA;
public const ushort VK_BROWSER_FAVORITES = 0xAB;
public const ushort VK_BROWSER_HOME = 0xAC;
(It's funny how many wrong defintions of VK constants are floating about, considering VK_* are 1 byte 0-255 values, and people have made them uints).
Looks slightly different from your consts. I think the function you're after is SendInput (but I haven't tried it) as it's a virtual key.
[DllImport("User32.dll")]
private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] KEYBOARD_INPUT[] input, int structSize);
Explanation about the parameters:
Parameters
nInputs- Number of structures in the pInputs array.
pInputs - Pointer to an array of INPUT structures. Each structure represents an event to be inserted into the keyboard or mouse input stream.
cbSize - Specifies the size, in bytes, of an INPUT structure. If cbSize is not the size of an INPUT structure, the function fails.
This needs a KEYBOARD_INPUT type:
[StructLayout(LayoutKind.Sequential)]
public struct KEYBOARD_INPUT
{
public uint type;
public ushort vk;
public ushort scanCode;
public uint flags;
public uint time;
public uint extrainfo;
public uint padding1;
public uint padding2;
}
And finally a sample, which I haven't tested if it works:
/*
typedef struct tagKEYBDINPUT {
WORD wVk;
WORD wScan;
DWORD dwFlags;
DWORD time;
ULONG_PTR dwExtraInfo;
} KEYBDINPUT, *PKEYBDINPUT;
*/
public static void sendKey(int scanCode, bool press)
{
KEYBOARD_INPUT[] input = new KEYBOARD_INPUT[1];
input[0] = new KEYBOARD_INPUT();
input[0].type = INPUT_KEYBOARD;
input[0].vk = VK_BROWSER_BACK;
uint result = SendInput(1, input, Marshal.SizeOf(input[0]));
}
Also you'll need to focus the Chrome window using SetForegroundWindow

Categories

Resources