My idea was to create a simple function to add/relocate/remove up to 30 unassigned tooltips. My experience in C# is about a month and the only idea how to make it was taken from autohotkey code and translated to C#. The code below is able to create and remove tooltips, but width of each tooltip equal ONE character. How to fix this bug?
using System;
using System.Runtime.InteropServices;

namespace ToolTipTest
    static class Program
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
            ToolTip.CoordMode(false); //we can define screen coordinates or active window relative coordinates, false is screen coordinates
            ToolTip.Show("111", 0, 0, 1); //adding tooltip number 1 with "111" at x=0,y=0 of a screen
            ToolTip.Show("222", 20, 20, 2); //adding tooltip number 2 with "222" at x=20,y=20 of a screen
            ToolTip.Remove(1); //removing tooltip 1
            ToolTip.Show("", null, null, 2); //removing tooltip 2 (another method)


    public struct ToolTip
        #region API
        [DllImport("User32.dll", SetLastError = true)]
        private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref TOOLINFO lParam);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern int SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);

        [DllImport("User32.dll", SetLastError = true)]
        private static extern int GetClientRect(IntPtr hWnd, ref RECT lpRect);

        [DllImport("User32.dll", SetLastError = true)]
        private static extern bool SetWindowPos(
            IntPtr hWnd,
            IntPtr hWndInsertAfter,
            int X,
            int Y,
            int cx,
            int cy,
            int uFlags);

        [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
        private static extern int GetSystemMetrics(int sysMetric);

        [DllImport("user32.dll", EntryPoint = "IsWindow")]
        private static extern bool IsWindow(IntPtr hWnd);

        [DllImport("user32.dll", EntryPoint = "GetWindowRect")]
        private static extern bool GetWindowRect(IntPtr handle, out RECT rect);

        [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
        private static extern IntPtr GetDesktopWindow();

        [DllImport("user32.dll", EntryPoint = "GetCursorPos")]
        private static extern int GetCursorPos(out POINTAPI point);

        [DllImport("user32.dll", EntryPoint = "GetForegroundWindow")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", EntryPoint = "CreateWindowEx")]
        private static extern IntPtr CreateWindowEx(
            int dwExStyle,
            string lpClassName,
            string lpWindowName,
            uint dwStyle,
            int x,
            int y,
            int nWidth,
            int nHeight,
            IntPtr hWndParent,
            IntPtr hMenu,
            IntPtr hInstance,
            IntPtr lpParam);

        private const ushort WM_USER = 0x0400;
        private const byte WS_EX_TOPMOST = 0x0008;
        private const int CW_USEDEFAULT = unchecked((int)0x80000000);
        private const byte TTS_ALWAYSTIP = 0x01;
        private const byte TTS_NOPREFIX = 0x02;
        private const byte TTF_TRACK = 0x0020;
        private const ushort TTM_ADDTOOLA = (WM_USER + 4);
        private const ushort TTM_UPDATETIPTEXT = (WM_USER + 12);
        private const ushort TTM_TRACKACTIVATE = (WM_USER + 17);
        private const ushort TTM_TRACKPOSITION = (WM_USER + 18);
        private const ushort TTM_SETMAXTIPWIDTH = (WM_USER + 24);

        private const int SM_CXVIRTUALSCREEN = 78;
        private const int SM_XVIRTUALSCREEN = 76;
        private const int SM_YVIRTUALSCREEN = 77;
        private const int SM_CYVIRTUALSCREEN = 79;
        private const int SM_CXSCREEN = 0;

        private struct RECT
            public int left;
            public int top;
            public int right;
            public int bottom;

        private struct POINTAPI
            public int x;
            public int y;


        private static bool winClose(IntPtr hWnd)
            if (hWnd != IntPtr.Zero)
                //WM_CLOSE = 0x10
                SendMessage(hWnd, 0x10, 0, 0);
                return true;
            return false;

        private static void GetVirtualDesktopRect(out RECT aRect)
            aRect.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
            if (aRect.right != 0) // A non-zero value indicates the OS supports multiple monitors or at least SM_CXVIRTUALSCREEN.
                aRect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);  // Might be negative or greater than zero.
                aRect.right += aRect.left;
       = GetSystemMetrics(SM_YVIRTUALSCREEN);   // Might be negative or greater than zero.
                aRect.bottom = + GetSystemMetrics(SM_CYVIRTUALSCREEN);
            else // Win95/NT do not support SM_CXVIRTUALSCREEN and such, so zero was returned.
                GetWindowRect(GetDesktopWindow(), out aRect);
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct TOOLINFO
            public int cbSize;
            public int uFlags;
            public IntPtr hwnd;
            public IntPtr uId;
            public RECT rect;
            public IntPtr hinst;
            public String lpszText;
            public IntPtr lParam;

        private static bool ToolTipCoordMode = true; //default coord mode - relative
        public static void CoordMode(bool if_screen_then_false)
            ToolTipCoordMode = if_screen_then_false;

        private static int MAKELONG(int wLow, int wHigh)
            int low = (int)LOWORD(wLow);
            short high = LOWORD(wHigh);
            int product = 0x00010000 * (int)high;
            int makeLong = (int)(low | product);
            return makeLong;
        private static short LOWORD(int dw)
            short loWord = 0;
            ushort andResult = (ushort)(dw & 0x00007FFF);
            ushort mask = 0x8000;
            if ((dw & 0x8000) != 0)
                loWord = (short)(mask | andResult);
                loWord = (short)andResult;
            return loWord;

        private static IntPtr[] g_hWndToolTip = new IntPtr[30]; //pointers to 30 tooltips

        public static bool Remove(int aID) // remove tooltip with aID
            return Show("", null, null, aID);

        public static bool Show(string aText, System.Nullable<int> aX, System.Nullable<int> aY, System.Nullable<int> aID) // create or modify tooltip
            int window_index = 0;
            if (aID.HasValue) { window_index = (int)(aID) - 1; }
            if (window_index < 0 | window_index >= 30) return false;
            IntPtr tip_hwnd = g_hWndToolTip[window_index];

            if (aText == "")
                if (tip_hwnd != IntPtr.Zero & IsWindow(tip_hwnd))
                g_hWndToolTip[window_index] = IntPtr.Zero;
                return true;

            RECT dtw = new RECT();
            GetVirtualDesktopRect(out dtw);

            bool one_or_both_coords_unspecified = (!aX.HasValue | !aY.HasValue);
            POINTAPI pt = new POINTAPI();
            POINTAPI pt_cursor = new POINTAPI();

            if (one_or_both_coords_unspecified)
                GetCursorPos(out pt_cursor);
                pt.x = pt_cursor.x + 16;  // Set default spot to be near the mouse cursor.
                pt.y = pt_cursor.y + 16;  // Use 16 to prevent the tooltip from overlapping large cursors.
            RECT rect = new RECT();

            if ((aX.HasValue | aY.HasValue) & ToolTipCoordMode)
                if (!GetWindowRect(GetForegroundWindow(), out rect))
                    return true;
            // This will convert from relative to screen coordinates if rect contains non-zero values:
            if (aX.HasValue)
                pt.x = (int)aX + (int)rect.left;
            if (aY.HasValue)
                pt.y = (int)aY + (int);

            TOOLINFO ti = new TOOLINFO();

            //ti.cbSize = Marshal.SizeOf(ti) - Marshal.SizeOf(typeof(void*)); //as in original autohotkey code
            ti.cbSize = Marshal.SizeOf(ti); //another version which is also working
            ti.uFlags = TTF_TRACK;
            ti.lpszText = aText;
            if (tip_hwnd == IntPtr.Zero | !IsWindow(tip_hwnd))
                tip_hwnd = g_hWndToolTip[window_index] = CreateWindowEx(WS_EX_TOPMOST, "tooltips_class32", "", TTS_NOPREFIX | TTS_ALWAYSTIP
                            , CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
                SendMessage(tip_hwnd, TTM_ADDTOOLA, 0, ref ti);
                // v1.0.21: GetSystemMetrics(SM_CXSCREEN) is used for the maximum width because even on a
                // multi-monitor system, most users would not want a tip window to stretch across multiple monitors:
                SendMessage(tip_hwnd, TTM_SETMAXTIPWIDTH, 0, GetSystemMetrics(SM_CXSCREEN));
                // Must do these next two when the window is first created, otherwise GetWindowRect() below will retrieve
                // a tooltip window size that is quite a bit taller than it winds up being:

                SendMessage(tip_hwnd, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));
                SendMessage(tip_hwnd, TTM_TRACKACTIVATE, 1, ref ti);

            SendMessage(tip_hwnd, TTM_UPDATETIPTEXT, 0, ref ti);

            RECT ttw = new RECT();
            GetWindowRect(tip_hwnd, out ttw); // Must be called this late to ensure the tooltip has been created by above.
            int tt_width = ttw.right - ttw.left;
            int tt_height = ttw.bottom -;

            // v1.0.21: Revised for multi-monitor support.  I read somewhere that dtw.left can be negative (perhaps
            // if the secondary monitor is to the left of the primary).  So it seems best to assume it is possible:
            if (pt.x + tt_width >= dtw.right)
                pt.x = dtw.right - tt_width - 1;
            if (pt.y + tt_height >= dtw.bottom)
                pt.y = dtw.bottom - tt_height - 1;

            if (one_or_both_coords_unspecified)
                // Since Tooltip is being shown at the cursor's coordinates, try to ensure that the above
                // adjustment doesn't result in the cursor being inside the tooltip's window boundaries,
                // since that tends to cause problems such as blocking the tray area (which can make a
                // tootip script impossible to terminate).  Normally, that can only happen in this case
                // (one_or_both_coords_unspecified == true) when the cursor is near the buttom-right
                // corner of the screen (unless the mouse is moving more quickly than the script's
                // ToolTip update-frequency can cope with, but that seems inconsequential since it
                // will adjust when the cursor slows down):
                ttw.left = pt.x;
       = pt.y;
                ttw.right = ttw.left + tt_width;
                ttw.bottom = + tt_height;
                if ((pt_cursor.x >= ttw.left) & (pt_cursor.x <= ttw.right) & (pt_cursor.y >= & (pt_cursor.y <= ttw.bottom))
                    // Push the tool tip to the upper-left side, since normally the only way the cursor can
                    // be inside its boundaries (when one_or_both_coords_unspecified == true) is when the
                    // cursor is near the bottom right corner of the screen.
                    pt.x = pt_cursor.x - tt_width - 3;    // Use a small offset since it can't overlap the cursor
                    pt.y = pt_cursor.y - tt_height - 3;   // when pushed to the the upper-left side of it.
            SendMessage(tip_hwnd, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));
            // And do a TTM_TRACKACTIVATE even if the tooltip window already existed upon entry to this function,
            // so that in case it was hidden or dismissed while its HWND still exists, it will be shown again:
            SendMessage(tip_hwnd, TTM_TRACKACTIVATE, 1, ref ti);
            return true;