Skip to content

Commit d958881

Browse files
kardianosrsc
authored andcommitted
runtime: use WriteConsole to implement print and panic on windows
Fixes #7864 Change-Id: Id13369352aeccac8387876f0b911e383c543c28e Reviewed-on: https://go-review.googlesource.com/16714 Reviewed-by: Alex Brainman <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 935faf3 commit d958881

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

src/runtime/os1_windows.go

+83
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
//go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll"
2222
//go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll"
2323
//go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll"
24+
//go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll"
2425
//go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW%0 "kernel32.dll"
2526
//go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress%2 "kernel32.dll"
2627
//go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll"
@@ -44,6 +45,7 @@ import (
4445
//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
4546
//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll"
4647
//go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll"
48+
//go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll"
4749
//go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll"
4850
//go:cgo_import_dynamic runtime._timeBeginPeriod timeBeginPeriod%1 "winmm.dll"
4951

@@ -63,6 +65,7 @@ var (
6365
_DuplicateHandle,
6466
_ExitProcess,
6567
_FreeEnvironmentStringsW,
68+
_GetConsoleMode,
6669
_GetEnvironmentStringsW,
6770
_GetProcAddress,
6871
_GetProcessAffinityMask,
@@ -86,6 +89,7 @@ var (
8689
_VirtualFree,
8790
_WSAGetOverlappedResult,
8891
_WaitForSingleObject,
92+
_WriteConsoleW,
8993
_WriteFile,
9094
_timeBeginPeriod stdFunction
9195

@@ -254,11 +258,90 @@ func write(fd uintptr, buf unsafe.Pointer, n int32) int32 {
254258
// assume fd is real windows handle.
255259
handle = fd
256260
}
261+
isASCII := true
262+
b := (*[1 << 30]byte)(buf)[:n]
263+
for _, x := range b {
264+
if x >= 0x80 {
265+
isASCII = false
266+
break
267+
}
268+
}
269+
270+
if !isASCII {
271+
var m uint32
272+
isConsole := stdcall2(_GetConsoleMode, handle, uintptr(unsafe.Pointer(&m))) != 0
273+
// If this is a console output, various non-unicode code pages can be in use.
274+
// Use the dedicated WriteConsole call to ensure unicode is printed correctly.
275+
if isConsole {
276+
return int32(writeConsole(handle, buf, n))
277+
}
278+
}
257279
var written uint32
258280
stdcall5(_WriteFile, handle, uintptr(buf), uintptr(n), uintptr(unsafe.Pointer(&written)), 0)
259281
return int32(written)
260282
}
261283

284+
var (
285+
utf16ConsoleBack [1000]uint16
286+
utf16ConsoleBackLock mutex
287+
)
288+
289+
// writeConsole writes bufLen bytes from buf to the console File.
290+
// It returns the number of bytes written.
291+
func writeConsole(handle uintptr, buf unsafe.Pointer, bufLen int32) int {
292+
const surr2 = (surrogateMin + surrogateMax + 1) / 2
293+
294+
// Do not use defer for unlock. May cause issues when printing a panic.
295+
lock(&utf16ConsoleBackLock)
296+
297+
b := (*[1 << 30]byte)(buf)[:bufLen]
298+
s := *(*string)(unsafe.Pointer(&b))
299+
300+
utf16tmp := utf16ConsoleBack[:]
301+
302+
total := len(s)
303+
w := 0
304+
for len(s) > 0 {
305+
if w >= len(utf16tmp)-2 {
306+
writeConsoleUTF16(handle, utf16tmp[:w])
307+
w = 0
308+
}
309+
r, n := charntorune(s)
310+
s = s[n:]
311+
if r < 0x10000 {
312+
utf16tmp[w] = uint16(r)
313+
w++
314+
} else {
315+
r -= 0x10000
316+
utf16tmp[w] = surrogateMin + uint16(r>>10)&0x3ff
317+
utf16tmp[w+1] = surr2 + uint16(r)&0x3ff
318+
w += 2
319+
}
320+
}
321+
writeConsoleUTF16(handle, utf16tmp[:w])
322+
unlock(&utf16ConsoleBackLock)
323+
return total
324+
}
325+
326+
// writeConsoleUTF16 is the dedicated windows calls that correctly prints
327+
// to the console regardless of the current code page. Input is utf-16 code points.
328+
// The handle must be a console handle.
329+
func writeConsoleUTF16(handle uintptr, b []uint16) {
330+
l := uint32(len(b))
331+
if l == 0 {
332+
return
333+
}
334+
var written uint32
335+
stdcall5(_WriteConsoleW,
336+
handle,
337+
uintptr(unsafe.Pointer(&b[0])),
338+
uintptr(l),
339+
uintptr(unsafe.Pointer(&written)),
340+
0,
341+
)
342+
return
343+
}
344+
262345
//go:nosplit
263346
func semasleep(ns int64) int32 {
264347
// store ms in ns to save stack space

0 commit comments

Comments
 (0)