カテゴリ:テクノロジ( 199 )

AutoHotKey_Lで、標準出力を得る方法。

 新しいの

 AHKでCUIアプリの吐く標準出力を得る方法が分からんかったので調べたらフォーラムにあったのでメモ。

 日本語環境用にラスト1行だけ書き換えた。

; http://www.autohotkey.com/board/topic/15455-stdouttovar/page-8#entry540600

MsgBox % sOutput := StdoutToVar_CreateProcess("tasklist /s " A_ComputerName)

StdoutToVar_CreateProcess(sCmd, bStream = False, sDir = "", sInput = "") {
  DllCall("CreatePipe", "UintP", hStdInRd , "UintP", hStdInWr , "Uint", 0, "Uint", 0)
  DllCall("CreatePipe", "UintP", hStdOutRd, "UintP", hStdOutWr, "Uint", 0, "Uint", 0)
  DllCall("SetHandleInformation", "Uint", hStdInRd , "Uint", 1, "Uint", 1)
  DllCall("SetHandleInformation", "Uint", hStdOutWr, "Uint", 1, "Uint", 1)
  VarSetCapacity(pi, 16, 0)
  NumPut(VarSetCapacity(si, 68, 0), si)  ; size of si
  NumPut(0x100 , si, 44)      ; STARTF_USESTDHANDLES
  NumPut(hStdInRd , si, 56)    ; hStdInput
  NumPut(hStdOutWr, si, 60)    ; hStdOutput
  NumPut(hStdOutWr, si, 64)    ; hStdError
  If Not DllCall("CreateProcess", "Uint", 0, "Uint", &sCmd, "Uint", 0, "Uint", 0, "int", True, "Uint", 0x08000000, "Uint", 0, "Uint", sDir ? &sDir : 0, "Uint", &si, "Uint", &pi)  ; bInheritHandles and CREATE_NO_WINDOW
    ExitApp
  DllCall("CloseHandle", "Uint", NumGet(pi,0))
  DllCall("CloseHandle", "Uint", NumGet(pi,4))
  DllCall("CloseHandle", "Uint", hStdOutWr)
  DllCall("CloseHandle", "Uint", hStdInRd)
  If sInput <>
  DllCall("WriteFile", "Uint", hStdInWr, "Uint", &sInput, "Uint", StrLen(sInput), "UintP", nSize, "Uint", 0)
  DllCall("CloseHandle", "Uint", hStdInWr)
  bStream ? (bAlloc:=DllCall("AllocConsole"),hCon:=DllCall("CreateFile","str","CON","Uint",0x40000000,"Uint",bAlloc ? 0 : 3,"Uint",0,"Uint",3,"Uint",0,"Uint",0)) : ""
  VarSetCapacity(sTemp, nTemp:=bStream ? 64-nTrim:=1 : 4095)
  Loop
    If DllCall("ReadFile", "Uint", hStdOutRd, "Uint", &sTemp, "Uint", nTemp, "UintP", nSize:=0, "Uint", 0)&&nSize
    {
      NumPut(0,sTemp,nSize,"Uchar"), VarSetCapacity(sTemp,-1), sOutput.=sTemp
      If bStream&&hCon+1
        Loop
          If RegExMatch(sOutput, "[^\n]*\n", sTrim, nTrim)
            DllCall("WriteFile", "Uint", hCon, "Uint", &sTrim, "Uint", StrLen(sTrim), "UintP", nSize:=0, "Uint", 0)&&nSize ? nTrim+=nSize : ""
          Else Break
    }
    Else Break
  DllCall("CloseHandle", "Uint", hStdOutRd)
  bStream ? (DllCall("Sleep","Uint",1000),hCon+1 ? DllCall("CloseHandle","Uint",hCon) : "",bAlloc ? DllCall("FreeConsole") : "") : ""
  Return StrGet(&sOutput,"CP932")
}

 なんで探してたかって、es.exe(コマンドライン版Everything)をAHKから使いたかったんすよ。これでSearchResult := StdoutToVar_CreateProcess("es.exe 検索文字列")とかやれば超便利。

 例えば、以前書いたパスリストをX-Finderのクリップフォルダに変換するスクリプトと組み合わせるとか? テキスト介せばlist2xf.exeでも同じことできるけど。


[PR]
by lordnoesis | 2014-06-10 20:20 | テクノロジ | Trackback | Comments(0)

AutoHotKey_Lで、Sz7内のジャンプ移動してみた。

 axpathlist2.spiはとても便利なんだが、リストが長大になると探索が大変で、フォルダ分けされてればビューアのフォルダ移動機能で楽できるのになーと思ったので、AHKでそれっぽくがんばってみた。

 空行でパス群を区切ったSz7を用意し、MassiGraでAlt+Up/Downすれば、フォルダ移動みたいな感じで次/前のパス群までジャンプする。例によって、MassiGra以外にも応用できるはず。

 ただし、Sz7に記述されたパスのリンク切れなどによって、ファイル名に含まれる連番と、パスの記述順がズレるとジャンプもズレる。ビューア側からは有効パスの連番しかわからず、それを元にSz7を走査してアタリをつけているので。

;--------------------------------------------------------------------------------------
; MassiGra
;--------------------------------------------------------------------------------------
#IfWinActive ahk_class TF811202_MassiGra_Main

; Sz7中の次/前のファイル群(空行区切り)までジャンプ

!Up::Sz7Jump(0)
!Down::Sz7Jump(1)

