-
Notifications
You must be signed in to change notification settings - Fork 72
/
StrPut.ahk
97 lines (92 loc) · 4.31 KB
/
StrPut.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
StrPut(String, Address="", Length=-1, Encoding=0)
{
if A_IsUnicode
{
MsgBox %A_ThisFunc% does not support Unicode. Use the AutoHotkey_L (revision 46+) built-in version instead. The script will now exit.
ExitApp
}
; Flexible parameter handling:
if Address is not integer ; StrPut(String [, Encoding])
Encoding := Address, Length := 0, Address := 1024
else if Length is not integer ; StrPut(String, Address, Encoding)
Encoding := Length, Length := -1
; Check for obvious errors.
if (Address+0 < 1024)
return
; Ensure 'Encoding' contains a numeric identifier.
if Encoding = UTF-16
Encoding = 1200
else if Encoding = UTF-8
Encoding = 65001
else if SubStr(Encoding,1,2)="CP"
Encoding := SubStr(Encoding,3)
if !Encoding ; "" or 0
{
; No conversion required.
char_count := StrLen(String) + 1 ; + 1 because generally a null-terminator is wanted.
if (Length)
{
; Check for sufficient buffer space.
if (StrLen(String) <= Length || Length == -1)
{
if (StrLen(String) == Length)
; Exceptional case: caller doesn't want a null-terminator.
char_count--
; Copy the string, including null-terminator if requested.
DllCall("RtlMoveMemory", "uint", Address, "uint", &String, "uint", char_count)
}
else
; For consistency with the sections below, don't truncate the string.
char_count = 0
}
;else: Caller just wants the the required buffer size (char_count), which will be returned below.
}
else if Encoding = 1200 ; UTF-16
{
; See the 'else' to this 'if' below for comments.
if (Length <= 0)
{
char_count := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, "uint", &String, "int", StrLen(String), "uint", 0, "int", 0) + 1
if (Length == 0)
return char_count
Length := char_count
}
char_count := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, "uint", &String, "int", StrLen(String), "uint", Address, "int", Length)
if (char_count && char_count < Length)
NumPut(0, Address+0, char_count++*2, "UShort")
}
else if Encoding is integer
{
; Convert native ANSI string to UTF-16 first. NOTE - wbuf_len includes the null-terminator.
VarSetCapacity(wbuf, 2 * wbuf_len := StrPut(String, "UTF-16")), StrPut(String, &wbuf, "UTF-16")
; UTF-8 and some other encodings do not support this flag. Avoid it for UTF-8
; (which is probably common) and rely on the fallback behaviour for other encodings.
flags := Encoding=65001 ? 0 : 0x400 ; WC_NO_BEST_FIT_CHARS
if (Length <= 0) ; -1 or 0
{
; Determine required buffer size.
loop 2 {
char_count := DllCall("WideCharToMultiByte", "uint", Encoding, "uint", flags, "uint", &wbuf, "int", wbuf_len, "uint", 0, "int", 0, "uint", 0, "uint", 0)
if (char_count || A_LastError != 1004) ; ERROR_INVALID_FLAGS
break
flags := 0 ; Try again without WC_NO_BEST_FIT_CHARS.
}
if (!char_count)
return ; FAIL
if (Length == 0) ; Caller just wants the required buffer size.
return char_count
; Assume there is sufficient buffer space and hope for the best:
Length := char_count
}
; Convert to target encoding.
char_count := DllCall("WideCharToMultiByte", "uint", Encoding, "uint", flags, "uint", &wbuf, "int", wbuf_len, "uint", Address, "int", Length, "uint", 0, "uint", 0)
; Since above did not null-terminate, check for buffer space and null-terminate if there's room.
; It is tempting to always null-terminate (potentially replacing the last byte of data),
; but that would exclude this function as a means to copy a string into a fixed-length array.
if (char_count && char_count < Length)
NumPut(0, Address+0, char_count++, "Char")
; else no space to null-terminate; or conversion failed.
}
; Return the number of characters copied.
return char_count
}