aardio 文档
aardio 范例: Windows 11 · 右键菜单风格
//RUNAS//Windows 11 右键菜单风格
//新版 Feature Flags 写入需要管理员权限
import win.ui;
import win.versionEx;
import win.reg;
import process;
if(!win.versionEx.isWin11Later){
win.msgboxErr("此范例仅用于 Windows 11。");
return;
}
// 按完整版本号判断,不能只比较 buildNumber,修订号也很重要。
var useFeatureFlags = win.versionEx.isGreaterOrEqual("10.0.26200.9807");
var featureFlags;
if(useFeatureFlags){
import sys.featureFlags;
featureFlags = sys.featureFlags;
}
// 这些 ID 控制 Windows 11 新版可自定义菜单,并非直接启用 Win10 经典菜单。
// 新系统切换经典菜单:先禁用新版路径,再设置原来的 CLSID 覆盖。
// Feature ID 属于 Windows 内部实现,后续系统更新可能改变其含义或效果。
var menuFeatures = [60813048,49453572,61934903];
var menuClsid = "{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}";
var classesPath = "HKCU\Software\Classes\CLSID";
var classicPath = classesPath ++ "\" ++ menuClsid;
// 只读查询,不创建注册表项。空字符串与“未设置默认值”不能混为一谈。
var readClassic = function(){
var reg,err,code = win.regReaderWow64(classicPath ++ "\InprocServer32");
if(!reg){
if(code == 2 || code == 3) return false;
return null,err;
}
var value = reg.queryValue("");
reg.close();
return value === "";
}
var writeClassic = function(enabled){
if(enabled){
var reg,err = win.regWow64(classicPath ++ "\InprocServer32");
if(!reg) return null,err;
var ok,err = reg.setSzValue("","");
reg.close();
return ok,err;
}
// 已不存在即为成功,避免恢复默认时无意创建空键。
var existing,err,code = win.regWow64(classicPath,true);
if(!existing){
if(code == 2 || code == 3) return true;
return null,err;
}
existing.close();
var reg,err = win.regWow64(classesPath,true);
if(!reg) return null,err;
var ok,code = reg.delKeyTree(menuClsid);
reg.close();
if(!ok) return null,"删除经典菜单注册表项失败,错误码:" ++ tostring(code);
return true;
}
// 这里只判断已保存的配置,不能据此断言正在运行的 Explorer 已经生效。
var classifyStyle = function(classic,features){
if(!useFeatureFlags) return classic ? "classic" : "modern";
var allDisabled,allEnabled,hasUserOverride = true,true,false;
for(i=1;#menuFeatures){
var info = features[i];
if(info.error) return null,"Feature " ++ tostring(info.featureId) ++ ":" ++ info.error;
if(info.enabledState != featureFlags.state.disabled) allDisabled = false;
if(info.enabledState != featureFlags.state.enabled) allEnabled = false;
if(info.priority >= featureFlags.priority.user) hasUserOverride = true;
}
if(classic && allDisabled) return "classic";
if(!classic && (allEnabled || allDisabled)) return "modern";
if(!classic && !hasUserOverride) return "system";
return "mixed";
}
var readStyle = function(){
var classic,err = readClassic();
if(classic === null) return null,err;
var features;
if(useFeatureFlags) features = featureFlags.queryMany(menuFeatures);
return classifyStyle(classic,features);
}
var applyStyle = function(mode){
if(mode != "classic" && mode != "modern" && mode != "system")
return null,"未知的菜单风格。";
if(useFeatureFlags){
// 查询失败不写入,避免在无法识别目标特性的系统上盲目修改。
var features = featureFlags.queryMany(menuFeatures);
for(i=1;#features){
if(features[i].error) return null,features[i].error;
}
var ok,err;
if(mode == "classic") ok,err = featureFlags.disable(menuFeatures);
elseif(mode == "modern") ok,err = featureFlags.enable(menuFeatures);
else ok,err = featureFlags.reset(menuFeatures); // 删除 User 覆盖,不等于强制启用
if(!ok) return null,"Feature Flags 写入失败,请以管理员权限运行。" ++ '\r\n' ++ err;
}
var ok,err = writeClassic(mode == "classic");
if(!ok){
// Feature API 与注册表不是同一个事务:明确报告部分成功,不伪装成全部成功。
if(useFeatureFlags) return null,"Feature Flags 已更新,但注册表操作失败。请重试或恢复系统默认。" ++ '\r\n' ++ err,true;
return null,err;
}
::Shell32.SHChangeNotify(0x8000000/*_SHCNE_ASSOCCHANGED*/,0,0,0);
return true;
}
/*DSG{{*/
var winform = win.form(text="Windows 11 · 右键菜单风格";right=659;bottom=587;bgcolor=0xFAF7F4;border="dialog frame";font=LOGFONT(h=-13;name='Microsoft YaHei UI');max=false)
winform.add(
apply={cls="plus";text="应用所选风格";left=504;top=534;right=632;bottom=574;border={radius=8};notify=1;z=13};
classicCard={cls="plus";text="经典菜单";left=28;top=176;right=322;bottom=410;align="left";border={color=0xEBE2DA;radius=14;width=1};color=0x30251B;font=LOGFONT(h=-18;name='Microsoft YaHei UI';weight=600);notify=1;textPadding={left=20;top=18;right=48};valign="top";z=6};
current={cls="plus";text="正在读取配置…";left=28;top=427;right=632;bottom=453;align="left";color=0x30251B;font=LOGFONT(h=-14;name='Microsoft YaHei UI';weight=600);z=8};
intro={cls="plus";text="熟悉的经典菜单,或简洁的 Windows 11,由你选择。";left=28;top=97;right=632;bottom=123;align="left";color=0x857466;z=3};
modernCard={cls="plus";text="Windows 11";left=338;top=176;right=632;bottom=410;align="left";border={color=0xEBE2DA;radius=14;width=1};color=0x30251B;font=LOGFONT(h=-18;name='Microsoft YaHei UI';weight=600);notify=1;textPadding={left=20;top=18;right=48};valign="top";z=7};
refresh={cls="plus";text="刷新状态";left=550;top=130;right=632;bottom=160;border={radius=7};color=0xC06125;z=5};
reset={cls="plus";text="恢复系统默认";left=28;top=534;right=170;bottom=574;border={color=0xEBE2DA;radius=8;width=1};z=11};
restart={cls="plus";text="重启资源管理器";left=326;top=534;right=490;bottom=574;border={color=0xEBE2DA;radius=8;width=1};z=12};
section={cls="plus";text="WINDOWS 11 / 个性化";left=28;top=24;right=440;bottom=44;align="left";color=0xC06125;font=LOGFONT(name='Microsoft YaHei UI';weight=600);z=1};
separator={cls="bkplus";left=28;top=515;right=632;bottom=516;bgcolor=0xEBE2DA;z=10};
status={cls="plus";left=28;top=461;right=632;bottom=501;align="left";color=0x7B6959;font=LOGFONT(h=-13;name='Microsoft YaHei UI');valign="top";z=9};
systemInfo={cls="plus";left=28;top=132;right=542;bottom=157;align="left";color=0x7B6959;font=LOGFONT(h=-13;name='Microsoft YaHei UI');z=4};
title={cls="plus";text="右键菜单风格";left=26;top=51;right=632;bottom=91;align="left";color=0x30251B;font=LOGFONT(h=-28;name='Microsoft YaHei UI';weight=700);z=2}
)
/*}}*/
// 使用 plus 的状态皮肤;卡片预览是示意图,不读取或模拟实际系统菜单。
for i,card in [winform.classicCard,winform.modernCard]{
card.skin({
background={default=0xFFFFFFFF;hover=0xFFF8FAFD;active=0xFFF0F5FC};
border={default={radius=14;width=1;color=0xFFDAE2EB};hover={radius=14;width=1;color=0xFF8AAED6}};
checked={background={default=0xFFF2F7FF;hover=0xFFEDF4FF};border={default={radius=14;width=2;color=0xFF2563C0}}};
cursor="hand"
});
}
for i,button in [winform.reset,winform.restart,winform.refresh]{
button.skin({background={default=0xFFFFFFFF;hover=0xFFEBF1FA;active=0xFFE1EAF6;disabled=0xFFF1F3F6};color={default=0xFF46566A;disabled=0xFF9BA7B6};cursor="hand"});
}
winform.apply.skin({background={default=0xFF2563C0;hover=0xFF3475D3;active=0xFF1D50A0;disabled=0xFFDFE6EF};color={default=0xFFFFFFFF;disabled=0xFF8C9CAF};cursor="hand"});
// 缓存绘图对象。自绘统一用逻辑坐标,避免高 DPI 下文字和菜单预览错位。
var drawing = {
font=gdip.font("Microsoft YaHei UI",11.5);
muted=gdip.solidBrush(0xFF59697B);
ink=gdip.solidBrush(0xFF334155);
stage=gdip.solidBrush(0xFFEAF0F7);
paper=gdip.solidBrush(0xFFFFFFFF);
highlight=gdip.solidBrush(0xFFE5EEFC);
accent=gdip.solidBrush(0xFF2563C0);
line=gdip.pen(0xFFD6DFEA,1);
ring=gdip.pen(0xFF8A9BAF,1.3)
};
var drawPreview = function(graphics,rc){
var modern = owner == winform.modernCard;
var dpiX,dpiY = winform.dpiScale(1,1);
var saved = graphics.save();
graphics.scale(dpiX,dpiY);
graphics.compositingQuality = 0;
graphics.drawString(modern ? "简洁布局 · 新系统支持自定义" : "Windows 10 风格 · 完整命令直达",drawing.font,20,52,drawing.muted);
graphics.drawCircle(drawing.ring,267,30,8);
if(owner.checked) graphics.fillCircle(drawing.accent,267,30,5);
graphics.fillRoundRect(drawing.stage,20,82,254,132,10);
graphics.fillRoundRect(drawing.paper,43,92,208,112,modern ? 8 : 2);
graphics.drawRoundRect(drawing.line,43,92,208,112,modern ? 8 : 2);
if(modern){
graphics.drawString("剪切 复制 删除",drawing.font,58,99,drawing.ink);
graphics.drawLine(drawing.line,53,124,241,124);
graphics.drawString("打开",drawing.font,59,131,drawing.ink);
graphics.drawString("应用扩展 ›",drawing.font,59,154,drawing.ink);
graphics.drawString("自定义菜单",drawing.font,59,177,drawing.muted);
}
else{
graphics.fillRect(drawing.highlight,47,95,200,20);
for i,text in ["打开","剪切","复制","删除","属性"]{
graphics.drawString(text,drawing.font,59,96+(i-1)*21,drawing.ink);
}
}
graphics.restore(saved);
return true;
}
winform.classicCard.onDrawForeground = drawPreview;
winform.modernCard.onDrawForeground = drawPreview;
winform.onDestroy = function(){
for name,obj in drawing { obj.delete(); }
}
var selectedMode;
var names = {classic="经典菜单";modern="Windows 11";system="跟随 Windows 默认";mixed="混合配置,请重新选择"};
var selectStyle = function(mode){
selectedMode = null;
if(mode == "classic" || mode == "modern") selectedMode = mode;
winform.classicCard.checked = mode == "classic";
winform.modernCard.checked = mode == "modern";
winform.apply.disabled = !selectedMode;
}
var setStatus = function(text,isError){
winform.status.text = text;
winform.status.color = isError ? 0xFFB34436 : 0xFF59697B;
}
var refreshState = function(){
var mode,err = readStyle();
selectStyle(mode);
winform.current.text = "当前配置:" ++ (names[mode] : "读取失败");
if(!mode) setStatus(err,true);
else setStatus("选择卡片不会修改系统。新版开关影响整台电脑,注册表项仅影响当前账户。");
}
winform.systemInfo.text = win.versionEx.full() ++ (useFeatureFlags ? " · Feature Flags + 注册表" : " · 注册表兼容模式");
winform.refresh.oncommand = refreshState;
winform.classicCard.oncommand = function(){
selectStyle("classic");
setStatus("已选择经典菜单。点击“应用所选风格”后保存,不会自动重启资源管理器。");
}
winform.modernCard.oncommand = function(){
selectStyle("modern");
setStatus("已选择 Windows 11。点击“应用所选风格”后保存,不会自动重启资源管理器。");
}
import win.ui.tooltip;
var balloonTipCtrl = win.ui.tooltip.tracking(winform,false);
var saveMode = function(mode){
var message = "即将设置为:" ++ names[mode] ++ "。";
if(useFeatureFlags) message ++= '\r\n新版 Feature 开关影响整台电脑;经典菜单注册表项仅影响当前账户。';
message ++= '\r\n保存后需重启资源管理器;如果仍未生效,请注销或重启系统。继续?';
if(!winform.msgboxTest(message,"保存菜单配置")) return;
var ok,err,partial = applyStyle(mode);
refreshState();
if(ok) {
setStatus("已保存。请先完成文件复制等操作,再点击“重启资源管理器”使配置生效。")
var x,y,cx,cy = winform.restart.getPos(true);
balloonTipCtrl.setText('请先完成文件复制等操作。\n然后继续点击「重启资源管理器」。').trackPopup(true,x+120,y+cy );
};
else{
setStatus(partial ? "部分设置已更新,请重试或恢复系统默认。" : "保存失败,请查看错误提示;当前配置已重新读取。",true);
winform.msgboxErr(err,"未能完成设置");
}
}
winform.apply.oncommand = function(){
if(selectedMode) saveMode(selectedMode);
}
winform.reset.oncommand = function(){ saveMode("system"); }
// 只结束当前桌面的 Shell 进程,不按进程名杀掉其他登录会话的 Explorer。
// 桌面由 Windows 的 AutoRestartShell 以原用户身份重新启动,避免提权启动 Explorer。
var restartExplorer = function(){
var shell = ::User32.GetShellWindow();
if(!shell) return null,"未找到桌面进程,请在任务管理器中手动重启 Windows 资源管理器。";
var pid = {INT value};
::User32.GetWindowThreadProcessId(shell,pid);
var prcs,err = process(pid.value,0x100001/*_SYNCHRONIZE | _PROCESS_TERMINATE*/);
if(!prcs) return null,err;
var ok = ::Kernel32.TerminateProcess(prcs.handle,0);
prcs.free();
if(!ok) return null,"无法结束桌面进程,请在任务管理器中手动重启。";
return pid.value;
}
winform.restart.oncommand = function(){
if(!winform.msgboxTest("此操作会关闭当前桌面进程中的文件夹窗口,并短暂隐藏任务栏。" ++ '\r\n请先完成文件复制、移动等操作。现在重启?',"重启资源管理器")) return;
winform.restart.disabledText = "正在重启…";
var previousPid,err = restartExplorer();
if(previousPid){
var ready = winform.waitUntil(function(){
var shell = ::User32.GetShellWindow();
if(!shell) return false;
var pid = {INT value};
::User32.GetWindowThreadProcessId(shell,pid);
return pid.value != previousPid;
},8000);
if(ready) setStatus("资源管理器已重启。可在桌面或文件上单击右键查看实际效果。");
else setStatus("桌面未自动恢复。请按 Ctrl+Shift+Esc,在任务管理器中运行 explorer.exe。",true);
}
else setStatus(err,true);
winform.restart.disabledText = null;
}
refreshState();
winform.show();
win.loopMessage();
Markdown 格式