Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
160 views
in Technique[技术] by (71.8m points)

c# - Change Window's Sticky Notes (StikyNot.exe) location (multiple instances)

It seems that all notes under StikyNot.exe are single exes instead of multiple. Also that means the coordinates of its location are always 0, 0, 0, 0. Is there a way to move it around? I tried using Win32's MoveWindow function without success.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here's an example of how to iterate through all the Sticky Note windows and move each of them. (Error checking has been removed for brevity. Also, be sure to read the note at the end for some comments on this implementation.)

First, we have to define the RECT struct.

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public RECT(int l, int t, int r, int b)
    {
        Left = l;
        Top = t;
        Right = r;
        Bottom = b;
    }
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

Then some key p/Invokes. We'll need FindWindowExW to locate the window with the correct window class for a sticky note. We also need GetWindowRect, so we can figure out the size of the window, so we only move it, rather than a move and resize. Finally, we need SetWindowPos which is pretty self-explanatory.

[DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr FindWindowExW(IntPtr hWndParent, IntPtr hWndAfter, 
                                          string lpszClass, string lpszWindow);

[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);

[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, 
                                       int Y, int cx, int cy, uint uFlags);

Finally our algorithm.

IntPtr hWnd = FindWindowExW(IntPtr.Zero, IntPtr.Zero, "Sticky_Notes_Note_Window", null);
if (hWnd == IntPtr.Zero)
{
    int error = Marshal.GetLastWin32Error();
    if (error > 0) throw new Win32Exception(error);
    else return;
}
IntPtr first = hWnd;
int currentX = 0;
while (hWnd != IntPtr.Zero)
{
    RECT r;
    bool result = GetWindowRect(hWnd, out r);
    if (!result)
    {
        int error = Marshal.GetLastWin32Error();
        if (error > 0) throw new Win32Exception(error);
        else return;
    }
    result = SetWindowPos(hWnd, 
                          IntPtr.Zero, 
                          currentX, 
                          0, 
                          r.Right - r.Left, 
                          r.Bottom - r.Top, 
                          0);
    if (!result)
    {
        int error = Marshal.GetLastWin32Error();
        if (error > 0) throw new Win32Exception(error);
        else return;
    }        

    currentX += r.Right - r.Left;
    hWnd = FindWindowExW(IntPtr.Zero, hWnd, "Sticky_Notes_Note_Window", null);
    if (hWnd == IntPtr.Zero)
    {
        int error = Marshal.GetLastWin32Error();
        if (error > 0) throw new Win32Exception(error);
        else return;
    }
    if (hWnd == first) hWnd = IntPtr.Zero;
}

How does it work? First, using a tool like Spy++, I found the window class. From the window's property sheet, we can see that the window's class name is Sticky_Notes_Note_Window.

Spy++ Property Sheet Screenshot

With the information from Spy++, the first window handle is obtained using FindWindowExW. This value is cached so that it can be determined when we've finished iterating all the windows. Inside the loop, we move the window, then use FindWindowEx to again locate the next window with the same class, if none are found, hWnd will be IntPtr.Zero aka NULL. We also have to check whether we are back to the start of our iteration. (If the notes are wider than the screen, they will spill off to the right. Wrapping the notes to another row is left as an exercise)

The issue with this implementation is that, if the first sticky note is closed, before we have iterated through all of them, then the program will never terminate. It would be better to keep track of all the windows that have been seen, and if any is seen again, then all have been enumerated.

An alternative method would be to use EnumWindows and inside the callback call GetClassName to see if it's a Sticky_Notes_Note_Window, and then act appropriately. The method above required less work, so it's the method I chose.

References:


Edit: Added error checking based on @DavidHeffernan's comment. Also added clarification about how I found the Window class name.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...