Introduction
Sometimes we need to know about the caret position inside another application for various purposes like showing Tooltip or message window along with caret. (Here the other application is an entirely different process and not connected in any way with our application.) This article describes how to show a tooltip window next to the caret in any application. The source code, attached here, is fully illustrative and easy to understand.
Background
Though there are many ways to get the caret position inside an application, the problem is that those methods are associated with the client region and non client regions and provide/use wrong handles. This association leads to wrong information about caret position inside Microsoft Office 2007 like products where the entire window results as the client area. Facing the same client and non client region problem, I spent some time looking for the possible solution and arrived at this approach. The utility is ready to use. You can enhance the UI or add some more information.
Using the Code
To test the caret postion, just run the application and follow the process below:
- Open a Notepad and type some text. It will show tooltip next to caret. While typing, Tooltip moves along with caret.
- Now move the Notepad window and observe the continuously changing caret position inside tooltip.
- When Notepad window reaches the edge of screen, Tooltip changes its position (By default, it gets displayed at the bottom right to the caret).
- Now click on Desktop, Tooltip disappears.
- Now open Microsoft Word and see the Tooltip re-appear. Tooltip can be moved by pressing the left mouse button and moving the cursor onto it.
Explanation
GetCaretPosition()
method populates GUI Thread information into guiInfo
object. guiInfo
is a structure variable of type GUIThreadInfo
that is required by GetGUIThreadInfo()
method of user32.dll.
public void GetCaretPosition()
{
guiInfo = new GUITHREADINFO();
guiInfo.cbSize = (uint)Marshal.SizeOf(guiInfo);
GetGUIThreadInfo(0, out guiInfo);
}
guiInfo
variable is a global variable that is further processed by EvaluateCaretPosition()
method. This method fetches caret information and handle of associated window from guiInfo
object and converts it to the screen coordinates.
private void EvaluateCaretPosition()
{
caretPosition = new Point();
GetCaretPosition();
caretPosition.X = (int)guiInfo.rcCaret.Left + 25;
caretPosition.Y = (int)guiInfo.rcCaret.Bottom + 25;
ClientToScreen(guiInfo.hwndCaret, out caretPosition);
txtCaretX.Text = (caretPosition.X).ToString();
txtCaretY.Text = caretPosition.Y.ToString();
}
Further the tooltip should be visible only for GUI applications and not for Windows Explorer likewise renaming any file on desktop. For this current active process is evaluated by GetActiveProcess()
method. This method returns the name of currently active process to the timer event.
private string GetActiveProcess()
{
const int nChars = 256;
int handle = 0;
StringBuilder Buff = new StringBuilder(nChars);
handle = (int)GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
uint lpdwProcessId;
uint dwCaretID = GetWindowThreadProcessId(handle, out lpdwProcessId);
uint dwCurrentID = (uint)Thread.CurrentThread.ManagedThreadId;
return Process.GetProcessById((int)lpdwProcessId).ProcessName;
}
return String.Empty;
}
In the timer event, just check whether the tooltip is foreground window (just because user might have clicked on that), if not then check whether Windows Explorer is an active process(just because of user click on desktop/ opening Wiindows Explorer), if so then hide tooltip else evaluate caret position and adjust tooltip position by AdjustUI()
method.
private void timer1_Tick(object sender, EventArgs e)
{
if (GetForegroundWindow() == this.Handle)
{
return;
}
string activeProcess = GetActiveProcess();
if ((activeProcess.ToLower().Contains("explorer") |
(activeProcess == string.Empty)))
{
this.Visible = false;
}
else
{
EvaluateCaretPosition();
AdjustUI();
lblCurrentApp.Text = " You are Currently inside : " + activeProcess;
this.Visible = true;
}
}
The AdjustUI
method is responsible to adjust UI of tooltip to keep it inside user screen.
private void AdjustUI()
{
Rectangle workingArea = SystemInformation.WorkingArea;
if (caretPosition.X + this.Width > workingArea.Width)
{
caretPosition.X = caretPosition.X - this.Width - 50;
}
if (caretPosition.Y + this.Height > workingArea.Height)
{
caretPosition.Y = caretPosition.Y - this.Height - 50;
}
this.Left = caretPosition.X;
this.Top = caretPosition.Y;
}
Points of Interest
After spending some time on Google to get some clue about it, I found some people saying that it is impossible for Microsoft Office 2007 products because it has its own paint logic. Then I tried to resolve this problem and reached the solution shared here that works for any application (Including Microsoft Office 2007 & Visual Studio).
History
- 30th March, 2009: Initial post