AutoHotkey AHK send 中文

来源:互联网 发布:西南交通网络大学远程 编辑:程序博客网 时间:2024/06/03 20:16

转自:http://jdev.tw/blog/713/autohotkey-output-chinese
修改了AHK的代码
增加了一段能在新版AU3中使用的代码


最简单的方法其实是先把要发送的内容先保存到剪贴板中去,然后在要发送的时候“粘贴”一下(Send一个 Ctrl+V)即可。不过这样做有一些缺点:
1、由于使用了剪贴板,用户在 Send 的过程中将不能正常使用 Ctrl+C 和 Ctrl+V 等剪贴板相关的功能;
2、有可能会在还没执行完“粘贴”操作之前剪贴板的内容就被修改了,结果发送了别的内容。


当然还有别的方法,先引用一下下面的内容:

我们通过键盘只能够输入键盘上有的字符,其实,按住 ALT 键,然后在数字键盘上按表示要输入字符的十进制代码值的键,等完成输入后,释放 ALT 键还可以输入键盘上没有的字符呢。

如 果键入的第一个数字是“0”,该值将被识别为当前输入区域设置中的代码点或字符值。例如,在当前的输入区域设置为“英语(美国)”时(代码页 1252:Windows Latin-1),按住 ALT 键,然后在数字键盘上键入“0163”将产生英镑符号 £ (U+00A3)。在当前输入区域是 "Russia" (代码页 1251:Windows Cyrillic),相同的键顺序会产生西里尔大写字母 JE (U+0408)。

而如果键入的第一位数是“1”到“9”的任意数,该值将被识别为系统 OEM 代码页中的代码点。根据在“控制面板”的“区域选项”中所指定的 Windows 系统区域设置,结果各有不同。例如,如果您的系统区域设置是“英语(美国)”,代码页为 437(MS-DOS 拉丁美洲),那么只要按住 ALT 键,然后在数字键盘上键入“163”,就可以输入 ú(U+00FA, 带重音符号的小写拉丁字母 U)。如果系统区域设置是“希腊语”(OEM 代码页 737 MS-DOS 希腊),相同序列将产生希腊语小写字母 MU (U+03BC)。

新建一个文本文件,输入“中文”这两个字并保存,然后用UltraEdit等十六进制编辑器打开并用十六进制视图显示,可看到如下内容:

00000000h: D6 D0 CE C4                                     ; 中文

可知中文这两个字的十六进制分别是 0xD6D0、0xCEC4(即十进制的54992、52932),那么如果要输入“中”字,则只要按住Alt键,逐个输入其十进制数字54992即可。
这样,要实现自己的 Send 函数就简单了:

AHK脚本:
; GBK是GB2312的扩展,是向下兼容的,因此GB2312中的汉字的编码与GBK中
; 汉字的相同。另外,GBK中还包含繁体字的编码,GBK中每个汉字仍然包含两
; 个字节,第一个字节的范围是0x81-0xFE(即129-254),第二个字节的范围
; 是0x40-0xFE(即64-254)。GBK中有码位23940个,包含汉字21003个。

#NoEnv
SetKeyDelay
,
20                 
; 如因速度过快导致发送不正常请尝试修改此行的延迟数值
SendMode InputThenPlay      
; 如因速度过快导致发送不正常请则注释此行或改为其它模式
SetWorkingDir %A_ScriptDir%

string1 :=
"简体中文字符发送测试"
string2 :=
"繁體中文字符發送測試"

F10
::
    Loop,
100
    {
SendString( "Sending #" . A_Index . " " . string1
)
        Send, {Enter}
}
Return

