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"лфорфпылвоарпфлыьтвмлафывафыва")
Related
In my application i am using SendKeys.SendWait to send text to screen:
SendKeys.SendWait("password");
The text is on English but when the keyboard set to other language the text that SendKeys.SendWait type is set in other language and not in English
Any suggestions how to make sure that the text will set only in English ?
I did a quick test using SendKeys.Send to send text to a couple of input fields. It sends the same text regardless of whether I have the keyboard in English or another language, so I'm not sure why you see a different result. Example:
SendKeys.Send("username");
SendKeys.Send("{TAB}");
SendKeys.Send("påsswørd");
SendKeys.SendWait("{ENTER}");
One possibility is that you could change the keyboard to English temporarily before calling SendKeys, then set it back to whatever it was before. There is an excellent example of the technique in this answer.
Another option is to use Win32 API functions to send messages to the window. The problem will be how to find the right windows to send the text to though. I'm not sure it could be done reliably. Here's an example (not tested):
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, int wParam, string lParam);
// Windows message constants
const int WM_SETTEXT = 0x000C;
public void DoLogin(string username, string password)
{
// Get handle for current active window
IntPtr hWndMain = GetForegroundWindow();
if (!hWndMain.Equals(IntPtr.Zero))
{
IntPtr hWnd;
// Here you would need to find the username text input window
if ((hWnd = FindWindowEx(hWndMain, IntPtr.Zero, "UserName", "")) != IntPtr.Zero)
// Send the username text to the active window
SendMessage(hWnd, WM_SETTEXT, 0, username);
// Here you would need to find the password text input window
if ((hWnd = FindWindowEx(hWndMain, IntPtr.Zero, "Password", "")) != IntPtr.Zero)
// Send the password text
SendMessage(hWnd, WM_SETTEXT, 0, password);
// Send ENTER key to invoke login
SendKeys.SendWait("{ENTER}");
}
}
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)
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.
I've been wondering how to do this for ages. I'm creating a little app, and I need to figure out how many apps or windows are displayed in the TaskBar.
I've yet to find any info on this at all, I'd appreciate any help at all.
Thank you :)
Here is an article that shows how to get the windows, that are shown when you are using the ALT+TAB key combination.
Basically, you will get the same windows that are shown in the taskbar (unless it is a tool window that is not displayed), but then again, you can always check against WS_EX_TOOLWINDOW (not shown) and WS_EX_APPWINDOW (shown).
You may have a look at my previous answer here; the main difference here is that you just have to count the windows that match the given requirements.
As other's have said you need to enumerate through the windows using the Win32 EnumWindows function, and get your count that way.
You can also enumerate through processes using Process.GetProcesses(); However windows like explorer windows which are not a separate process will not show up in that list.
int appCount = 0;
public bool EnumerateWindows(IntPtr hwnd, IntPtr lParam)
{
if (IsWindowVisible(hwnd))
{
StringBuilder sb = new StringBuilder();
string text = "";
GetWindowText(hwnd, sb, 1024);
text = sb.ToString();
if (text != string.Empty && text != "Program Manager")
{
appCount++;
}
}
return true;
}
private int GetAppCount()
{
appCount = 0;
EnumWindows(EnumerateWindows, new IntPtr(0));
return appCount;
}
internal delegate bool EnumThreadWindowsCallback(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern bool IsWindowVisible(IntPtr hwnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int GetWindowText(IntPtr hWnd, [Out, MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpString, int nMaxCount);
As far as I know there is no managed way of accessing the taskbar. Here is a link that describes how to access the taskbar by the Windows API. However, I a quick scan did not show any "number of items" or something similar. Still it might point you in the right direction.
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.