【至0.2版本】Winhole(窗口洞洞波)——洞穿当前窗口,从下层窗口或桌面拖文件给上层窗口

那设置里可不可以写通配符,就算穿一层也允许任意软件都可以穿吧?

你直接用原版的 winhole 就好了。
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=30622

谢了,原版没有编译的版本。。

昨天花了6个小时尝试理解代码,重写一个最小可用版本,让AHK按键映射工具能调用,结果失败了,整个软件的行为完全不可控
早上再次尝试洞洞波,发现其实也有很多问题,只是之前快速试用没发现,这个功能其实挺有用,但从winhole开始的实现方式就不是很好,很难进行适配
后面有时间再尝试了,暂时放弃这个好东西

这个应用超级 nice.我已经用了很久了.目前重度使用中.目前最多只能做到二次穿透对吗?能不能多次穿透呢?比如说按住alt后滚动滚轮一次就穿透一次,滚动两次就穿透两次,滚动三次就穿透三次,直到最终穿透到桌面.因为我一次打开好多个窗口,急需多次穿透.大佬能不能给一下指点该怎么做?

你说的这个功能我倒是有想到方法实现,简单来说就是不搞真实穿透了,而是假装穿洞。类似我之前写的desktoptop原理, 把目标窗口置顶且只显示圆形局部,就会有一种穿洞的感觉。
不过那样也会带来很多问题。

如果真实穿透的话,每穿一次,cpu都会增高一截、卡顿感增高一大截,基本上除非微软自己弄个支持,不然很难优雅地多次穿透。

有时候,按~,会出现圆,但在chrome下一闪一闪的,可以看到桌面包括浏览器后面的内容。但在资源管理器下,圆里面显示好像是资源管理器的皮肤颜色,全是一个颜色,并没有穿透的下面的什么颜色之类的东西。win11 23h2系统。

参考这个人说的

另外,有些窗口是显卡渲染的,也不能穿透。

我倒是安装使用了start11工具,不知道是不是这个影响到了,但是好像有时候还能正常使用。
不管怎么说,应该继续优化一下。

狗哥,.ahk文件要使用什么版本的autohotkey运行啊,我把autohotkey1.1.37.02的unicode32、64、ansi和autohotkey2都试了一下都不行……单独运行你编译好的.exe版就行,主要是我想改个快捷键

就是 autohotkey1.1.37.02 啊?你报的什么错?
以下是代码:

#InstallKeybdHook
#UseHook
DetectHiddenWindows On
IniRead,radius,setting.ini,radius,radius
IniRead,winlist,setting.ini,winlist

; Note: Exit script with Esc::
OnExit("exit")
; Settings
;radius:=500			; Starting radius of the hole.
;increment:=25		; Amount to decrease/increase radius of circle when turning scroll wheel
inverted:=false		; If false, the region is see-throughable.
rate:=40			; The period (ms) of the timer. 40 ms is 25 "fps"

running:=0
running2:=0
Toggle:=0
StartTime:=0

; Make the region
region:=makeCircle(radius)
; Script settings
SetWinDelay,-1
ListLines, off ; Remove when debugging.

$`::
	{
		ElapsedTime := A_TickCount - StartTime
		if ElapsedTime<400
			return
		KeyWait,``,T0.4
		if A_TimeSinceThisHotkey<400
		{
			;·tooltip,hh
			if ElapsedTime>2000
				SendInput,``
			return
		}
		if ! running
		{
			{
				MouseGetPos, , ,nowid
				arr := EnumerateAltTabWindows()
				altTabWindows:=""
				for k, v in arr
				{
					WinGetClass, winClass, ahk_id %v%
					if nowid=%v%
						continue
					WinGet, MMstate, MinMax , ahk_id %v%
					if (MMstate>0.5) or (!MMstate)
						altTabWindows .= v "`n"
				}
				WinGet, exename, ProcessName, ahk_id %nowid%
					loop, Parse, winlist, `n, `r
					{
						if exename=%A_LoopField%
							altTabWindows:=""
					}

				loop, Parse, altTabWindows, `n, `r
				{
					WinGet, exename, ProcessName, ahk_id %A_LoopField%
					loop, Parse, winlist, `n, `r
					{
						if exename=%A_LoopField%
							continue,2
					}
					WinMinimize, ahk_id %A_LoopField%
				}
			}
			timer(1,region,inverted,rate)
		Toggle:=!Toggle
		Pause:=0
		running:=!running
	}
}
return

