C# Outlook - How can I get the cursor position in

2019-07-26 22:09发布

问题:

I want to insert a text at the current cursor position in the subject field of a MailItem in my C# AddIn.

I additionally must know if the cursor is in the subject field.

Does anyone know how to resolve it?

EDIT

To clearify what I want to do: With the Message my AddIn opens a WPF Window, where the user can select one of a couple of tags (e.g. "{stuff}"). If the user hits the insert button in the WPF window, the AddIn must insert the tag at the current cursor position. Actually I can just insert it in the mail body.

If the cursor is in the subject field, the tag must be added in the subject field at the cursor position.

回答1:

This is not an easy task, but possible I think.

Theoretically it should be possible by following these steps:

You can get the Carret-Position of an active UI thread by calling GetGUIThreadInfo (refer to: pInvoke: GetGuiThreadInfo

The evaluated structure contains a public System.Drawing.Rectangle rcCaret which is the caret's bounding rectangle, in client coordinates, relative to the window." (Refer to: MSD GuiThreadInfoStructure)

Than followup with the steps described also in the MSDN-Article: GetGUIThreadInfo function

MSDN says:
To get the actual insertion point in the rcCaret rectangle, perform the following steps:

  1. Call GetKeyboardLayout to retrieve the current input language.

  2. Determine the character used for the cursor, based on the current input language.

  3. Call CreateFont using Sans Serif for the font, the height given by rcCaret, and a width of zero. For fnWeight, call SystemParametersInfo(SPI_GETCARETWIDTH, 0, pvParam, 0). If pvParam is greater than 1, set fnWeight to 700, otherwise set fnWeight to 400.

  4. Select the font into a device context (DC) and use GetCharABCWidths to get the B width of the appropriate cursor character.

  5. Add the B width to rcCaret.left to obtain the actual insertion point.

Hint: The function may not return valid window handles in the GUITHREADINFO structure when called to retrieve information for the foreground thread, such as when a window is losing activation.

There is also a CodeProject-Article (Getting Caret Position Inside Any Application) where someone is showing up tooltips in 3rd party applications by evaluating the screenlocation (coordinates) of the carret.

Long story short... I don't know your explicit task, but this feels like a 'Hack' - can't you solve it in a better 'formally' way? Like attaching to an event (PropertyChnaged) or something else to dedect subject changes?

UPDATE
Finally I was able to read the value from the Subject-Field from another application. The Subject-Field in a Microsoft Outlook E-Mail-Window is a RichEdit-Control (ClassName: RichEdit20WPT) with the ControlID "4101". I was not able to hook on messages for this control (it is not possible from a different application/thread and I have not built an AddIn - I think its possible to attach if you build an add in for this task: (attach to WM_NOTIFY and filter EN_MSGFILTER for keyboard events ENM_KEYEVENTS)

But you can read the current value by:

 [DllImport("user32.dll")]
 static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);

 const int WM_GETTEXT = 0x000D;
 const int WM_GETTEXTLENGTH = 0x000E;

 public static string GetControlText(IntPtr hWndOfControl)
  {    
    StringBuilder title = new StringBuilder();

    // Get the size of the string 
    Int32 size = SendMessage((int)hWndOfControl, WM_GETTEXTLENGTH, 0, 0).ToInt32();

     // If Size > 0 ? -> Text available...
     if (size > 0)
      {
       title = new StringBuilder(size + 1);    
       SendMessage(hWndOfControl, (int)WM_GETTEXT, title.Capacity, title);
      }

     return title.ToString();
   }

If you'r not able to get the carret position, maybe you can set the Focus back to the control and use SendKeys to insert the text at the current position. Setting the text can also be done my using SendMessage WM_SETTEXT. Maybe this will help you...