找回密码
 注册
搜索
[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
查看: 467|回复: 6

[转载代码] 终于解决了不重启explorer修改桌面图标大小的问题

[复制链接]
发表于 2026-11-4 09:24:47 | 显示全部楼层 |阅读模式
  1. # =====================================================================
  2. # Set-DesktopIconSize.ps1
  3. # 桌面图标大小自适应脚本(兼容 Win7 / Win10 / Win11,PS 2.0+)
  4. # 逻辑:读取主屏水平分辨率(物理像素)
  5. #       <= 1920  -> 桌面图标设为“小图标”(32px)
  6. #       >  1920  -> 不做更改(加 -ForceMedium 参数则设为“中等图标”(48px))
  7. #
  8. # 主路径(COM,参考 AutoHotkey 社区脚本,文档化 API,一次生效):
  9. #   Shell.Application.Windows 中找到桌面项
  10. #   -> QueryService(SID_STopLevelBrowser) 取 IShellBrowser
  11. #   -> QueryActiveShellView 取当前 Shell 视图
  12. #   -> IFolderView2::SetViewModeAndIconSize(FVM_ICON, size)   精确设置像素
  13. #   -> IFolderView2::GetViewModeAndIconSize 读回实际值校验
  14. # 备用路径:WM_COMMAND 视图命令(0x7050 小 / 0x704E 中,带重发,兼容 Win7 吞消息)
  15. # 兜底路径:-FixByRestart 写注册表 IconSize 并重启 explorer
  16. # =====================================================================
  17. param(
  18.     [switch]$ForceMedium,
  19.     [switch]$FixByRestart
  20. )

  21. $ErrorActionPreference = 'SilentlyContinue'

  22. Add-Type -TypeDefinition @'
  23. using System;
  24. using System.Text;
  25. using System.Runtime.InteropServices;

  26. public static class DesktopIcon
  27. {
  28.     [DllImport("user32.dll")] public static extern bool SetProcessDPIAware();
  29.     [DllImport("user32.dll")] public static extern int GetSystemMetrics(int nIndex);
  30.     [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
  31.     public delegate bool EnumProc(IntPtr hWnd, IntPtr lParam);
  32.     [DllImport("user32.dll")] public static extern bool EnumWindows(EnumProc cb, IntPtr lParam);
  33.     [DllImport("user32.dll")] public static extern bool EnumChildWindows(IntPtr parent, EnumProc cb, IntPtr lParam);
  34.     [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetClassName(IntPtr h, StringBuilder sb, int max);
  35.     [DllImport("user32.dll")] public static extern IntPtr GetAncestor(IntPtr hwnd, uint flags);

  36.     public static IntPtr Found = IntPtr.Zero;
  37.     public static IntPtr List = IntPtr.Zero;
  38.     private static bool ChildCb(IntPtr h, IntPtr l) {
  39.         StringBuilder sb = new StringBuilder(256);
  40.         GetClassName(h, sb, 256);
  41.         string cls = sb.ToString();
  42.         if (cls == "SHELLDLL_DefView") { Found = h; }
  43.         if (cls == "SysListView32") { List = h; }
  44.         return true;
  45.     }
  46.     private static bool TopCb(IntPtr h, IntPtr l) {
  47.         StringBuilder sb = new StringBuilder(256);
  48.         GetClassName(h, sb, 256);
  49.         string cls = sb.ToString();
  50.         // 桌面视图正常挂在 Progman 下;Win11 壁纸轮播等场景挂在 WorkerW 下
  51.         if (cls == "Progman" || cls == "WorkerW") {
  52.             EnumChildWindows(h, ChildCb, IntPtr.Zero);
  53.         }
  54.         return true;
  55.     }
  56.     public static IntPtr FindDesktopView() {
  57.         Found = IntPtr.Zero; List = IntPtr.Zero;
  58.         EnumWindows(TopCb, IntPtr.Zero);
  59.         return Found;
  60.     }
  61.     // LVM_GETITEMSPACING (0x1033):读取桌面列表实际图标间距
  62.     public static int GetItemSpacing() {
  63.         if (List == IntPtr.Zero) { return 0; }
  64.         IntPtr r = SendMessage(List, 0x1033, IntPtr.Zero, IntPtr.Zero);
  65.         return (int)(r.ToInt64() & 0xFFFFFFFF);
  66.     }
  67.     public static IntPtr GetDesktopRootWindow() {
  68.         FindDesktopView();
  69.         if (Found == IntPtr.Zero) { return IntPtr.Zero; }
  70.         return GetAncestor(Found, 2);   // GA_ROOT
  71.     }
  72. }

  73. public static class ShellViewCom
  74. {
  75.     [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  76.     public interface IServiceProvider {
  77.         [PreserveSig] int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject);
  78.     }

  79.     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  80.     public delegate int QueryActiveShellView_t(IntPtr self, out IntPtr ppshv);
  81.     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  82.     public delegate int GetViewModeAndIconSize_t(IntPtr self, out int viewMode, out int iconSize);
  83.     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  84.     public delegate int SetViewModeAndIconSize_t(IntPtr self, int viewMode, int iconSize);

  85.     // 通过 COM 设置桌面图标大小;actualSize/actualMode 返回设置后读回的实际值
  86.     public static int SetIconSize(object desktopObj, int size, out int actualSize, out int actualMode, out string step) {
  87.         actualSize = -1; actualMode = -1; step = "";
  88.         IntPtr pUnk = Marshal.GetIUnknownForObject(desktopObj);
  89.         try {
  90.             IServiceProvider sp = (IServiceProvider)Marshal.GetObjectForIUnknown(pUnk);
  91.             Guid sid  = new Guid("4C96BE40-915C-11CF-99D3-00AA004AE837"); // SID_STopLevelBrowser
  92.             Guid iidB = new Guid("000214E2-0000-0000-C000-000000000046"); // IID_IShellBrowser
  93.             IntPtr pBrowser;
  94.             int hr = sp.QueryService(ref sid, ref iidB, out pBrowser);
  95.             if (hr != 0) { step = "QueryService failed 0x" + hr.ToString("X"); return hr; }
  96.             try {
  97.                 IntPtr vtbl = Marshal.ReadIntPtr(pBrowser);
  98.                 IntPtr fn = Marshal.ReadIntPtr(vtbl, 15 * IntPtr.Size);
  99.                 QueryActiveShellView_t qasv = (QueryActiveShellView_t)Marshal.GetDelegateForFunctionPointer(fn, typeof(QueryActiveShellView_t));
  100.                 IntPtr pView;
  101.                 hr = qasv(pBrowser, out pView);
  102.                 if (hr != 0 || pView == IntPtr.Zero) { step = "QueryActiveShellView failed 0x" + hr.ToString("X"); return (hr == 0) ? -1 : hr; }
  103.                 try {
  104.                     Guid iidFV2 = new Guid("1AF3A467-214F-4298-908E-06B03E0B39F9"); // IID_IFolderView2
  105.                     IntPtr pFV2;
  106.                     hr = Marshal.QueryInterface(pView, ref iidFV2, out pFV2);
  107.                     if (hr != 0) { step = "IFolderView2 QI failed 0x" + hr.ToString("X"); return hr; }
  108.                     try {
  109.                         IntPtr vtbl2 = Marshal.ReadIntPtr(pFV2);
  110.                         GetViewModeAndIconSize_t getFn = (GetViewModeAndIconSize_t)Marshal.GetDelegateForFunctionPointer(
  111.                             Marshal.ReadIntPtr(vtbl2, 36 * IntPtr.Size), typeof(GetViewModeAndIconSize_t));
  112.                         SetViewModeAndIconSize_t setFn = (SetViewModeAndIconSize_t)Marshal.GetDelegateForFunctionPointer(
  113.                             Marshal.ReadIntPtr(vtbl2, 35 * IntPtr.Size), typeof(SetViewModeAndIconSize_t));

  114.                         int mode, isz;
  115.                         hr = getFn(pFV2, out mode, out isz);
  116.                         if (hr == 0) { actualMode = mode; actualSize = isz; }

  117.                         hr = setFn(pFV2, 1, size);   // FVM_ICON = 1
  118.                         if (hr != 0) { step = "SetViewModeAndIconSize failed 0x" + hr.ToString("X"); return hr; }

  119.                         hr = getFn(pFV2, out mode, out isz);
  120.                         if (hr == 0) { actualMode = mode; actualSize = isz; }
  121.                         return 0;
  122.                     } finally { Marshal.Release(pFV2); }
  123.                 } finally { Marshal.Release(pView); }
  124.             } finally { Marshal.Release(pBrowser); }
  125.         } finally { Marshal.Release(pUnk); }
  126.     }
  127. }
  128. '@

  129. # 先声明 DPI 感知,保证读到物理分辨率而不是被缩放虚拟化的值
  130. [void][DesktopIcon]::SetProcessDPIAware()

  131. $WM_COMMAND = 0x0111
  132. $CMD_SMALL  = 0x7050    # FVM_SMALL      -> 小图标   IconSize=32
  133. $CMD_MEDIUM = 0x704E    # FVM_MEDIUMICON -> 中等图标 IconSize=48
  134. $REG_PATH   = 'HKCU:\Software\Microsoft\Windows\Shell\Bags\1\Desktop'

  135. function Get-CurrentIconSize {
  136.     # 当前桌面图标大小(像素):256=特大 96=大 48=中 32=小
  137.     $v = Get-ItemProperty -Path $REG_PATH -Name 'IconSize'
  138.     if ($v -ne $null -and $v.IconSize -ne $null) { return [int]$v.IconSize }
  139.     return -1
  140. }

  141. # ---------- 主路径:COM (IFolderView2::SetViewModeAndIconSize) ----------
  142. function Set-IconSizeByCom([int]$targetSize) {
  143.     # 在 Shell.Windows 集合中找到桌面项:HWND 与桌面视图根窗口一致优先
  144.     $shell = New-Object -ComObject Shell.Application
  145.     $sw = $shell.Windows()
  146.     $rootHwnd = [DesktopIcon]::GetDesktopRootWindow()
  147.     $desktopObj = $null
  148.     if ($sw -ne $null) {
  149.         # 策略1:文档化技巧 Item(SWC_DESKTOP=8),与 AHK 脚本一致
  150.         try {
  151.             $d = $sw.Item(8)
  152.             if ($d -ne $null) { $desktopObj = $d }
  153.         } catch {}
  154.         # 策略2:遍历集合按 HWND 匹配桌面视图根窗口
  155.         if ($desktopObj -eq $null -and $rootHwnd -ne [IntPtr]::Zero) {
  156.             for ($i = 0; $i -lt $sw.Count; $i++) {
  157.                 $item = $sw.Item($i)
  158.                 if ($item -ne $null -and $item.HWND -eq $rootHwnd.ToInt64()) { $desktopObj = $item; break }
  159.             }
  160.         }
  161.     }
  162.     if ($desktopObj -eq $null) {
  163.         Write-Host '[INFO] COM: desktop item not found in Shell.Windows.'
  164.         return $false
  165.     }

  166.     $actualSize = 0; $actualMode = 0; $step = ''
  167.     $hr = [ShellViewCom]::SetIconSize($desktopObj, $targetSize, [ref]$actualSize, [ref]$actualMode, [ref]$step)
  168.     if ($hr -ne 0) {
  169.         Write-Host ('[INFO] COM: failed at {0} (hr=0x{1:X})' -f $step, $hr)
  170.         return $false
  171.     }
  172.     if ($actualSize -eq $targetSize) {
  173.         Write-Host ('[OK] COM SetViewModeAndIconSize applied, actual IconSize = {0}px (read back)' -f $actualSize)
  174.         return $true
  175.     }
  176.     Write-Host ('[INFO] COM: read-back {0}px != target {1}px' -f $actualSize, $targetSize)
  177.     return $false
  178. }

  179. # ---------- 备用路径:WM_COMMAND 视图命令(带 Win7 吞消息重发) ----------
  180. function Set-DesktopIconSizeByMessage([int]$targetSize) {
  181.     if ($targetSize -le 32) { $cmd = $CMD_SMALL; $targetSize = 32 }
  182.     else                    { $cmd = $CMD_MEDIUM; $targetSize = 48 }

  183.     $defview = [DesktopIcon]::FindDesktopView()
  184.     if ($defview -eq [IntPtr]::Zero) {
  185.         Write-Host '[FAIL] Desktop view window (SHELLDLL_DefView) not found.'
  186.         return $false
  187.     }

  188.     # Win7 已知行为:桌面视图可能吞掉第一次视图切换消息,需重发才生效
  189.     # 校验依据:实际图标间距变化,或注册表 IconSize 变为目标值
  190.     $maxAttempts = 3
  191.     for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
  192.         $spacingBefore = [DesktopIcon]::GetItemSpacing()
  193.         [void][DesktopIcon]::SendMessage($defview, $WM_COMMAND, [IntPtr]$cmd, [IntPtr]::Zero)

  194.         $verified = $false
  195.         for ($i = 0; $i -lt 4; $i++) {
  196.             Start-Sleep -Milliseconds 250
  197.             if ([DesktopIcon]::GetItemSpacing() -ne $spacingBefore) { $verified = $true; break }
  198.             if ((Get-CurrentIconSize) -eq $targetSize) { $verified = $true; break }
  199.         }
  200.         if ($verified) {
  201.             if ($attempt -eq 1) {
  202.                 Write-Host ('[OK] Command 0x{0:X} applied, IconSize = {1}px' -f $cmd, $targetSize)
  203.             } else {
  204.                 Write-Host ('[OK] Command 0x{0:X} applied on attempt {1}, IconSize = {2}px' -f $cmd, $attempt, $targetSize)
  205.             }
  206.             return $true
  207.         }
  208.         # 命令为空操作且注册表已等于目标值 => 实际状态本来就正确
  209.         if ((Get-CurrentIconSize) -eq $targetSize) {
  210.             Write-Host ('[OK] No visual change needed, IconSize = {0}px' -f $targetSize)
  211.             return $true
  212.         }
  213.         if ($attempt -lt $maxAttempts) {
  214.             Write-Host ('[INFO] Attempt {0} had no effect, resending (Win7 may swallow the first message)...' -f $attempt)
  215.             Start-Sleep -Milliseconds 1000
  216.         }
  217.     }

  218.     Write-Host ('[WARN] No change detected after command 0x{0:X}.' -f $cmd)
  219.     return $false
  220. }

  221. # ------------------------- 主流程 -------------------------

  222. $cx = [DesktopIcon]::GetSystemMetrics(0)
  223. Write-Host ('Primary screen width = {0}px' -f $cx)

  224. $ok = $true
  225. if ($cx -le 1920) {
  226.     Write-Host 'Target: SMALL icons (32px)'
  227.     $ok = Set-IconSizeByCom 32
  228. } elseif ($ForceMedium) {
  229.     Write-Host 'Target: MEDIUM icons (48px)'
  230.     $ok = Set-IconSizeByCom 48
  231. } else {
  232.     Write-Host 'Width > 1920, no change.'
  233. }

  234. # COM 失败时回退到 WM_COMMAND
  235. if (-not $ok -and ($cx -le 1920 -or $ForceMedium)) {
  236.     if ($cx -le 1920) { $ok = Set-DesktopIconSizeByMessage 32 }
  237.     else              { $ok = Set-DesktopIconSizeByMessage 48 }
  238. }

  239. # 最后兜底:写注册表 + 重启 explorer
  240. if (-not $ok -and $FixByRestart) {
  241.     $targetSize = 32
  242.     if ($cx -gt 1920 -and $ForceMedium) { $targetSize = 48 }
  243.     Set-ItemProperty -Path $REG_PATH -Name 'IconSize' -Value $targetSize -Type DWord
  244.     Stop-Process -Name explorer -Force
  245.     Start-Sleep -Milliseconds 800
  246.     Start-Process explorer.exe
  247.     Write-Host ('[OK] IconSize={0} written to registry and explorer restarted' -f $targetSize)
  248.     $ok = $true
  249. }

  250. if (-not $ok) { exit 1 }
  251. exit 0
复制代码
感谢CodeBuddy
 楼主| 发表于 2026-11-4 09:27:50 | 显示全部楼层
就是运行速度有点慢,用AutoHotkey把下面的ahk脚本编译成exe文件,运行速度极快
  1. ; 这个ahk脚本要求AutoHotkey1.1版本,实测在win7 win10 win11均能将小图标改为中等图标
  2. ; 设置桌面图标为中等大小
  3. ; 用法: 直接运行即可

  4. SetDesktopIconSize(48)  ; 48 是中等图标典型像素值

  5. SetDesktopIconSize(size) {
  6.     ; 常量定义
  7.     static SID_STopLevelBrowser := "{4C96BE40-915C-11CF-99D3-00AA004AE837}"
  8.     static IID_IShellBrowser    := "{000214E2-0000-0000-C000-000000000046}"
  9.     static IID_IFolderView2     := "{1AF3A467-214F-4298-908E-06B03E0B39F9}"
  10.     static SWC_DESKTOP := 0x8
  11.     static FVM_ICON := 1

  12.     ; 1. 创建 Shell.Application 对象,获取桌面窗口
  13.     shellWindows := ComObjCreate("Shell.Application").Windows
  14.     desktop := shellWindows.Item(ComObject(0x13, SWC_DESKTOP))

  15.     ; 2. 获取 IShellBrowser
  16.     IShellBrowser := ComObjQuery(desktop, SID_STopLevelBrowser, IID_IShellBrowser)
  17.     if !IShellBrowser
  18.         return false

  19.     ; 3. 获取 IShellView (vtable 偏移 15)
  20.     DllCall(NumGet(NumGet(IShellBrowser + 0) + A_PtrSize*15), "Ptr", IShellBrowser, "PtrP", IShellView)

  21.     ; 4. 获取 IFolderView2
  22.     IFolderView2 := ComObjQuery(IShellView, IID_IFolderView2)
  23.     if !IFolderView2
  24.         return false

  25.     ; 5. 调用 SetViewModeAndIconSize (vtable 偏移 35)
  26.     hr := DllCall(NumGet(NumGet(IFolderView2 + 0) + A_PtrSize*35), "Ptr", IFolderView2, "Int", FVM_ICON, "Int", size)

  27.     ; 6. 释放资源
  28.     ObjRelease(IFolderView2)
  29.     ObjRelease(IShellView)
  30.     ObjRelease(IShellBrowser)

  31.     return hr = 0
  32. }
复制代码
 楼主| 发表于 2026-11-4 17:15:20 | 显示全部楼层

我也看不懂。AI编程的
发表于 2026-11-5 06:15:40 | 显示全部楼层
czjt1234 发表于 2026-11-4 09:27
就是运行速度有点慢,用AutoHotkey把下面的ahk脚本编译成exe文件,运行速度极快 ...
  1. ;===============================================================================
  2. ;  Set-DesktopIconSize.au3
  3. ;  桌面图标大小自适应 —— AutoIt3 版(编译成 exe 后毫秒级启动,不重启 explorer)
  4. ;  Copyright (C) 2026 洋芋哥
  5. ;
  6. ;  逻辑:
  7. ;     主屏宽度 <= 1920  -> 小图标 (32px)
  8. ;     主屏宽度 >  1920  -> 不做更改
  9. ;
  10. ;  参数:
  11. ;     /s   强制小图标 (32)
  12. ;     /m   强制中等图标 (48)
  13. ;     /q   静默模式(不弹任何提示)
  14. ;===============================================================================

  15. Opt("MustDeclareVars", 1)
  16. #NoTrayIcon

  17. Global Const $WM_COMMAND       = 0x0111
  18. Global Const $CMD_ICON_SMALL   = 0x7050   ; 小图标 32px
  19. Global Const $CMD_ICON_MEDIUM  = 0x704E   ; 中等图标 48px
  20. Global Const $REG_BAG          = "HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Bags\1\Desktop"

  21. _Main()
  22. Exit

  23. ;-------------------------------------------------------------------------------
  24. Func _Main()
  25.     ; 1) 先声明 DPI 感知,保证读到的是物理分辨率,不是被缩放虚拟化后的值
  26.     DllCall("user32.dll", "bool", "SetProcessDPIAware")

  27.     Local $bForceSmall  = False
  28.     Local $bForceMedium = False
  29.     Local $bQuiet       = False
  30.     Local $i

  31.     For $i = 1 To $CmdLine[0]
  32.         Switch StringLower($CmdLine[$i])
  33.             Case "/s", "-s"
  34.                 $bForceSmall = True
  35.             Case "/m", "-m"
  36.                 $bForceMedium = True
  37.             Case "/q", "-q"
  38.                 $bQuiet = True
  39.         EndSwitch
  40.     Next

  41.     ; 2) 取主屏宽度(0 = SM_CXSCREEN)
  42.     Local $aRet = DllCall("user32.dll", "int", "GetSystemMetrics", "int", 0)
  43.     Local $iWidth = 0
  44.     If Not @error Then $iWidth = $aRet[0]

  45.     ; 3) 决定目标尺寸;0 表示"不动"
  46.     Local $iTarget = 0
  47.     If $bForceSmall Then
  48.         $iTarget = 32
  49.     ElseIf $bForceMedium Then
  50.         $iTarget = 48
  51.     ElseIf $iWidth <= 1920 Then
  52.         $iTarget = 32
  53.     EndIf

  54.     If $iTarget = 0 Then
  55.         _Log("主屏宽度 " & $iWidth & "px,图标大小保持不变。", $bQuiet)
  56.         Return
  57.     EndIf

  58.     ; 4) 已经是目标值就直接退出(省掉一次消息发送,也避免无谓刷新)
  59.     If _GetIconSizeReg() = $iTarget Then
  60.         _Log("当前已是 " & $iTarget & "px,无需更改。", $bQuiet)
  61.         Return
  62.     EndIf

  63.     ; 5) 执行设置
  64.     Local $iRet = _SetIconSize($iTarget)
  65.     Switch $iRet
  66.         Case 1
  67.             _Log("已设置桌面图标为 " & $iTarget & "px。", $bQuiet)
  68.         Case -1
  69.             _Log("未找到桌面视图窗口(Progman / WorkerW),请确认 explorer.exe 正常运行。", True)
  70.         Case Else
  71.             _Log("命令已发送,但注册表未读到 " & $iTarget & "px,可能被系统吞掉或命令号不匹配。", True)
  72.     EndSwitch
  73. EndFunc

  74. ;-------------------------------------------------------------------------------
  75. ; 返回值:1 成功 / 0 未生效 / -1 找不到桌面视图
  76. Func _SetIconSize($iTarget)
  77.     Local $hView = _FindDesktopView()
  78.     If $hView = 0 Then Return -1

  79.     Local $iCmd
  80.     If $iTarget <= 32 Then
  81.         $iCmd = $CMD_ICON_SMALL
  82.     Else
  83.         $iCmd = $CMD_ICON_MEDIUM
  84.     EndIf

  85.     ; Win7 的桌面视图有时会吞掉第一次视图切换消息,所以重发 + 校验
  86.     Local $iTry, $iWait
  87.     For $iTry = 1 To 3
  88.         DllCall("user32.dll", "lresult", "SendMessageW", _
  89.                 "hwnd", $hView, "uint", $WM_COMMAND, "wparam", $iCmd, "lparam", 0)

  90.         For $iWait = 1 To 6
  91.             Sleep(150)
  92.             If _GetIconSizeReg() = $iTarget Then Return 1
  93.         Next
  94.         Sleep(300)
  95.     Next
  96.     Return 0
  97. EndFunc

  98. ;-------------------------------------------------------------------------------
  99. ; 找桌面的 SHELLDLL_DefView
  100. ; Win7/10 一般挂在 Progman 下;Win11 壁纸轮播等场景挂在 WorkerW 下
  101. Func _FindDesktopView()
  102.     Local $aList = WinList()
  103.     Local $i, $hTop, $sCls, $hDef
  104.     For $i = 1 To $aList[0][0]
  105.         $hTop = $aList[$i][1]
  106.         $sCls = _GetClassName($hTop)
  107.         If $sCls = "Progman" Or $sCls = "WorkerW" Then
  108.             $hDef = _FindWindowEx($hTop, "SHELLDLL_DefView")
  109.             If $hDef <> 0 Then Return $hDef
  110.         EndIf
  111.     Next
  112.     Return 0
  113. EndFunc

  114. Func _GetClassName($hWnd)
  115.     Local $tBuf = DllStructCreate("wchar[256]")
  116.     DllCall("user32.dll", "int", "GetClassNameW", _
  117.             "hwnd", $hWnd, "ptr", DllStructGetPtr($tBuf), "int", 256)
  118.     Return DllStructGetData($tBuf, 1)
  119. EndFunc

  120. Func _FindWindowEx($hParent, $sClass)
  121.     Local $aRet = DllCall("user32.dll", "hwnd", "FindWindowExW", _
  122.                           "hwnd", $hParent, "hwnd", 0, "wstr", $sClass, "ptr", 0)
  123.     If @error Then Return 0
  124.     Return $aRet[0]
  125. EndFunc

  126. ;-------------------------------------------------------------------------------
  127. Func _GetIconSizeReg()
  128.     Local $vVal = RegRead($REG_BAG, "IconSize")
  129.     If @error Then Return -1
  130.     Return Int($vVal)
  131. EndFunc

  132. Func _Log($sMsg, $bQuiet)
  133.     ConsoleWrite($sMsg & @CRLF)
  134.     If Not $bQuiet Then
  135.         TrayTip("桌面图标", $sMsg, 2, 1)
  136.     EndIf
  137. EndFunc
