메뉴 여닫기
개인 메뉴 토글
로그인하지 않음
만약 지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.

커서 Caret 텍스트 찾기 프로그램: 두 판 사이의 차이

데브카페
잔글 개발노트 양식 일괄 적용
 
1번째 줄: 1번째 줄:
{{개발노트
|요약=Windows 에서 캐럿(커서) 위치의 텍스트를 출력하는 C++과 AutoIt ,Python 소스 예제입니다.
|핵심=
|분야=Python
|작성일=2025-06-17
}}
== Windows 에서 커서(Caret)가 위치 텍스트 출력 프로그램 ==
== Windows 에서 커서(Caret)가 위치 텍스트 출력 프로그램 ==



2026년 11월 12일 (목) 12:45 기준 최신판

DEVCAFE NOTE
Python
Windows 에서 캐럿(커서) 위치의 텍스트를 출력하는 C++과 AutoIt ,Python 소스 예제입니다.
KEY POINTS · 핵심 정리
작성일2025년 6월 17일

Windows 에서 커서(Caret)가 위치 텍스트 출력 프로그램

  • Windows 에서 캐럿(커서) 위치의 텍스트를 출력하는 C++과 AutoIt ,Python 소스 예제입니다.

C++ 버전

#include <windows.h>
#include <iostream>
#include <string>

std::string GetTextAroundCaret(HWND hWnd) {
    DWORD startPos, endPos;
    CHAR buffer[1024] = {0};
    
    // 현재 포커스가 있는 윈도우 핸들 가져오기
    if (hWnd == NULL) {
        hWnd = GetFocus();
    }
    
    // Edit 컨트롤인지 확인
    char className[256];
    GetClassNameA(hWnd, className, sizeof(className));
    
    if (strcmp(className, "Edit") != 0) {
        return "Not an Edit control";
    }
    
    // 현재 선택 영역 가져오기
    SendMessageA(hWnd, EM_GETSEL, (WPARAM)&startPos, (LPARAM)&endPos);
    
    // 텍스트 길이 가져오기
    int textLength = GetWindowTextLengthA(hWnd);
    
    // 전체 텍스트 가져오기
    GetWindowTextA(hWnd, buffer, textLength + 1);
    
    // 캐럿 주변 텍스트 추출 (예: 캐럿 앞 10자, 뒤 10자)
    int start = max(0, (int)startPos - 10);
    int end = min(textLength, (int)startPos + 10);
    
    return std::string(buffer + start, buffer + end);
}

int main() {
    std::cout << "Caret 위치 텍스트를 가져오는 프로그램 (10초간 실행)" << std::endl;
    
    for (int i = 0; i < 10; i++) {
        HWND foregroundWindow = GetForegroundWindow();
        HWND focusWindow = GetFocus();
        
        std::string text = GetTextAroundCaret(focusWindow);
        std::cout << "Caret 주변 텍스트: " << text << std::endl;
        
        Sleep(1000); // 1초 대기
    }
    
    return 0;
}

AutoIt 버전


#include <WinAPI.au3>
#include <EditConstants.au3>
#include <Math.au3>

Func GetCaretText()
    Local $hWnd = _WinAPI_GetFocus()
    If $hWnd = 0 Then Return "포커스된 창이 없습니다."
    
    ; 클래스 이름 확인
    Local $sClass = _WinAPI_GetClassName($hWnd)
    If $sClass <> "Edit" Then Return "Edit 컨트롤이 아닙니다: " & $sClass
    
    ; 현재 선택 위치 가져오기
    Local $iStart = _SendMessage($hWnd, $EM_GETSEL)
    Local $iEnd = BitShift($iStart, 16)
    $iStart = BitAND($iStart, 0xFFFF)
    
    ; 전체 텍스트 가져오기
    Local $sText = ControlGetText("[ACTIVE]", "", $hWnd)
    If @error Then Return "텍스트를 가져올 수 없습니다."
    
    ; 캐럿 주변 텍스트 추출 (앞 10자, 뒤 10자)
    Local $iTextLen = StringLen($sText)
    Local $iFrom = _Max(0, $iStart - 10)
    Local $iTo = _Min($iTextLen, $iStart + 10)
    
    Return StringMid($sText, $iFrom + 1, $iTo - $iFrom)
EndFunc

; 메인 프로그램
ToolTip("캐럿 위치 텍스트를 가져오는 AutoIt 프로그램 (10초간 실행)", 0, 0)

For $i = 1 To 10
    Local $sText = GetCaretText()
    ConsoleWrite("캐럿 주변 텍스트: " & $sText & @CRLF)
    Sleep(1000)
Next

Exit

프로그램 사용 방법

    • C++ 버전:
      - Visual Studio 등으로 컴파일 후 실행
      - 10초 동안 1초 간격으로 포커스된 Edit 컨트롤의 캐럿 위치 주변 텍스트 출력
    • AutoIt 버전:
      - AutoIt 스크립트로 저장 후 실행

