#include <Misc.au3>
HotKeySet("^+r", "ReadCurrentCaretControl")
HotKeySet("^+x", "_Exit")
ConsoleWrite("텍스트 입력 위치에서 Ctrl+Shift+R을 눌러주세요." & @CRLF)
ConsoleWrite("종료: Ctrl+Shift+X" & @CRLF)
While 1
Sleep(100)
WEnd
Func _Exit()
Exit
EndFunc
Func ReadCurrentCaretControl()
Local $aInfo = _GetCurrentCaretInfo()
If @error Or Not IsArray($aInfo) Then
ConsoleWrite("[오류] 현재 포커스/캐럿 정보를 가져오지 못했습니다." & @CRLF)
Return
EndIf
Local $hActive = $aInfo[0]
Local $hFocus = $aInfo[1]
Local $hCaret = $aInfo[2]
; 캐럿 HWND가 있으면 우선 사용하고, 없으면 포커스 HWND 사용
Local $hTarget = $hCaret
If $hTarget = 0 Then $hTarget = $hFocus
If $hTarget = 0 Then $hTarget = $hActive
If $hTarget = 0 Then
ConsoleWrite("[오류] 대상 컨트롤 핸들이 없습니다." & @CRLF)
Return
EndIf
; 대상이 속한 최상위 프로그램 창
Local $hRoot = _GetRootWindow($hTarget)
If $hRoot = 0 Then $hRoot = $hActive
Local $iPID = _GetWindowPID($hTarget)
Local $sTitle = WinGetTitle($hRoot)
Local $sClass = _GetWindowClassName($hTarget)
ConsoleWrite(@CRLF)
ConsoleWrite("========================================" & @CRLF)
ConsoleWrite("활성 창 HWND : " & $hActive & @CRLF)
ConsoleWrite("포커스 HWND : " & $hFocus & @CRLF)
ConsoleWrite("캐럿 HWND : " & $hCaret & @CRLF)
ConsoleWrite("읽기 대상 HWND : " & $hTarget & @CRLF)
ConsoleWrite("최상위 HWND : " & $hRoot & @CRLF)
ConsoleWrite("PID : " & $iPID & @CRLF)
ConsoleWrite("창 제목 : " & $sTitle & @CRLF)
ConsoleWrite("컨트롤 클래스 : " & $sClass & @CRLF)
ConsoleWrite("캐럿 좌표 : X=" & $aInfo[3] & ", Y=" & $aInfo[4] & @CRLF)
; 표준 Edit/RichEdit 컨트롤의 선택 텍스트 확인
Local $sSelected = ""
If $hRoot <> 0 Then
$sSelected = ControlCommand( _
$hRoot, _
"", _
$hTarget, _
"GetSelected", _
"")
EndIf
If $sSelected <> "" Then
ConsoleWrite("=== 선택된 텍스트 ===" & @CRLF)
ConsoleWrite($sSelected & @CRLF)
ToolTip( _
"선택된 텍스트: " & $sSelected, _
MouseGetPos(0), _
MouseGetPos(1) - 60)
Sleep(2000)
ToolTip("")
Return
EndIf
; 선택 영역이 없거나 GetSelected를 지원하지 않는 경우 전체 텍스트
Local $sText = _GetWindowTextSafe($hTarget)
; 대상 자식 컨트롤에서 실패하면 AutoIt ControlGetText도 시도
If $sText = "" And $hRoot <> 0 Then
$sText = ControlGetText($hRoot, "", $hTarget)
EndIf
If $sText <> "" Then
ConsoleWrite("=== 컨트롤 전체 텍스트 ===" & @CRLF)
ConsoleWrite(StringLeft($sText, 1000) & @CRLF)
Local $sPreview = StringLeft($sText, 200)
If StringLen($sText) > 200 Then $sPreview &= "..."
ToolTip( _
"현재 컨트롤 텍스트:" & @CRLF & $sPreview, _
MouseGetPos(0), _
MouseGetPos(1) - 60)
Sleep(2000)
ToolTip("")
Else
ConsoleWrite("[경고] 핸들은 확인했지만 텍스트를 읽을 수 없습니다." & @CRLF)
ConsoleWrite("브라우저, Electron, Java, UWP 등의 컨트롤은 UIA가 필요할 수 있습니다." & @CRLF)
ToolTip( _
"대상 HWND: " & $hTarget & @CRLF & _
"Class: " & $sClass & @CRLF & _
"텍스트 읽기 실패", _
MouseGetPos(0), _
MouseGetPos(1) - 60)
Sleep(2000)
ToolTip("")
EndIf
EndFunc
; ------------------------------------------------------------
; 현재 활성 스레드의 활성 창, 포커스 창, 캐럿 창과 좌표를 가져온다.
;
; 반환 배열:
; [0] 활성 창 HWND
; [1] 포커스 HWND
; [2] 캐럿 HWND
; [3] 캐럿 화면 X
; [4] 캐럿 화면 Y
; ------------------------------------------------------------
Func _GetCurrentCaretInfo()
Local $tGUI = DllStructCreate( _
"dword cbSize;" & _
"dword flags;" & _
"hwnd hwndActive;" & _
"hwnd hwndFocus;" & _
"hwnd hwndCapture;" & _
"hwnd hwndMenuOwner;" & _
"hwnd hwndMoveSize;" & _
"hwnd hwndCaret;" & _
"long rcLeft;" & _
"long rcTop;" & _
"long rcRight;" & _
"long rcBottom")
DllStructSetData($tGUI, "cbSize", DllStructGetSize($tGUI))
; dwThreadId=0이면 현재 전경 스레드의 GUI 정보를 얻는다.
Local $aCall = DllCall( _
"user32.dll", _
"bool", "GetGUIThreadInfo", _
"dword", 0, _
"struct*", $tGUI)
If @error Or Not IsArray($aCall) Or Not $aCall[0] Then
Return SetError(1, 0, 0)
EndIf
Local $hActive = DllStructGetData($tGUI, "hwndActive")
Local $hFocus = DllStructGetData($tGUI, "hwndFocus")
Local $hCaret = DllStructGetData($tGUI, "hwndCaret")
Local $iX = DllStructGetData($tGUI, "rcLeft")
Local $iY = DllStructGetData($tGUI, "rcTop")
; 캐럿 좌표는 hwndCaret 기준의 클라이언트 좌표이므로 화면 좌표로 변환
If $hCaret <> 0 Then
Local $tPoint = DllStructCreate("long X;long Y")
DllStructSetData($tPoint, "X", $iX)
DllStructSetData($tPoint, "Y", $iY)
Local $aPoint = DllCall( _
"user32.dll", _
"bool", "ClientToScreen", _
"hwnd", $hCaret, _
"struct*", $tPoint)
If Not @error And IsArray($aPoint) And $aPoint[0] Then
$iX = DllStructGetData($tPoint, "X")
$iY = DllStructGetData($tPoint, "Y")
EndIf
EndIf
Local $aResult[5]
$aResult[0] = $hActive
$aResult[1] = $hFocus
$aResult[2] = $hCaret
$aResult[3] = $iX
$aResult[4] = $iY
Return $aResult
EndFunc
; 대상 HWND가 속한 최상위 창을 반환
Func _GetRootWindow($hWnd)
If $hWnd = 0 Then Return 0
; GA_ROOT = 2
Local $aCall = DllCall( _
"user32.dll", _
"hwnd", "GetAncestor", _
"hwnd", $hWnd, _
"uint", 2)
If @error Or Not IsArray($aCall) Then Return 0
Return $aCall[0]
EndFunc
; HWND의 프로세스 ID 반환
Func _GetWindowPID($hWnd)
If $hWnd = 0 Then Return 0
Local $aCall = DllCall( _
"user32.dll", _
"dword", "GetWindowThreadProcessId", _
"hwnd", $hWnd, _
"dword*", 0)
If @error Or Not IsArray($aCall) Then Return 0
Return $aCall[2]
EndFunc
; HWND의 실제 클래스명 반환
Func _GetWindowClassName($hWnd)
If $hWnd = 0 Then Return ""
Local $tBuffer = DllStructCreate("wchar[512]")
Local $aCall = DllCall( _
"user32.dll", _
"int", "GetClassNameW", _
"hwnd", $hWnd, _
"struct*", $tBuffer, _
"int", 512)
If @error Or Not IsArray($aCall) Or $aCall[0] = 0 Then Return ""
Return DllStructGetData($tBuffer, 1)
EndFunc
; WM_GETTEXT를 SendMessageTimeout으로 호출한다.
; 응답하지 않는 프로그램 때문에 스크립트가 멈추는 것을 방지한다.
Func _GetWindowTextSafe($hWnd)
If $hWnd = 0 Then Return SetError(1, 0, "")
Local Const $WM_GETTEXT = 0x000D
Local Const $WM_GETTEXTLENGTH = 0x000E
Local Const $SMTO_ABORTIFHUNG = 0x0002
; 텍스트 길이 확인
Local $aLength = DllCall( _
"user32.dll", _
"lresult", "SendMessageTimeoutW", _
"hwnd", $hWnd, _
"uint", $WM_GETTEXTLENGTH, _
"wparam", 0, _
"lparam", 0, _
"uint", $SMTO_ABORTIFHUNG, _
"uint", 1000, _
"ulong_ptr*", 0)
If @error Or Not IsArray($aLength) Or Not $aLength[0] Then
Return SetError(2, 0, "")
EndIf
; DllCall 매개변수: 0=반환값,1=hwnd,2=msg,3=wparam,4=lparam,5=flags,6=timeout,7=result
Local $iLength = $aLength[7]
If $iLength <= 0 Then Return ""
; 과도하게 큰 버퍼 할당 방지
If $iLength > 1000000 Then $iLength = 1000000
Local $tBuffer = DllStructCreate("wchar[" & ($iLength + 1) & "]")
Local $aText = DllCall( _
"user32.dll", _
"lresult", "SendMessageTimeoutW", _
"hwnd", $hWnd, _
"uint", $WM_GETTEXT, _
"wparam", $iLength + 1, _
"lparam", DllStructGetPtr($tBuffer), _
"uint", $SMTO_ABORTIFHUNG, _
"uint", 1000, _
"ulong_ptr*", 0)
If @error Or Not IsArray($aText) Or Not $aText[0] Then
Return SetError(3, 0, "")
EndIf
Return DllStructGetData($tBuffer, 1)
EndFunc