Initial D-InSAR bundle restore tool
This commit is contained in:
+591
@@ -0,0 +1,591 @@
|
||||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
windowTitle = "D-InSAR 数据包恢复工具"
|
||||
|
||||
wmDestroy = 0x0002
|
||||
wmCommand = 0x0111
|
||||
wmClose = 0x0010
|
||||
wmSetFont = 0x0030
|
||||
|
||||
bnClicked = 0
|
||||
|
||||
wsOverlappedWindow = 0x00CF0000
|
||||
wsVisible = 0x10000000
|
||||
wsChild = 0x40000000
|
||||
wsTabStop = 0x00010000
|
||||
wsBorder = 0x00800000
|
||||
wsVScroll = 0x00200000
|
||||
wsHScroll = 0x00100000
|
||||
esLeft = 0x0000
|
||||
esMultiline = 0x0004
|
||||
esAutoVScroll = 0x0040
|
||||
esAutoHScroll = 0x0080
|
||||
esReadOnly = 0x0800
|
||||
bsPushButton = 0x00000000
|
||||
bsAutoCheckBox = 0x00000003
|
||||
|
||||
swShow = 5
|
||||
cwUseDefault = 0x80000000
|
||||
|
||||
gwlUserData = -21
|
||||
|
||||
idInputEdit = 1001
|
||||
idInputBrowse = 1002
|
||||
idOutputEdit = 1003
|
||||
idOutputBrowse = 1004
|
||||
idDryRun = 1005
|
||||
idOverwrite = 1006
|
||||
idLimitEdit = 1007
|
||||
idStart = 1008
|
||||
idLogEdit = 1009
|
||||
idCheck = 1010
|
||||
|
||||
bmGetCheck = 0x00F0
|
||||
bstChecked = 1
|
||||
|
||||
maxPathBuffer = 4096
|
||||
)
|
||||
|
||||
var (
|
||||
user32 = syscall.NewLazyDLL("user32.dll")
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
gdi32 = syscall.NewLazyDLL("gdi32.dll")
|
||||
shell32 = syscall.NewLazyDLL("shell32.dll")
|
||||
ole32 = syscall.NewLazyDLL("ole32.dll")
|
||||
|
||||
procRegisterClassExW = user32.NewProc("RegisterClassExW")
|
||||
procCreateWindowExW = user32.NewProc("CreateWindowExW")
|
||||
procDefWindowProcW = user32.NewProc("DefWindowProcW")
|
||||
procDestroyWindow = user32.NewProc("DestroyWindow")
|
||||
procDispatchMessageW = user32.NewProc("DispatchMessageW")
|
||||
procGetMessageW = user32.NewProc("GetMessageW")
|
||||
procPostQuitMessage = user32.NewProc("PostQuitMessage")
|
||||
procSendMessageW = user32.NewProc("SendMessageW")
|
||||
procShowWindow = user32.NewProc("ShowWindow")
|
||||
procUpdateWindow = user32.NewProc("UpdateWindow")
|
||||
procSetWindowTextW = user32.NewProc("SetWindowTextW")
|
||||
procGetWindowTextW = user32.NewProc("GetWindowTextW")
|
||||
procGetWindowTextLenW = user32.NewProc("GetWindowTextLengthW")
|
||||
procEnableWindow = user32.NewProc("EnableWindow")
|
||||
procSetWindowLongPtrW = user32.NewProc("SetWindowLongPtrW")
|
||||
procGetWindowLongPtrW = user32.NewProc("GetWindowLongPtrW")
|
||||
procMessageBoxW = user32.NewProc("MessageBoxW")
|
||||
|
||||
procGetModuleHandleW = kernel32.NewProc("GetModuleHandleW")
|
||||
|
||||
procCreateFontW = gdi32.NewProc("CreateFontW")
|
||||
|
||||
procSHBrowseForFolderW = shell32.NewProc("SHBrowseForFolderW")
|
||||
procSHGetPathFromIDListW = shell32.NewProc("SHGetPathFromIDListW")
|
||||
procCoTaskMemFree = ole32.NewProc("CoTaskMemFree")
|
||||
procOleInitialize = ole32.NewProc("OleInitialize")
|
||||
procOleUninitialize = ole32.NewProc("OleUninitialize")
|
||||
)
|
||||
|
||||
type point struct {
|
||||
x int32
|
||||
y int32
|
||||
}
|
||||
|
||||
type msg struct {
|
||||
hwnd uintptr
|
||||
message uint32
|
||||
wParam uintptr
|
||||
lParam uintptr
|
||||
time uint32
|
||||
pt point
|
||||
}
|
||||
|
||||
type wndClassEx struct {
|
||||
cbSize uint32
|
||||
style uint32
|
||||
lpfnWndProc uintptr
|
||||
cbClsExtra int32
|
||||
cbWndExtra int32
|
||||
hInstance uintptr
|
||||
hIcon uintptr
|
||||
hCursor uintptr
|
||||
hbrBackground uintptr
|
||||
lpszMenuName *uint16
|
||||
lpszClassName *uint16
|
||||
hIconSm uintptr
|
||||
}
|
||||
|
||||
type browseInfo struct {
|
||||
hwndOwner uintptr
|
||||
pidlRoot uintptr
|
||||
pszDisplayName *uint16
|
||||
lpszTitle *uint16
|
||||
ulFlags uint32
|
||||
lpfn uintptr
|
||||
lParam uintptr
|
||||
iImage int32
|
||||
}
|
||||
|
||||
type guiApp struct {
|
||||
hwnd uintptr
|
||||
font uintptr
|
||||
inputEdit uintptr
|
||||
outputEdit uintptr
|
||||
dryRun uintptr
|
||||
overwrite uintptr
|
||||
limitEdit uintptr
|
||||
startButton uintptr
|
||||
checkButton uintptr
|
||||
logEdit uintptr
|
||||
mu sync.Mutex
|
||||
logText string
|
||||
running bool
|
||||
}
|
||||
|
||||
func runGUI() error {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
procOleInitialize.Call(0)
|
||||
defer procOleUninitialize.Call()
|
||||
|
||||
instance, _, _ := procGetModuleHandleW.Call(0)
|
||||
className, _ := syscall.UTF16PtrFromString("DinsarRestoreWindow")
|
||||
title, _ := syscall.UTF16PtrFromString(windowTitle)
|
||||
|
||||
wc := wndClassEx{
|
||||
cbSize: uint32(unsafe.Sizeof(wndClassEx{})),
|
||||
lpfnWndProc: syscall.NewCallback(windowProc),
|
||||
hInstance: instance,
|
||||
hbrBackground: 5 + 1,
|
||||
lpszClassName: className,
|
||||
}
|
||||
if atom, _, err := procRegisterClassExW.Call(uintptr(unsafe.Pointer(&wc))); atom == 0 {
|
||||
return fmt.Errorf("RegisterClassExW failed: %v", err)
|
||||
}
|
||||
|
||||
app := &guiApp{}
|
||||
hwnd, _, err := procCreateWindowExW.Call(
|
||||
0,
|
||||
uintptr(unsafe.Pointer(className)),
|
||||
uintptr(unsafe.Pointer(title)),
|
||||
wsOverlappedWindow|wsVisible,
|
||||
cwUseDefault,
|
||||
cwUseDefault,
|
||||
1040,
|
||||
720,
|
||||
0,
|
||||
0,
|
||||
instance,
|
||||
uintptr(unsafe.Pointer(app)),
|
||||
)
|
||||
if hwnd == 0 {
|
||||
return fmt.Errorf("CreateWindowExW failed: %v", err)
|
||||
}
|
||||
app.hwnd = hwnd
|
||||
|
||||
procShowWindow.Call(hwnd, swShow)
|
||||
procUpdateWindow.Call(hwnd)
|
||||
|
||||
var m msg
|
||||
for {
|
||||
ret, _, _ := procGetMessageW.Call(uintptr(unsafe.Pointer(&m)), 0, 0, 0)
|
||||
if int32(ret) <= 0 {
|
||||
break
|
||||
}
|
||||
procDispatchMessageW.Call(uintptr(unsafe.Pointer(&m)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func windowProc(hwnd uintptr, msg uint32, wParam uintptr, lParam uintptr) uintptr {
|
||||
app := appFromWindow(hwnd)
|
||||
switch msg {
|
||||
case wmClose:
|
||||
if app != nil && app.isRunning() {
|
||||
messageBox(hwnd, "任务正在运行,请等待结束。", windowTitle)
|
||||
return 0
|
||||
}
|
||||
procDestroyWindow.Call(hwnd)
|
||||
return 0
|
||||
case wmDestroy:
|
||||
procPostQuitMessage.Call(0)
|
||||
return 0
|
||||
case wmCommand:
|
||||
id := int(wParam & 0xffff)
|
||||
notify := int((wParam >> 16) & 0xffff)
|
||||
if notify == bnClicked && app != nil {
|
||||
switch id {
|
||||
case idInputBrowse:
|
||||
if selected := chooseDirectory(hwnd, "选择 BundleRoot 输入目录"); selected != "" {
|
||||
setWindowText(app.inputEdit, selected)
|
||||
}
|
||||
case idOutputBrowse:
|
||||
if selected := chooseDirectory(hwnd, "选择 OutputRoot 输出目录"); selected != "" {
|
||||
setWindowText(app.outputEdit, selected)
|
||||
}
|
||||
case idStart:
|
||||
app.startRestore()
|
||||
case idCheck:
|
||||
app.startCheck()
|
||||
}
|
||||
}
|
||||
return 0
|
||||
case 0x0081: // WM_NCCREATE
|
||||
cs := (*createStruct)(unsafe.Pointer(lParam))
|
||||
procSetWindowLongPtrW.Call(hwnd, winIndex(gwlUserData), cs.createParams)
|
||||
return 1
|
||||
case 0x0001: // WM_CREATE
|
||||
app = appFromWindow(hwnd)
|
||||
if app != nil {
|
||||
app.hwnd = hwnd
|
||||
app.createControls()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
ret, _, _ := procDefWindowProcW.Call(hwnd, uintptr(msg), wParam, lParam)
|
||||
return ret
|
||||
}
|
||||
|
||||
type createStruct struct {
|
||||
createParams uintptr
|
||||
instance uintptr
|
||||
menu uintptr
|
||||
parent uintptr
|
||||
cy int32
|
||||
cx int32
|
||||
y int32
|
||||
x int32
|
||||
style int32
|
||||
name *uint16
|
||||
class *uint16
|
||||
exStyle uint32
|
||||
}
|
||||
|
||||
func appFromWindow(hwnd uintptr) *guiApp {
|
||||
if hwnd == 0 {
|
||||
return nil
|
||||
}
|
||||
ptr, _, _ := procGetWindowLongPtrW.Call(hwnd, winIndex(gwlUserData))
|
||||
if ptr == 0 {
|
||||
return nil
|
||||
}
|
||||
return (*guiApp)(unsafe.Pointer(ptr))
|
||||
}
|
||||
|
||||
func (a *guiApp) createControls() {
|
||||
a.font = createFont("Microsoft YaHei UI", 20)
|
||||
|
||||
createLabel(a.hwnd, "输入目录 BundleRoot", 24, 28, 210, 30, a.font)
|
||||
a.inputEdit = createEdit(a.hwnd, "", idInputEdit, 240, 24, 640, 34, false, a.font)
|
||||
createButton(a.hwnd, "选择...", idInputBrowse, 895, 24, 100, 34, a.font)
|
||||
|
||||
createLabel(a.hwnd, "输出目录 OutputRoot", 24, 78, 210, 30, a.font)
|
||||
a.outputEdit = createEdit(a.hwnd, "", idOutputEdit, 240, 74, 640, 34, false, a.font)
|
||||
createButton(a.hwnd, "选择...", idOutputBrowse, 895, 74, 100, 34, a.font)
|
||||
|
||||
a.dryRun = createCheckBox(a.hwnd, "Dry run 只打印计划", idDryRun, 240, 128, 230, 32, a.font)
|
||||
a.overwrite = createCheckBox(a.hwnd, "覆盖已有 Task", idOverwrite, 500, 128, 190, 32, a.font)
|
||||
createLabel(a.hwnd, "Limit", 720, 130, 60, 30, a.font)
|
||||
a.limitEdit = createEdit(a.hwnd, "", idLimitEdit, 780, 126, 100, 34, false, a.font)
|
||||
|
||||
a.checkButton = createButton(a.hwnd, "数据自检", idCheck, 760, 174, 105, 38, a.font)
|
||||
a.startButton = createButton(a.hwnd, "开始恢复", idStart, 890, 174, 105, 38, a.font)
|
||||
createLabel(a.hwnd, "运行日志", 24, 226, 140, 30, a.font)
|
||||
a.logEdit = createEdit(a.hwnd, "", idLogEdit, 24, 262, 970, 365, true, a.font)
|
||||
|
||||
a.appendLog("请选择输入目录后可先数据自检;选择输出目录后可开始恢复。\r\n")
|
||||
}
|
||||
|
||||
func (a *guiApp) startRestore() {
|
||||
if a.isRunning() {
|
||||
return
|
||||
}
|
||||
|
||||
input := strings.TrimSpace(getWindowText(a.inputEdit))
|
||||
output := strings.TrimSpace(getWindowText(a.outputEdit))
|
||||
if input == "" {
|
||||
messageBox(a.hwnd, "请选择输入目录 BundleRoot。", windowTitle)
|
||||
return
|
||||
}
|
||||
if output == "" {
|
||||
messageBox(a.hwnd, "请选择输出目录 OutputRoot。", windowTitle)
|
||||
return
|
||||
}
|
||||
limit, ok := a.parseLimit()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config{
|
||||
inputRoot: input,
|
||||
outputRoot: output,
|
||||
skipExisting: true,
|
||||
overwrite: isChecked(a.overwrite),
|
||||
limit: limit,
|
||||
dryRun: isChecked(a.dryRun),
|
||||
logWriter: guiLogWriter{app: a},
|
||||
}
|
||||
if cfg.overwrite {
|
||||
cfg.skipExisting = false
|
||||
}
|
||||
|
||||
a.setRunning(true)
|
||||
a.setLog("")
|
||||
a.appendLog("开始执行...\r\n")
|
||||
go func() {
|
||||
result, err := run(cfg)
|
||||
if err != nil {
|
||||
a.appendLog("错误: " + err.Error() + "\r\n")
|
||||
messageBox(a.hwnd, "执行失败:\r\n"+err.Error(), windowTitle)
|
||||
} else {
|
||||
a.appendLog(fmt.Sprintf("完成: restored=%d skipped=%d failed=%d warnings=%d\r\n",
|
||||
result.report.Restored,
|
||||
result.report.Skipped,
|
||||
result.report.Failed,
|
||||
len(result.report.Warnings),
|
||||
))
|
||||
messageBox(a.hwnd, "执行完成。", windowTitle)
|
||||
}
|
||||
a.setRunning(false)
|
||||
}()
|
||||
}
|
||||
|
||||
func (a *guiApp) startCheck() {
|
||||
if a.isRunning() {
|
||||
return
|
||||
}
|
||||
|
||||
input := strings.TrimSpace(getWindowText(a.inputEdit))
|
||||
if input == "" {
|
||||
messageBox(a.hwnd, "请选择输入目录 BundleRoot。", windowTitle)
|
||||
return
|
||||
}
|
||||
limit, ok := a.parseLimit()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config{
|
||||
inputRoot: input,
|
||||
limit: limit,
|
||||
checkOnly: true,
|
||||
logWriter: guiLogWriter{app: a},
|
||||
}
|
||||
|
||||
a.setRunning(true)
|
||||
a.setLog("")
|
||||
a.appendLog("开始数据自检...\r\n")
|
||||
go func() {
|
||||
result, err := runCheck(cfg)
|
||||
if err != nil {
|
||||
a.appendLog("错误: " + err.Error() + "\r\n")
|
||||
messageBox(a.hwnd, "自检失败:\r\n"+err.Error(), windowTitle)
|
||||
} else if result.report.Failed > 0 {
|
||||
a.appendLog(fmt.Sprintf("自检完成: total=%d checked=%d valid=%d failed=%d warnings=%d\r\n",
|
||||
result.report.TotalPairs,
|
||||
result.report.Checked,
|
||||
result.report.Valid,
|
||||
result.report.Failed,
|
||||
len(result.report.Warnings),
|
||||
))
|
||||
messageBox(a.hwnd, fmt.Sprintf("自检完成,但发现 %d 个失败项。请查看日志。", result.report.Failed), windowTitle)
|
||||
} else {
|
||||
a.appendLog(fmt.Sprintf("自检通过: total=%d checked=%d valid=%d warnings=%d\r\n",
|
||||
result.report.TotalPairs,
|
||||
result.report.Checked,
|
||||
result.report.Valid,
|
||||
len(result.report.Warnings),
|
||||
))
|
||||
messageBox(a.hwnd, "自检通过。", windowTitle)
|
||||
}
|
||||
a.setRunning(false)
|
||||
}()
|
||||
}
|
||||
|
||||
func (a *guiApp) parseLimit() (int, bool) {
|
||||
limitText := strings.TrimSpace(getWindowText(a.limitEdit))
|
||||
if limitText == "" {
|
||||
return 0, true
|
||||
}
|
||||
parsed, err := strconv.Atoi(limitText)
|
||||
if err != nil || parsed < 0 {
|
||||
messageBox(a.hwnd, "Limit 必须是大于等于 0 的整数。", windowTitle)
|
||||
return 0, false
|
||||
}
|
||||
return parsed, true
|
||||
}
|
||||
|
||||
func (a *guiApp) isRunning() bool {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.running
|
||||
}
|
||||
|
||||
func (a *guiApp) setRunning(running bool) {
|
||||
a.mu.Lock()
|
||||
a.running = running
|
||||
a.mu.Unlock()
|
||||
enable := uintptr(1)
|
||||
if running {
|
||||
enable = 0
|
||||
}
|
||||
procEnableWindow.Call(a.startButton, enable)
|
||||
procEnableWindow.Call(a.checkButton, enable)
|
||||
procEnableWindow.Call(a.inputEdit, enable)
|
||||
procEnableWindow.Call(a.outputEdit, enable)
|
||||
procEnableWindow.Call(a.dryRun, enable)
|
||||
procEnableWindow.Call(a.overwrite, enable)
|
||||
procEnableWindow.Call(a.limitEdit, enable)
|
||||
}
|
||||
|
||||
func (a *guiApp) setLog(text string) {
|
||||
a.mu.Lock()
|
||||
a.logText = text
|
||||
a.mu.Unlock()
|
||||
setWindowText(a.logEdit, text)
|
||||
}
|
||||
|
||||
func (a *guiApp) appendLog(text string) {
|
||||
a.mu.Lock()
|
||||
a.logText += text
|
||||
fullText := a.logText
|
||||
a.mu.Unlock()
|
||||
setWindowText(a.logEdit, fullText)
|
||||
}
|
||||
|
||||
type guiLogWriter struct {
|
||||
app *guiApp
|
||||
}
|
||||
|
||||
func (w guiLogWriter) Write(p []byte) (int, error) {
|
||||
if w.app != nil {
|
||||
w.app.appendLog(strings.ReplaceAll(string(p), "\n", "\r\n"))
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func createLabel(parent uintptr, text string, x, y, width, height int32, font uintptr) uintptr {
|
||||
return createControl("STATIC", text, wsChild|wsVisible, 0, parent, 0, x, y, width, height, font)
|
||||
}
|
||||
|
||||
func createEdit(parent uintptr, text string, id int, x, y, width, height int32, multiline bool, font uintptr) uintptr {
|
||||
style := uint32(wsChild | wsVisible | wsBorder | wsTabStop | esLeft | esAutoHScroll)
|
||||
if multiline {
|
||||
style = wsChild | wsVisible | wsBorder | wsVScroll | wsHScroll | esLeft | esMultiline | esAutoVScroll | esAutoHScroll | esReadOnly
|
||||
}
|
||||
return createControl("EDIT", text, style, 0, parent, id, x, y, width, height, font)
|
||||
}
|
||||
|
||||
func createButton(parent uintptr, text string, id int, x, y, width, height int32, font uintptr) uintptr {
|
||||
return createControl("BUTTON", text, wsChild|wsVisible|wsTabStop|bsPushButton, 0, parent, id, x, y, width, height, font)
|
||||
}
|
||||
|
||||
func createCheckBox(parent uintptr, text string, id int, x, y, width, height int32, font uintptr) uintptr {
|
||||
return createControl("BUTTON", text, wsChild|wsVisible|wsTabStop|bsAutoCheckBox, 0, parent, id, x, y, width, height, font)
|
||||
}
|
||||
|
||||
func createControl(className string, text string, style uint32, exStyle uint32, parent uintptr, id int, x, y, width, height int32, font uintptr) uintptr {
|
||||
classPtr, _ := syscall.UTF16PtrFromString(className)
|
||||
textPtr, _ := syscall.UTF16PtrFromString(text)
|
||||
hwnd, _, _ := procCreateWindowExW.Call(
|
||||
uintptr(exStyle),
|
||||
uintptr(unsafe.Pointer(classPtr)),
|
||||
uintptr(unsafe.Pointer(textPtr)),
|
||||
uintptr(style),
|
||||
uintptr(x),
|
||||
uintptr(y),
|
||||
uintptr(width),
|
||||
uintptr(height),
|
||||
parent,
|
||||
uintptr(id),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if hwnd != 0 && font != 0 {
|
||||
procSendMessageW.Call(hwnd, wmSetFont, font, 1)
|
||||
}
|
||||
return hwnd
|
||||
}
|
||||
|
||||
func createFont(name string, size int32) uintptr {
|
||||
namePtr, _ := syscall.UTF16PtrFromString(name)
|
||||
font, _, _ := procCreateFontW.Call(
|
||||
uintptr(-size),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
400,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
uintptr(unsafe.Pointer(namePtr)),
|
||||
)
|
||||
return font
|
||||
}
|
||||
|
||||
func setWindowText(hwnd uintptr, text string) {
|
||||
ptr, _ := syscall.UTF16PtrFromString(text)
|
||||
procSetWindowTextW.Call(hwnd, uintptr(unsafe.Pointer(ptr)))
|
||||
}
|
||||
|
||||
func getWindowText(hwnd uintptr) string {
|
||||
length, _, _ := procGetWindowTextLenW.Call(hwnd)
|
||||
buffer := make([]uint16, int(length)+1)
|
||||
procGetWindowTextW.Call(hwnd, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)))
|
||||
return syscall.UTF16ToString(buffer)
|
||||
}
|
||||
|
||||
func isChecked(hwnd uintptr) bool {
|
||||
ret, _, _ := procSendMessageW.Call(hwnd, bmGetCheck, 0, 0)
|
||||
return ret == bstChecked
|
||||
}
|
||||
|
||||
func messageBox(hwnd uintptr, text string, title string) {
|
||||
textPtr, _ := syscall.UTF16PtrFromString(text)
|
||||
titlePtr, _ := syscall.UTF16PtrFromString(title)
|
||||
procMessageBoxW.Call(hwnd, uintptr(unsafe.Pointer(textPtr)), uintptr(unsafe.Pointer(titlePtr)), 0)
|
||||
}
|
||||
|
||||
func chooseDirectory(hwnd uintptr, title string) string {
|
||||
titlePtr, _ := syscall.UTF16PtrFromString(title)
|
||||
displayName := make([]uint16, maxPathBuffer)
|
||||
bi := browseInfo{
|
||||
hwndOwner: hwnd,
|
||||
pszDisplayName: &displayName[0],
|
||||
lpszTitle: titlePtr,
|
||||
ulFlags: 0x00000001 | 0x00000010 | 0x00000040,
|
||||
}
|
||||
pidl, _, _ := procSHBrowseForFolderW.Call(uintptr(unsafe.Pointer(&bi)))
|
||||
if pidl == 0 {
|
||||
return ""
|
||||
}
|
||||
defer procCoTaskMemFree.Call(pidl)
|
||||
|
||||
pathBuffer := make([]uint16, maxPathBuffer)
|
||||
ret, _, _ := procSHGetPathFromIDListW.Call(pidl, uintptr(unsafe.Pointer(&pathBuffer[0])))
|
||||
if ret == 0 {
|
||||
return ""
|
||||
}
|
||||
return syscall.UTF16ToString(pathBuffer)
|
||||
}
|
||||
|
||||
func winIndex(value int32) uintptr {
|
||||
return uintptr(value)
|
||||
}
|
||||
|
||||
var _ io.Writer = guiLogWriter{}
|
||||
Reference in New Issue
Block a user