SendString( string
)
{
Len := StrLen(string)  
; 得到字符串的长度,注意一个中文字符的长度是2,即占2个字节
    Keys := ""                  
; 将要发送的字符序列
    Index := 1                  
; 用于循环
    
Loop
    {
IsUnicodeChar :=
false
        Code2 := 0                                             
; 字符2的ASCII码
        Code1 := Asc( SubStr(string, Index, 1) )    
; 得到第一个字符的ASCII值
        if(Code1 >= 129 && Code1 <= 254 && Index < Len)   
; 判断是否中文字符的第一个字符
        {
Code2 := Asc( SubStr(string, Index+1, 1) )            
; 得到第二个字符的ASCII值
            if(Code2 >= 64 && Code2 <= 254)        
; 若条件成立则说明是中文字符
            {
IsUnicodeChar :=
true
                Code1 <<= 8                                  
; 第一个字符应放到高8位上
                Code1 += Code2                              
; 第二个字符放在低8位上
            }
++Index
}
if( IsUnicodeChar
)
            Keys .= "{ASC " . Code1 .
"}"
       
else
        {
Keys .= "{ASC 0" . Code1 . "}"                
; 如果非中文字符,则需要前缀一个0
            if( Code2 > 0
)
                Keys .= "{ASC 0" . Code2 .
"}"
        }
++Index
if(Index > Len
)
            
Break
    }
Send % Keys
}

SendByClipboard( string, BackupClipBoard = false
)
{
if(BackupClipBoard
)
        ClipSaved :=
ClipboardAll
    ClipBoard := string
Send ^v
if(BackupClipBoard
)
    {
Clipboard := ClipSaved
ClipSaved
=
    }
}

///////////////////////////////////////////////////////////////////////////////

AU3脚本(3.2.4.0 之后的版本)
由于AutoIt自从版本3.2.4.0+开始已不再提供ANSI版本,因此再写个能在新版用的

测试版本:v3.3.0.0
在编写的过程中,一开始是打算使用 StringToBinary/Binary/DllStructSetData 来生成一个Ansi字符串方便处理的,但因为AutoIt存在将字符串截断的问题(这个问题也算是历史悠久了),因此不得不使用API来进行转换。
有意思的是,在AutoIt的更新日志里面却说这个问题已经解决了face:

Fixed #92: DllStruct data truncated with char[]/wchar[].

最后还是要抱怨一下默认情况下AutoIt的 Send 函数速度实在是太慢了,好在AutoHotkey的速度很理想,尽管Send这种功能我本来也很少用 :)

HotKeySet("{F10}", "SendTest")

While 1
Sleep(100)
WEnd

Func SendTest()
Local $string = "A中文字符串A"
$begin = TimerInit()

For $i = 1 To 10
_Send( $string )
Send("{Enter}")
Next
$dif = TimerDiff($begin)
MsgBox(0,"Time passed", $dif)
EndFunc

; 函数: _Send
; 用途:发送字符串
; 参数:$string,待转换的字符串,既可以是字符串字面值常量也可以是一个指向包含
;                Unicode 字符数组的 DllStruct 元素的指针
;       $bSendKeys,是否发送字符串,为 false 时只返回待发送的 Keys(请参考返回
;                值的说明) 而不发送字符串
; 返回值:使用 Send 函数时传递给它的第一个参数(Keys),形如 {Asc nn1}{Asc nn2}
Func _Send( Const $string, $bSendKeys = true )
Local $szKeys = ""                           ; 待发送的按键序列
Local $nLen = StringLen($string)             ; 字符串的长度
; 字符串的 Unicode 编码数组
Local $UnicodeStringASCIIArray = StringToASCIIArray( $string )
; 因须将 $string 转换为多字节版本,下面计算足够用以保存转换成后数据的空间大小   
Local $nAnsiBufferSize = ($nLen+1) * 2              
; 用以保存转换后的结果
Local $pAnsiStringStruct = DllStructCreate("ubyte[" & $nAnsiBufferSize & "]")
; 将 $string 转换为多字节版本
Local $nBytesWritten = WideCharToMultiByte( $string, DllStructGetPtr($pAnsiStringStruct) )
If $nBytesWritten <= 0 Then
$pAnsiStringStruct = 0
Return SetError(@error, 0, "")
EndIf

