WinLIRC 클라이언트
이 스크립트는 리모트 콘트롤에 버튼을 누를 때마다 WinLIRC 로부터 고지를 받습니다. Winamp, Windows Media Player, 등등을 자동화 하는 데 사용할 수 있습니다. 쉽게 환경을 구성할 수 있습니다. 예를 들어 WinLIRC가 리모트 콘트롤에서 이름이 "VolUp"인 버튼을 인지하면, 이름이 VolUp인 라벨을 생성하고 그 아래에 명령어 SoundSet +5
를 사용하여 사운드카드의 볼륨을 5%만큼 높입니다.
이 스크립트 내려받기 | 다른 샘플 스크립트 | 홈으로
; 다음은 이 스크립트를 사용하는 방법입니다: ; 1) 리모트 콘트롤과 버튼을 인식하도록 WinLIRC를 구성합니다. ; WinLIRC는 다음에 있습니다: http://winlirc.sourceforge.net ; 2) 아래의 CONFIG 섹션에서 WinLIRC 경로, 주소, 그리고 포트 번호를 편집합니다. ; 3) 이 스크립트를 기동시킵니다. 필요하면 WinLIRC 서버가 자동으로 기동됩니다. ; 4) 리모트 콘트롤에서 버튼을 눌러 봅니다. 버튼을 누를 때마다 ; 작은 창이 나타나 그 버튼의 이름을 보여줍니다. ; 5) 버튼을 구성해 Winamp, Media Player, 등등과 같은 창에 ; 키눌림과 마우스 클릭을 전송합니다. 아래 예제를 참조하십시오. ; 이 스크립트는 AutoHotkey 1.0.38.04 이상을 요구합니다. ; 변경 이력 ; March 2, 2007: ; - ReceiveData()에 "Critical"를 통하여 신뢰성 개선. ; October 5, 2005: ; - 시스템이 셧다운/로그오프할 때 Winsock 경고 대화상자 "10054"가 나타나는 것을 제거. ; - 반복 속도를 감소시키기 위해 옵션 "DelayBetweenButtonRepeats"를 추가. ; ------------------------------------------------------------- ; CONFIGURATION SECTION: 여기에 원하는 대로 설정합니다. ; ------------------------------------------------------------- ; 어떤 리모콘은 버튼을 누르고 있는 동안 신호를 빠르게 반복해 보냅니다. ; 이 때문에 리모트 콘트롤이 오직 하나의 신호만 전송하도록 만들기가 어렵습니다 ; 다음 설정은 지정된 시간이 경과할 때까지 반복된 신호를 무시함으로써 ; 이 문제를 해결합니다. 200이면 보통 좋은 설정입니다. ; 0으로 설정하면 이 특징을 불능으로 만듭니다. DelayBetweenButtonRepeats = 200 ; 경로를 WinLIRC에 지정합니다. 예를 들어 C:\WinLIRC\winlirc.exe WinLIRC_Path = %A_ProgramFiles%\WinLIRC\winlirc.exe ; WinLIRC의 주소와 포트를 지정합니다. 보통 127.0.0.1 (localhost)과 8765입니다. WinLIRC_Address = 127.0.0.1 WinLIRC_Port = 8765 ; 다음 두 줄은 변경하지 마십시오. 건너뛰고 아래에서 계속하십시오. Gosub WinLIRC_Init return ; -------------------------------------------- ; ASSIGN ACTIONS TO THE BUTTONS ON YOUR REMOTE ; -------------------------------------------- ; 아래에서 리모트 콘트롤 버튼을 구성하십시오. 버튼에 WinLIRC의 이름을 사용합니다. ; 이름은 WinLIRC 환경구성 파일(.cf)에서 보실 수 있습니다. ; -- 아니면 리모트 콘트롤에 버튼을 마우것나 누르면 ; 스크립트가 작은 창에 그 버튼의 이름을 잠시 보여줍니다. ; ; 아래는 몇 가지 예제입니다. ; 자유롭게 여러분의 기호에 맞게 개조하거나 지원도 됩니다. VolUp: SoundSet +5 ; 마스터 볼륨을 5%만큼 늘립니다. Vista에서는 이 줄을 다음으로 교체하십시오: Send {Volume_Up} return VolDown: SoundSet -5 ; 마스터 볼륨을 5%만큼 줄입니다. Vista에서는 이 줄을 다음으로 교체하십시오: Send {Volume_Down} return ChUp: WinGetClass, ActiveClass, A if ActiveClass in Winamp v1.x,Winamp PE ; Winamp가 활성화 되어 있습니다. Send {right} ; 오른쪽-화살표 키눌림을 전송합니다. else ; 다른 유형의 창이 활성화 되어 있습니다. Send {WheelUp} ; 마우스 휠을 한 눈금 위로 돌립니다. return ChDown: WinGetClass, ActiveClass, A if ActiveClass in Winamp v1.x,Winamp PE ; Winamp가 활성화 되어 있습니다. Send {left} ; 왼쪽-화살표 키눌림을 전송합니다. else ; 다른 유형의 창이 활성화 되어 있습니다. Send {WheelDown} ; 마우스 휠을 한 눈금 아래로 돌립니다. return Menu: IfWinExist, Untitled - Notepad { WinActivate } else { Run, Notepad WinWait, Untitled - Notepad WinActivate } Send Here are some keystrokes sent to Notepad.{Enter} return ; 위의 예제에서 어떻게 일반적인 과업을 달성하는지 감을 잡을 수 있습니다. ; 오토핫키의 기본 지식을 습득하려면 빨지 시작하기 자습서를 찾아 보시기 바랍니다: ; http://www.autohotkey.com/docs/Tutorial.htm ; ---------------------------- ; END OF CONFIGURATION SECTION ; ---------------------------- ; 이 시점 이후로 변경하지 마십시오. ; 단, 스크립트의 핵심 기능을 바꾸고 싶다면 예외입니다. WinLIRC_Init: OnExit, ExitSub ; 접속 청소 목적을 위해. ; 아직 실행되지 않았다면 WinLIRC를 기동합니다: Process, Exist, winlirc.exe if not ErrorLevel ; WinLIRC에 대한 PID가 발견되지 않았습니다. { IfNotExist, %WinLIRC_Path% { MsgBox 파일 "%WinLIRC_Path%"가 존재하지 않습니다. 이 스크립트를 편집해 그 위치를 지정하십시오. ExitApp } Run %WinLIRC_Path% Sleep 200 ; WinLIRC에 초기화 할 시간을 줍니다 (아마도 거의 필요하지 않을 겁니다. 신경쓰지 마십시오). } ; WinLIRC에 접속합니다 (또는 그를 대신하면 어떤 종류의 서버이든 접속합니다): socket := ConnectToAddress(WinLIRC_Address, WinLIRC_Port) if socket = -1 ; 접속 실패 (이미 이유는 보여주었습니다). ExitApp ; 이 스크립트의 메인 창을 찾습니다: Process, Exist ; 이렇게 하면 ErrorLevel에 이 스크립트의 PID가 설정됩니다 (이런식으로 하면 컴파일된 스크립트를 지원할 수 있습니다). DetectHiddenWindows On ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel) DetectHiddenWindows Off ; OS가 스크립트에게 들어오는 데이터가 수신되기를 기다리고 있다고 고지할 때, ; 다음은 그 데이터를 읽기 위하여 함수를 기동시킵니다: NotificationMsg = 0x5555 ; 임의의 메시지 번호, 그러나 0x1000 보다는 커야 합니다. OnMessage(NotificationMsg, "ReceiveData") ; 접속을 설정해 이 스크립트에게 새로 데이터가 도착할 때마다 메시지를 통하여 고지합니다. ; 이렇게 하면 접속을 폴할 필요가 없으므로, 자원 낭비가 감소합니다. FD_READ = 1 ; 데이터를 읽을 수 있을 때 수신됩니다. FD_CLOSE = 32 ; 접속이 닫힐 때 수신됩니다. if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_READ|FD_CLOSE) { MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") ExitApp } return ConnectToAddress(IPAddress, Port) ; 대부분의 유형의 TCP 서버에 접속할 수 있습니다. WinLIRC 만으로 제한되지 않습니다. ; 실패하면 -1 (INVALID_SOCKET)을 돌려주고 성공하면 소켓 ID를 돌려줍니다. { VarSetCapacity(wsaData, 400) result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "UInt", &wsaData) ; Winsock 2.0 (0x0002)을 요구합니다 ; WSAStartup()이 이스크립트가 첫 번째로 호출한 Winsock 함수일 가능성이 높으므로, ; ErrorLevel을 점검해 OS에 Winsock 2.0이 있는지 알아 봅니다: if ErrorLevel { MsgBox WSAStartup() could not be called due to error %ErrorLevel%. Winsock 2.0 or higher is required. return -1 } if result ; 0이 아니라면, 실패했다는 뜻입니다 (대부분의 Winsock 함수는 성공하면 0을 돌려줍니다). { MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") return -1 } AF_INET = 2 SOCK_STREAM = 1 IPPROTO_TCP = 6 socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP) if socket = -1 { MsgBox % "socket() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") return -1 } ; 접속을 준비합니다: SizeOfSocketAddress = 16 VarSetCapacity(SocketAddress, SizeOfSocketAddress) InsertInteger(2, SocketAddress, 0, AF_INET) ; sin_family InsertInteger(DllCall("Ws2_32\htons", "UShort", Port), SocketAddress, 2, 2) ; sin_port InsertInteger(DllCall("Ws2_32\inet_addr", "AStr", IPAddress), SocketAddress, 4, 4) ; sin_addr.s_addr ; 접속을 시도합니다: if DllCall("Ws2_32\connect", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress) { MsgBox % "connect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . ". Is WinLIRC running?" return -1 } return socket ; -1 말고 유효한 소켓 ID를 돌려주어 성공을 알립니다. } ReceiveData(wParam, lParam) ; OnMessage()를 수단으로, 이 함수는 새 데이터가 접속에 도착할 때마다 ; 자동으로 호출되도록 설정되어 있습니다. 데이터를 WinLIRC로부터 읽어서 ; 그 내용에 따라 적절하게 조치를 취합니다. { Critical ; 이미 쓰레드가 실행되어 있기 때문에 또다른 같은 메시지가 폐기되지 않도록 막습니다. socket := wParam ReceivedDataSize = 4096 ; 이전 데이터를 처리하는데 지연 시간이 있어서 많은 데이터가 버퍼에 찰 경우를 대비해 크게 잡습니다. VarSetCapacity(ReceivedData, ReceivedDataSize, 0) ; recv()에 사용하기 위해 0을 마지막 매개변수에 지정해 문자열을 종료합니다. ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Str", ReceivedData, "Int", ReceivedDataSize, "Int", 0) if ReceivedDataLength = 0 ; 접속이 우아하게 닫혔습니다. 아마도 WinLIRC를 끝냈기 때문일 것입니다. ExitApp ; OnExit 루틴이 우리를 대신하여 WSACleanup()을 호출해 줍니다. if ReceivedDataLength = -1 { WinsockError := DllCall("Ws2_32\WSAGetLastError") if WinsockError = 10035 ; WSAEWOULDBLOCK, "더 이상 읽을 데이터가 없다"는 뜻입니다. return 1 if WinsockError <> 10054 ; WSAECONNRESET, WinLIRC가 시스템 셧다운/로그오프를 통하여 닫힐 때 일어납니다. ; 예측 하지 못한 에러이므로 보고합니다. 또 무한 회돌이를 피하기 위해 종료합니다. MsgBox % "recv() indicated Winsock error " . WinsockError ExitApp ; OnExit 루틴이 우리를 대신에 WSACleanup()를 호출합니다. } ; 그렇지 않으면, 수신된 데이터를 처리합니다. 테스트에 의하면 한 번에 한 줄 이상을 얻을 수 있습니다 ; (명시적으로-전송된 IR 신호에 대해서도 마찬가지입니다), 다음 메쏘드가 적절하게 처리합니다. ; WinLIRC로부터 얻은 데이터는 다음 예제와 비슷합니다 (자세한 것은 WinLIRC 문서를 참조하십시오): ; 0000000000eab154 00 NameOfButton NameOfRemote Loop, parse, ReceivedData, `n, `r { if A_LoopField in ,BEGIN,SIGHUP,END ; 빈 줄과 WinLIRC의 시작 메시지를 무시합니다. continue ButtonName = ; 아래에서 3 개 보다 적게 필드가 발견될 경우를 대비해 비워서 초기화합니다. Loop, parse, A_LoopField, %A_Space% ; 버튼 이름을 추출합니다. 세 번째 필드입니다. if A_Index = 3 ButtonName := A_LoopField global DelayBetweenButtonRepeats ; 전역 변수를 선언해 이 함수에 적용합니다. static PrevButtonName, PrevButtonTime, RepeatCount ; 이 변수들은 호출 사이에 값을 기억합니다. if (ButtonName != PrevButtonName || A_TickCount - PrevButtonTime > DelayBetweenButtonRepeats) { if IsLabel(ButtonName) ; 이 버튼에 연관된 서브루틴이 있습니다. Gosub %ButtonName% ; 그 서브루틴을 기동시킵니다. else ; 연관된 서브루틴이 없으므로, 간략하게 어느 버튼이 눌렸는지 보여줍니다. { if (ButtonName == PrevButtonName) RepeatCount += 1 else RepeatCount = 1 SplashTextOn, 150, 20, Button from WinLIRC, %ButtonName% (%RepeatCount%) SetTimer, SplashOff, 3000 ; 이렇게 하면 창이 보이는 동안에 더 많은 신호를 처리할 수 있습니다. } PrevButtonName := ButtonName PrevButtonTime := A_TickCount } } return 1 ; 프로그램에게 더 이상 이 메시지를 처리할 필요가 없다고 알립니다. } SplashOff: SplashTextOff SetTimer, SplashOff, Off return InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4) ; 호출자는 pDest의 가용 능력이 충분한지 확인해야 합니다. 기존의 내용을 pDest에 보존하기 위해, ; pOffset에서 시작해서 오직 pSize 개수의 바이트만 변경됩니다. { Loop %pSize% ; 각 바이트를 구조체 안으로 날 이진 데이터로 복사합니다. DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF) } ExitSub: ; 이 서브루틴은 스크립트가 어떤 이유로 종료될 때 자동으로 호출됩니다. ; MSDN에 의하면: "WSACleanup이 호출될 때 열린 소켓은 모두 리셋되고 ; 마치 closesocket 이 호출될 것처럼 자동으로 할당이 해제됩니다." DllCall("Ws2_32\WSACleanup") ExitApp