托盘图标鼠标操作、脚本开机自启丢图标处理

关于脚本托盘图标鼠标操作与自启丢图标处理方法
#NoEnv
#MaxMem 2048
;#NoTrayIcon
#SingleInstance, Force
SetBatchLines, -1
#MaxThreadsPerHotkey 100
#MaxHotkeysPerInterval 400
#Persistent
#WinActivateForce
#Include %A_ScriptDir%
;;脚本托盘图标消息监控
OnMessage(0x404, "AHK_NOTIFYICON")
;自启任务名称
TaskName:="自启动示例脚本"  

If !A_IsAdmin {
	Try
	{
		If A_IsCompiled {
			Run *RunAs "%A_ScriptFullPath%" /restart
			ExitApp
		}else{
			Run *RunAs "%A_AhkPath%" /restart "%A_ScriptFullPath%"
		}
	}Catch e{
		MsgBox, 262160,Error,% e.Extra?e.Extra:"以管理员身份运行失败!",15
		ExitApp
	}
}
;;检查开机自启后脚本是否出现丢失托盘图标,如果丢失托盘图标Reload至出现托盘图标为止
TrayIconInfo:=TrayIcon_GetInfo(A_IsCompiled?A_ScriptName:RegExReplace(A_AhkPath,".+\\"))
if (!A_IconHidden) {
	if (A_IsCompiled&&!objcount(TrayIconInfo)){
		Gosub OnReload
	}else if (!A_IsCompiled&&!objcount(TrayIconInfo)){
		Gosub OnReload
	}else if (!A_IsCompiled&&objcount(TrayIconInfo)){
		IsNoHide:=0
		for key,value In TrayIconInfo
		{
			if (value.pid=DllCall("GetCurrentProcessId"))
				IsNoHide++
		}
		if !IsNoHide
			Gosub OnReload
	}
}
;;检查脚本托盘图标是否末显示在任务栏被收缩托盘区并提示
if objcount(TrayIconInfo){
	for key,value In TrayIconInfo
	{
		if (InStr(TrayIconInfo[key,"Tray"],"NotifyIconOverflowWindow")&&TrayIconInfo[key,"pid"]=DllCall("GetCurrentProcessId")){
			Traytip,Warning,请把程序图标从托盘区移至`n任务栏以方便更好的操作!,,2
		}Else if (!InStr(TrayIconInfo[key,"Tray"],"NotifyIconOverflowWindow")&&TrayIconInfo[key,"pid"]=DllCall("GetCurrentProcessId")){
			Traytip,Warning,程序图标末收缩正常显示!,,1
		}
	}
}

Gosub TrayMenu
Return










;;==============================================================
TrayMenu:
	Menu, TRAY, NoStandard
	Menu, TRAY, DeleteAll
	Menu, Tray, UseErrorLevel
	Menu, TRAY,Color, ffffff
	Menu, TRAY,Add, 自启,AutoRunScript
	Menu, Tray, Icon, 自启,shell32.dll,234
	;自启项有效性检测并自动校正
	Try
	{
		if objCount(TaskActionExecPaths:=GetTaskActionExecPaths(TaskName)){
			if A_IsCompiled {
				if (TaskActionExecPaths[1,1]=A_ScriptFullPath){
					Menu, TRAY,Check, 自启
				}Else{
					if DisableAutorun(TaskName){
						if (EnableAutoRun(TaskName,"这是一个演示自启动示例",(A_IsCompiled?A_ScriptFullPath:A_AhkPath),!A_IsCompiled?A_ScriptFullPath:"")=TaskName){
							Menu, TRAY,Check, 自启
						}
					}
				}
			}else{
				if (TaskActionExecPaths[1,1]=A_AhkPath&&TaskActionExecPaths[1,2]=A_ScriptFullPath){
					Menu, TRAY,Check, 自启
				}Else{
					if DisableAutorun(TaskName){
						if (EnableAutoRun(TaskName,"这是一个演示自启动示例",(A_IsCompiled?A_ScriptFullPath:A_AhkPath),!A_IsCompiled?A_ScriptFullPath:"")=TaskName){
							Menu, TRAY,Check, 自启
						}
					}
				}
			}
		}
	}Catch{
		TargetPath:=IsExistShortcut(A_Startup,A_IsCompiled?A_ScriptFullPath:A_AhkPath,!A_IsCompiled?A_ScriptFullPath:"")
		if !TargetPath&&FileExist(A_Startup "\" TaskName ".lnk") {
			FileDelete,%A_Startup%\%TaskName%.lnk
			if CreateShortcut(A_Startup "\" TaskName ".lnk",A_IsCompiled?A_ScriptFullPath:A_AhkPath,!A_IsCompiled?A_ScriptFullPath:"","这是一个演示自启动示例"){
				Menu, Tray, Check, 自启
			}Else{
				Menu, Tray, Uncheck, 自启
				Menu, Tray, Icon, 自启,shell32.dll,234
			}
		}Else if TargetPath {
			Menu, Tray, Check, 自启
		}
	}
	Menu, TRAY,Add,
	Menu, TRAY,Add, 重启,OnReload
	Menu, TRAY,Add, 退出,OnExits
	Menu, TRAY,Tip, ahk模板示例
Return

AutoRunScript:
	Try
	{
		if IsAutoRun:=EnableAutoRun(TaskName,"这是一个演示自启动示例",(A_IsCompiled?A_ScriptFullPath:A_AhkPath),!A_IsCompiled?A_ScriptFullPath:"") {
			if (IsAutoRun=1){
				Menu, Tray, Uncheck, 自启
				Menu, Tray, Icon, 自启,shell32.dll,234
				traytip,%TaskName%,已取消开机自启任务!,,1
			}Else if (IsAutoRun<0){
				MsgBox, 262160, Error,添加异常!,8
			}Else if (IsAutoRun=TaskName){
				Menu, Tray, Check, 自启
				traytip,%TaskName%,已建立开机自启任务!,,1
			}Else{
				Menu, Tray, Uncheck, 自启
				Menu, Tray, Icon, 自启,shell32.dll,234
				traytip,%TaskName%,%IsAutoRun%,,2
			}
		}else{
			Menu, Tray, Uncheck, 自启
			Menu, Tray, Icon, 自启,shell32.dll,234
			traytip,Error,建立开机自启任务失败!,,3
		}
	}Catch{
		TargetPath:=IsExistShortcut(A_Startup,A_IsCompiled?A_ScriptFullPath:A_AhkPath,!A_IsCompiled?A_ScriptFullPath:"")
		if !TargetPath {
			FileDelete,%A_Startup%\%TaskName%.lnk
			if CreateShortcut(A_Startup "\" TaskName ".lnk",A_IsCompiled?A_ScriptFullPath:A_AhkPath,!A_IsCompiled?A_ScriptFullPath:"","这是一个演示自启动示例"){
				Menu, Tray, Check, 自启
				traytip,%TaskName%,已建立自启快捷方式!,,1
			}Else{
				Menu, Tray, Uncheck, 自启
				Menu, Tray, Icon, 自启,shell32.dll,234
				traytip,Error,建立自启快捷方式失败!,,3
			}
		}Else{
			FileDelete,%TargetPath%
			Menu, Tray, Uncheck, 自启
			Menu, Tray, Icon, 自启,shell32.dll,234
			traytip,%TaskName%,已取消自启快捷方式!,,1
		}
	}
Return

OnReload:
	Reload
Return

OnExits:
	ExitApp
Return

;;;根据进程文件名获取托盘图标信息列表,以判断托盘图标是否正常显示
TrayIcon_GetInfo(sExeName := "")
{
	d := A_DetectHiddenWindows
	DetectHiddenWindows, On
 
	oTrayInfo := []
	For key,sTray in ["Shell_TrayWnd", "NotifyIconOverflowWindow"]
	{
		idxTB := TrayIcon_GetTrayBar(sTray)
		WinGet, pidTaskbar, PID, ahk_class %sTray%
		
		hProc := DllCall("OpenProcess",	UInt,0x38, Int,0, UInt,pidTaskbar)
		pRB   := DllCall("VirtualAllocEx", Ptr,hProc, Ptr,0, UPtr,20, UInt,0x1000, UInt,0x04)
 
		szBtn := VarSetCapacity(btn, (A_Is64bitOS ? 32 : 20), 0)
		szNfo := VarSetCapacity(nfo, (A_Is64bitOS ? 32 : 24), 0)
		szTip := VarSetCapacity(tip, 128 * 2, 0)
 
		; TB_BUTTONCOUNT = 0x0418
		SendMessage, 0x0418, 0, 0, ToolbarWindow32%idxTB%, ahk_class %sTray%
		Loop, %ErrorLevel%
		{
			 ; TB_GETBUTTON 0x0417
			SendMessage, 0x0417, A_Index-1, pRB, ToolbarWindow32%idxTB%, ahk_class %sTray%
 
			DllCall("ReadProcessMemory", Ptr,hProc, Ptr,pRB, Ptr,&btn, UPtr,szBtn, UPtr,0)
 
			iBitmap := NumGet(btn, 0, "Int")
			idCmd   := NumGet(btn, 4, "Int")
			fsState := NumGet(btn, 8, "UChar")
			fsStyle := NumGet(btn, 9, "UChar")
			dwData  := NumGet(btn, (A_Is64bitOS ? 16 : 12), "UPtr")
			iString := NumGet(btn, (A_Is64bitOS ? 24 : 16), "Ptr")
 
			DllCall("ReadProcessMemory", Ptr,hProc, Ptr,dwData, Ptr,&nfo, UPtr,szNfo, UPtr,0)
 
			hWnd  := NumGet(nfo, 0, "Ptr")
			uId   := NumGet(nfo, (A_Is64bitOS ?  8 :  4), "UInt")
			msgId := NumGet(nfo, (A_Is64bitOS ? 12 :  8), "UPtr")
			hIcon := NumGet(nfo, (A_Is64bitOS ? 24 : 20), "Ptr")
 
			WinGet, nPid, PID, ahk_id %hWnd%
			WinGet, sProcess, ProcessName, ahk_id %hWnd%
			WinGetClass, sClass, ahk_id %hWnd%
 
			If ( !sExeName || sExeName == sProcess || sExeName == nPid )
			{
				DllCall("ReadProcessMemory", Ptr,hProc, Ptr,iString, Ptr,&tip, UPtr,szTip, UPtr,0)
				oTrayInfo.Push({ "idx"	 : A_Index-1
							   , "idcmd"   : idCmd
							   , "pid"	 : nPid
							   , "uid"	 : uId
							   , "msgid"   : msgId
							   , "hicon"   : hIcon
							   , "hwnd"	: hWnd
							   , "class"   : sClass
							   , "process" : sProcess
							   , "tooltip" : StrGet(&tip, "UTF-16")
							   , "tray"	: sTray })
			}
		}
		DllCall("VirtualFreeEx", Ptr,hProc, Ptr,pRB, UPtr,0, UInt,0x8000)
		DllCall("CloseHandle",   Ptr,hProc)
	}
	DetectHiddenWindows, %d%
	Return oTrayInfo
}

TrayIcon_GetTrayBar(sTray:="Shell_TrayWnd")
{
	d := A_DetectHiddenWindows
	DetectHiddenWindows, On
	WinGet, ControlList, ControlList, ahk_class %sTray%
	RegExMatch(ControlList, "(?<=ToolbarWindow32)\d+(?!.*ToolbarWindow32)", nTB)
	Loop, %nTB%
	{
		ControlGet, hWnd, hWnd,, ToolbarWindow32%A_Index%, ahk_class %sTray%
		hParent := DllCall( "GetParent", Ptr, hWnd )
		WinGetClass, sClass, ahk_id %hParent%
		If !(sClass == "SysPager" || sClass == "NotifyIconOverflowWindow" )
			Continue
		idxTB := A_Index
		Break
	}
	DetectHiddenWindows, %d%
	Return idxTB
}

AHK_NOTIFYICON(wParam, lParam, uMsg, hWnd)
{
	;;脚本托盘图标单击与双击尽量不要同时启用
	if (lParam = 0x201){  ;鼠标左键单击脚本托盘图标
		MsgBox,你单击了鼠标左键
	}
	if (lParam = 0x203){  ;鼠标左键双击脚本托盘图标
		MsgBox,你双击了鼠标左键
	}
	if (lParam = 0x204){  ;鼠标右键单击脚本托盘图标
		MsgBox,你单击了鼠标右键
	}
	if (lParam = 0x206){  ;鼠标右键双击脚本托盘图标
		MsgBox,你双击了鼠标右键
	}
	if (lParam = 0x207){  ;鼠标中键单击脚本托盘图标
		MsgBox,你单击了鼠标中键
	}
	if (lParam = 0x209){  ;鼠标中键双击脚本托盘图标
		MsgBox,你双击了鼠标中键
	}

}

;获取系统计划任务自启列表
GetTaskInfos() {
	objService := ComObjCreate("Schedule.Service")
	objService.Connect()
	rootFolder := objService.GetFolder("\")
	taskCollection := rootFolder.GetTasks(0)
	numberOfTasks := taskCollection.Count
	; ?RegistrationInfo.Author
	For registeredTask, state in taskCollection
	{
		if (registeredTask.state == 0)
			state:= "Unknown"
		else if (registeredTask.state == 1)
			state:= "Disabled"
		else if (registeredTask.state == 2)
			state:= "Queued"
		else if (registeredTask.state == 3)
			state:= "Ready"
		else if (registeredTask.state == 4)
			state:= "Running"
		tasklist .= registeredTask.Name "=" state "=" registeredTask.state "`n"
	}
	return RTrim(tasklist,"`n")
}
;判断是否自启
IsAutorunEnabled(TaskName)
{
	objService := ComObjCreate("Schedule.Service") 
	objService.Connect()
	objFolder := objService.GetFolder("\")
	Try {
		RunAsTask := objFolder.GetTask( TaskName )
		return  ! A_LastError 
	}Catch
		Return ""
}
;取消自启
DisableAutorun(TaskName){
	objService := ComObjCreate("Schedule.Service") 
	objService.Connect()
	objFolder := objService.GetFolder("\")
	Try {
		objFolder.DeleteTask(TaskName, 0)
		Return !A_LastError
	}Catch
		Return ""
}
;根据自启任务名获取自启文件长路径
GetTaskActionExecPaths(taskName, folderPath := "\") {
	service := ComObjCreate("Schedule.Service")
	service.Connect()
	Try {
		Paths:=[]
		for action in service.GetFolder(folderPath).GetTask(taskName).Definition.Actions
			if (action.Type ==0)
				Paths.Push([RegExReplace(action.Path,"[\""""]"),RegExReplace(action.Arguments,"[\""""]")])
		Return Paths
	}Catch
		Return []
}
;设置自启
EnableAutoRun(taskName:="",DescriptionInfo:="",ProcessPath="",ScriptPath="",TaskState=3)
{
	if (!TaskName||!ProcessPath)
		Return -1
	TaskExists := IsAutorunEnabled(taskName)
	if (A_IsAdmin and TaskExists) {
		if DisableAutorun(TaskName)
			Return 1
	}else If ( not A_IsAdmin and TaskExists ) {
		Return !A_IsAdmin?"建立失败,当前非管理员身份!":""
	}else If ( not A_IsAdmin and not TaskExists ){
		Return !A_IsAdmin?"建立失败,当前非管理员身份!":""
	}else If ( A_IsAdmin and not TaskExists ) {
		TriggerType = 9   ; trigger on logon. 
		ActionTypeExec = 0  ; specifies an executable action. 
		TaskCreateOrUpdate = 6 
		Task_Runlevel_Highest = 1 

		objService := ComObjCreate("Schedule.Service") 
		objService.Connect() 

		objFolder := objService.GetFolder("\") 
		objTaskDefinition := objService.NewTask(0) 

		principal := objTaskDefinition.Principal 
		principal.LogonType := 1    ; Set the logon type to TASK_LOGON_PASSWORD 
		principal.RunLevel := Task_Runlevel_Highest  ; Tasks will be run with the highest privileges. 

		colTasks := objTaskDefinition.Triggers 
		objTrigger := colTasks.Create(TriggerType) 
		colActions := objTaskDefinition.Actions 
		objAction := colActions.Create(ActionTypeExec) 
		objAction.ID := "Autorun" 
		if(A_IsCompiled)
			objAction.Path := """" Trim(ProcessPath) """"
		else
		{
			objAction.Path := """" Trim(ProcessPath) """"
			objAction.Arguments := """" Trim(ScriptPath) """"
		}
		objAction.WorkingDirectory := A_ScriptDir
		objInfo := objTaskDefinition.RegistrationInfo 
		objInfo.Author := A_UserName 
		objInfo.Description := DescriptionInfo
		objSettings := objTaskDefinition.Settings 
		objSettings.Enabled := True 
		objSettings.Hidden := False 
		objSettings.StartWhenAvailable := True 
		objSettings.ExecutionTimeLimit := "PT0S"
		objSettings.DisallowStartIfOnBatteries := False
		objSettings.StopIfGoingOnBatteries := False
		objFolder.RegisterTaskDefinition(taskName, objTaskDefinition, TaskCreateOrUpdate , "", "", TaskState ) 
		if IsAutorunEnabled(taskName)
			Return taskName
	}
}

GetShortcut(Path){
	Local obj
	obj:={}
	FileGetShortcut, %Path% , TargetPath,WorkingDir,ArgsName,Description,IconPath,IconNum
	if (!ErrorLevel&&TargetPath){
		obj["Target"]:=TargetPath,obj["Args"]:=ArgsName,obj["Dir"]:=WorkingDir,obj["Description"]:=Description
		,obj["IconPath"]:=Trim(IconPath,""""),obj["IconNum"]:=IconNum
	}
	Return obj
}

CreateShortcut(LinkFile,Target,Args:="",Description:="",WorkingDir:="",IconFile:="",IconNumber:="",ShortcutKey:="",RunState:=1){
	IconFile:=IconFile?IconFile:A_IsCompiled?A_ScriptFullPath:A_AhkPath
	WorkingDir:=WorkingDir?WorkingDir:RegExReplace(Target,"\\[^\\]+$")
	Args:=FileExist(Trim(Trim(Args),""""))?"""" Trim(Trim(Args),"""") """":Args
	FileCreateShortcut, %Target%, %LinkFile% , %WorkingDir%, %Args%, %Description%, %IconFile%, %ShortcutKey%, %IconNumber%, %RunState%

	Return !ErrorLevel 
}

IsExistShortcut(Dir,Target,Args:=""){
	Loop, Files, %Dir%\*.lnk
	{
		FileGetShortcut, %A_LoopFileFullPath% , TargetPath,WorkingDir,ArgsName,Description,IconPath,IconNum, RunState
		if (!ErrorLevel&&TargetPath){
			Args:=Trim(Trim(Args),""""),ArgsName:=Trim(Trim(ArgsName),""""),Target:=Trim(Trim(Target),""""),TargetPath:=Trim(Trim(TargetPath),"""")
			if !FileExist(TargetPath){
				FileDelete,%A_LoopFileFullPath%
				Continue
			}
			if (Target=TargetPath&&Args=ArgsName){
				Return A_LoopFileFullPath
			}
		}
	}
	Return False
}

给TA捐赠
共{{data.count}}人
人已捐赠
其他

Edge.ahk

2021-11-29 15:33:48

其他

气泡提示框

2021-11-30 14:54:37

2 条回复 A文章作者 M管理员
  1. AHK中文社区

    用代码框包起来,效果更好!

  2. AHK中文社区
    1河许人给您打赏了¥2
个人中心
购物车
优惠劵
有新私信 私信列表
搜索