Send mouse click to old caret position after display of new inspector window OUT-498
authorThomas
Fri, 31 Aug 2018 09:40:03 +0200
branchOUT-498
changeset 235276687cbac48d
parent 2351 1a6bc4294582
child 2356 b1c3af7ae954
Send mouse click to old caret position after display of new inspector window
CryptableMailItem.cs
NativeMethods.cs
     1.1 --- a/CryptableMailItem.cs	Wed Aug 29 11:16:37 2018 +0200
     1.2 +++ b/CryptableMailItem.cs	Fri Aug 31 09:40:03 2018 +0200
     1.3 @@ -1,13 +1,14 @@
     1.4 -using pEpCOMServerAdapterLib;
     1.5 +using MimeKit;
     1.6 +using pEp.UI;
     1.7 +using pEpCOMServerAdapterLib;
     1.8  using System;
     1.9  using System.Collections.Generic;
    1.10  using System.ComponentModel;
    1.11 +using System.Diagnostics;
    1.12 +using System.IO;
    1.13 +using System.Runtime.InteropServices;
    1.14  using System.Threading;
    1.15  using Outlook = Microsoft.Office.Interop.Outlook;
    1.16 -using System.Diagnostics;
    1.17 -using System.IO;
    1.18 -using MimeKit;
    1.19 -using pEp.UI;
    1.20  
    1.21  namespace pEp
    1.22  {
    1.23 @@ -424,6 +425,45 @@
    1.24                      // Get the pEp drafts folder and the current mail item's folder
    1.25                      pEpDraftsFolder = Globals.ThisAddIn.GetPEPStoreDraftsFolder();
    1.26  
    1.27 +                    // Get the caret position to later set it again
    1.28 +                    bool caretPositionRetrieved = false;
    1.29 +                    NativeMethods.Point caretPosition = new NativeMethods.Point(-1,-1);
    1.30 +                    IntPtr inspectorHandle = IntPtr.Zero;
    1.31 +
    1.32 +                    try
    1.33 +                    {
    1.34 +                        // First, get the current process to retrieve a handle to the caret's window
    1.35 +                        uint currentThreadId = NativeMethods.GetCurrentThreadId();
    1.36 +                        NativeMethods.GuiThreadInfo guiThreadInfo = new NativeMethods.GuiThreadInfo();
    1.37 +                        guiThreadInfo.size = Marshal.SizeOf(guiThreadInfo);
    1.38 +
    1.39 +                        if (NativeMethods.GetGUIThreadInfo(currentThreadId, ref guiThreadInfo))
    1.40 +                        {
    1.41 +                            inspectorHandle = guiThreadInfo.hwndCaret;
    1.42 +
    1.43 +                            // Get caret position and convert to screen coordinates
    1.44 +                            if (NativeMethods.GetCaretPos(out caretPosition) &&
    1.45 +                                NativeMethods.ClientToScreen(inspectorHandle, ref caretPosition))
    1.46 +                            {
    1.47 +                                caretPositionRetrieved = true;
    1.48 +                            }
    1.49 +                            else
    1.50 +                            {
    1.51 +                                int error = Marshal.GetLastWin32Error();
    1.52 +                                Log.Error("MailItem_Write: Error getting caret position. Last Win32 error: " + error.ToString("X"));
    1.53 +                            }
    1.54 +                        }
    1.55 +                        else
    1.56 +                        {
    1.57 +                            int error = Marshal.GetLastWin32Error();
    1.58 +                            Log.Error("MailItem_Write: Error getting inspector Handle. Last Win32 error: " + error.ToString("X"));
    1.59 +                        }
    1.60 +                    }
    1.61 +                    catch (Exception ex)
    1.62 +                    {
    1.63 +                        Log.Error("MailItem_Write: Error getting caret position. " + ex.ToString());
    1.64 +                    }
    1.65 +
    1.66                      /* Inline responses need a special treatment.
    1.67                       * We cannot just intercept the Write event and check, if the item
    1.68                       * is in the pEp folder, as it gets synced to the server anyhow if we
    1.69 @@ -553,6 +593,31 @@
    1.70                                  newInspector.Display();
    1.71                                  newInspector.WindowState = windowState;
    1.72                                  currentInspector.Close(Outlook.OlInspectorClose.olDiscard);
    1.73 +
    1.74 +                                // Set caret position again to where it was before
    1.75 +                                if (caretPositionRetrieved)
    1.76 +                                {
    1.77 +                                    try
    1.78 +                                    {
    1.79 +                                        // Set cursor to the caret's previous position
    1.80 +                                        NativeMethods.SetCursorPos(caretPosition.X, caretPosition.Y);
    1.81 +
    1.82 +                                        // Send mouse click event (mouse down and mouse up)
    1.83 +                                        NativeMethods.Input[] inputs = new NativeMethods.Input[1];
    1.84 +                                        inputs[0].Type = (int)NativeMethods.InputType.InputMouse;
    1.85 +                                        inputs[0].Mi.Flags = (int)NativeMethods.MouseEvents.MouseEventFLeftDown | (int)NativeMethods.MouseEvents.MouseEventFLeftUp;
    1.86 +
    1.87 +                                        if (NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(inputs[0])) == 0)
    1.88 +                                        {
    1.89 +                                            int error = Marshal.GetLastWin32Error();
    1.90 +                                            Log.Error("MailItem_Write: Error setting caret to original position. Win32 error " + error.ToString("X"));
    1.91 +                                        }
    1.92 +                                    }
    1.93 +                                    catch (Exception ex)
    1.94 +                                    {
    1.95 +                                        Log.Error("MailItem_Write: Error setting caret to original position. " + ex.ToString());
    1.96 +                                    }
    1.97 +                                }
    1.98                              }
    1.99                              else
   1.100                              {
     2.1 --- a/NativeMethods.cs	Wed Aug 29 11:16:37 2018 +0200
     2.2 +++ b/NativeMethods.cs	Fri Aug 31 09:40:03 2018 +0200
     2.3 @@ -1,6 +1,5 @@
     2.4  using System;
     2.5  using System.Runtime.InteropServices;
     2.6 -using System.Text;
     2.7  
     2.8  namespace pEp
     2.9  {
    2.10 @@ -9,45 +8,157 @@
    2.11      /// </summary>
    2.12      internal class NativeMethods
    2.13      {
    2.14 -
    2.15 -        /// <summary>
    2.16 -        /// Specifies options for the GdipEmfToWmfBits() method, which converts an Enhanced Metafile (EMF) metafile to a Windows Metafile Format (WMF) metafile.
    2.17 -        /// </summary>
    2.18 -        [Flags]
    2.19 -        internal enum EmfToWmfBitsFlags
    2.20 -        {
    2.21 -            EmfToWmfBitsFlagsDefault = 0x00000000,
    2.22 -            EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
    2.23 -            EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
    2.24 -            EmfToWmfBitsFlagsNoXORClip = 0x00000004
    2.25 -        }
    2.26 -
    2.27          /// <summary>
    2.28          /// Flag to use for FindMimeFromData(). Returns "image/png" and "image/jpeg" instead of "image/x-png" and "image/pjpeg". 
    2.29          /// </summary>
    2.30          internal const int FMFD_RETURNUPDATEDIMGMIMES = 0x00000020;
    2.31  
    2.32 +        #region Enumerations
    2.33          /// <summary>
    2.34 -        /// Uses the EmfToWmfBits function in the GDI+ specification to convert a
    2.35 -        /// Enhanced Metafile to a Windows Metafile.
    2.36 -        /// See: http://www.pinvoke.net/default.aspx/gdiplus.gdipemftowmfbits
    2.37 +        /// Specifies the type of the input event.
    2.38 +        /// See: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/ns-winuser-taginput
    2.39          /// </summary>
    2.40 -        /// <param name="hEmf">handle to the Enhanced Metafile to be converted</param>
    2.41 -        /// <param name="bufferSize">The size of the buffer used to store the Windows Metafile bits returned</param>
    2.42 -        /// <param name="buffer">An array of bytes used to hold the Windows Metafile bits returned</param>
    2.43 -        /// <param name="mappingMode">The mapping mode of the image. This control uses MM_ANISOTROPIC.</param>
    2.44 -        /// <param name="flags">Flags used to specify the format of the Windows Metafile returned</param>
    2.45 -        [DllImport("gdiplus.dll")]
    2.46 -        internal static extern uint GdipEmfToWmfBits(IntPtr hEmf, uint bufferSize, byte[] buffer, int mappingMode, EmfToWmfBitsFlags flags);
    2.47 +        internal enum InputType
    2.48 +        {
    2.49 +            InputMouse = 0,
    2.50 +            InputKeyboard = 1,
    2.51 +            InputHardware = 2
    2.52 +        }
    2.53  
    2.54          /// <summary>
    2.55 -        /// Deletes an enhanced-format metafile or an enhanced-format metafile handle.
    2.56 -        /// See: http://www.pinvoke.net/default.aspx/gdi32.deleteenhmetafile
    2.57 +        /// A set of bit flags that specify various aspects of mouse motion and button clicks.
    2.58 +        /// See: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/ns-winuser-tagmouseinput
    2.59          /// </summary>
    2.60 -        /// <param name="hEmf">A handle to an enhanced metafile.</param>
    2.61 +        [Flags]
    2.62 +        internal enum MouseEvents
    2.63 +        {
    2.64 +            MouseEventFAbsolute = 0x8000,
    2.65 +            MouseEventFHWheel = 0x01000,
    2.66 +            MouseEventFMove = 0x0001,
    2.67 +            MouseEventFMoveNoCoalesce = 0x2000,
    2.68 +            MouseEventFLeftDown = 0x0002,
    2.69 +            MouseEventFLeftUp = 0x0004,
    2.70 +            MouseEventFRightDown = 0x0008,
    2.71 +            MouseEventFRightUp = 0x0010,
    2.72 +            MouseEventFMiddleDown = 0x0020,
    2.73 +            MouseEventFMiddleUp = 0x0040,
    2.74 +            MouseEventFVirtualDesk = 0x4000,
    2.75 +            MouseEventFWheel = 0x0800,
    2.76 +            MouseEventFXDown = 0x0080,
    2.77 +            MouseEventFXUp = 0x0100
    2.78 +        }
    2.79 +        #endregion
    2.80 +
    2.81 +        #region Structures
    2.82 +        /// <summary>
    2.83 +        /// Defines the x- and y- coordinates of a point.
    2.84 +        /// </summary>
    2.85 +        internal struct Point
    2.86 +        {
    2.87 +            public int X;
    2.88 +            public int Y;
    2.89 +
    2.90 +            public Point(int x, int y)
    2.91 +            {
    2.92 +                X = x;
    2.93 +                Y = y;
    2.94 +            }
    2.95 +        }
    2.96 +
    2.97 +        /// <summary>
    2.98 +        /// Used by SendInput to store information for synthesizing input events such as keystrokes, 
    2.99 +        /// mouse movement, and mouse clicks.
   2.100 +        /// </summary>
   2.101 +        [StructLayout(LayoutKind.Explicit)]
   2.102 +        public struct Input
   2.103 +        {
   2.104 +            [FieldOffset(0)]
   2.105 +            public int              Type;
   2.106 +            [FieldOffset(4)]
   2.107 +            public MouseInput       Mi;
   2.108 +            [FieldOffset(4)]
   2.109 +            public KeyBdInput       Ki;
   2.110 +            [FieldOffset(4)]
   2.111 +            public HardwareInput    Hi;
   2.112 +        }
   2.113 +
   2.114 +        /// <summary>
   2.115 +        /// Contains information about a simulated message generated by an input device other than a keyboard or mouse.
   2.116 +        /// </summary>
   2.117 +        [StructLayout(LayoutKind.Sequential)]
   2.118 +        public struct HardwareInput
   2.119 +        {
   2.120 +            public int      Msg;
   2.121 +            public short    ParamL;
   2.122 +            public short    ParamH;
   2.123 +        }
   2.124 +
   2.125 +        /// <summary>
   2.126 +        /// Contains information about a simulated keyboard event.
   2.127 +        /// </summary>
   2.128 +        [StructLayout(LayoutKind.Sequential)]
   2.129 +        public struct KeyBdInput
   2.130 +        {
   2.131 +            public short    Vk;
   2.132 +            public short    Scan;
   2.133 +            public int      Flags;
   2.134 +            public int      Time;
   2.135 +            public IntPtr   ExtraInfo;
   2.136 +        }
   2.137 +
   2.138 +        /// <summary>
   2.139 +        /// Contains information about a simulated mouse event.
   2.140 +        /// </summary>
   2.141 +        [StructLayout(LayoutKind.Sequential)]
   2.142 +        internal struct MouseInput
   2.143 +        {
   2.144 +            public int     X;
   2.145 +            public int     Y;
   2.146 +            public int     MouseData;
   2.147 +            public int     Flags;
   2.148 +            public int     Time;
   2.149 +            public IntPtr  ExtraInfo;
   2.150 +        }
   2.151 +
   2.152 +        /// <summary>
   2.153 +        /// Defines the coordinates of the upper-left and lower-right corners of a rectangle.
   2.154 +        /// </summary>
   2.155 +        internal struct Rect
   2.156 +        {
   2.157 +            public int Left;
   2.158 +            public int Top;
   2.159 +            public int Right;
   2.160 +            public int Bottom;
   2.161 +        }
   2.162 +
   2.163 +        /// <summary>
   2.164 +        /// Contains information about a GUI thread.
   2.165 +        /// </summary>
   2.166 +        [Serializable, StructLayout(LayoutKind.Sequential)]
   2.167 +        internal struct GuiThreadInfo
   2.168 +        {
   2.169 +            public int      size;
   2.170 +            public int      flags;
   2.171 +            public IntPtr   hwndActive;
   2.172 +            public IntPtr   hwndFocus;
   2.173 +            public IntPtr   hwndCapture;
   2.174 +            public IntPtr   hwndMenuOwner;
   2.175 +            public IntPtr   hwndMoveSize;
   2.176 +            public IntPtr   hwndCaret;
   2.177 +            public Rect     caretRectangle;
   2.178 +        }
   2.179 +        #endregion
   2.180 +
   2.181 +        #region Methods
   2.182 +        /// <summary>
   2.183 +        /// Converts the client-area coordinates of a specified point to screen coordinates.
   2.184 +        /// </summary>
   2.185 +        /// <param name="hWnd">A handle to the window whose client area is used for the conversion.</param>
   2.186 +        /// <param name="point">A Point structure that contains the client coordinates to be converted. 
   2.187 +        /// The new screen coordinates are copied into this structure if the function succeeds.</param>
   2.188          /// <returns>True if the method succeeds, otherwise false.</returns>
   2.189 -        [DllImport("gdi32.dll")]
   2.190 -        internal static extern bool DeleteEnhMetaFile(IntPtr hEmf);
   2.191 +        [DllImport("user32.dll", SetLastError = true)]
   2.192 +        internal static extern bool ClientToScreen(IntPtr hWnd, ref Point point);
   2.193  
   2.194          /// <summary>
   2.195          /// Datermines the MIME type of a file (or byte array).
   2.196 @@ -64,41 +175,66 @@
   2.197          /// <param name="reserved">Reserved. Must be set to 0.</param>
   2.198          /// <returns></returns>
   2.199          [DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
   2.200 -        internal static extern int FindMimeFromData(IntPtr bc, 
   2.201 -                                                    [MarshalAs(UnmanagedType.LPWStr)] string url, 
   2.202 -                                                    [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.I1, SizeParamIndex=3)] byte[] buffer, 
   2.203 -                                                    uint bufferSize, 
   2.204 -                                                    [MarshalAs(UnmanagedType.LPWStr)] string mimeProposed, 
   2.205 -                                                    int mimeFlags, 
   2.206 +        internal static extern int FindMimeFromData(IntPtr bc,
   2.207 +                                                    [MarshalAs(UnmanagedType.LPWStr)] string url,
   2.208 +                                                    [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)] byte[] buffer,
   2.209 +                                                    uint bufferSize,
   2.210 +                                                    [MarshalAs(UnmanagedType.LPWStr)] string mimeProposed,
   2.211 +                                                    int mimeFlags,
   2.212                                                      out IntPtr mimeOut,
   2.213                                                      int reserved);
   2.214  
   2.215          /// <summary>
   2.216 -        /// Gets the handle of the window that has the clipboard open.
   2.217 -        /// See: http://www.pinvoke.net/default.aspx/user32/GetOpenClipboardWindow.html
   2.218 +        /// Copies the caret's position to the specified Point structure.
   2.219          /// </summary>
   2.220 -        /// <returns>If the function succeeds, the handle to the window that has the clipboard open.</returns>
   2.221 -        [DllImport("user32.dll")]
   2.222 -        internal static extern IntPtr GetOpenClipboardWindow();
   2.223 +        /// <param name="point">A Point structure that is to receive the client coordinates of the caret.</param>
   2.224 +        /// <returns>True if the method succeeds, otherwise false.</returns>
   2.225 +        [DllImport("user32.dll", SetLastError = true)]
   2.226 +        internal static extern bool GetCaretPos(out Point point);
   2.227  
   2.228          /// <summary>
   2.229 -        /// Copies the specified window's title bar (if it has one) into a StringBuilder.
   2.230 -        /// See: http://www.pinvoke.net/default.aspx/user32/Getwindowtext.html
   2.231 +        /// Retrieves the thread identifier of the calling thread.
   2.232          /// </summary>
   2.233 -        /// <param name="hwnd">A handle to the window or control containing the text.</param>
   2.234 -        /// <param name="text">The StringBuilder that will receive the text.</param>
   2.235 -        /// <param name="count">The maximum number of characters to copy to the StringBuilder. If the text exceeds this limit, it is truncated.</param>
   2.236 -        /// <returns>If the function succeeds, the return value is the length, in characters, of the copied text.</returns>
   2.237 -        [DllImport("user32.dll", CharSet=CharSet.Unicode)]
   2.238 -        internal static extern int GetWindowText(IntPtr hwnd, StringBuilder text, int count);
   2.239 +        /// <returns>The thread identifier of the calling thread.</returns>
   2.240 +        [DllImport("kernel32.dll", SetLastError = true)]
   2.241 +        internal static extern uint GetCurrentThreadId();
   2.242  
   2.243          /// <summary>
   2.244 -        /// Removes the caret from the screen. Hiding a caret does not destroy its current shape or invalidate the insertion point. 
   2.245 -        /// See: https://msdn.microsoft.com/en-us/library/ms648403(v=VS.85).aspx
   2.246 +        /// Retrieves information about the active window or a specified GUI thread.
   2.247          /// </summary>
   2.248 -        /// <param name="hWnd"></param> A handle to the window that owns the caret. If this parameter is NULL, HideCaret searches the current task for the window that owns the caret. 
   2.249 -        /// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError.</returns>
   2.250 -        [DllImport("user32.dll")]
   2.251 -        internal static extern bool HideCaret(IntPtr hWnd);
   2.252 +        /// <param name="threadId">The identifier for the thread for which information is to be retrieved. 
   2.253 +        /// To retrieve this value, use the GetWindowThreadProcessId function. If this parameter is null, 
   2.254 +        /// the function returns information for the foreground thread.</param>
   2.255 +        /// <param name="guiThreadInfo">The GuiThreadInfo structure that receives information describing the thread. 
   2.256 +        /// Note that you must set the size member to Marshal.SizeOf(guiThreadInfo) before calling this method.</param>
   2.257 +        /// <returns>True if the method succeeds, otherwise false.</returns>
   2.258 +        [DllImport("user32.dll", SetLastError = true)]
   2.259 +        internal static extern bool GetGUIThreadInfo(uint threadId, ref GuiThreadInfo guiThreadInfo);
   2.260 +
   2.261 +        /// <summary>
   2.262 +        /// Synthesizes keystrokes, mouse motions, and button clicks.
   2.263 +        /// </summary>
   2.264 +        /// <param name="inputCount">The number of structures in the inputs array.</param>
   2.265 +        /// <param name="inputs">An array of Input structures. Each structure represents an event to be 
   2.266 +        /// inserted into the keyboard or mouse input stream.</param>
   2.267 +        /// <param name="size">The size, in bytes, of an Input structure. If size is not the size of an Input structure, 
   2.268 +        /// the function fails.</param>
   2.269 +        /// <returns>The number of events that it successfully inserted into the keyboard or mouse input stream. 
   2.270 +        /// If the function returns zero, the input was already blocked by another thread.</returns>
   2.271 +        [DllImport("user32.dll", SetLastError = true)]
   2.272 +        internal static extern uint SendInput(uint inputCount, [MarshalAs(UnmanagedType.LPArray), In] Input[] inputs, int size);
   2.273 +
   2.274 +        /// <summary>
   2.275 +        /// Moves the cursor to the specified screen coordinates. If the new coordinates are not within 
   2.276 +        /// the screen rectangle set by the most recent ClipCursor function call, the system automatically 
   2.277 +        /// adjusts the coordinates so that the cursor stays within the rectangle.
   2.278 +        /// </summary>
   2.279 +        /// <param name="x">The new x-coordinate of the cursor, in screen coordinates.</param>
   2.280 +        /// <param name="y">The new y-coordinate of the cursor, in screen coordinates.</param>
   2.281 +        /// <returns>True if the method succeeds, otherwise false.</returns>
   2.282 +        [DllImport("user32.dll", SetLastError = true)]
   2.283 +        [return: MarshalAs(UnmanagedType.Bool)]
   2.284 +        internal static extern bool SetCursorPos(int x, int y);
   2.285 +        #endregion
   2.286      }
   2.287  }