++: - 10초 동안 1초 간격으로 콘솔에 캐럿 위치 주변 텍스트 출력

주의 사항

- 이 프로그램은 표준 Edit 컨트롤에서만 작동합니다.
- RichEdit, Chrome, 워드프로세서 등 다른 컨트롤에서는 작동하지 않을 수 있습니다.
- 관리자 권한이 필요한 경우도 있을 수 있습니다.

Python 버전

  • pywin32 라이브러리 (pip install pywin32) 설치
pip install pywinauto keyboard


import sys
import threading
import time
import tkinter as tk
from tkinter import ttk
import keyboard
import pywinauto
from pywinauto.keyboard import send_keys

class CaretReaderApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Caret Reader v1.1")
        self.root.geometry("320x130")
        self.root.attributes("-topmost", True)  # 창을 항상 위에 유지
        self.root.resizable(False, False)

        # UI 스타일 설정
        style = ttk.Style()
        style.theme_use("clam")

        # UI 요소 배치
        self.label_info = ttk.Label(root, text="[Ctrl + Shift + F12]를 누르면\n커서 위치의 문자를 읽습니다.", justify="center", font=("Malgun Gothic", 10))
        self.label_info.pack(pady=12)

        self.label_result = ttk.Label(root, text="읽은 문자: 없음", font=("Malgun Gothic", 12, "bold"), foreground="#0078D7")
        self.label_result.pack(pady=5)

        # 단축키 리스너 등록 (데몬 스레드)
        self.hotkey_thread = threading.Thread(target=self.setup_hotkey, daemon=True)
        self.hotkey_thread.start()

    def setup_hotkey(self):
        # 전역 단축키 매핑
        keyboard.add_hotkey("ctrl+shift+f12", self.trigger_caret_reading)
        keyboard.wait()

    def trigger_caret_reading(self):
        # 무거운 키보드/UI 매크로 작업 시 GUI 멈춤을 방지하기 위해 스레드 분리
        threading.Thread(target=self.read_caret_pipeline, daemon=True).start()

    def read_caret_pipeline(self):
        print("\n[이벤트] 단축키 감지! 커서 위치 분석 시작...")
        
        # 1단계: 정석적인 UI Automation (UIA) 시도
        character = self.try_uia_backend()
        
        # 2단계: UIA 실패 시, 질문하신 '커서 + 라이트키 + 클립보드' 매크로 보안책 가동
        if not character:
            print("[UIA 실패] -> 커서 + Right Key 매크로 우회(Fallback)를 시작합니다.")
            character = self.try_cursor_right_fallback()

        # GUI 화면 레이블 업데이트
        if character and character.strip() != "":
            cleaned_char = character.replace("\n", "\\n").replace("\r", "\\r")
            self.root.after(0, lambda: self.label_result.config(text=f"읽은 문자: '{cleaned_char}'"))
            print(f"[성공] 최종 가져온 문자: '{cleaned_char}'")
        else:
            self.root.after(0, lambda: self.label_result.config(text="읽은 문자: 추출 실패"))
            print("[실패] 문자를 읽지 못했습니다.")

    def try_uia_backend(self):
        try:
            # 활성화된 앱 내부에서 포커스된 컨트롤 찾기
            app = pywinauto.Application(backend="uia").connect(active_only=True)
            focused_control = app.top_window().set_focus()
            
            text_pattern = focused_control.get_text_block()
            selection = text_pattern.get_selection()
            
            if selection:
                caret_range = selection
                caret_range.expand_to_enclosing_unit("Character")
                return caret_range.get_text()
        except Exception as e:
            # UIA를 안 만들어둔 일반 창이나 게임 등에서는 예외 발생 -> 자연스럽게 폴백으로 이동
            pass
        return None

    def try_cursor_right_fallback(self):
        try:
            import win32clipboard
            
            # 기존 클립보드 문자열 백업 (유저 데이터 보호)
            original_text = ""
            try:
                win32clipboard.OpenClipboard()
                if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT):
                    original_text = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
                win32clipboard.CloseClipboard()
            except Exception:
                pass

            # 1. 클립보드를 깔끔하게 비우기 (이전 데이터 오염 방지)
            win32clipboard.OpenClipboard()
            win32clipboard.EmptyClipboard()
            win32clipboard.CloseClipboard()

            # 2. Shift + Right Arrow 전송 (커서 기준 오른쪽 한 글자 블록 선택)
            send_keys("+{RIGHT}")
            time.sleep(0.06)  # 타겟 프로그램이 텍스트 선택 영역을 갱신할 시간 부여

            # 3. Ctrl + C 전송 (복사)
            send_keys("^c")
            time.sleep(0.06)  # 클립보드에 데이터가 올라갈 때까지 대기

            # 4. 클립보드에서 가로챈 문자열 열기
            target_char = None
            win32clipboard.OpenClipboard()
            try:
                if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT):
                    target_char = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
            except Exception as ce:
                print(f"클립보드 읽기 에러: {ce}")
            finally:
                win32clipboard.CloseClipboard()

            # 5. Left Arrow 전송 (블록 지정을 풀고 원래 커서 위치로 원위치 복구)
            send_keys("{LEFT}")
            time.sleep(0.02)

            # 6. 백업해 뒀던 원래 유저의 클립보드 데이터 복원
            if original_text:
                try:
                    win32clipboard.OpenClipboard()
                    win32clipboard.EmptyClipboard()
                    win32clipboard.SetClipboardText(original_text, win32clipboard.CF_UNICODETEXT)
                    win32clipboard.CloseClipboard()
                except Exception:
                    pass

            return target_char
        except Exception as e:
            print(f"매크로 로직 내부 에러: {e}")
        return None