复制代码

评分

参与人数 1技术 +1 收起 理由
czjt1234 + 1 感谢分享

查看全部评分

 楼主| 发表于 2026-11-5 21:36:37 | 显示全部楼层
让CodeBuddy按ahk的方式简化一下脚本
它自己测试了好几分钟,发现"Shell.Application".Windows不含桌面
我把AutoHotkey和脚本发给它测试成功,它自己去AutoHotkey社区找答案
发现AutoHotkey在"Shell.Application".Windows找不到桌面后会自己FindWindowSW找桌面
然后它生成了新的c#
用win10自带的csc.exe编译成exe文件,速度也是极快
适用于极度不想用第三方软件的人

用以下代码编译
  1. $cs="D:\1\SetIconMedium.cs";
  2. $csc="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe";
  3. & $csc /nologo /target:winexe /platform:anycpu /optimize+ /out:"D:\1\SetIconMedium.exe" $cs;
复制代码


c#源码
  1. // =====================================================================
  2. // SetIconMedium.cs v6 —— 将桌面图标统一设为中等图标 (48px)
  3. //
  4. // 编译为 /target:winexe 时运行无任何窗口(适合开机/登录静默部署),
  5. // 编译为 /target:exe 时(SetIconMedium_console.exe)带控制台输出供排查。
  6. //
  7. // 路径A(首选,文档化 COM API,与验证过的 AHK 脚本同链路):
  8. //   IShellWindows::FindWindowSW(SWC_DESKTOP) 定位桌面浏览器
  9. //   (该 API 对 SWC_DESKTOP 走特殊分支,桌面无需出现在可枚举集合中,
  10. //     与 AHK 的 Item(VT_I4 SWC_DESKTOP) 等效)
  11. //   -> QueryService(SID_STopLevelBrowser) -> IShellBrowser
  12. //   -> QueryActiveShellView(虚表偏移 15)
  13. //   -> IFolderView2::SetViewModeAndIconSize(FVM_ICON=1, size)(偏移 35)
  14. //   -> GetViewModeAndIconSize(偏移 36)读回确认
  15. //
  16. // 路径B(补位):当 FindWindowSW 也找不到桌面浏览器时(极少数壳环境),
  17. //   向可见 SHELLDLL_DefView 发送视图命令:
  18. //   0x704E(FVM_MEDIUMICON)=中48px / 0x7050(FVM_SMALL)=小32px
  19. //   (Win7 可能吞掉第一次消息,自动重发,最多 3 次)
  20. //
  21. // 校验:读回像素值 + LVM_GETITEMSPACING 实际图标间距变化
  22. // 用法:SetIconMedium.exe [size]   (缺省 48;32=小图标,其他值走 48)
  23. // 返回码:0 成功,1 失败
  24. // =====================================================================
  25. using System;
  26. using System.Collections.Generic;
  27. using System.Reflection;
  28. using System.Runtime.InteropServices;
  29. using System.Text;

  30. class Program
  31. {
  32.     const int SWC_DESKTOP = 8;
  33.     const int FVM_ICON = 1;
  34.     const int WM_COMMAND = 0x0111;
  35.     const int CMD_MEDIUM = 0x704E;   // FVM_MEDIUMICON -> 48px
  36.     const int CMD_SMALL  = 0x7050;   // FVM_SMALL      -> 32px
  37.     static readonly Guid SID_STopLevelBrowser = new Guid("4C96BE40-915C-11CF-99D3-00AA004AE837");
  38.     static readonly Guid IID_IShellBrowser    = new Guid("000214E2-0000-0000-C000-000000000046");
  39.     static readonly Guid IID_IFolderView2     = new Guid("1AF3A467-214F-4298-908E-06B03E0B39F9");
  40.     static readonly Guid CLSID_ShellWindows   = new Guid("9BA05972-F6A8-11CF-A442-00A0C90A8F39");

  41.     static void Log(string s) {
  42.         try { Console.WriteLine(s); } catch {}
  43.     }

  44.     [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  45.     interface IServiceProvider
  46.     {
  47.         [PreserveSig] int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject);
  48.     }

  49.     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  50.     delegate int QueryActiveShellView_t(IntPtr self, out IntPtr ppshv);
  51.     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  52.     delegate int GetViewModeAndIconSize_t(IntPtr self, out int viewMode, out int iconSize);
  53.     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  54.     delegate int SetViewModeAndIconSize_t(IntPtr self, int viewMode, int iconSize);
  55.     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  56.     delegate int GetWindow_t(IntPtr self, out IntPtr hwnd);            // IOleWindow::GetWindow(偏移3)

  57.     // ---------- Win32 窗口枚举 ----------
  58.     delegate bool EnumProc(IntPtr hWnd, IntPtr lParam);
  59.     [DllImport("user32.dll")] static extern bool EnumWindows(EnumProc cb, IntPtr lParam);
  60.     [DllImport("user32.dll")] static extern bool EnumChildWindows(IntPtr parent, EnumProc cb, IntPtr lParam);
  61.     [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetClassName(IntPtr h, StringBuilder sb, int max);
  62.     [DllImport("user32.dll")] static extern IntPtr GetAncestor(IntPtr hwnd, uint flags);
  63.     [DllImport("user32.dll")] static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

  64.     static IntPtr defView = IntPtr.Zero;
  65.     static IntPtr listView = IntPtr.Zero;
  66.     static List<IntPtr> allDefViews = new List<IntPtr>();

  67.     static bool ChildCb(IntPtr h, IntPtr l) {
  68.         StringBuilder sb = new StringBuilder(256);
  69.         GetClassName(h, sb, 256);
  70.         string cls = sb.ToString();
  71.         if (cls == "SHELLDLL_DefView") { defView = h; allDefViews.Add(h); }
  72.         if (cls == "SysListView32" && listView == IntPtr.Zero) { listView = h; }
  73.         return true;
  74.     }
  75.     static bool TopCb(IntPtr h, IntPtr l) {
  76.         StringBuilder sb = new StringBuilder(256);
  77.         GetClassName(h, sb, 256);
  78.         string cls = sb.ToString();
  79.         if (cls == "Progman" || cls == "WorkerW") {
  80.             EnumChildWindows(h, ChildCb, IntPtr.Zero);
  81.         }
  82.         return true;
  83.     }
  84.     static void FindDesktop() {
  85.         defView = IntPtr.Zero; listView = IntPtr.Zero;
  86.         allDefViews.Clear();
  87.         EnumWindows(TopCb, IntPtr.Zero);
  88.     }
  89.     static bool IsDesktopView(IntPtr hwnd) {
  90.         foreach (IntPtr v in allDefViews) { if (v == hwnd) { return true; } }
  91.         return false;
  92.     }
  93.     // LVM_GETITEMSPACING:实际图标间距(布局变化的直接证据)
  94.     static string GetSpacing() {
  95.         if (listView == IntPtr.Zero) { return "n/a"; }
  96.         long r = SendMessage(listView, 0x1033, IntPtr.Zero, IntPtr.Zero).ToInt64() & 0xFFFFFFFF;
  97.         return (r & 0xFFFF) + "x" + ((r >> 16) & 0xFFFF);
  98.     }

  99.     static object GetProp(object obj, string name) {
  100.         try { return obj.GetType().InvokeMember(name, BindingFlags.GetProperty, null, obj, null); }
  101.         catch { return null; }
  102.     }

  103.     // 对 ShellWindows 项取其视图窗口句柄:QueryService -> QueryActiveShellView -> GetWindow
  104.     static IntPtr GetCandidateViewHwnd(object candidate) {
  105.         IntPtr pUnk = Marshal.GetIUnknownForObject(candidate);
  106.         try {
  107.             IServiceProvider sp = (IServiceProvider)Marshal.GetObjectForIUnknown(pUnk);
  108.             Guid sid = SID_STopLevelBrowser;
  109.             Guid iidB = IID_IShellBrowser;
  110.             IntPtr pBrowser;
  111.             int hr = sp.QueryService(ref sid, ref iidB, out pBrowser);
  112.             if (hr != 0) { return IntPtr.Zero; }
  113.             try {
  114.                 IntPtr vtbl = Marshal.ReadIntPtr(pBrowser);
  115.                 QueryActiveShellView_t qasv = (QueryActiveShellView_t)Marshal.GetDelegateForFunctionPointer(
  116.                     Marshal.ReadIntPtr(vtbl, 15 * IntPtr.Size), typeof(QueryActiveShellView_t));
  117.                 IntPtr pView;
  118.                 hr = qasv(pBrowser, out pView);
  119.                 if (hr != 0 || pView == IntPtr.Zero) { return IntPtr.Zero; }
  120.                 try {
  121.                     IntPtr vtblV = Marshal.ReadIntPtr(pView);
  122.                     GetWindow_t gw = (GetWindow_t)Marshal.GetDelegateForFunctionPointer(
  123.                         Marshal.ReadIntPtr(vtblV, 3 * IntPtr.Size), typeof(GetWindow_t));
  124.                     IntPtr hwnd;
  125.                     hr = gw(pView, out hwnd);
  126.                     return (hr == 0) ? hwnd : IntPtr.Zero;
  127.                 } finally { Marshal.Release(pView); }
  128.             } finally { Marshal.Release(pBrowser); }
  129.         } catch { return IntPtr.Zero; }
  130.         finally { Marshal.Release(pUnk); }
  131.     }

  132.     // 文档化 API:IShellWindows::FindWindowSW(SWC_DESKTOP)(经 IDispatch 后期绑定,安全)
  133.     static object FindDesktopByFindWindowSW(object shellWindows) {
  134.         Type t = shellWindows.GetType();
  135.         object[] locCandidates = new object[] { 0, Type.Missing };   // 0=CSIDL_DESKTOP 或 VT_EMPTY
  136.         foreach (object loc in locCandidates) {
  137.             try {
  138.                 object[] args = new object[] { loc, Type.Missing, SWC_DESKTOP, 0, 1 /*SWFO_NEEDDISPATCH*/ };
  139.                 ParameterModifier mod = new ParameterModifier(5);
  140.                 mod[3] = true;   // pHWND 为出参
  141.                 ParameterModifier[] mods = new ParameterModifier[] { mod };
  142.                 object ret = t.InvokeMember("FindWindowSW", BindingFlags.InvokeMethod, null,
  143.                     shellWindows, args, mods, null, null);
  144.                 if (ret != null) {
  145.                     Log("[INFO] FindWindowSW ok, browser hwnd=" + args[3]);
  146.                     return ret;
  147.                 }
  148.                 Log("[INFO] FindWindowSW returned null (loc=" + (loc is int ? "0" : "empty") + ")");
  149.             } catch (Exception ex) {
  150.                 Log("[INFO] FindWindowSW failed: " + ex.Message);
  151.             }
  152.         }
  153.         return null;
  154.     }

  155.     static int Main(string[] args)
  156.     {
  157.         int size = 48;
  158.         if (args.Length > 0) {
  159.             int v;
  160.             if (int.TryParse(args[0], out v) && v >= 16 && v <= 256) { size = v; }
  161.         }
  162.         try { return Run(size); }
  163.         catch (Exception ex) {
  164.             Log("[FAIL] " + ex.Message);
  165.             return 1;
  166.         }
  167.     }

  168.     static int Run(int size)
  169.     {
  170.         // 1. 定位可见桌面视图
  171.         FindDesktop();
  172.         if (defView == IntPtr.Zero) {
  173.             Log("[FAIL] SHELLDLL_DefView not found");
  174.             return 1;
  175.         }
  176.         IntPtr root = GetAncestor(defView, 2);
  177.         string spacingBefore = GetSpacing();
  178.         Log("[INFO] DefView=0x" + defView.ToInt64().ToString("X") +
  179.             " Root=0x" + root.ToInt64().ToString("X") + " spacing=" + spacingBefore);

  180.         // 2. 路径A:COM 定位桌面项。FindWindowSW(SWC_DESKTOP) 是文档化的桌面查找 API,
  181.         //    其返回的浏览器句柄即桌面根窗口,可直接信任,无需再做视图匹配
  182.         object desktop = null;
  183.         Type t = Type.GetTypeFromCLSID(CLSID_ShellWindows);
  184.         object shellWindows = Activator.CreateInstance(t);

  185.         object viaSW = FindDesktopByFindWindowSW(shellWindows);
  186.         if (viaSW != null) { desktop = viaSW; }

  187.         if (desktop == null) {
  188.             // 兜底定位:集合枚举 + 视图窗口句柄匹配
  189.             List<object> candidates = new List<object>();
  190.             try {
  191.                 object cand = t.InvokeMember("Item", BindingFlags.InvokeMethod, null, shellWindows,
  192.                     new object[] { SWC_DESKTOP });
  193.                 if (cand != null) { candidates.Add(cand); }
  194.             } catch {}
  195.             int count = 0;
  196.             object c1 = GetProp(shellWindows, "Count");
  197.             if (c1 is int) { count = (int)c1; }
  198.             for (int i = 0; i < count; i++) {
  199.                 object item = null;
  200.                 try {
  201.                     item = t.InvokeMember("Item", BindingFlags.InvokeMethod, null, shellWindows,
  202.                         new object[] { i });
  203.                 } catch {}
  204.                 if (item != null) { candidates.Add(item); }
  205.             }
  206.             Log("[INFO] ShellWindows candidates: " + candidates.Count);
  207.             foreach (object cand in candidates) {
  208.                 IntPtr viewHwnd = GetCandidateViewHwnd(cand);
  209.                 if (viewHwnd != IntPtr.Zero && IsDesktopView(viewHwnd)) { desktop = cand; break; }
  210.             }
  211.         }

  212.         // 3. 路径A执行:IFolderView2::SetViewModeAndIconSize
  213.         if (desktop != null) {
  214.             Log("[INFO] desktop item located, applying via IFolderView2...");
  215.             int mode, isz;
  216.             int hr = ApplyByCom(desktop, size, out mode, out isz);
  217.             if (hr == 0) {
  218.                 System.Threading.Thread.Sleep(300);
  219.                 string spacingAfter = GetSpacing();
  220.                 Log("[INFO] after: spacing=" + spacingAfter + " (was " + spacingBefore + ")");
  221.                 if (spacingAfter != spacingBefore) {
  222.                     Log("[OK] desktop icons set to " + size + "px via COM, layout changed");
  223.                     return 0;
  224.                 }
  225.                 if (isz == size) {
  226.                     Log("[OK] view reports " + isz + "px, already at target (no layout change needed)");
  227.                     return 0;
  228.                 }
  229.                 Log("[INFO] COM applied but unverified, trying message path...");
  230.             } else {
  231.                 Log("[INFO] COM path failed (hr=0x" + hr.ToString("X") + "), trying message path...");
  232.             }
  233.         } else {
  234.             Log("[INFO] desktop item not found via COM, using message path...");
  235.         }

  236.         // 4. 路径B:消息方式(Win7 可能吞掉第一次消息,自动重发)
  237.         return ApplyByMessage(size, spacingBefore);
  238.     }

  239.     static int ApplyByCom(object desktop, int size, out int mode, out int isz) {
  240.         mode = -1; isz = -1;
  241.         IntPtr pUnk = Marshal.GetIUnknownForObject(desktop);
  242.         try {
  243.             IServiceProvider sp = (IServiceProvider)Marshal.GetObjectForIUnknown(pUnk);
  244.             Guid sid = SID_STopLevelBrowser;
  245.             Guid iidB = IID_IShellBrowser;
  246.             IntPtr pBrowser;
  247.             int hr = sp.QueryService(ref sid, ref iidB, out pBrowser);
  248.             if (hr != 0) { return hr; }
  249.             try {
  250.                 IntPtr vtbl = Marshal.ReadIntPtr(pBrowser);
  251.                 QueryActiveShellView_t qasv = (QueryActiveShellView_t)Marshal.GetDelegateForFunctionPointer(
  252.                     Marshal.ReadIntPtr(vtbl, 15 * IntPtr.Size), typeof(QueryActiveShellView_t));
  253.                 IntPtr pView;
  254.                 hr = qasv(pBrowser, out pView);
  255.                 if (hr != 0 || pView == IntPtr.Zero) { return (hr == 0) ? -1 : hr; }
  256.                 try {
  257.                     Guid iidFV2 = IID_IFolderView2;
  258.                     IntPtr pFV2;
  259.                     hr = Marshal.QueryInterface(pView, ref iidFV2, out pFV2);
  260.                     if (hr != 0) { return hr; }
  261.                     try {
  262.                         IntPtr vtbl2 = Marshal.ReadIntPtr(pFV2);
  263.                         SetViewModeAndIconSize_t setFn = (SetViewModeAndIconSize_t)Marshal.GetDelegateForFunctionPointer(
  264.                             Marshal.ReadIntPtr(vtbl2, 35 * IntPtr.Size), typeof(SetViewModeAndIconSize_t));
  265.                         GetViewModeAndIconSize_t getFn = (GetViewModeAndIconSize_t)Marshal.GetDelegateForFunctionPointer(
  266.                             Marshal.ReadIntPtr(vtbl2, 36 * IntPtr.Size), typeof(GetViewModeAndIconSize_t));

  267.                         hr = setFn(pFV2, FVM_ICON, size);
  268.                         if (hr != 0) { return hr; }
  269.                         hr = getFn(pFV2, out mode, out isz);
  270.                         if (hr == 0 && isz > 0) {
  271.                             Log("[INFO] read-back: mode=" + mode + " size=" + isz + "px");
  272.                         }
  273.                         return 0;
  274.                     } finally { Marshal.Release(pFV2); }
  275.                 } finally { Marshal.Release(pView); }
  276.             } finally { Marshal.Release(pBrowser); }
  277.         } finally { Marshal.Release(pUnk); }
  278.     }

  279.     static int ApplyByMessage(int size, string spacingBefore) {
  280.         int cmd = (size <= 32) ? CMD_SMALL : CMD_MEDIUM;
  281.         if (defView == IntPtr.Zero) {
  282.             Log("[FAIL] no DefView for message path");
  283.             return 1;
  284.         }
  285.         for (int attempt = 1; attempt <= 3; attempt++) {
  286.             SendMessage(defView, WM_COMMAND, (IntPtr)cmd, IntPtr.Zero);
  287.             for (int i = 0; i < 4; i++) {
  288.                 System.Threading.Thread.Sleep(250);
  289.                 if (GetSpacing() != spacingBefore) {
  290.                     Log("[OK] desktop icons set to " + ((size <= 32) ? 32 : 48) +
  291.                         "px via message 0x" + cmd.ToString("X") + " (attempt " + attempt + "), layout changed");
  292.                     return 0;
  293.                 }
  294.             }
  295.             if (attempt < 3) {
  296.                 Log("[INFO] message attempt " + attempt + " no effect, resending...");
  297.                 System.Threading.Thread.Sleep(1000);
  298.             }
  299.         }
  300.         Log("[FAIL] no layout change after message path");
  301.         return 1;
  302.     }
  303. }
复制代码
 楼主| 发表于 2026-11-6 19:44:32 | 显示全部楼层
记得在vbs中
CreateObject("Shell.Application").Windows().Item()
返回的是桌面,不需要自己FindWindowSW找桌面

win7 中遍历 ShellWindows 集合时包含了桌面对象
win10 win11 中遍历 ShellWindows 集合时不包含桌面对象
但 win7 win10 win11 中使用 ShellWindows.Item() 方法且不指定参数,返回的都是桌面对象

让AI跑了一下,确认无误
只留主路径,去掉补位路径和验证环节
得到精简版代码
  1. using System;
  2. using System.Reflection;
  3. using System.Runtime.InteropServices;

  4. class Program
  5. {
  6.     const int FVM_ICON = 1;
  7.     static readonly Guid SID_STopLevelBrowser = new Guid("4C96BE40-915C-11CF-99D3-00AA004AE837");
  8.     static readonly Guid IID_IShellBrowser    = new Guid("000214E2-0000-0000-C000-000000000046");
  9.     static readonly Guid IID_IFolderView2     = new Guid("1AF3A467-214F-4298-908E-06B03E0B39F9");
  10.     static readonly Guid CLSID_ShellWindows   = new Guid("9BA05972-F6A8-11CF-A442-00A0C90A8F39");

  11.     [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  12.     interface IServiceProvider
  13.     {
  14.         [PreserveSig] int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject);
  15.     }

  16.     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  17.     delegate int QueryActiveShellView_t(IntPtr self, out IntPtr ppshv);
  18.     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  19.     delegate int SetViewModeAndIconSize_t(IntPtr self, int viewMode, int iconSize);

  20.     static int Main(string[] args)
  21.     {
  22.         int size = 48;
  23.         if (args.Length > 0) {
  24.             int v;
  25.             if (int.TryParse(args[0], out v) && v >= 16 && v <= 256) { size = v; }
  26.         }
  27.         try {
  28.             Type t = Type.GetTypeFromCLSID(CLSID_ShellWindows);
  29.             object shellWindows = Activator.CreateInstance(t);

  30.             // 1. 桌面浏览器:Item() 无参数调用,Shell 特殊分支直接返回桌面
  31.             object desktop = t.InvokeMember("Item", BindingFlags.InvokeMethod, null,
  32.                 shellWindows, new object[0]);
  33.             if (desktop == null) {
  34.                 Console.Error.WriteLine("[FAIL] Item() returned null");
  35.                 return 1;
  36.             }

  37.             // 2. IShellBrowser
  38.             IntPtr pUnk = Marshal.GetIUnknownForObject(desktop);
  39.             try {
  40.                 IServiceProvider sp = (IServiceProvider)Marshal.GetObjectForIUnknown(pUnk);
  41.                 Guid sid = SID_STopLevelBrowser;
  42.                 Guid iidB = IID_IShellBrowser;
  43.                 IntPtr pBrowser;
  44.                 int hr = sp.QueryService(ref sid, ref iidB, out pBrowser);
  45.                 if (hr != 0) {
  46.                     Console.Error.WriteLine("[FAIL] QueryService hr=0x" + hr.ToString("X"));
  47.                     return 1;
  48.                 }
  49.                 try {
  50.                     // 3. IShellView
  51.                     IntPtr vtbl = Marshal.ReadIntPtr(pBrowser);
  52.                     QueryActiveShellView_t qasv = (QueryActiveShellView_t)Marshal.GetDelegateForFunctionPointer(
  53.                         Marshal.ReadIntPtr(vtbl, 15 * IntPtr.Size), typeof(QueryActiveShellView_t));
  54.                     IntPtr pView;
  55.                     hr = qasv(pBrowser, out pView);
  56.                     if (hr != 0 || pView == IntPtr.Zero) {
  57.                         Console.Error.WriteLine("[FAIL] QueryActiveShellView hr=0x" + hr.ToString("X"));
  58.                         return 1;
  59.                     }
  60.                     try {
  61.                         // 4. IFolderView2
  62.                         Guid iidFV2 = IID_IFolderView2;
  63.                         IntPtr pFV2;
  64.                         hr = Marshal.QueryInterface(pView, ref iidFV2, out pFV2);
  65.                         if (hr != 0) {
  66.                             Console.Error.WriteLine("[FAIL] IFolderView2 QI hr=0x" + hr.ToString("X"));
  67.                             return 1;
  68.                         }
  69.                         try {
  70.                             // 5. 设置图标视图 + 指定像素
  71.                             IntPtr vtbl2 = Marshal.ReadIntPtr(pFV2);
  72.                             SetViewModeAndIconSize_t setFn = (SetViewModeAndIconSize_t)Marshal.GetDelegateForFunctionPointer(
  73.                                 Marshal.ReadIntPtr(vtbl2, 35 * IntPtr.Size), typeof(SetViewModeAndIconSize_t));
  74.                             hr = setFn(pFV2, FVM_ICON, size);
  75.                             if (hr != 0) {
  76.                                 Console.Error.WriteLine("[FAIL] SetViewModeAndIconSize hr=0x" + hr.ToString("X"));
  77.                                 return 1;
  78.                             }
  79.                             Console.WriteLine("[OK] IconSize = " + size + "px");
  80.                             return 0;
  81.                         } finally { Marshal.Release(pFV2); }
  82.                     } finally { Marshal.Release(pView); }
  83.                 } finally { Marshal.Release(pBrowser); }
  84.             } finally { Marshal.Release(pUnk); }
  85.         } catch (Exception ex) {
  86.             Console.Error.WriteLine("[FAIL] " + ex.Message);
  87.             return 1;
  88.         }
  89.     }
  90. }
复制代码


// =====================================================================
// SetIconMedium_min.cs —— 极简版:将桌面图标统一设为中等图标 (48px)
//
// 与实测可用的 AHK 脚本完全同链路,仅主路径,无补位、无校验:
//   ShellWindows.Item()                       桌面浏览器(Shell 特殊分支)
//   -> QueryService(SID_STopLevelBrowser)     IShellBrowser
//   -> QueryActiveShellView(虚表偏移 15)      IShellView
//   -> QueryInterface(IFolderView2)
//   -> SetViewModeAndIconSize(FVM_ICON=1, 48)(虚表偏移 35)文档化 API
//
// 编译为 /target:winexe 运行无窗口;/target:exe 带控制台输出。
// 用法:SetIconMedium_min.exe [size](缺省 48)
// 返回码:0 成功,1 失败
// =====================================================================
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|批处理之家 ( 渝ICP备10000708号 )

GMT+8, 2026-11-15 07:19

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表