#if Toggle
return
1::
	if ! running2
	{
		timer2(1,region,inverted,rate)
		running2:=1
	}
return

1 Up::
timer2(0)
running2:=0
return

$`::
return

$` up::
{
	;msgbox,% altTabWindows
	if altTabWindows
	{
		Array := StrSplit(altTabWindows, "`n", "`r")

		loop, % array.length()
		{
			thiswin:=array[array.length()-A_Index+1]
			WinRestore, ahk_id %thiswin%
			WinGet, exename, ProcessName, ahk_id %thiswin%
					loop, Parse, winlist, `n, `r
					{
						if exename=%A_LoopField%
						{
							WinMinimize, ahk_id %thiswin%
							WinRestore, ahk_id %thiswin%
						}
					}
		}
	}
	Toggle:=!Toggle
	timer2(0)
	timer(0)
	Pause:=0
	running:=!running
	running2:=0
	StartTime := A_TickCount
	WinActivate,ahk_id %nowid%
}
return

#if
exit(){
	timer(0) ; For restoring the window if region applied when script closes.
	timer2(0)
	ExitApp
	return
}
timer(state,region:="",inverted:=false,rate:=100){
	; Call with state=0 to restore window and stop timer, state=-1 stop timer but do not restore
	; region, inverted, see WinSetRegion()
	; rate, the period of the timer.
	static timerFn, paused, hWin, aot
	if (state=0) {												; Restore window and turn off timer
		if timerFn
			SetTimer,% timerFn, Off
		if !hWin
			return
		WinSet, Region,, % "ahk_id " hWin
		if !aot													; Restore not being aot if appropriate.
			WinSet, AlwaysOnTop, off, % "ahk_id " hWin
		hWin:="",timerFn:="",aot:="",paused:=0
		return
	} else if (timerFn) {										; Pause/unpause or...
		if (state=-1) {
			SetTimer,% timerFn, Off
			return paused:=1
		} else if paused {
			SetTimer,% timerFn, On
			return paused:=0
		} else {												; ... stop timer before starting a new one.
			SetTimer,% timerFn, Off
		}
	}
	if !hWin {													; Get the window under the Mouse.
		MouseGetPos,,,hWin
		WinGet, aot, ExStyle, % "ahk_id " hWin 					; Get always-on-top state, to preserve it.
		aot&=0x8
		if !aot
			WinSet, AlwaysOnTop, On, % "ahk_id " hWin
	}
	timerFn:=Func("timerFunction").Bind(hWin,region,inverted)	; Initialise the timer.
	timerFn.Call(1)												; For better responsiveness, 1 is for reset static
	SetTimer, % timerFn, % rate
	return
}

timer2(state,region:="",inverted:=false,rate:=100){
	; Call with state=0 to restore window and stop timer, state=-1 stop timer but do not restore
	; region, inverted, see WinSetRegion()
	; rate, the period of the timer.
	static timerFn, paused, hWin, aot
	if (state=0) {												; Restore window and turn off timer
		if timerFn
			SetTimer,% timerFn, Off
		if !hWin
			return
		WinSet, Region,, % "ahk_id " hWin
		if !aot													; Restore not being aot if appropriate.
			WinSet, AlwaysOnTop, off, % "ahk_id " hWin
		hWin:="",timerFn:="",aot:="",paused:=0
		return
	} else if (timerFn) {										; Pause/unpause or...
		if (state=-1) {
			SetTimer,% timerFn, Off
			return paused:=1
		} else if paused {
			SetTimer,% timerFn, On
			return paused:=0
		} else {												; ... stop timer before starting a new one.
			SetTimer,% timerFn, Off
		}
	}
	if !hWin {													; Get the window under the Mouse.
		MouseGetPos,,,hWin
		WinGet, aot, ExStyle, % "ahk_id " hWin 					; Get always-on-top state, to preserve it.
		aot&=0x8
		if !aot
			WinSet, AlwaysOnTop, On, % "ahk_id " hWin
	}
	timerFn:=Func("timerFunction").Bind(hWin,region,inverted)	; Initialise the timer.
	timerFn.Call(1)												; For better responsiveness, 1 is for reset static
	SetTimer, % timerFn, % rate
	return
}