if __name__ == "__main__":
    # OS 전역 키 입력을 제어하므로 반드시 '관리자 권한'으로 실행해야 끊김이 없습니다.
    root = tk.Tk()
    app = CaretReaderApp(root)
    root.mainloop()

코드 설명

  • 필요한 모듈: pywin32를 사용하여 Windows API를 호출합니다.
  • 포커스된 창 확인: GetForegroundWindow로 현재 포커스된 창의 핸들을 가져옵니다.
  • 텍스트 컨트롤 확인: 창의 클래스 이름이 Edit 또는 RichEdit인지 확인하여 텍스트 입력 창인지 판단합니다.
  • 텍스트 가져오기: WM_GETTEXT 메시지로 창의 전체 텍스트를 가져오고, EM_GETSEL 메시지로 캐럿의 시작/끝 위치를 가져옵니다.
  • 캐럿 주변 텍스트 추출: 캐럿 위치를 기준으로 앞뒤 10자 내의 텍스트를 추출합니다.
  • 실행: 프로그램 실행 후 5초 대기하여 사용자가 원하는 텍스트 창(예: 메모장)에 포커스를 맞출 시간을 줍니다.

사용 방법

  • Python 환경에 pywin32를 설치합니다:
pip install pywin32
    위 코드를 .py 파일로 저장합니다(예:caret_text.py).
  • 메모장이나 다른 텍스트 편집기(예: Notepad++, Word 등)를 열고 텍스트를 입력한 뒤 커서를 원하는 위치에 놓습니다.
  • 코드를 실행하면 5초 후 현재 캐럿 위치 주변의 텍스트와 전체 텍스트를 출력합니다.

주의사항

  • 이 코드는 Windows의 기본 텍스트 편집 컨트롤(Edit, RichEdit)에서 작동합니다. 일부 응용프로그램(예: 웹 브라우저의 텍스트 입력란)은 다른 방식으로 구현될 수 있어 작동하지 않을 수 있습니다.
  • 웹 브라우저나 특수 응용프로그램의 경우, 추가로 UIAutomation이나 pyautogui 같은 라이브러리가 필요할 수 있습니다.
  • 코드 실행 전, 포커스된 창이 텍스트 입력 창인지 확인하세요(예: 메모장).

pywinauto 이용

만약 더 복잡한 응용프로그램(예: 브라우저)에서 캐럿 위치의 텍스트를 가져오고 싶다면, Microsoft의 UI Automation 프레임워크를 사용할 수 있습니다. 아래는 pywinauto를 사용한 간단한 예제입니다:

from pywinauto import Application
import time

def get_caret_text_uia():
    try:
        app = Application().connect(focused=True)
        focused_window = app.window(focused=True)
        # 텍스트 컨트롤 찾기
        text_control = focused_window.child_window(control_type="Edit")
        if text_control.exists():
            text = text_control.window_text()
            caret_pos = text_control.get_selection()[0]
            caret_text = text[max(0, caret_pos-10):caret_pos+10]
            return f"캐럿 위치 텍스트: {caret_text}\n전체 텍스트: {text}\n캐럿 위치: {caret_pos}"
        else:
            return "텍스트 컨트롤을 찾을 수 없습니다."
    except Exception as e:
        return f"오류 발생: {str(e)}"

if __name__ == "__main__":
    print("5초 후에 현재 캐럿 위치의 텍스트를 조회합니다...")
    time.sleep(5)
    result = get_caret_text_uia()
    print(result)
  • 추가 설치
pip install pywinauto

참고

    • pywin32 방식은 가볍고 빠르지만 기본 Windows 컨트롤에 한정됩니다.
    • pywinauto는 더 복잡한 응용프로그램에서 작동할 가능성이 높지만 설정이 더 복잡할 수 있습니다.