-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmodMemListMgr.twin
507 lines (460 loc) · 22.5 KB
/
modMemListMgr.twin
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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
/* ********************************************************************
* *
* Memory List Manager Version v2.1.4 *
* (Main module) *
* A simple tool to free standby memory and flush caches. *
* *
* Requirements: *
* -Windows XP or newer (note: Some functions require Windows 7, *
* or Windows 8/Windows 8.1). *
* -This program generally must be run as admin for all commands. *
* Specifically, most operations require the *
* SeProfileSingleProcessPrivilege and emptying the file cache *
* requires the SeIncreaseQuotaPrivilege. *
* -For the IDE, Windows Development Library for twinBASIC v8.3. *
* *
* Command line usage: *
* In addition to the GUI, the memory management actions can be *
* invoked through the command line without loading the main form: *
* /auto Automatic optimization *
* /clearstdby Clears standby list *
* /clearlpstdby Clears low-priority standby list *
* /flushmod Flush modified memory to disk *
* /combine Combines memory pages *
* /emptyws Empties working sets *
* /flushreg Flushes system registry cache *
* /flushfiles Flushes the system file cache *
* Note: Multiple commands are supported, and will be executed in *
* the order that they appear, left to right. *
* *
* Released: 29 June 2024 *
* *
* Author: Jon Johnson (aka fafalone). *
* (c) 2024 Licensed under the MIT License, see LICENSE.md *
* *
* Project home page: https://github.com/fafalone/MemListMgr *
* *
* Changelog *
* Version 2.1.4 (29 Jun 2024) *
* - Added toggle for memory bar at top *
* - GUI will now update when jump list commands are used *
* - Always call UpdateMemoryInfo immediately after op *
* - Bug fix: Autooptmize taskbar command not working *
* - Bug fix: Taskbar command accelerators not supported. *
* - Bug fix: Mem free display mismatch *
* - Temp. workaround for exit while minimized failure *
* *
* Version 2.0.2 (21 Jun 2024) *
* - Bug fix, debug MsgBox on exit still enabled. Built with new *
* version info fix. *
* Version 2.0 (21 Jun 2024) *
* -Added graphical Memory Bar to visualize modified/standby usage *
* Can change colors, double click key (small circle) to set *
* -Added Auto-optimize option for common procedure *
* -Added option to monitor status and auto-exec AutoOptimize *
* -Added minimize to tray option with commands available from a *
* popup menu when right-clicking the tray icon. (Vista+) *
* -New settings are saved to/loaded from registry *
* -Moved strings to constant list at top for easier translation. *
* *
* Version 1.3 (14 Jun 2024) - Added support for multiple command *
* line commands, executed in order left to right. *
* Version 1.2 (13 Jun 2024) - Free memory label width and commit *
* charge % regressed from tests to release, displaying wrong *
* Version 1.1 (13 Jun 2024) - SetCursor calls fixed. *
* Version 1.0 (13 Jun 2024) - Initial release. *
* *
***********************************************************************
In this file:
Module modMemListMgr - Main program module
Class cJumpList - Taskbar Jump List manager
******************************************************************** */
Module modMemListMgr
Option Explicit
[Description("Handle to our exe, or if IDE the last exe build to enable running from the IDE.")]
Public hMod As LongPtr
[Description("The Taskbar JumpList object class wrapper.")]
Public cJL As cJumpList
Public Const WM_MLMUPDATEINFO = WM_USER + &H140
'Icon resource IDs
Public Const IDI_MAIN = "101"
Public Const IDI_WS = "102"
Public Const IDI_REG = "103"
Public Const IDI_DISK = "104"
Public Const IDI_CMB = "105"
Public Const IDI_REFRESH = "106"
Public Const IDI_AUTO = "107"
Public Const szAutoOpt As String = "Auto-optimize"
Public Const szAutoOptTT As String = "Empties working sets, then flushes modified and standby."
Public Const IDM_AUTOOPT = 400
Public Const szClearStd As String = "Clear standby RAM"
Public Const szClearStdTT As String = "Clears memory marked as standby by the system."
Public Const IDM_CLEARSTD = 401
Public Const szClearStdLP As String = "Clear standby RAM (LP)"
Public Const szClearStdLPTT As String = "Clears memory marked as low-priority standby by the system."
Public Const IDM_CLEARSTDLP = 402
Public Const szEmptyWS As String = "Empty working sets"
Public Const szEmptyWSTT As String = "Empties working sets."
Public Const IDM_EMPTYWS = 403
Public Const szFlushMod As String = "Flush modified RAM"
Public Const szFlushModTT As String = "Flushes memory waiting to be written to disk."
Public Const IDM_FLUSHMOD = 404
Public Const szCmb As String = "Combine pages"
Public Const szCmbTT As String = "Combines memory pages for efficiency."
Public Const IDM_COMBINE = 405
Public Const szFlushReg As String = "Flush registry cache"
Public Const szFlushRegTT As String = "Flushes the system registry cache."
Public Const IDM_FLUSHREG = 406
Public Const szFlushFiles As String = "Flush system file cache"
Public Const szFlushFilesTT As String = "Flushes the system file cache."
Public Const IDM_FLUSHFILES = 407
Public Const szRestore As String = "Open Memory List Manager"
Public Const szRestoreTT As String = "Shows the full program window."
Public Const IDM_RESTORE = 408
Public Const szExit As String = "Exit"
Public Const szExitTT As String = "Completely exit the program."
Public Const IDM_EXIT = 409
Sub Main()
Dim fEnabled As Byte
Dim status As NTSTATUS = RtlAdjustPrivilege(SE_PROF_SINGLE_PROCESS_PRIVILEGE, 1, 0, fEnabled)
'Debug.Print "RtlAdjustPrivilege status=0x" & Hex$(status) & " " & GetNtErrorString(status)
Dim sCmd As String = Command$()
If sCmd = "" Then
If App.PrevInstance Then
Debug.Print "Previous instance detected.."
Dim hApp As LongPtr = FindWindow("ThunderRT6Main", "Memory List Manager")
If hApp Then
ShowWindow hApp, SW_SHOW
ShowWindow hApp, SW_RESTORE
SetForegroundWindow hApp
Dim tFWI As FLASHWINFO
tFWI.cbSize = LenB(Of FLASHWINFO)
tFWI.hWnd = hApp
tFWI.dwFlags = FLASHW_ALL
tFWI.uCount = 3
FlashWindowEx tFWI
End If
Exit Sub
End If
hMod = GetModuleHandleW()
Dim InIde As Boolean: Debug.Assert MakeTrue(InIde)
If InIde Then
'Attempt to load resources from last .exe
If (App.LastBuildPath = "") Or (PathFileExists(App.LastBuildPath) = 0) Then
MsgBox "Please build prior to running.", vbCritical + vbOKOnly, App.Title
Exit Sub
Else
hMod = LoadLibraryEx(App.LastBuildPath, 0, LOAD_LIBRARY_AS_DATAFILE Or LOAD_LIBRARY_AS_IMAGE_RESOURCE)
End If
End If
If NTDDI_VERSION >= NTDDI_WIN7 Then
CreateJumpList "MemListMgr.1.2.App.ID"
End If
frmMain.Show
Else
'Do not use the following for general command line parsing;
'it's tailored specifically to our case of simple commands
'with no arguments.
sCmd = Replace$(sCmd, " ", "")
sCmd = Replace$(sCmd, ChrW$(34), "")
If Len(sCmd) < 4 Then 'Length less than shortest command
MsgBox "Invalid command line.", vbCritical + vbOKOnly, App.Title
Exit Sub
End If
Dim sCmds() As String
sCmd = LCase$(sCmd)
Dim nCmd As Long = InStrCCnt(sCmd, "/")
If nCmd = 0 Then
MsgBox "Invalid command line.", vbCritical + vbOKOnly, App.Title
Exit Sub
End If
If nCmd = 1 Then
ReDim sCmds(0)
sCmds(0) = Mid$(sCmd, 2) 'Split will remove the delimiters
Else
sCmds() = Split(Mid$(sCmd, 2), "/")
End If
Dim i As Long
Dim wp As LongPtr
For i = 0 To UBound(sCmds)
If sCmds(i) = "clearstdby" Then
status = ClearStandby(False)
wp = IDM_CLEARSTD
ElseIf sCmds(i) = "clearlpstdby" Then
status = ClearStandby(True)
wp = IDM_CLEARSTDLP
ElseIf sCmds(i) = "flushmod" Then
status = FlushModified()
wp = IDM_FLUSHMOD
ElseIf sCmds(i) = "emptyws" Then
status = EmptyWorkingSets()
wp = IDM_EMPTYWS
ElseIf sCmds(i) = "combine" Then
status = CombinePages()
wp = IDM_COMBINE
ElseIf sCmds(i) = "flushreg" Then
status = FlushRegistryCache()
wp = IDM_FLUSHREG
ElseIf sCmds(i) = "flushfiles" Then
status = FlushFileCache()
wp = IDM_FLUSHFILES
ElseIf sCmds(i) = "auto" Then
status = AutoOptimize()
wp = IDM_AUTOOPT
Else
MsgBox "Invalid command: " & sCmds(i), vbCritical + vbOKOnly, App.Title
Exit Sub
End If
Next
Dim hWndApp As LongPtr = FindWindow("ThunderFormDC", "Memory List Manager")
If hWndApp Then 'If an existing instance is running, tell it to update
SendMessage hWndApp, WM_MLMUPDATEINFO, wp, ByVal status
End If
' MsgBox "Sent WM_MLMUPDATEINFO(" & wp & ", " & status & ") to " & Hex$(hWndApp)
End If
End Sub
Public Function MakeTrue(ByRef x As Boolean) As Boolean
x = True: MakeTrue = True
End Function
Private Function InStrCCnt(ByVal str As String, ByVal char As String) As Long
If Len(char) > 1 Then char = Left$(char, 1)
Dim i As Long, j As Long
For i = 1 To Len(str)
If Mid$(str, i, 1) = char Then j += 1
Next
Return j
End Function
Public Function GetExePath() As String
Dim sTmp As String = String$(MAX_PATH, 0)
Dim lRet As Long = GetModuleFileName(hMod, sTmp, Len(sTmp))
If lRet Then
Return Left$(sTmp, lRet)
Else
Return ""
End If
End Function
Private Sub CreateJumpList(sID As String)
On Error GoTo e0
Set cJL = New cJumpList
cJL.InitList sID
Dim pTasks As New EnumerableObjectCollection
cJL.AddToList pTasks, szAutoOpt, "/auto", szAutoOptTT, CLng(IDI_AUTO) - 101
cJL.AddToList pTasks 'Leave blank for separator
cJL.AddToList pTasks, szClearStd, "/clearstdby", szClearStdTT, CLng(IDI_MAIN) - 101 'The icon id needed is the zero-based index
cJL.AddToList pTasks, szClearStdLP, "/clearlpstdby", szClearStdLPTT, CLng(IDI_MAIN) - 101
cJL.AddToList pTasks, szFlushMod, "/flushmod", szFlushModTT, CLng(IDI_MAIN) - 101
cJL.AddToList pTasks 'Leave blank for separator
cJL.AddToList pTasks, szEmptyWS, "/emptyws", szEmptyWSTT, CLng(IDI_WS) - 101
cJL.AddToList pTasks, szCmb, "/combine", szCmbTT, CLng(IDI_CMB) - 101
cJL.AddToList pTasks, szFlushReg, "/flushreg", szFlushRegTT, CLng(IDI_REG) - 101
cJL.AddToList pTasks, szFlushFiles, "/flushfiles", szFlushFilesTT, CLng(IDI_DISK) - 101
cJL.AddUserTasks pTasks
cJL.CommitList
Exit Sub
e0:
Debug.Print "Error creating jump list, " & Err.Number & ": " & Err.Description
End Sub
Public Function ClearStandby(Optional bLowPriority As Boolean = False) As NTSTATUS
Dim nCmd As SYSTEM_MEMORY_LIST_COMMAND
If bLowPriority Then
nCmd = MemoryPurgeLowPriorityStandbyList
Else
nCmd = MemoryPurgeStandbyList
End If
Dim status As NTSTATUS
SetCursor LoadCursor(0, ByVal IDC_WAIT)
status = NtSetSystemInformation(SystemMemoryListInformation, nCmd, LenB(nCmd))
SetCursor LoadCursor(0, ByVal IDC_ARROW)
Return status
End Function
Public Function FlushModified() As NTSTATUS
Dim nCmd As SYSTEM_MEMORY_LIST_COMMAND = MemoryFlushModifiedList
Dim status As NTSTATUS
SetCursor LoadCursor(0, ByVal IDC_WAIT)
status = NtSetSystemInformation(SystemMemoryListInformation, nCmd, LenB(nCmd))
SetCursor LoadCursor(0, ByVal IDC_ARROW)
Return status
End Function
Public Function EmptyWorkingSets() As NTSTATUS
Dim nCmd As SYSTEM_MEMORY_LIST_COMMAND = MemoryEmptyWorkingSets
Dim status As NTSTATUS
SetCursor LoadCursor(0, ByVal IDC_WAIT)
status = NtSetSystemInformation(SystemMemoryListInformation, nCmd, LenB(nCmd))
SetCursor LoadCursor(0, ByVal IDC_ARROW)
Return status
End Function
Public Function CombinePages(Optional pNumCombined As LongLong) As NTSTATUS
Dim mci As MEMORY_COMBINE_INFORMATION_EX
Dim status As NTSTATUS
SetCursor LoadCursor(0, ByVal IDC_WAIT)
status = NtSetSystemInformation(SystemCombinePhysicalMemoryInformation, mci, LenB(Of MEMORY_COMBINE_INFORMATION_EX))
pNumCombined = mci.PagesCombined
SetCursor LoadCursor(0, ByVal IDC_ARROW)
Return status
End Function
Public Function FlushRegistryCache() As NTSTATUS
SetCursor LoadCursor(0, ByVal IDC_WAIT)
FlushRegistryCache = NtSetSystemInformation(SystemRegistryReconciliationInformation, ByVal 0&, 0&)
SetCursor LoadCursor(0, ByVal IDC_ARROW)
End Function
Public Function FlushFileCache(Optional pSize As LongLong) As NTSTATUS
Dim sfi As SYSTEM_FILECACHE_INFORMATION
Dim sfiSet As SYSTEM_FILECACHE_INFORMATION
Dim status As NTSTATUS
Dim bRet As Byte
Dim cb As Long
SetCursor LoadCursor(0, ByVal IDC_WAIT)
status = RtlAdjustPrivilege(SE_INCREASE_QUOTA_PRIVILEGE, 1, 0, bRet)
status = NtQuerySystemInformation(SystemFileCacheInformationEx, sfi, LenB(Of SYSTEM_FILECACHE_INFORMATION), cb)
If NT_SUCCESS(status) Then
sfiSet.MinimumWorkingSet = MAXSIZE_T
sfiSet.MaximumWorkingSet = MAXSIZE_T
status = NtSetSystemInformation(SystemFileCacheInformationEx, sfiSet, LenB(Of SYSTEM_FILECACHE_INFORMATION))
pSize = sfi.CurrentSize
End If
SetCursor LoadCursor(0, ByVal IDC_ARROW)
Return status
End Function
Public Function AutoOptimize() As NTSTATUS
Dim status As NTSTATUS
status = EmptyWorkingSets()
If Not NT_SUCCESS(status) Then
AutoOptimize = status
End If
status = FlushModified()
If Not NT_SUCCESS(status) Then
AutoOptimize = status
End If
status = ClearStandby()
If Not NT_SUCCESS(status) Then
AutoOptimize = status
End If
End Function
Public Function NTDDI_VERSION() As Long
'Gets the NTDDI version value associated with the current OS
'Note: This is imperfect; not all Win10/11 builds are represented,
' and it's not future proof as those are hard coded.
'Ignores version lie compatibility shims
Static ntddi As Long
If ntddi Then Return ntddi
Dim dwMajor As Long, dwMinor As Long, dwBuild As Long
CopyMemory dwMajor, ByVal &H7FFE026C, 4
CopyMemory dwMinor, ByVal &H7FFE0270, 4
Dim tPeb As PEB, lpPeb As LongPtr
Dim usCSD As Integer
lpPeb = RtlGetCurrentPeb()
If lpPeb Then
CopyMemory tPeb, ByVal lpPeb, LenB(Of PEB)
usCSD = tPeb.OSCSDVersion
' Debug.Print "OSCSDVersion = " & usCSD
End If
If dwMajor < 10 Then
ntddi += ((dwMajor And &HFF&) << 24&)
ntddi += ((dwMinor And &HFF&) << 16&)
ntddi += ((usCSD >> 8) And &HFF)
ntddi += (usCSD And &HFF&)
ElseIf (dwMajor = 10) And (dwMinor = 0) Then
CopyMemory dwBuild, ByVal &H7FFE0260, 4
If dwBuild > 26100 Then
ntddi = WDK_NTDDI_VERSION
ElseIf dwBuild >= 26100 Then: ntddi = NTDDI_WIN11_GE
ElseIf dwBuild >= 25346 Then: ntddi = NTDDI_WIN11_ZN
ElseIf dwBuild >= 22631 Then: ntddi = NTDDI_WIN11_CU
ElseIf dwBuild >= 22621 Then: ntddi = NTDDI_WIN11_NI
ElseIf dwBuild >= 22000 Then: ntddi = NTDDI_WIN11
ElseIf dwBuild >= 19045 Then: ntddi = NTDDI_WIN10_CU
ElseIf dwBuild >= 19044 Then: ntddi = NTDDI_WIN10_CO
ElseIf dwBuild >= 19043 Then: ntddi = NTDDI_WIN10_FE
ElseIf dwBuild >= 19042 Then: ntddi = NTDDI_WIN10_MN
ElseIf dwBuild >= 19041 Then: ntddi = NTDDI_WIN10_VB
ElseIf dwBuild >= 18362 Then: ntddi = NTDDI_WIN10_19H1
ElseIf dwBuild >= 17763 Then: ntddi = NTDDI_WIN10_RS5
ElseIf dwBuild >= 17134 Then: ntddi = NTDDI_WIN10_RS4
ElseIf dwBuild >= 16299 Then: ntddi = NTDDI_WIN10_RS3
ElseIf dwBuild >= 15063 Then: ntddi = NTDDI_WIN10_RS2
ElseIf dwBuild >= 14393 Then: ntddi = NTDDI_WIN10_RS1
ElseIf dwBuild >= 10586 Then: ntddi = NTDDI_WIN10_TH2
ElseIf dwBuild >= 10240 Then: ntddi = NTDDI_WINTHRESHOLD
End If
Else
ntddi = MAXLONG
End If
Return ntddi
End Function
End Module
Class cJumpList
Option Explicit
'cJumpList v1.1: Using Jump Lists in twinBASIC
'by fafalone
'
'Requires Windows Development Library for twinBASIC (WinDevLib)
Private pCDL As ICustomDestinationList
Public Function InitList(sAppID As String, Optional nMinSlot As Long) As IObjectArray
On Error GoTo e0
SetCurrentProcessExplicitAppUserModelID StrPtr(sAppID)
Set pCDL = New DestinationList
pCDL.SetAppId StrPtr(sAppID)
pCDL.BeginList nMinSlot, IID_IObjectArray, InitList
Exit Function
e0:
Debug.Print "Error initializing jump list, " & Err.Number & ": " & Err.Description
End Function
Public Sub AddToList(pList As IObjectCollection, Optional sTitle As String, Optional sArgs As String, Optional sToolTip As String, Optional ByVal nIconID As Long)
Dim hr As Long
Dim pLink As ShellLinkW
Dim pStore As IPropertyStore
Dim pUnk As IUnknownUnrestricted
Set pLink = New ShellLinkW
Dim sFile As String
Dim bWow64 As BOOL
IsWow64Process(GetCurrentProcess(), bWow64)
Dim InIde As Boolean: Debug.Assert MakeTrue(InIde)
If InIde() Then
If App.LastBuildPath <> "" Then
sFile = App.LastBuildPath
Else
'TODO: Change when tB Bug relating to EXEName is fixed.
If bWow64 Then
sFile = App.Path & "\" & App.EXEName & "_win32.exe"
Else
sFile = App.Path & "\" & App.EXEName & "_win64.exe"
End If
End If
Else
sFile = GetExePath()
End If
pLink.SetPath StrPtr(sFile)
pLink.SetArguments StrPtr(sArgs)
pLink.SetDescription StrPtr(sToolTip)
pLink.SetIconLocation StrPtr(sFile), nIconID
Set pUnk = pLink
hr = pUnk.QueryInterface(IID_IPropertyStore, pStore)
If SUCCEEDED(hr) Then
If sTitle <> "" Then
Dim vTitle As Variant
vTitle = CVar(sTitle)
pStore.SetValue PKEY_Title, vTitle
pStore.Commit
Else
pStore.SetValue PKEY_AppUserModel_IsDestListSeparator, CVar(True)
pStore.Commit
End If
pList.AddObject pLink
End If
Exit Sub
e0:
Debug.Print "Error adding item to jump list, " & Err.Number & ": " & Err.Description
End Sub
Public Sub AddUserTasks(pList As IObjectCollection)
pCDL.AddUserTasks ByVal ObjPtr(pList)
End Sub
Public Sub AppendCategory(sCat As String, pList As IObjectCollection)
pCDL.AppendCategory ByVal StrPtr(sCat), ByVal ObjPtr(pList)
End Sub
Public Sub AbortList()
pCDL.AbortList
End Sub
Public Sub CommitList()
pCDL.CommitList
End Sub
End Class