none
How do I get the selected Text from the reading pane in Outlook 2003?

    Question

  • I'm using Visual C# 2005, and have a plug-in almost working with Outlook 2003.

    What I lack is how to get the selected Text from the Reading (preview) pane.

    Any ideas?
    Thursday, September 11, 2008 6:46 PM

Answers

  • Well, the "Clipboard" approach worked well for plain text or rich text controls in the reading pane, but for HTML ones, apparently there is an instance of "Internet Explorer_Server" doing the display... at least in Outlook 2003 there is.

    Here's some code that got me up and running:

    Inside of my "Button click" function, this is where I get the Selected Text.
    Code Snippet
    if (this.Application.ActiveExplorer().IsPaneVisible(Microsoft.Office.Interop.Outlook.OlPane.olPreview) == true)
    {
    //MessageBox.Show("Reading Pane is On");

    IntPtr focusedHandle = GetFocus();

    if (isIEServerWindow(focusedHandle))
    {
    //MessageBox.Show ("Found IEServer: " + focusedHandle.ToString());

    IHTMLDocument2 thisDoc = IEDOMFromhWnd(pFoundWindow);
    IHTMLTxtRange thisRange = (mshtml.IHTMLTxtRange)
    thisDoc.selection.createRange();
    string thisTxt = thisRange.text.ToString();
    }
    else
    {
    Clipboard.Clear();
    SendMessage(focusedHandle, WM_COPY, IntPtr.Zero, IntPtr.Zero);
    if (Clipboard.ContainsText())
    {
    string thisTxt = Clipboard.GetText().ToString();
    }
    }
    //MessageBox.Show (thisTxt);
    // Do what you want with thisTxt
    }
    else
    {
    //MessageBox.Show("Reading Pane is off");

    // Do appropriate actions...
    }

    My "using" section grew...

    Code Snippet

    using System;
    using System.Diagnostics;
    using System.Windows.Forms;
    using Microsoft.VisualStudio.Tools.Applications.Runtime;
    using Outlook = Microsoft.Office.Interop.Outlook;
    using Office = Microsoft.Office.Core;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Collections.Generic;
    using System.Globalization;
    using mshtml;


    "SendMessage" is from user32.dll:
    Code Snippet

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
    static extern IntPtr SendMessage(IntPtr hWnd,

    uint Msg,

    IntPtr wParam,

    IntPtr lParam);


    I found the value for "WM_COPY = 0x0301" somewhere

    So the remaining mysteries are isIEServerWindow &
    IEDOMFromhWnd.  I'll try to follow up with them...
    Friday, September 12, 2008 1:24 PM