Sz7Jump(SW) {
  WinGet, WHwnd, ID
  WinGetTitle, WTitle, ahk_id %WHwnd%
  if !RegExMatch(WTitle, "i)^([^<]+\.sz\d+)\\([^<]+)\.[^<]+(?:\s+<.+)?$", $)
    return
  Sz7Path := $1
  ItemNum := $2
  
  FileRead, Sz7Buf, *t *P932 %Sz7Path%
  Sz7Buf := "`n`n" Sz7Buf
  
  if SW {  ; 次
    if !RegExMatch(Sz7Buf, "^(?:\n+[^\n]+){" ItemNum ",}?\n{2,}[^\n]+\.([^\n\.]+)", $)
      return
  } else {  ; 前
    if !RegExMatch(Sz7Buf, "^(?:\n+[^\n]+){0," ItemNum - 2 "}\n{2,}[^\n]+\.([^\n\.]+)", $)
      return
  }
  TxtBuf := $
  FileExt := $1
  
  ErrorLevel := 0
  while !ErrorLevel
    StringReplace, TxtBuf, TxtBuf, `n`n, `n, All
  
  StringReplace, TxtBuf, TxtBuf, `n, `n, UseErrorLevel
  DropFiles(WHwnd, Sz7Path "\" ZeroSup(ErrorLevel, 9) "." FileExt)
}



ZeroSup(Num, NumDigit) {
  RegExMatch(Num, "^0*+(\d*)$", $)
  StringLen, NumLen, $1
  LoopNum := NumDigit - NumLen
  Loop, %LoopNum%
    $1 := 0 $1
  return, $1
}


DropFiles(hwnd, files, ptX=0, ptY=0, fNC=False) {  ; AutoHotkey スレッド part11 >>332
  static char_type:= A_IsUnicode ? "UShort" : "UChar"
    , char_size := A_IsUnicode ? 2 : 1
    , isUnicode := A_IsUnicode ? 1 : 0
  files := RTrim(files, "`r`n`t ") . "`n`n"
  byte_length := StrLen(files) * char_size
  Loop, Parse, files
    If (A_LoopField = "`n")
      NumPut(0x00, files, (A_Index-1) * char_size, char_type)
  
  hDrop := DllCall("GlobalAlloc", "UInt", 0x42, "UInt",20 + byte_length, "Ptr")
  p := DllCall("GlobalLock", "Ptr", hDrop)
  NumPut(20 , p + 00, "Int") ; offset
  NumPut(ptX , p + 04, "Int") ; pt.x
  NumPut(ptY , p + 08, "Int") ; pt.y
  NumPut(fNC , p + 12, "Int") ; fNC
  NumPut(isUnicode, p + 16, "Int") ; fWide
  DllCall("RtlMoveMemory", "Ptr", p + 20, "Str", files, "UInt", byte_length)
  DllCall("GlobalUnlock", "Ptr", hDrop)
  PostMessage, WM_DROPFILES := 0x233, hDrop , 0, , ahk_id %hwnd%
  if ErrorLevel
    MsgBox, DropFiles Err %errorlevel%
}

 たぶん、問題ない(ジャンプ先検出の正規表現に辿り着いたのかなり偶然なのでry)。

 axpathlist2.spi(てかSz7)自体がフォルダ分けに対応してればさー。あと、ファイル名とか内臓Docで元のパスわかるようにするとかー、リンク切れはダミー画像とかー、Unicode対応とかー、別のSz7のインクルードとかー、ワイルドカードとかー、相対パスとかさー、などと好き勝手いってたら、相対パスにはとっくの昔に対応済みだったという恥ずかしエピソードが。


[PR]
by lordnoesis | 2014-02-26 20:00 | テクノロジ | Trackback | Comments(0)

AutoHotKey_Lで、メディアプレーヤーを目覚ましアラームに。

 快適な目覚めのためには、時間をかけて徐々に目覚める必要があり、それに音楽を使うというのは知られた方法だが、音量までタイマー操作できるハード/ソフトってそうなくね? てかAHKで操作すればよくね? というわけで書いた。

 ウチはサブのオーディオデバイスのみスピーカーに繋がっている構成なので、出力するオーディオデバイスが選択できて、かつシンプルなプレーヤーということでQonohaを使った。自由度それなりに書いたので、その辺は各自適当に。

;--------------------------------------------------------------------------------------
; Qonoha
;--------------------------------------------------------------------------------------
#IfWinActive ahk_class QonohaPlayerMainWnd