Local $AnsiIndex = 1  ; Ansi 字符串元素的索引
Local $value          ; 要传给 Send 函数的数值,用以构成 {Asc $value}
For $i = 0 To $nLen-1
$value = DllStructGetData($pAnsiStringStruct, 1, $AnsiIndex)
If $UnicodeStringASCIIArray[$i] > 255 Then  ; 大于255的字符说明是Unicode字符
$AnsiIndex += 1
$value = $value * 256 + DllStructGetData($pAnsiStringStruct, 1, $AnsiIndex)
ElseIf $value > 0 Then
$value = "0" & $value
EndIf
$szKeys &= "{Asc " & $value & "}"
$AnsiIndex += 1
Next
If $bSendKeys Then Send($szKeys)
$pAnsiStringStruct = 0
Return $szKeys
EndFunc    ;==>_Send

Func SendByClipboard( Const $string, $BackupClipBoard = false )
If $BackupClipBoard Then
Local $bak = ClipGet()
EndIf
ClipPut($string)
Send("^v")
If $BackupClipBoard Then ClipPut($bak)
EndFunc

; 函数: WideCharToMultiByte
; 用途:将 Unicode 字符串转换为多字节字符串
; 参数:$UnicodeString,待转换的字符串,既可以是字符串字面值常量也可以是一个指
;                向包含 Unicode 字符数组的 DllStruct 元素的指针
;       $pMultiByte,用以保存转换结果的地址,指向一个char/byte数组的 DllStruct
;                元素的指针
;       $iCodePage,代码页
; 返回值:写入到 $pMultiByte的字节数
Func WideCharToMultiByte($UnicodeString, $pMultiByte, $iCodePage = 0)
Local $aResult, $ParamType = "wstr"
If IsPtr($UnicodeString) Then $ParamType = "ptr"

$aResult = DllCall("Kernel32.dll", "int", "WideCharToMultiByte", "int", $iCodePage, "int", 0, _
$ParamType, $UnicodeString, "int", -1, "int", 0, "int", 0, "int", 0, "int", 0)
If @error Then Return SetError(@error, 0, 0)
$aResult = DllCall("Kernel32.dll", "int", "WideCharToMultiByte", "int", $iCodePage, "int", 0, _
$ParamType, $UnicodeString, "int", -1, "ptr", $pMultiByte, "int", $aResult[0], "int", 0, "int", 0)
If @error Then Return SetError(@error, 1, 0)
Return $aResult[0]
EndFunc   ;==>WideCharToMultiByte

///////////////////////////////////////////////////////////////////////////////

AU3脚本(3.2.4.0 之前的版本)
(注意,最新版本(3.2.4.0+)已不再提供ANSI版本!因此请注意你的AutoIt版本):
#cs
运行脚本时需用 AutoIt3A.exe
将 AutoIt 目录下的AutoIt3A.exe重命名为AutoIt3.exe即可(建议先备份AutoIt3.exe)
编译脚本时需用 Aut2exeA.exe
#ce
Run("notepad")
WinWaitActive("[CLASS:Notepad]")

_SendRaw("简体中文And繁體中文")

Func _SendRaw($Keys)
Local $KeysInUnicode = ""
Local $len = StringLen($Keys)
Local $char1
Local $code1
Local $char2
Local $code2
Local $index = 1
While True
$code2 = 0
$char1 = StringMid($Keys, $index, 1)
$code1 = Asc($char1)
If $code1 >= 129 And $code1 <= 254 And $index < $len Then
$char2 = StringMid($Keys, $index+1, 1)
$code2 = Asc($char2)
If $code2 >= 64 And $code2 <= 254 Then
$code1 *= 256
$code1 += $code2
EndIf
$index += 1
EndIf
If $code1 <= 255 Then $code1 = "0" & $code1
$KeysInUnicode &= "{ASC " & $code1 & "}"
If $code2 > 0 And $code2 < 64 Then
$code2 = "0" & $code2
$KeysInUnicode &= "{ASC " & $code2 & "}"
EndIf
$index += 1
If $index > $len Then ExitLoop
WEnd
Send($KeysInUnicode)
EndFunc

原创粉丝点击