All replies

  • what about getting the selected mail item's body?

     

    Thursday, September 11, 2008 7:18 PM
  • I need to be able to tell what (if anything) is actually selected in the reading pane.  Not the whole body of the email.  If nothing is selected, I do something else.  I basically want the add-in to start with a "Copy" command and then branch based on what shows up on the "Clipboard".
    Thursday, September 11, 2008 9:19 PM
  • Well, the "Clipboard" approach worked well for plain text or rich text controls in the reading pane, but for HTML ones, apparently there is an instance of "Internet Explorer_Server" doing the display... at least in Outlook 2003 there is.

    Here's some code that got me up and running:

    Inside of my "Button click" function, this is where I get the Selected Text.
    Code Snippet
    if (this.Application.ActiveExplorer().IsPaneVisible(Microsoft.Office.Interop.Outlook.OlPane.olPreview) == true)
    {
    //MessageBox.Show("Reading Pane is On");

    IntPtr focusedHandle = GetFocus();

    if (isIEServerWindow(focusedHandle))
    {
    //MessageBox.Show ("Found IEServer: " + focusedHandle.ToString());

    IHTMLDocument2 thisDoc = IEDOMFromhWnd(pFoundWindow);
    IHTMLTxtRange thisRange = (mshtml.IHTMLTxtRange)
    thisDoc.selection.createRange();
    string thisTxt = thisRange.text.ToString();
    }
    else
    {
    Clipboard.Clear();
    SendMessage(focusedHandle, WM_COPY, IntPtr.Zero, IntPtr.Zero);
    if (Clipboard.ContainsText())
    {
    string thisTxt = Clipboard.GetText().ToString();
    }
    }
    //MessageBox.Show (thisTxt);
    // Do what you want with thisTxt
    }
    else
    {
    //MessageBox.Show("Reading Pane is off");

    // Do appropriate actions...
    }

    My "using" section grew...

    Code Snippet

    using System;
    using System.Diagnostics;
    using System.Windows.Forms;
    using Microsoft.VisualStudio.Tools.Applications.Runtime;
    using Outlook = Microsoft.Office.Interop.Outlook;
    using Office = Microsoft.Office.Core;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Collections.Generic;
    using System.Globalization;
    using mshtml;


    "SendMessage" is from user32.dll:
    Code Snippet

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
    static extern IntPtr SendMessage(IntPtr hWnd,

    uint Msg,

    IntPtr wParam,

    IntPtr lParam);


    I found the value for "WM_COPY = 0x0301" somewhere

    So the remaining mysteries are isIEServerWindow &
    IEDOMFromhWnd.  I'll try to follow up with them...
    Friday, September 12, 2008 1:24 PM
  • As mentioned, here are isIEServerWindow & IEDOMFromhWnd along with the dllimports

    Code Snippet
    [DllImport("oleacc.dll", PreserveSig = false)]
    [return: MarshalAs(UnmanagedType.Interface)]
    static extern object ObjectFromLresult(UIntPtr lResult,
    [MarshalAs(UnmanagedType.LPStruct)] Guid refiid, IntPtr wParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint RegisterWindowMessage(string lpString);

    [Flags]
    enum SendMessageTimeoutFlags : uint
    {
    SMTO_NORMAL = 0x0000,
    SMTO_BLOCK = 0x0001,
    SMTO_ABORTIFHUNG = 0x0002,
    SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
    }

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern IntPtr SendMessageTimeout(
    IntPtr hWnd,
    uint Msg,
    UIntPtr wParam,
    IntPtr lParam,
    SendMessageTimeoutFlags fuFlags,
    uint uTimeout,
    out UIntPtr lpdwResult);

    mshtml.IHTMLDocument2 IEDOMFromhWnd(IntPtr hwnd)
    {
    UIntPtr lRes;
    uint lMsg;
    object hr=null;

    if (hwnd != IntPtr.Zero)
    {
    // Register the message
    lMsg = RegisterWindowMessage("WM_HTML_GETOBJECT");

    // Get the object
    SendMessageTimeout(hwnd, lMsg, UIntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 1000, out lRes);

    if ( lRes != UIntPtr.Zero )
    {
    // Get the object from lRes
    hr = ObjectFromLresult(lRes, typeof(mshtml.IHTMLDocument2).GUID, IntPtr.Zero);
    if ( hr != null )
    {
    //MessageBox.Show(Convert.ToString(hr));
    }
    }
    }
    else
    {
    //MessageBox.Show("Bad hwnd!");
    }
    return (mshtml.IHTMLDocument2)hr;
    }


    private static bool isIEServerWindow(IntPtr hWnd)
    {
    IntPtr Res;
    StringBuilder ClassName = new StringBuilder(100);
    //Get the window class name
    Res = (IntPtr) GetClassName(hWnd, ClassName, ClassName.Capacity);
    if (Res != IntPtr.Zero)
    {
    //MessageBox.Show(ClassName.ToString());
    return (string.Compare(ClassName.ToString(), "Internet Explorer_Server", true, CultureInfo.InvariantCulture) == 0);
    }
    else
    {
    return false;
    }
    }


    Friday, September 12, 2008 1:29 PM