From bb1574641fe5b501ad0afa60e4e48064f1ff6b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Mon, 25 May 2026 19:54:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E5=A3=81=E7=BA=B8?= =?UTF-8?q?=E5=88=87=E6=8D=A2=EF=BC=88=E4=B8=BB=E9=A2=98/=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E7=89=87/Bing/=E7=BA=AF=E8=89=B2=E6=B8=90?= =?UTF-8?q?=E5=8F=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bing.go | 82 +++++++++ config.go | 56 +++++- dialog.go | 119 +++++++++++++ main.go | 6 +- systray.go | 162 +++++++++++++++-- wallpaper.go | 113 ++++++++++++ weather.go | 4 +- web/overlay.html | 133 ++++++++++++++ web/themes/aurora.html | 77 +++++++++ web/themes/gradient.html | 17 ++ web/themes/particles.html | 60 +++++++ web/themes/starfield.html | 64 +++++++ web/wallpaper.html | 356 -------------------------------------- win32.go | 14 +- 14 files changed, 868 insertions(+), 395 deletions(-) create mode 100644 bing.go create mode 100644 dialog.go create mode 100644 wallpaper.go create mode 100644 web/overlay.html create mode 100644 web/themes/aurora.html create mode 100644 web/themes/gradient.html create mode 100644 web/themes/particles.html create mode 100644 web/themes/starfield.html delete mode 100644 web/wallpaper.html diff --git a/bing.go b/bing.go new file mode 100644 index 0000000..b1ba3a4 --- /dev/null +++ b/bing.go @@ -0,0 +1,82 @@ +package main + +import ( + "encoding/json" + "io" + "log" + "os" + "path/filepath" + "strings" + "time" +) + +const bingAPI = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN" + +type bingResponse struct { + Images []struct { + URL string `json:"url"` + URLBase string `json:"urlbase"` + Copyright string `json:"copyright"` + } `json:"images"` +} + +func fetchBingWallpaper() { + resp, err := httpClient.Get(bingAPI) + if err != nil { + log.Println("Bing API 请求失败:", err) + return + } + defer resp.Body.Close() + data, err := io.ReadAll(resp.Body) + if err != nil { + return + } + var br bingResponse + if json.Unmarshal(data, &br) != nil || len(br.Images) == 0 { + log.Println("Bing API 解析失败") + return + } + + imgURL := br.Images[0].URL + if !strings.HasPrefix(imgURL, "http") { + imgURL = "https://www.bing.com" + imgURL + } + + imgResp, err := httpClient.Get(imgURL) + if err != nil { + log.Println("Bing 图片下载失败:", err) + return + } + defer imgResp.Body.Close() + imgData, err := io.ReadAll(imgResp.Body) + if err != nil { + return + } + + bingPath := filepath.Join(configDir(), "bing_wallpaper.jpg") + if err := os.WriteFile(bingPath, imgData, 0644); err != nil { + log.Println("Bing 壁纸缓存失败:", err) + return + } + log.Printf("Bing 壁纸已缓存: %s (%d bytes)", bingPath, len(imgData)) + + reloadWallpaper() +} + +func bingWallpaperLoop() { + cfg := loadConfig() + if cfg.WallpaperType == WPBing { + bingPath := filepath.Join(configDir(), "bing_wallpaper.jpg") + if _, err := os.Stat(bingPath); err != nil { + fetchBingWallpaper() + } + } + + ticker := time.NewTicker(4 * time.Hour) + for range ticker.C { + cfg := loadConfig() + if cfg.WallpaperType == WPBing { + fetchBingWallpaper() + } + } +} diff --git a/config.go b/config.go index 15c68c6..79024ad 100644 --- a/config.go +++ b/config.go @@ -8,32 +8,80 @@ import ( "image/color" "image/png" "os" + "path/filepath" +) + +type WallpaperType string + +const ( + WPTheme WallpaperType = "theme" + WPImage WallpaperType = "image" + WPBing WallpaperType = "bing" + WPColor WallpaperType = "color" +) + +type ThemeName string + +const ( + ThemeAurora ThemeName = "aurora" + ThemeStar ThemeName = "starfield" + ThemeGradient ThemeName = "gradient" + ThemeParticle ThemeName = "particles" ) type Config struct { - Zodiac string `json:"zodiac"` - City string `json:"city"` + Zodiac string `json:"zodiac"` + City string `json:"city"` + WallpaperType WallpaperType `json:"wallpaperType"` + Theme ThemeName `json:"theme"` + ImagePath string `json:"imagePath"` + Color1 string `json:"color1"` + Color2 string `json:"color2"` + ColorGradient bool `json:"colorGradient"` } const defaultZodiac = "射手座" var configPath string +func configDir() string { + return filepath.Dir(configPath) +} + func loadConfig() *Config { data, err := os.ReadFile(configPath) if err != nil { - return &Config{Zodiac: defaultZodiac} + return defaultConfig() } var cfg Config if json.Unmarshal(data, &cfg) != nil { - return &Config{Zodiac: defaultZodiac} + return defaultConfig() } if cfg.Zodiac == "" { cfg.Zodiac = defaultZodiac } + if cfg.WallpaperType == "" { + cfg.WallpaperType = WPTheme + } + if cfg.Theme == "" { + cfg.Theme = ThemeAurora + } + if cfg.Color1 == "" { + cfg.Color1 = "#1a1a2e" + } return &cfg } +func defaultConfig() *Config { + return &Config{ + Zodiac: defaultZodiac, + WallpaperType: WPTheme, + Theme: ThemeAurora, + Color1: "#1a1a2e", + Color2: "#16213e", + } +} + func saveConfig(cfg *Config) error { data, _ := json.MarshalIndent(cfg, "", " ") return os.WriteFile(configPath, data, 0644) diff --git a/dialog.go b/dialog.go new file mode 100644 index 0000000..138f099 --- /dev/null +++ b/dialog.go @@ -0,0 +1,119 @@ +package main + +import ( + "fmt" + "unicode/utf16" + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + comdlg32 = windows.NewLazySystemDLL("comdlg32.dll") + procGetOpenFileNameW = comdlg32.NewProc("GetOpenFileNameW") + procChooseColorW = comdlg32.NewProc("ChooseColorW") +) + +func slicePtr(s interface{}) uintptr { + switch v := s.(type) { + case []uint16: + if len(v) == 0 { return 0 } + return uintptr(unsafe.Pointer(&v[0])) + case []uint32: + if len(v) == 0 { return 0 } + return uintptr(unsafe.Pointer(&v[0])) + } + return 0 +} + +func openFileDialog(owner uintptr) string { + type openFileName struct { + lStructSize uint32 + hwndOwner uintptr + hInstance uintptr + lpstrFilter uintptr + lpstrCustomFilter uintptr + nMaxCustFilter uint32 + nFilterIndex uint32 + lpstrFile uintptr + nMaxFile uint32 + lpstrFileTitle uintptr + nMaxFileTitle uint32 + lpstrInitialDir uintptr + lpstrTitle uintptr + Flags uint32 + nFileOffset uint16 + nFileExtension uint16 + lpstrDefExt uintptr + lCustData uintptr + lpfnHook uintptr + lpTemplateName uintptr + pvReserved uintptr + dwReserved uint32 + FlagsEx uint32 + } + + // Filter: "Images\0*.jpg;*.png\0All\0*.*\0\0" + filterStr := "图片文件\x00*.jpg;*.jpeg;*.png;*.bmp;*.webp;*.gif\x00所有文件\x00*.*\x00\x00" + filterUTF16 := utf16.Encode([]rune(filterStr)) + + titleUTF16 := utf16.Encode([]rune("选择壁纸图片")) + fileBuf := make([]uint16, 260) + + ofn := openFileName{ + lStructSize: uint32(unsafe.Sizeof(openFileName{})), + hwndOwner: owner, + lpstrFilter: slicePtr(filterUTF16), + lpstrFile: slicePtr(fileBuf), + nMaxFile: 260, + lpstrTitle: slicePtr(titleUTF16), + Flags: 0x00001000 | 0x00000800 | 0x00000004, + } + + ret, _, _ := procGetOpenFileNameW.Call(uintptr(unsafe.Pointer(&ofn))) + if ret == 0 { + return "" + } + return windows.UTF16ToString(fileBuf) +} + +func colorPickerDialog(owner uintptr, initialColor string) string { + type chooseColor struct { + lStructSize uint32 + hwndOwner uintptr + hInstance uintptr + rgbResult uint32 + lpCustColors uintptr + Flags uint32 + lCustData uintptr + lpfnHook uintptr + lpTemplateName uintptr + } + + custColors := make([]uint32, 16) + + var initRGB uint32 + if len(initialColor) > 4 && initialColor[0] == '#' { + var r, g, b uint32 + fmt.Sscanf(initialColor[1:], "%02x%02x%02x", &r, &g, &b) + initRGB = r | (g << 8) | (b << 16) + } + + cc := chooseColor{ + lStructSize: uint32(unsafe.Sizeof(chooseColor{})), + hwndOwner: owner, + rgbResult: initRGB, + lpCustColors: slicePtr(custColors), + Flags: 0x00000002, + } + + ret, _, _ := procChooseColorW.Call(uintptr(unsafe.Pointer(&cc))) + if ret == 0 { + return "" + } + + r := cc.rgbResult & 0xFF + g := (cc.rgbResult >> 8) & 0xFF + b := (cc.rgbResult >> 16) & 0xFF + return fmt.Sprintf("#%02x%02x%02x", r, g, b) +} diff --git a/main.go b/main.go index d819ddf..adfc7af 100644 --- a/main.go +++ b/main.go @@ -25,9 +25,9 @@ func main() { defer windows.CloseHandle(mutex) exePath, _ := os.Executable() - configDir := filepath.Join(filepath.Dir(exePath), "config") - os.MkdirAll(configDir, 0755) - configPath = filepath.Join(configDir, "settings.json") + cfgDir := filepath.Join(filepath.Dir(exePath), "config") + os.MkdirAll(cfgDir, 0755) + configPath = filepath.Join(cfgDir, "settings.json") procSetProcessDPIAware.Call() systray.Run(onSystrayReady, nil) } diff --git a/systray.go b/systray.go index c9c3756..c8b39cc 100644 --- a/systray.go +++ b/systray.go @@ -16,14 +16,44 @@ import ( var zodiacItems []*systray.MenuItem var cityItems []*systray.MenuItem +var themeItems []*systray.MenuItem + +var themeNames = []struct { + Name ThemeName + Label string +}{ + {ThemeAurora, "极光"}, + {ThemeStar, "星空"}, + {ThemeGradient, "渐变"}, + {ThemeParticle, "粒子"}, +} func onSystrayReady() { systray.SetIcon(generateIcon()) systray.SetTooltip("动态壁纸引擎") + cfg := loadConfig() + mPause := systray.AddMenuItem("暂停", "暂停/继续") systray.AddSeparator() + // 壁纸主题 + mTheme := systray.AddMenuItem("壁纸主题", "") + for _, t := range themeNames { + item := mTheme.AddSubMenuItem(t.Label, t.Label) + if cfg.WallpaperType == WPTheme && cfg.Theme == t.Name { + item.Check() + } + themeItems = append(themeItems, item) + } + mLocalImage := systray.AddMenuItem("本地图片", "选择本地图片作为壁纸") + mBingDaily := systray.AddMenuItem("Bing 每日壁纸", "使用 Bing 每日壁纸") + mSolidColor := systray.AddMenuItem("纯色壁纸", "选择纯色壁纸") + mGradientColor := systray.AddMenuItem("渐变壁纸", "选择渐变壁纸") + + systray.AddSeparator() + + // 星座 mZodiac := systray.AddMenuItem("星座设置", "") zodiacs := []string{ "白羊座", "金牛座", "双子座", @@ -31,7 +61,6 @@ func onSystrayReady() { "天秤座", "天蝎座", "射手座", "摩羯座", "水瓶座", "双鱼座", } - cfg := loadConfig() for _, z := range zodiacs { item := mZodiac.AddSubMenuItem(z, z) if z == cfg.Zodiac { @@ -42,6 +71,7 @@ func onSystrayReady() { systray.AddSeparator() + // 城市 mCity := systray.AddMenuItem("城市设置", "") for _, c := range cities { item := mCity.AddSubMenuItem(c.Name, c.Adm1+" "+c.Name) @@ -54,14 +84,113 @@ func onSystrayReady() { systray.AddSeparator() mQuit := systray.AddMenuItem("退出", "退出程序") + // 主题切换监听 + for i, item := range themeItems { + go func(idx int, mi *systray.MenuItem) { + for { + <-mi.ClickedCh + cfg := loadConfig() + cfg.WallpaperType = WPTheme + cfg.Theme = themeNames[idx].Name + saveConfig(cfg) + for _, it := range themeItems { + it.Uncheck() + } + mi.Check() + log.Printf("主题切换: %s", themeNames[idx].Label) + reloadWallpaper() + } + }(i, item) + } + + // 本地图片 + go func() { + for { + <-mLocalImage.ClickedCh + path := openFileDialog(wvHwnd) + if path == "" { + continue + } + cfg := loadConfig() + cfg.WallpaperType = WPImage + cfg.ImagePath = path + saveConfig(cfg) + for _, it := range themeItems { + it.Uncheck() + } + log.Printf("本地图片: %s", path) + reloadWallpaper() + } + }() + + // Bing 每日 + go func() { + for { + <-mBingDaily.ClickedCh + cfg := loadConfig() + cfg.WallpaperType = WPBing + saveConfig(cfg) + for _, it := range themeItems { + it.Uncheck() + } + log.Println("切换 Bing 壁纸") + go fetchBingWallpaper() + } + }() + + // 纯色壁纸 + go func() { + for { + <-mSolidColor.ClickedCh + color := colorPickerDialog(wvHwnd, "") + if color == "" { + continue + } + cfg := loadConfig() + cfg.WallpaperType = WPColor + cfg.Color1 = color + cfg.ColorGradient = false + saveConfig(cfg) + for _, it := range themeItems { + it.Uncheck() + } + log.Printf("纯色壁纸: %s", color) + reloadWallpaper() + } + }() + + // 渐变壁纸 + go func() { + for { + <-mGradientColor.ClickedCh + c1 := colorPickerDialog(wvHwnd, "") + if c1 == "" { + continue + } + c2 := colorPickerDialog(wvHwnd, "") + if c2 == "" { + c2 = "#16213e" + } + cfg := loadConfig() + cfg.WallpaperType = WPColor + cfg.Color1 = c1 + cfg.Color2 = c2 + cfg.ColorGradient = true + saveConfig(cfg) + for _, it := range themeItems { + it.Uncheck() + } + log.Printf("渐变壁纸: %s -> %s", c1, c2) + reloadWallpaper() + } + }() + // 星座选择监听 for i, item := range zodiacItems { go func(idx int, mi *systray.MenuItem) { name := zodiacs[idx] - log.Printf("星座监听启动: %s", name) for { <-mi.ClickedCh - log.Printf("星座点击: %s", name) cfg := loadConfig() cfg.Zodiac = name saveConfig(cfg) @@ -77,11 +206,9 @@ func onSystrayReady() { // 城市选择监听 for i, item := range cityItems { go func(idx int, mi *systray.MenuItem) { - log.Printf("城市监听启动: %s", cities[idx].Name) for { <-mi.ClickedCh city := cities[idx] - log.Printf("城市点击: %s", city.Name) cfg := loadConfig() cfg.City = city.ID saveConfig(cfg) @@ -96,13 +223,11 @@ func onSystrayReady() { // 暂停 go func() { - log.Println("暂停监听启动") for { <-mPause.ClickedCh newVal := 1 - atomic.LoadInt32(&paused) atomic.StoreInt32(&paused, newVal) isPaused := newVal == 1 - log.Printf("暂停切换: paused=%v", isPaused) if isPaused { mPause.SetTitle("继续") } else { @@ -120,6 +245,7 @@ func onSystrayReady() { go startWebView() go weatherLoop() + go bingWallpaperLoop() } func startWebView() { @@ -145,18 +271,13 @@ func startWebView() { log.Fatal("WebView2 create failed") } - htmlData, err := fs.ReadFile("web/wallpaper.html") - if err != nil { - log.Fatal("读取 wallpaper.html 失败:", err) - } - wv.Bind("setZodiacFromGo", func(zodiac string) error { cfg := loadConfig() cfg.Zodiac = zodiac return saveConfig(cfg) }) - wv.SetHtml(string(htmlData)) + wv.SetHtml(buildWallpaperHTML(loadConfig())) time.Sleep(1 * time.Second) wvHwnd = uintptr(wv.Window()) @@ -165,17 +286,14 @@ func startWebView() { procMoveWindow.Call(wvHwnd, uintptr(^uint(0)), uintptr(^uint(0)), uintptr(screenW+2), uintptr(screenH+2), 1) log.Printf("壁纸已嵌入: HWND=0x%x, %dx%d", wvHwnd, screenW, screenH) - // 注入配置 go func() { time.Sleep(500 * time.Millisecond) cfg := loadConfig() evalJS(fmt.Sprintf(`window.userZodiac = %q;`, cfg.Zodiac)) - log.Printf("配置已注入: zodiac=%s", cfg.Zodiac) }() go fullscreenMonitor() - // 延迟嵌入 WorkerW go func() { time.Sleep(3 * time.Second) workerw := findWorkerW() @@ -186,7 +304,6 @@ func startWebView() { } }() - // 消息循环 type msg struct { hwnd uintptr message uint32 @@ -196,7 +313,6 @@ func startWebView() { pt struct{ x, y int32 } } var m msg - log.Println("启动自定义消息循环...") for { ret, _, _ := procGetMessageW.Call( uintptr(unsafe.Pointer(&m)), @@ -215,7 +331,15 @@ func startWebView() { } } } - nextMsg: + if m.message == wmSetHtml { + select { + case html := <-htmlQueue: + wv.SetHtml(html) + default: + } + goto nextMsg + } +nextMsg: procTranslateMessage.Call(uintptr(unsafe.Pointer(&m))) procDispatchMessageW.Call(uintptr(unsafe.Pointer(&m))) } diff --git a/wallpaper.go b/wallpaper.go new file mode 100644 index 0000000..1dbc59d --- /dev/null +++ b/wallpaper.go @@ -0,0 +1,113 @@ +package main + +import ( + _ "embed" + "encoding/base64" + "fmt" + "log" + "os" + "path/filepath" + "strings" + "time" +) + +//go:embed web/overlay.html +var overlayHTML string + +//go:embed web/themes/aurora.html +var themeAurora string + +//go:embed web/themes/starfield.html +var themeStarfield string + +//go:embed web/themes/gradient.html +var themeGradient string + +//go:embed web/themes/particles.html +var themeParticles string + +var themeMap = map[ThemeName]string{ + ThemeAurora: themeAurora, + ThemeStar: themeStarfield, + ThemeGradient: themeGradient, + ThemeParticle: themeParticles, +} + +func buildWallpaperHTML(cfg *Config) string { + var bg string + + switch cfg.WallpaperType { + case WPTheme: + if t, ok := themeMap[cfg.Theme]; ok { + bg = t + } else { + bg = themeAurora + } + case WPImage: + if cfg.ImagePath != "" { + src := imageToDataURI(cfg.ImagePath) + if src != "" { + bg = fmt.Sprintf(``, src) + } + } + case WPBing: + bingPath := filepath.Join(configDir(), "bing_wallpaper.jpg") + if _, err := os.Stat(bingPath); err == nil { + src := imageToDataURI(bingPath) + if src != "" { + bg = fmt.Sprintf(``, src) + } + } + case WPColor: + if cfg.ColorGradient && cfg.Color2 != "" { + bg = fmt.Sprintf(`
`, cfg.Color1, cfg.Color2) + } else { + bg = fmt.Sprintf(`
`, cfg.Color1) + } + } + + if bg == "" { + bg = themeAurora + } + return strings.Replace(overlayHTML, "{{BACKGROUND}}", bg, 1) +} + +func imageToDataURI(path string) string { + data, err := os.ReadFile(path) + if err != nil { + log.Println("读取图片失败:", err) + return "" + } + ext := strings.ToLower(filepath.Ext(path)) + mime := "image/jpeg" + switch ext { + case ".png": + mime = "image/png" + case ".gif": + mime = "image/gif" + case ".webp": + mime = "image/webp" + case ".bmp": + mime = "image/bmp" + } + return fmt.Sprintf("data:%s;base64,%s", mime, base64.StdEncoding.EncodeToString(data)) +} + +func reloadWallpaper() { + if wv == nil || wvHwnd == 0 { + return + } + cfg := loadConfig() + html := buildWallpaperHTML(cfg) + select { + case htmlQueue <- html: + default: + } + procPostMessageW.Call(wvHwnd, wmSetHtml, 0, 0) + go func() { + time.Sleep(1 * time.Second) + evalJS(fmt.Sprintf(`window.userZodiac = %q;`, cfg.Zodiac)) + city := getCurrentCity() + go fetchAndPushWeather(city) + }() +} diff --git a/weather.go b/weather.go index 83ca515..7fd5453 100644 --- a/weather.go +++ b/weather.go @@ -70,7 +70,7 @@ func getLocation() City { if city := locateByIPIP(); city != nil { return *city } - if city := locateByQWeather(); city != nil { + if city := locateByGeoAPI(); city != nil { return *city } log.Println("所有定位失败,使用默认城市:", defaultCity.Name) @@ -104,7 +104,7 @@ func locateByIPIP() *City { return nil } -func locateByQWeather() *City { +func locateByGeoAPI() *City { data, err := httpGet("https://myip.ipip.net") if err == nil { re := regexp.MustCompile(`(\d+\.\d+\.\d+\.\d+)`) diff --git a/web/overlay.html b/web/overlay.html new file mode 100644 index 0000000..59ce403 --- /dev/null +++ b/web/overlay.html @@ -0,0 +1,133 @@ + + + + + + + +{{BACKGROUND}} + +
+
00:00
+
1月1日 周一
+
+
🌤️ 加载中...
+
未来24小时
+
+
未来7天
+
+
+
✨ 射手座运势
+
+ + + + diff --git a/web/themes/aurora.html b/web/themes/aurora.html new file mode 100644 index 0000000..d32a8b2 --- /dev/null +++ b/web/themes/aurora.html @@ -0,0 +1,77 @@ + + diff --git a/web/themes/gradient.html b/web/themes/gradient.html new file mode 100644 index 0000000..03c0134 --- /dev/null +++ b/web/themes/gradient.html @@ -0,0 +1,17 @@ + +
diff --git a/web/themes/particles.html b/web/themes/particles.html new file mode 100644 index 0000000..9afafcf --- /dev/null +++ b/web/themes/particles.html @@ -0,0 +1,60 @@ + + diff --git a/web/themes/starfield.html b/web/themes/starfield.html new file mode 100644 index 0000000..9df3613 --- /dev/null +++ b/web/themes/starfield.html @@ -0,0 +1,64 @@ + + diff --git a/web/wallpaper.html b/web/wallpaper.html deleted file mode 100644 index a43ccc5..0000000 --- a/web/wallpaper.html +++ /dev/null @@ -1,356 +0,0 @@ - - - - -动态壁纸 - - - - - -
-
00:00
-
1月1日 周一
- -
-
🌤️ 加载中...
- -
未来24小时
-
- -
未来7天
-
-
- -
✨ 射手座运势
-
- - - - diff --git a/win32.go b/win32.go index 19d2234..eb2e102 100644 --- a/win32.go +++ b/win32.go @@ -1,7 +1,6 @@ package main import ( - "embed" "log" "unsafe" @@ -9,9 +8,6 @@ import ( "golang.org/x/sys/windows" ) -//go:embed web/wallpaper.html -var fs embed.FS - var ( user32 = windows.NewLazySystemDLL("user32.dll") procFindWindowW = user32.NewProc("FindWindowW") @@ -37,6 +33,9 @@ var jsQueue = make(chan string, 64) var paused int32 const wmEvalJS = 0x0401 +const wmSetHtml = 0x0402 + +var htmlQueue = make(chan string, 1) func evalJS(js string) { select { @@ -50,13 +49,6 @@ func evalJS(js string) { } } -func min(a, b int) int { - if a < b { - return a - } - return b -} - func findWorkerW() uintptr { progman, _, _ := procFindWindowW.Call( uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("Progman"))), 0)