F12::
  SetTimer, AAlarmRun, Off
  ;~ TipView()

  Loop {
    InputBox, AlarmTimeSet, AudioAlarm, アラーム時刻を24時間表記で入力してください`n例:午後1時5分 => 1305, , , 150
    if ErrorLevel
      return
    if RegExMatch(AlarmTimeSet, "([0-1][0-9]|2[0-3])([0-5][0-9])", $)
      break
  }
  AlarmH := $1
  AlarmM := $2

  AlarmTime := A_Now
  if (A_Hour > AlarmH) || (A_Hour == AlarmH && A_Min >= AlarmM) {
    AlarmTxt := "明日"
    EnvAdd, AlarmTime, 1, D
  } else {
    AlarmTxt := "本日"
  }

  FormatTime, AlarmTime, %AlarmTime%, yyyyMMdd'%AlarmH%%AlarmM%00'

  FormatTime, AlarmTxt, %AlarmTime%, %AlarmTxt%HH時mm分
  MsgBox, 262177, AudioAlarm, %AlarmTxt% にセットします。
  IfMsgBox, Cancel
    return

  FormatTime, AlarmTxt, %AlarmTime%, アラーム日時:dd日HH時mm分

  EnvSub, AlarmTime, , S
  if !AlarmTime {
    MsgBox, 262160, AudioAlarm, %AlarmTxt%を過ぎています。
    return
  }

  ;~ TipView(AlarmTxt, AlarmTime "000", 0, 0)
  SetTimer, AAlarmRun, -%AlarmTime%000
return

AAlarmRun:
  AudioFilePath := "C:\Users\Public\Music\Sample Music\Sleep Away.mp3"  ; 再生したいファイルのパス。
  SplitPath, AudioFilePath, AudioFileName

  PlayerPath := "C:\Qonoha\Qonoha.exe"
  PlayerTitle := AudioFileName " ahk_class QonohaPlayerMainWnd"
  VolUpKey := "Up"  ; 音量を上げるキー
  VolDnKey := "Down"  ; 音量を下げるキー

  VolMax := 25  ; 音量の最大値(VolUpKeyを何回叩くか)
  VolMaxPeriod := 3600000  ; 音量が最大になるまでにかかる時間(ミリ秒)

  Run, "%PlayerPath%" "%AudioFilePath%"
  WinWait, %PlayerTitle%
  Send, {%VolDnKey% %VolMax%}  ; 音量をゼロに

  VolUpWait := VolMaxPeriod / VolMax
  MACnt := 0
  SetTimer, AAlarmSnooze, %VolUpWait%

  MsgBox, 262208, AudioAlarm, %AlarmTxt% になりました。`n`n%AudioFileName%
  SetTimer, AAlarmSnooze, Off
  WinClose, %PlayerTitle%
return

AAlarmSnooze:
  MACnt++
  WinActivate, %PlayerTitle%
  Send, {%VolUpKey%}
  WinActivate, AudioAlarm ahk_class #32770
  if (VolMax <= MACnt)
    SetTimer, AAlarmSnooze, Off
return

 セット後にアラーム時刻を表示したいなら、コメントアウトしてあるTipView()を使う。

;--------------------------------------------------------------------------------------
; TipView.ahk
;--------------------------------------------------------------------------------------

TipView(TextBuf = "", Timer = 3000, X = "", Y = "", SW = 0) {
  if SW
    CoordMode, ToolTip

  ToolTip, %TextBuf%, X, Y
  if !Timer
    Timer := "Off"
  SetTimer, TipClose, %Timer%
}

TipClose:
  SetTimer, TipClose, Off
  ToolTip
return


[PR]
by lordnoesis | 2014-02-16 22:15 | テクノロジ | Trackback | Comments(0)

AutoHotKey_Lで、パスリストをX-Finderのクリップフォルダに変換。Ver.1.2

 先々週書いたEverythingの検索結果をX-Finderで開くシリーズの最新版なんだけど、関数をEverything以外でも使えるようにしてみた。これで\n区切りのパスリストがあれば、なんでもサムネで確認できるよ!やったねry
 もっと機能追加してからPostしようと思って大していじらず1週間たってしまったのでエターナる前にPost。

 使用例は相変わらずEverything。検索結果の任意のアイテムを選択状態にしてF11を打鍵すると、X-Finderでクリップフォルダとして開く。Ctrl+F11なら、前回のクリップフォルダに追加。

 Everything以外は各自勝手に。InputBoxでIni名指定できたらブックマーク的に使えて便利かも。

 デフォルトのIni名にプレフィックスつけたんで、X-Finderのクリップフォルダ設定に;Extra:%X-Finder%CF_*.iniと加えとくと便利。

;--------------------------------------------------------------------------------------
; Everything
;--------------------------------------------------------------------------------------
#IfWinActive ahk_class ahk_class EVERYTHING

F11::
  #ClipboardTimeout 200000
  
  ClipWaitNull()
  Send, ^c
  ClipWait, 3
  if !ErrorLevel {
    CFPath := Path2ClipFld(Clipboard, "CF_Everything.ini")
    Run, "C:\X-Finder\XF.exe" "Extra:%CFPath%"  ; X-Finderのフルパス。
  }
  ClipWaitRestore()
return

^F11::
  #ClipboardTimeout 200000
  
  ClipWaitNull()
  Send, ^c
  ClipWait, 3
  if !ErrorLevel {
    CFPath := Path2ClipFld(Clipboard, "CF_Everything.ini", 1)
    Run, "C:\X-Finder\XF.exe" "Extra:%CFPath%"  ; X-Finderのフルパス。
  }
  ClipWaitRestore()
return


Path2ClipFld(PathList, ClipFldPath = 0, AddSW = 0) {  ; Ver.1.2
  XFPath := "C:\X-Finder\"  ; パスリストのデフォルト保存先。
  if ClipFldPath {
    IfNotInString, ClipFldPath, :\
      ClipFldPath := XFPath ClipFldPath
  } else {
    ClipFldPath := XFPath "CF_.ini"
  }
  
  StringReplace, PathList, PathList, `n, `n, UseErrorLevel
  PathNum := ErrorLevel + 1
  
  if AddSW
    IniRead, IniCnt, %ClipFldPath%, X-Finder, Count, 0
  else
    IniCnt := 0
  
  if !IniCnt
    ClipFldBuf := "[X-Finder]`nCount=" PathNum
  
  Loop, Parse, PathList, `n, `r
  {
    if ("\" == SubStr(A_LoopField, 0))
      ItemPath := SubStr(A_LoopField, 1, -1)
    else
      ItemPath := A_LoopField
    
    SplitPath, ItemPath, ItemName
    ItemNum := A_Index + IniCnt - 1
    
    ClipFldBuf .= "`nName" ItemNum "=" Str2RefStr(ItemName) "`nPath" ItemNum "=""" Str2RefStr(ItemPath) """`nType" ItemNum "=1"
  }
  
  if AddSW {
    IniCnt += PathNum
    IniWrite, %IniCnt%, %ClipFldPath%, X-Finder, Count
  } else {
    FileDelete, %ClipFldPath%
  }
  
  FileAppend, %ClipFldBuf%, %ClipFldPath%, CP932
  return, ClipFldPath
}

Str2RefStr(StrBuf) {
  Transform, StrBuf, HTML, %StrBuf%, 2
  SetFormat, Integer, H
  Loop {
    if !RegExMatch(StrBuf, "&#(\d+);", $)
      break
    
    TipNum := 0 + $1
    StringTrimLeft, TipNum, TipNum, 2
    if (0 != TipLen := 4 - StrLen(TipNum))
      Loop, %TipLen%
        TipNum := "0" + TipNum
    
    StringReplace, StrBuf, StrBuf, &#%$1%;, &#x%TipNum%;, All
  }
  SetFormat, Integer, D
  StringReplace, StrBuf, StrBuf, &, &, All
  return, StrBuf
}


ClipWaitNull(SW = 1) {
  if SW {
    global CBBackup
    CBBackup := ClipboardAll
  }
  
  Clipboard :=
  while Clipboard
    Sleep, 100
}

ClipWaitRestore() {
  global CBBackup
  
  ClipWaitNull(0)
  Clipboard := CBBackup
  ClipWait
}

 先人の作った「list2xf.exe」との違いは――AHKで応用しやすいってのと、Unicode文字対応ってあたりかなー。

 Everything Ver.1.4は普通のListViewになってたので、Clipboard経由せずControlGet, PathList, List, Selected, SysListView321, Aで取得して加工してもよい。



おまけ。
[PR]
by lordnoesis | 2014-01-17 19:25 | テクノロジ | Trackback(1) | Comments(0)

AutoHotKey_Lで、Everythingの検索結果をX-Finderで開けるように。 Ver.1.1.1

 Everythingの検索結果をX-Finderのクリップフォルダに変換するAHKスクリプトを微修正。どうせ手作業で編集しないのだから、パスも文字参照にして、X-Finder上で扱いやすく。使い方等は前回参照。

;--------------------------------------------------------------------------------------
; Everything
;--------------------------------------------------------------------------------------
#IfWinActive ahk_class ahk_class EVERYTHING

F11::
  ClipWaitNull()
  Send, ^c
  ClipWait, 3
  if !ErrorLevel {
    Path2ClipFld(Clipboard)
    IfWinExist, ahk_class TXFinder.UnicodeClass
      WinActivate
    else
      Run, "C:\X-Finder\XF.exe"  ; X-Finderのパス。
  }
  ClipWaitRestore()
return

Path2ClipFld(PathList) {
  ClipFldPath := "C:\X-Finder\EverythingResult.ini"  ; パスリストの保存先。
  
  StringReplace, PathList, PathList, `n, `n, UseErrorLevel
  ClipFldBuf := "[X-Finder]`nCount=" ErrorLevel + 1
  
  Loop, Parse, PathList, `n, `r
  {
    if ("\" == SubStr(A_LoopField, 0))
      ItemPath := SubStr(A_LoopField, 1, -1)
    else
      ItemPath := A_LoopField
    
    SplitPath, ItemPath, ItemName
    
    ItemPath := Str2RefStr(ItemPath)
    ItemName := Str2RefStr(ItemName)
    
    ItemNum := A_Index - 1
    ClipFldBuf .= "`nName" ItemNum "=" ItemName "`nPath" ItemNum "=""" ItemPath """`nType" ItemNum "=1"
  }
  FileDelete, %ClipFldPath%
  FileAppend, %ClipFldBuf%, %ClipFldPath%, CP932
}

Str2RefStr(StrBuf) {
  Transform, StrBuf, HTML, %StrBuf%, 2
  SetFormat, Integer, H
  Loop {
    if !RegExMatch(StrBuf, "&#(\d+);", $)
      break
    
    TipNum := 0 + $1
    StringReplace, StrBuf, StrBuf, &#%$1%;, &#%TipNum%;, All
  }
  SetFormat, Integer, D
  StringReplace, StrBuf, StrBuf, &#0, &#, All
  StringReplace, StrBuf, StrBuf, &amp;, &, All
  return, StrBuf
}



ClipWaitNull(SW = 1) {
  if SW {
    global CBBackup
    CBBackup := ClipboardAll
  }
  
  Clipboard :=
  while ("" != Clipboard)
    Sleep, 50
}

ClipWaitRestore() {
  global CBBackup
  
  ClipWaitNull(0)
  Clipboard := CBBackup
  ClipWait
}


[PR]
by lordnoesis | 2014-01-04 17:56 | テクノロジ | Trackback | Comments(3)

AutoHotKey_Lで、Everythingの検索結果をX-Finderで開けるように。

 Everything Search Engineのような、NTFSのMFTに対応した検索ソフトはとても高速だが、どうしてもMFTの情報だけでは絞り込めなかったり、条件に当てはまるファイルを俯瞰したい場合などに、エクスプローラのようなサムネイルの一覧表示が欲しくなるが、そういうソフトは中々ない。というわけで、Everythingの検索結果を、X-Finderのクリップフォルダ(任意のファイル等を集約できる仮想フォルダ)として開くAHKスクリプト書いた。

;--------------------------------------------------------------------------------------
; Everything
;--------------------------------------------------------------------------------------
#IfWinActive ahk_class ahk_class EVERYTHING

F11::
  ClipWaitNull()
  Send, ^c
  ClipWait, 3
  if !ErrorLevel {
    Path2ClipFld(Clipboard)
    IfWinExist, ahk_class TXFinder.UnicodeClass
      WinActivate
    else
      Run, "C:\X-Finder\XF.exe"  ; X-Finderのパス。
  }
  ClipWaitRestore()
return

Path2ClipFld(PathList) {
  ClipFldPath := "C:\X-Finder\EverythingResult.ini"  ; パスリストの保存先。
  
  StringReplace, PathList, PathList, `n, `n, UseErrorLevel
  ClipFldBuf := "[X-Finder]`nCount=" ErrorLevel + 1
  
  Loop, Parse, PathList, `n, `r
  {
    if ("\" == SubStr(A_LoopField, 0))
      ItemPath := SubStr(A_LoopField, 1, -1)
    else
      ItemPath := A_LoopField
    SplitPath, ItemPath, ItemName
    
    Transform, ItemName, HTML, %ItemName%, 2
    
    SetFormat, Integer, H
    Loop {
      if !RegExMatch(ItemName, "&#(\d+);", $)
        break
      
      TipNum := 0 + $1
      StringReplace, ItemName, ItemName, &#%$1%;, &#%TipNum%;, All
    }
    SetFormat, Integer, D
    StringReplace, Itemname, ItemName, &#0, &#, All
    
    Loop, %ItemPath%, 1
      ItemPath := A_LoopFileShortPath
    
    ItemNum := A_Index - 1
    ClipFldBuf .= "`nName" ItemNum "=" ItemName "`nPath" ItemNum "=""" ItemPath """`nType" ItemNum "=1"
  }
  FileDelete, %ClipFldPath%
  FileAppend, %ClipFldBuf%, %ClipFldPath%, CP932
}



ClipWaitNull(SW = 1) {
  if SW {
    global CBBackup
    CBBackup := ClipboardAll
  }
  
  Clipboard :=
  while ("" != Clipboard)
    Sleep, 50
}

ClipWaitRestore() {
  global CBBackup
  
  ClipWaitNull(0)
  Clipboard := CBBackup
  ClipWait
}

 Everythingで検索結果を選択し、F11を打鍵するとX-Finder形式のiniを吐く。ウチのX-Finderはクリップフォルダを開きっぱなしの想定なので、アクティブ化or起動に留めたが、そうでないなら引数にExtra:%X-Finder%EverythingResult.iniとでも。

 それと、ツール>基本オプション>その他>クリップフォルダへのパス登録を忘れないように。大抵の場合、既にClipFolder:があるはずなので、ClipFolder:;Extra:%X-Finder%EverythingResult.iniとなる。

 INIはS-JISでユニコード文字は文字参照という仕様なのだが、文字列中のユニコード文字だけ文字参照に置き換える簡単な方法は思いつかず、文字列すべて文字参照とショートネームにしたった。どうせ手作業で編集することなんてないしね。

voidtools > Everything
タブファイラー・X-Finder


[PR]
by lordnoesis | 2014-01-02 19:58 | テクノロジ | Trackback | Comments(0)

AutoHotKey_Lで、音量ミキサーをもう少し便利に。

 WindowsVista以降の音量ミキサーを、特にキーボードで操作しやすくするAHKスクリプト Ver.1.1。

 Ver.1.0。#v打鍵で画面幅いっぱいに音量ミキサー呼び出し。上下、PageUp/Down、Home/Endキーで音量調整。ソフトウェアごとの音量は、左端のマスターボリュームの値が上限となるよう制限。左右キーで対象ソフトウェア変更。ツールチップとタイトルバーで音量の概略を表示。

 Ver.1.1。音量ミキサー起動中は、Vol.Up/Down、Muteキーでも音量ミキサーを操作するように。数字キーで対象デバイスを変更できるように。Spaceキーで対象をミュートに。対象の切り替え順が、見た目通りになるように修正。

 ver.1.2b。アイテムが10個以上あると正常動作しないのを修正。

; 音量ミキサー最大化呼び出し
#v::
  Run, SndVol.exe, , , svpid
  winWait, 音量ミキサー, , 10
  if ErrorLevel
    return
  WinGetPos, , , , WH
  WinMove, , , 0, A_ScreenHeight - WH, A_ScreenWidth
return

$Volume_Mute::
  IfWinExist, 音量ミキサー ahk_class #32770
  {
    VM_Mute()
    return
  }
  
  Send, {Volume_Mute}
return

$Volume_Down::
  IfWinExist, 音量ミキサー ahk_class #32770
  {
    VM_LvUD(0)
    return
  }
  
  Send, {Volume_Down}
return

$Volume_Up::
  IfWinExist, 音量ミキサー ahk_class #32770
  {
    VM_LvUD(1)
    return
  }
  
  Send, {Volume_Up}
return


;--------------------------------------------------------------------------------------
; 音量ミキサー
;--------------------------------------------------------------------------------------
#IfWinActive 音量ミキサー ahk_class #32770

1::VM_DevChange(1)
2::VM_DevChange(2)

Up::
  ControlGetFocus, ACtrl
  IfInString, ACtrl, msctls_trackbar32
    VM_LvUD(1)
  else
    Send, {Up}
return

Down::
  ControlGetFocus, ACtrl
  IfInString, ACtrl, msctls_trackbar32
    VM_LvUD(0)
  else
    Send, {Down}
return

PGUP::VM_LvUD(1, 20)
PGDN::VM_LvUD(0, 20)

Home::VM_LvUD(1, 100)
End::VM_LvUD(0, 100)

Right::VM_FocusMove(1)
Left::VM_FocusMove(0)

Space::
  ControlGetFocus, ACtrl
  IfInString, ACtrl, msctls_trackbar32
    VM_Mute()
  else
    Send, {Space}
return


VM_DevChange(DevNum) {
  SetKeyDelay, -1, 1
  DevVolNN := VM_GetDevTrkNN()
  DevNN := DevVolNN * 2 - 1
  
  ControlFocus, ToolbarWindow32%DevNN%
  ControlSend, ToolbarWindow32%DevNN%, {Space}{Down %DevNum%}{Enter}
  Sleep, 300
  VM_FocusMove(-1)
}

VM_LvUD(Direct = 1, MP = 1) {
  DevVolCNN := "msctls_trackbar32" VM_GetDevTrkNN()
  ControlGetFocus, AVolCNN
  if AVolCNN {
    IfNotInString, AVolCNN, msctls_trackbar32
      return
  } else
    AVolCNN := DevVolCNN
  
  SendMessage, 0x400, , , %AVolCNN%  ; TBM_GETPOS
  AVolNum := ErrorLevel
  
  if (AVolCNN != DevVolCNN) {
    SendMessage, 0x400, , , %DevVolCNN%  ; TBM_GETPOS
    DevVolNum := ErrorLevel
  } else
    DevVolNum := 0
  
  if Direct {
    if (AVolNum - MP > DevVolNum)
      VM_SetLv(AVolCNN, AVolNum - MP)
    else  ; DevVolより音量が大きくなる場合
      VM_SetLv(AVolCNN, DevVolNum)
  } else {
    VM_SetLv(AVolCNN, AVolNum + MP)
  }
  
  VM_LvView(AVolCNN, DevVolCNN)
}

VM_SetLv(AVolCNN, VolNum) {
  ControlGet, AVolHwnd, Hwnd, , %AVolCNN%
  SendMessage, 0x405, 0x1, %VolNum%, %AVolCNN%  ; TBM_SETPOS
  
  ParCNN := "#32770" RegExReplace(AVolCNN, "^msctls_trackbar32(\d+)$", "$1")
  ControlGet, ParHwnd, Hwnd, , %ParCNN%
  if ParHwnd
    SendMessage, 0x115, 0x4,%AVolHwnd%, , ahk_id %ParHwnd%  ; WM_VSCROLL
  else
    SendMessage, 0x115, 0x4,%AVolHwnd%  ; WM_VSCROLL
}

VM_Mute() {
  DevVolNN := VM_GetDevTrkNN()
  ControlGetFocus, AVolCNN
  if !AVolCNN
    AVolCNN := "msctls_trackbar32" DevVolNN
  
  if !RegExMatch(AVolCNN, "^msctls_trackbar32(\d+)$", $)
    return
  else if ($1 == DevVolNN)
    MuteCNN := "ToolbarWindow32" $1 * 2 + 1
  else
    MuteCNN := "ToolbarWindow32" $1 * 2
  ControlClick, %MuteCNN%, , , , , NA
}

VM_FocusMove(Direct = 1) {
  DevVolNN := VM_GetDevTrkNN()
  
  ControlGetFocus, AVolCNN
  if (-1 == Direct) || !RegExMatch(AVolCNN, "^(msctls_trackbar32|ToolbarWindow32)(\d+)$", $) {
    ControlFocus, msctls_trackbar32%DevVolNN%
    VM_LvView("msctls_trackbar32" DevVolNN, "msctls_trackbar32" DevVolNN)
    return
  } else if ("msctls_trackbar32" == $1) {
    AVolNN := $2
  } else if !Mod($2, 2) {
    AVolNN := $2 // 2
  } else {
    if ($2 > DevVolNN * 2)
      AVolNN := $2 // 2
    else
      AVolNN := $2 // 2 + 1
  }
  
  VolList := VM_GetTrkNNList(DevVolNN)
  
  if Direct {
    if RegExMatch(VolList, AVolNN "`n(\d+)", $)
    if RegExMatch(VolList, "(?:^|`n)" AVolNN "`n(\d+)", $)
      TrgVolNN := $1
    else
      TrgVolNN := DevVolNN
  } else {
    RegExMatch(VolList, "(\d+)(?:\n" AVolNN "|$)", $)
    TrgVolNN := $1
  }
  ControlFocus, msctls_trackbar32%TrgVolNN%
  
  VM_LvView("msctls_trackbar32" TrgVolNN, "msctls_trackbar32" DevVolNN)
}

VM_GetDevTrkNN() {
  WinGet, CList, ControlList, 音量ミキサー ahk_class #32770
  Sort, CList, P18 R
  Sort, CList, N P18 R
  Loop, Parse, CList, `n
    return SubStr(A_LoopField, 18)
}

VM_GetTrkNNList(DevVolNN) {
  static BefPosXList, Result
  
  Loop, %DevVolNN% {
    ControlGetPos, CX, , , , msctls_trackbar32%A_Index%
    PosXList .= CX "`n"
  }
  if (BefPosXList == PosXList)
    return Result
  BefPosXList := PosXList
  
  Result := ""
  Loop, %DevVolNN%
    Result .= A_Index "`n"
  Sort, Result, F VM_TrkPosXComp
  StringTrimRight, Result, Result, 1
  return Result
}

VM_TrkPosXComp(NN1, NN2) {
  ControlGetPos, CX1, , , , msctls_trackbar32%NN1%
  ControlGetPos, CX2, , , , msctls_trackbar32%NN2%
  if (CX1 > CX2)
    return 1
  else if (CX1 < CX2)
    return -1
  else
    return 0
}

VM_LvView(AVolCNN, DevVolCNN) {
  SendMessage 0x400, , , %AVolCNN%  ; TBM_GETPOS
  AVolNum := ErrorLevel
  AVolLv := 100 - AVolNum
  SendMessage 0x400, , , %DevVolCNN%  ; TBM_GETPOS
  DevVolLv := 100 - ErrorLevel
  
  WinGetTitle, AWTitle
  TitleBuf := RegExReplace(AWTitle, "(.*?) Vol:[\d/ ]+?$", "$1") " Vol:" DevVolLv
  
  LoopCnt := RegExReplace(DevVolCNN, "^msctls_trackbar32(\d+)$", "$1") - 1
  Loop, %LoopCnt% {
    SendMessage, 0x400, , , msctls_trackbar32%A_Index%  ; TBM_GETPOS
    TitleBuf .= " / " 100 - ErrorLevel
  }
  WinSetTitle, %TitleBuf%
  
  IfWinActive, 音量ミキサー ahk_class #32770
  {
    ControlGetPos, CX, CY, , CH, %AVolCNN%
    TY := CY + (CH - 24) / 100 * AVolNum - 24
    ToolTip, Vol:%AVolLv% / %DevVolLv%, %CX%, %TY%, 10  ; ToolTip番号は適当に
    SetTimer, VM_LvCheck, 100
  } else {
    ToolTip, Vol:%AVolLv% / %DevVolLv%, , , 10  ; ToolTip番号は適当に
    SetTimer, VM_LvCheck, 3000
  }
}

VM_LvCheck:
  IfWinNotActive, 音量ミキサー ahk_class #32770
  {
    ToolTip, , , , 10  ; ToolTip番号は適当に
    IfWinNotExist, 音量ミキサー ahk_class #32770
      SetTimer, VM_LvCheck, Off
  }
return

 デバイス変更は力技。

 Vol.キーで音量変更した場合、音量ミキサー非表示でもツールチップを表示しようと思ったのだが、音量ミキサーの目盛りと実際の音量が一定でなかったので断念。

c0031643_20503090.png


[PR]
by lordnoesis | 2013-12-13 20:36 | テクノロジ | Trackback | Comments(0)

AutoHotKey_Lで、音量ミキサーを少し便利に。

 WindowsVista以降では、ソフトウェアごとに音量調整が可能になったが、標準の音量ミキサーはマウスだと加減しにくく、キーボード操作も微妙なのでAHK書いた。たぶんVista以降対応だと思うがWin7でしか確認してない。

 #v打鍵で画面幅いっぱいに音量ミキサー呼び出し。上下、PageUp/Down、Home/Endキーで音量調整。ソフトウェアごとの音量は、左端のマスターボリュームの値が上限となるよう制限。左右キーで対象ソフトウェア変更。ツールチップとタイトルバーで音量の概略を表示。

; 音量ミキサー最大化呼び出し
#V::
Run, SndVol.exe, , , svpid
winWait, 音量ミキサー, , 10
if errorLevel
return
WinGetPos, , , , WH
WinMove, , , 0, A_ScreenHeight - WH, A_ScreenWidth
return


;--------------------------------------------------------------------------------------
; 音量ミキサー
;--------------------------------------------------------------------------------------
#IfWinActive 音量ミキサー ahk_class #32770

Up::
ControlGetFocus, ACtrl
IfInString, ACtrl, msctls_trackbar32
AVolLvUD(1)
else
Send, {Up}
return

Down::
ControlGetFocus, ACtrl
IfInString, ACtrl, msctls_trackbar32
AVolLvUD(0)
else
Send, {Down}
return

PGUP::AVolLvUD(1, 20)
PGDN::AVolLvUD(0, 20)

Home::AVolLvUD(1, 100)
End::AVolLvUD(0, 100)

Right::VolFocusMove(1)
Left::VolFocusMove(0)

AVolLvUD(Direct = 1, MP = 1) {
ControlGetFocus, AVolCNN
IfNotInString, AVolCNN, msctls_trackbar32
return

MVolCNN := "msctls_trackbar32" GetMVolNN()

SendMessage, 0x400, , , %AVolCNN% ; TBM_GETPOS
AVolNum := ErrorLevel

if (AVolCNN != MVolCNN) {
SendMessage, 0x400, , , %MVolCNN% ; TBM_GETPOS
MVolNum := ErrorLevel
} else
MVolNum := 0

if Direct {
if (AVolNum - MP > MVolNum)
SetVolLv(AVolCNN, AVolNum - MP)
else ; MVolより音量が大きくなる場合
SetVolLv(AVolCNN, MVolNum)
} else {
SetVolLv(AVolCNN, AVolNum + MP)
}

VolLvView(AVolCNN, MVolCNN)
return
}

SetVolLv(AVolCNN, VolNum) {
ControlGet, AVolHwnd, Hwnd, , %AVolCNN%
SendMessage, 0x405, 0x1, %VolNum%, %AVolCNN% ; TBM_SETPOS

ParCNN := "#32770" RegExReplace(AVolCNN, "^msctls_trackbar32(\d+)$", "$1")
ControlGet, ParHwnd, Hwnd, , %ParCNN%
if ParHwnd
SendMessage, 0x115, 0x4,%AVolHwnd%, , ahk_id %ParHwnd% ; WM_VSCROLL
else
SendMessage, 0x115, 0x4,%AVolHwnd% ; WM_VSCROLL
}

VolFocusMove(Direct = 1) {
MVolNN := GetMVolNN()

ControlGetFocus, AVolCNN
if !RegExMatch(AVolCNN, "^(msctls_trackbar32|ToolbarWindow32)(\d+)$", $) {
ControlFocus, msctls_trackbar32%MVolNN%
return
}
if ("msctls_trackbar32" == $1)
AVolNN := $2
else
AVolNN := $2 // 2

if Direct {
if (MVolNN == AVolNN++)
TrgVolCNN := "msctls_trackbar321"
else
TrgVolCNN := "msctls_trackbar32" AVolNN
} else {
if (1 == AVolNN--)
TrgVolCNN := "msctls_trackbar32" MVolNN
else
TrgVolCNN := "msctls_trackbar32" AVolNN
}
ControlFocus, %TrgVolCNN%

VolLvView(TrgVolCNN, "msctls_trackbar32" MVolNN)
}

GetMVolNN() {
WinGet, CList, ControlList, 音量ミキサー ahk_class #32770
MVolNN := 0
Loop, Parse, CList, `n
if RegExMatch(A_LoopField, "^msctls_trackbar32(\d+)$", $)
if (MVolNN < $1)
MVolNN := $1
return MVolNN
}

VolLvView(AVolCNN, MVolCNN) {
SendMessage 0x400, , , %AVolCNN% ; TBM_GETPOS
AVolNum := ErrorLevel
AVolLv := 100 - AVolNum
SendMessage 0x400, , , %MVolCNN% ; TBM_GETPOS
MVolLv := 100 - ErrorLevel

WinGetTitle, AWTitle
TitleBuf := RegExReplace(AWTitle, "(.*?) Vol:[\d/ ]+?$", "$1") " Vol:" MVolLv

LoopCnt := RegExReplace(MVolCNN, "^msctls_trackbar32(\d+)$", "$1") - 1
Loop, %LoopCnt% {
SendMessage, 0x400, , , msctls_trackbar32%A_Index% ; TBM_GETPOS
TitleBuf .= " / " 100 - ErrorLevel
}
WinSetTitle, %TitleBuf%

ControlGetPos, CX, CY, , CH, %AVolCNN%
TY := CY + (CH - 24) / 100 * AVolNum - 24
ToolTip, Vol:%AVolLv% / %MVolLv%, %CX%, %TY%, 10 ; ToolTip番号は適当に

SetTimer, VolLvCheck, 100
}

VolLvCheck:
IfWinNotActive, 音量ミキサー ahk_class #32770
ToolTip, , , , 10 ; ToolTip番号は適当に
IfWinNotExist, 音量ミキサー ahk_class #32770
SetTimer, VolLvCheck, Off
return

 以前も書いたが、音量ミキサーのソフトウェアごとの音量は絶対値でなく、マスターボリュームに対する割合にした方がいいと思ってるのだが、ひとまず満足したんでやめた。

 正直、ソフトウェア側でなくOS側で音量いじることってあまりないが、まあAHKでトラックバー(スライドバー)いじる練習になったからいいかな……。

c0031643_20503090.png

TODO:実用上問題ないが、微妙なところがあるので機能強化のついでに直す。来週。直した


[PR]
by lordnoesis | 2013-12-06 20:43 | テクノロジ | Trackback | Comments(0)

AutoHotKey_Lで、Link Shell Extensionを使ってリパースポイントを相対パスで作り直す。

 親フォルダをリネームしただけで、絶対パスで作ってあったシンボリックリンクが死んだので、相対パスで作り直そうと思ったのだけど、数が多くて面倒なのでAHKスクリプト書いた。

 自前でリパースポイントいじるにはDeviceIoControl叩く必要があるらしく面倒なので、「Link Shell Extension」が拡張するプロパティ項目を利用する。

;--------------------------------------------------------------------------------------
; WindowsExplorer
;--------------------------------------------------------------------------------------
#IfWinActive, ahk_class CabinetWClass

!l::  ; 選択したリパースポイントを相対パスに
  KeyWait, l
  KeyWait, alt
  
  SetTitleMatchMode, Slow
  
  CurPath := RegExEscape(GetCurPath())
  
  PathList := GetSelectItemPath()
  Loop, Parse, PathList, `n, `r%A_Space%"
  {
    Run, AutoHotkeyU64 _Script\FilePropertyOpen.ahk %A_LoopField%
    SplitPath, A_LoopField, FileName, DirName
    
    WinWaitActive, %FileName%のプロパティ ahk_class #32770, %DirName%
    Loop
    {
      Control, TabRight , 1, SysTabControl321
      WinGetText, WinTxt
      IfInString, WinTxt, リンクの種類
        break
    }
    ControlGetText, CtrlTxt, Edit2
    CtrlTxt2 := RegExReplace(CtrlTxt, CurPath "[^\\]*\\", ".\")
    if (CtrlTxt2 != CtrlTxt)
      ControlSetText, Edit2, %CtrlTxt2%
    Sleep, 50
    Send, {Enter}
  }
return

GetCurPath(DQSW = 0) {  ; アクティブなエクスプローラのパス。
  WinGetText, CurPath, A
  RegExMatch(CurPath, "([A-Z]:\\[^\r\n]+)", $)
  if DQSW
    return """" $1 """"
  else
    return $1
}

GetSelectItemPath(DQSW = 0, FldSW = 0, ParseStr = "`n") {  ; エクスプローラで選択中のアイテムのパス。AutoHotkey スレッド part11 >>913
  for Window in ComObjCreate("Shell.Application").Windows
    if (Window.hwnd == WinExist()) {
      for Item in Window.Document.SelectedItems
      {
        if (FldSW && !Item.IsFolder)
          continue
        if DQSW
          FileList .= """" Item.Path """" ParseStr
        else
          FileList .= Item.Path ParseStr
        Cnt++
      }
      break
    }
  ErrorLevel := Cnt
  return, SubStr(FileList, 1, -1)
}

RegExEscape(StrBuf) {  ;正規表現で特別な文字を回避。
  EscList := "
(
\
.
*
?
+
[
{
}
|
(
`)
^
$
)"
  
  Loop, Parse, EscList, `n
    StringReplace, StrBuf, StrBuf, %A_LoopField%, \%A_LoopField%, All
  
  return, StrBuf
}

;--------------------------------------------------------------------------------------
; FilePropertyOpen.ahk
;  渡されたファイルのプロパティを開く。
;--------------------------------------------------------------------------------------

  SetTitleMatchMode, 3
  SetTitleMatchMode, Slow
  
  Loop, %0%
    Cmdline .= %A_Index% " "
  Cmdline := SubStr(Cmdline, 1, -1)
  
  RunWait, properties %Cmdline%
  SplitPath, Cmdline, FileName, DirName
  
  WinWait, %FileName%のプロパティ ahk_class #32770, %DirName%
  WinWaitClose
  
ExitApp

 エクスプローラでジャンクション/シンボリックリンクを選択してAlt+Lすると、現在表示しているフォルダを起点とした相対パスでリンクを作り直す。ただし「../」には非対応。

 AHKは32bit版を使っているため、64bit版Link Shell Extensionのプロパティ項目を開くのに、わざわざ64bit版AHKでFilePropertyOpen.ahkを実行する仕組みなので、Runのパスに注意。普通に開けるのなら、そのように。


[PR]
by lordnoesis | 2013-10-20 22:31 | テクノロジ | Trackback | Comments(0)

AutoHotKeyで、MoniMoniを少し便利に。

 モニタの表示内容をウィンドウで確認できる、MoniMoniというソフトがある。例えば、セカンダリモニタの内容を表示したMoniMoniをプライマリモニタに配置しておけば、本物のセカンダリモニタを確認する必要がなくなる(その逆も可)。サブモニタの電源がOFFだったり、視認できない配置だったり、グラフィックチップはマルチモニタ対応だけどモニタがない環境などで活躍するんじゃないかなぁ。

 で。ワシの使い方だと、操作する時だけセカンダリモニタを確認できればよかったので、カーソルをセカンダリモニタに移動した時だけMoniMoniを表示するAHKスクリプト書いた。

;--------------------------------------------------------------------------------------
; MoniMoni
;--------------------------------------------------------------------------------------
#IfWinActive MoniMoni ahk_class #32770

!2::
  WinGetPos, , , WW, WH, MoniMoni ahk_class #32770
  ControlGetPos, , , CW, CH, Magnifier1, MoniMoni ahk_class #32770
  SysGet, Mnt2, Monitor, 2
  
  MoveW := Mnt2Right - Mnt2Left + WW - CW
  MoveH := Mnt2Bottom - Mnt2Top + WH - CH
  MoveX := A_ScreenWidth / 2 - MoveW / 2
  MoveY := A_ScreenHeight / 2 - MoveH / 2
  WinMove, MoniMoni ahk_class #32770, , MoveX, MoveY, MoveW, MoveH
  ControlClick, Button2, Operation ahk_class #32770
  
  SetTimer, MoniMoniActivate, 500
return

MoniMoniActivate:
  IfWinNotExist, MoniMoni ahk_class #32770
  {
    SetTimer, MoniMoniActivate, Off
    return
  }
  CoordMode, Mouse
  MouseGetPos, MX, MY
  if (0 <= MX) && (MX < A_ScreenWidth) && (0 <= MY) && (MY < A_ScreenHeight) {  ; Mnt1
    if MMASW {
      MMASW := 0
      WinMinimize
    }
  } else {
    if !MMASW {  ; Mnt2
      MMASW := 1
      WinActivate
    }
  }
return

 MoniMoniがアクティブの状態でAlt+2を押すと、セカンダリモニタの解像度に合わせてMoniMoniをリサイズし、カーソルの監視を始める。以後MoniMoniを終了するまで、セカンダリモニタに移動した時はMoniMoniをアクティブにし、プライマリモニタに移動した時はMoniMoniを最小化する。

OrangeMaker > MoniMoni


[PR]
by lordnoesis | 2013-09-02 20:52 | テクノロジ | Trackback | Comments(0)
ブログトップ | ファンになる