timerFunction(hWin,region,inverted,resetStatic:=0){
	; Get mouse position and convert coords to win coordinates, for displacing the circle
	static px,py
	WinGetPos,wx,wy,,, % "ahk_id " hWin
	CoordMode, Mouse, Screen
	MouseGetPos,x,y
	x-=wx,y-=wy
	if (x=px && y=py && !resetStatic)
		return
	else
		px:=x,py:=y
	WinSetRegion(hWin,region,x,y,inverted)

	return
}

WinSetRegion(hWin,region,dx:=0,dy:=0,inverted:=false){
	; hWin, handle to the window to apply region to.
	; Region should be on the form, region:=[{x:x0,y:y0},{x:x1,y:y1},...,{x:xn,y:yn},{x:x0,y:y0}]
	; dx,dy is displacing the the region by fixed amount in x and y direction, respectively.
	; inverted=true, make the region the only part visible, vs the only part see-throughable for inverted=false
	if !inverted {
		WinGetPos,,,w,h, % "ahk_id " hWin
		regionDefinition.= "0-0 0-" h " " w "-" h " " w "-0 " "0-0 "
	}
	for k, pt in region
		regionDefinition.= dx+pt.x "-" dy+pt.y " "
	WinSet, Region, % regionDefinition, % "ahk_id " hWin
}
; Function for making the circle
makeCircle(r:=100,n:=-1){
	; r is the radius.
	; n is the number of points, let n=-1 to set automatically (highest quality).
	static pi:=ATan(1)*4
	pts:=[]
	n:= n=-1 ? Ceil(2*r*pi) : n
	n:= n>=1994 ? 1994 : n			; There is a maximum of 2000 points for WinSet,Region,...
	loop, % n+1
		t:=2*pi*(A_Index-1)/n, pts.push({x:Round(r*Cos(t)),y:Round(r*Sin(t))})
	return pts
}

heart4tidbit(r:=100)
{
	n:=r*4
	n:= n>=997 ? 997 : n			; There is a maximum of 2000 points for WinSet,Region,... Edit: Changed to 997, since there are two loops
	region:=[]
	oY:=-r//2
	loop, % n
	{
		x:=-2+4*(A_Index-1)/(n-1)
		y:=-Sqrt(1-(Abs(x)-1)**2)
		region.push({x:x*r, y:y*r+oY})
	}
	loop, % n
	{
		x:=2-4*(A_Index-1)/(n-1)
		y:=3*Sqrt(1-Sqrt(Abs(x/2)))
		region.push({x:x*r, y:y*r+oY})
	}
	return region
}

