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.
-
Can you show us what you tried? A glance with Spy++ says that each note has its own window, perhaps you're using the wrong handle. – theB Sep 13 '15 at 01:01
-
Aha! I tried Spy++ equalevant WinSpector and it gave me an idea to try to get process' child windows. Now I got it working, thanks a lot. I didn't know they existed. :) – user1422494 Sep 13 '15 at 10:32
-
Okay I was wrong. I used this method to grab them, but it returns over 50 handles while there are only 6 notes running. http://pastebin.com/7g2f7Ay6 Does that make sense to you? – user1422494 Sep 13 '15 at 20:59
-
On windows, most things are 'windows'. You'd need to filter those based on the window class, to get the filtered list. – theB Sep 13 '15 at 21:06
1 Answers
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
.
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.

- 6,450
- 1
- 28
- 38
-
-
Very, very interesting. Thanks a lot for the big post! I wonder how you foundnd 'Sticky_Notes_Note_Window'? I've never heard of it and didn't know all application had their own keys/classnames like this. I got it to work fully thanks to you. Thanks a lot! – user1422494 Sep 14 '15 at 12:01
-
1@DavidHeffernan - I added error checking. I removed it initially to make the code block non-scrolling. – theB Sep 14 '15 at 12:12
-
@user1422494 - I added information about how to obtain the class name for the window. Only some applications create their own custom window classes. It's entirely application specific, and it's not contractual. That is, the next version of Sticky Notes is free to change the window class name. – theB Sep 14 '15 at 12:13
-