-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSortIcons.cs
More file actions
275 lines (234 loc) · 9.97 KB
/
Copy pathSortIcons.cs
File metadata and controls
275 lines (234 loc) · 9.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/* Copyright (C) 2023 - 2025 Thomas F. Hornyak, Tom Hornyak Enterprise LLC - All Rights Reserved
* You may use, distribute and modify this code under the
* terms of the MIT LICENSE
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
namespace SortDesktopIcons
{
static class SortIcons
{
[ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IServiceProvider
{
[return: MarshalAs(UnmanagedType.IUnknown)]
object QueryService([MarshalAs(UnmanagedType.LPStruct)] Guid service, [MarshalAs(UnmanagedType.LPStruct)] Guid riid);
}
// note: for the following interfaces, not all methods are defined as we don't use them here
[ComImport, Guid("000214E2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IShellBrowser
{
void _VtblGap1_12(); // skip 12 methods https://stackoverflow.com/a/47567206/403671
[return: MarshalAs(UnmanagedType.IUnknown)]
object QueryActiveShellView();
}
[ComImport, Guid("cde725b0-ccc9-4519-917e-325d72fab4ce"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IFolderView
{
void _VtblGap1_3(); // skip 3 methods
IntPtr Item(int iItemIndex);
int ItemCount(uint uFlags = 0);
void _VtblGap2_3(); // skip 2 methods
void GetItemPosition(IntPtr pidl, out POINT ppt);
void _VtblGap1_4(); // skip 4 methods
void SelectAndPositionItems(int cidl, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] apidl, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] POINT[] apt, SVSIF dwFlags);
// more undefined methods
}
public enum FOLDERFLAGS
{
FWF_AUTOARRANGE = 0x1,
FWF_SNAPTOGRID = 0x4
}
[ComImport, Guid("1af3a467-214f-4298-908e-06b03e0b39f9"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IFolderView2
{
//void _VtblGap1_26(); // skip 14 (IFolderView) + 12 methods
void _VtblGap1_21(); // skip 14 (IFolderView) + 12 methods
uint SetCurrentFolderFlags(int dwMask, int dwFlags);
void _VtblGap1_4();
IShellItem GetItem(int iItemIndex, [MarshalAs(UnmanagedType.LPStruct)] Guid riid);
// more undefined methods
}
[ComImport, Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IShellItem
{
[return: MarshalAs(UnmanagedType.IUnknown)]
object BindToHandler(System.Runtime.InteropServices.ComTypes.IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, [MarshalAs(UnmanagedType.LPStruct)] Guid riid);
IShellItem GetParent();
[return: MarshalAs(UnmanagedType.LPWStr)]
string GetDisplayName(SIGDN sigdnName);
[return: MarshalAs(UnmanagedType.U4)]
uint GetAttributesOf(uint sfgaoMask);
// more undefined methods
}
public struct POINT
{
public int x;
public int y;
//Eliminate stupit warning about uninitialized y
public POINT()
{
x = -1;
y = -1;
}
public override string ToString()
{
return $"({x},{y})";
}
}
public enum SIGDN
{
SIGDN_NORMALDISPLAY,
SIGDN_PARENTRELATIVEPARSING,
SIGDN_DESKTOPABSOLUTEPARSING,
SIGDN_PARENTRELATIVEEDITING,
SIGDN_DESKTOPABSOLUTEEDITING,
SIGDN_FILESYSPATH,
SIGDN_URL,
SIGDN_PARENTRELATIVEFORADDRESSBAR,
SIGDN_PARENTRELATIVE,
SIGDN_PARENTRELATIVEFORUI
}
[Flags]
public enum SVSIF
{
SVSI_DESELECT = 0,
SVSI_SELECT = 0x1,
SVSI_EDIT = 0x3,
SVSI_DESELECTOTHERS = 0x4,
SVSI_ENSUREVISIBLE = 0x8,
SVSI_FOCUSED = 0x10,
SVSI_TRANSLATEPT = 0x20,
SVSI_SELECTIONMARK = 0x40,
SVSI_POSITIONITEM = 0x80,
SVSI_CHECK = 0x100,
SVSI_CHECK2 = 0x200,
SVSI_KEYBOARDSELECT = 0x401,
SVSI_NOTAKEFOCUS = 0x40000000
}
public enum SFGAO
{
CANCOPY = 0x1,
CANMOVE = 0x2,
CANLINK = 0x4,
STORAGE = 0x8,
CANRENAME = 0x10,
CANDELETE = 0x20,
HASPROPSHEET = 0x40,
DROPTARGET = 0x100,
CAPABILITYMASK = 0x177,
ENCRYPTED = 0x2000,
ISSLOW = 0x4000,
GHOSTED = 0x8000,
LINK = 0x10000,
SHARE = 0x20000,
READONLY = 0x40000,
HIDDEN = 0x80000,
DISPLAYATTRMASK = 0xFC000,
STREAM = 0x400000,
STORAGEANCESTOR = 0x800000,
VALIDATE = 0x1000000,
REMOVABLE = 0x2000000,
COMPRESSED = 0x4000000,
BROWSABLE = 0x8000000,
FILESYSANCESTOR = 0x10000000,
FOLDER = 0x20000000,
FILESYSTEM = 0x40000000,
HASSUBFOLDER = unchecked((int)0x80000000),
CONTENTSMASK = unchecked((int)0x80000000),
STORAGECAPMASK = 0x70C50008,
PKEYSFGAOMASK = unchecked((int)0x81044000)
}
struct icon
{
public int index;
public string name;
public POINT locn;
public IntPtr pid;
public string type;
public override string ToString()
{
return $"Index: {index}, {type}, Name: {name}, Location: {locn}";
}
}
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern IntPtr GetConsoleWindow();
[DllImport("User32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ShowWindow([In] IntPtr hWnd, [In] Int32 nCmdShow);
const string SHELL_CLASS_NAME = "Progman";
const uint WM_COMMAND = 0x111;
const int CMD_SORT_BY_NAME = 0x800020;
[STAThread]
public static void Main()
{
IntPtr handle = GetConsoleWindow();
ShowWindow(handle, 6);
IntPtr desktopHandle = FindWindow(SHELL_CLASS_NAME, null);
dynamic app = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
var windows = app.Windows;
const int SWC_DESKTOP = 8;
const int SWFO_NEEDDISPATCH = 1;
var hwnd = 0;
var disp = windows.FindWindowSW(Type.Missing, Type.Missing, SWC_DESKTOP, ref hwnd, SWFO_NEEDDISPATCH);
var sp = (IServiceProvider)disp;
var SID_STopLevelBrowser = new Guid("4c96be40-915c-11cf-99d3-00aa004ae837");
var browser = (IShellBrowser)sp.QueryService(SID_STopLevelBrowser, typeof(IShellBrowser).GUID);
var view = (IFolderView)browser.QueryActiveShellView();
var view2 = (IFolderView2)view;
// First let windows sort the desktop - let it figure our spacing, size, etc.
// Then sort by type and name
view2.SetCurrentFolderFlags((int)(FOLDERFLAGS.FWF_SNAPTOGRID | FOLDERFLAGS.FWF_AUTOARRANGE),
(int)(FOLDERFLAGS.FWF_SNAPTOGRID | FOLDERFLAGS.FWF_AUTOARRANGE));
if (desktopHandle != IntPtr.Zero)
{
SendMessage(desktopHandle, WM_COMMAND, new IntPtr(CMD_SORT_BY_NAME), IntPtr.Zero);
//Console.WriteLine("Desktop icons sorted by name.");
}
view2.SetCurrentFolderFlags((int)(FOLDERFLAGS.FWF_AUTOARRANGE), 0);
var icons = new List<icon>();
// replace the icons with the sorted list.
for (var i = 0; i < view.ItemCount(); i++)
{
var item = view2.GetItem(i, typeof(IShellItem).GUID);
uint attrib = (uint)SFGAO.FOLDER;
var type = item.GetAttributesOf(attrib) == 0 ? "File" : "Folder";
var displayName = item.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING);
var pidl = view.Item(i);
view.GetItemPosition(pidl, out var pt);
var ic = new icon { index = i, locn = pt, name = displayName, pid = pidl, type = type };
icons.Add(ic);
//Console.WriteLine(ic.ToString());
}
var byPos = new List<icon>();
byPos =
icons.OrderBy(icon => icon.locn.x)
.ThenBy(icon => icon.locn.y)
.ToList();
var sortedIcons = new List<icon>();
sortedIcons =
byPos.OrderByDescending(icon => icon.type)
.ThenBy(icon => icon.name)
.ToList();
var ptrs = new List<IntPtr>();
var locns = new List<POINT>();
for (int i = 0; i < sortedIcons.Count(); i++)
{
var tempIcon = sortedIcons[i];
tempIcon.locn = byPos[i].locn;
sortedIcons[i] = tempIcon;
ptrs.Add(tempIcon.pid);
locns.Add(tempIcon.locn);
}
IntPtr[] apidl = ptrs.ToArray();
POINT[] apt = locns.ToArray();
view.SelectAndPositionItems(apidl.Length, apidl, apt, SVSIF.SVSI_POSITIONITEM);
}
}
}