makeTriangle(side:=100){
	heigth:=Round(side*Sqrt(3)/2)
	region:=[{x:0,y:0}, {x:side//2,y:heigth}, {x:-side//2,y:heigth}, {x:0,y:0}]
	oY:=-heigth//2			; Make center, note: oX:=0
	for k, pt in region
		pt.y+=oY			; Make correction for center, note: pt.x+=oX
	return region
}

EnumerateAltTabWindows()
{
	AltTabList := []
	WinGet, list, List
	loop % list
	{
		if IsAltTabWindow(list%A_Index%)
			AltTabList.Push(list%A_Index%)
	}
	return AltTabList
}

IsAltTabWindow(hWnd)
{
	static WS_EX_APPWINDOW := 0x40000, WS_EX_TOOLWINDOW := 0x80, DWMWA_CLOAKED := 14, DWM_CLOAKED_SHELL := 2, WS_EX_NOACTIVATE := 0x8000000, GA_PARENT := 1, GW_OWNER := 4, MONITOR_DEFAULTTONULL := 0, VirtualDesktopExist, PropEnumProcEx := RegisterCallback("PropEnumProcEx", "Fast", 4)
	if (VirtualDesktopExist = "")
	{
		OSbuildNumber := StrSplit(A_OSVersion, ".")[3]
		if (OSbuildNumber < 14393)
			VirtualDesktopExist := 0
		else
			VirtualDesktopExist := 1
	}
	if !DllCall("IsWindowVisible", "uptr", hWnd)
		return
	DllCall("DwmApi\DwmGetWindowAttribute", "uptr", hWnd, "uint", DWMWA_CLOAKED, "uint*", cloaked, "uint", 4)
	if (cloaked = DWM_CLOAKED_SHELL)
		return
	if (realHwnd(DllCall("GetAncestor", "uptr", hwnd, "uint", GA_PARENT, "ptr")) != realHwnd(DllCall("GetDesktopWindow", "ptr")))
		return
	WinGetClass, winClass, ahk_id %hWnd%
	if (winClass = "Windows.UI.Core.CoreWindow")
		return
	if (winClass = "ApplicationFrameWindow")
	{
		VarSetCapacity(ApplicationViewCloakType, 4, 0)
		DllCall("EnumPropsEx", "uptr", hWnd, "ptr", PropEnumProcEx, "ptr", &ApplicationViewCloakType)
		if (NumGet(ApplicationViewCloakType, 0, "int") = 1)   ; https://github.com/kvakulo/Switcheroo/commit/fa526606d52d5ba066ba0b2b5aa83ed04741390f
			return
	}
	; if !DllCall("MonitorFromWindow", "uptr", hwnd, "uint", MONITOR_DEFAULTTONULL, "ptr")   ; test if window is shown on any monitor. alt-tab shows any window even if window is out of monitor.
	;   return
	WinGet, exStyles, ExStyle, ahk_id %hWnd%
	if (exStyles & WS_EX_APPWINDOW)
	{
		if DllCall("GetProp", "uptr", hWnd, "str", "ITaskList_Deleted", "ptr")
			return
		if (VirtualDesktopExist = 0) or IsWindowOnCurrentVirtualDesktop(hwnd)
			return true
		else
			return
	}
	if (exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)
		return
	loop
	{
		hwndPrev := hwnd
		hwnd := DllCall("GetWindow", "uptr", hwnd, "uint", GW_OWNER, "ptr")
		if !hwnd
		{
			if DllCall("GetProp", "uptr", hwndPrev, "str", "ITaskList_Deleted", "ptr")
				return
			if (VirtualDesktopExist = 0) or IsWindowOnCurrentVirtualDesktop(hwndPrev)
				return true
			else
				return
		}
		if DllCall("IsWindowVisible", "uptr", hwnd)
			return
		WinGet, exStyles, ExStyle, ahk_id %hwnd%
		if ((exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)) and !(exStyles & WS_EX_APPWINDOW)
			return
	}
}

GetLastActivePopup(hwnd)
{
	static GA_ROOTOWNER := 3
	hwnd := DllCall("GetAncestor", "uptr", hwnd, "uint", GA_ROOTOWNER, "ptr")
	hwnd := DllCall("GetLastActivePopup", "uptr", hwnd, "ptr")
	return hwnd
}

IsWindowOnCurrentVirtualDesktop(hwnd)
{
	static IVirtualDesktopManager
	if !IVirtualDesktopManager
		IVirtualDesktopManager := ComObjCreate(CLSID_VirtualDesktopManager := "{AA509086-5CA9-4C25-8F95-589D3C07B48A}", IID_IVirtualDesktopManager := "{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}")
	DllCall(NumGet(NumGet(IVirtualDesktopManager+0), 3*A_PtrSize), "ptr", IVirtualDesktopManager, "uptr", hwnd, "int*", onCurrentDesktop)   ; IsWindowOnCurrentVirtualDesktop
	return onCurrentDesktop
}

PropEnumProcEx(hWnd, lpszString, hData, dwData)
{
	if (StrGet(lpszString, "UTF-16") = "ApplicationViewCloakType")
	{
		NumPut(hData, dwData+0, 0, "int")
		return false
	}
	return true
}

realHwnd(hwnd)
{
	VarSetCapacity(var, 8, 0)
	NumPut(hwnd, var, 0, "uint64")
	return NumGet(var, 0, "uint")
}

很奇怪,使用原版WinHole就没问题,我先调试下看看

原来是.ahk文件需要和setting.ini放同一目录下,单独下载的.ahk文件我就没放在一起 :rofl: