5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-10 22:19:46 +08:00

[v3 windows] Initial support for start_on_login plugin for windows

This commit is contained in:
Lea Anthony 2023-06-24 21:12:24 +10:00
parent 3827ca2d78
commit b898b79aaf
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
13 changed files with 506 additions and 27 deletions

View File

@ -78,4 +78,36 @@ When adding a common event, ensure that the platform specific events are mapped.
})
```
NOTE: We may try to automate this in the future by adding the mapping to the event definition.
NOTE: We may try to automate this in the future by adding the mapping to the event definition.
### Plugins
Plugins are a way to extend the functionality of your Wails application.
#### Creating a plugin
Plugins are standard Go structure that adhere to the following interface:
```go
type Plugin interface {
Name() string
Init(*application.App) error
Shutdown()
CallableByJS() []string
InjectJS() string
}
```
The `Name()` method returns the name of the plugin. This is used for logging purposes.
The `Init(*application.App) error` method is called when the plugin is loaded. The `*application.App`
parameter is the application that the plugin is being loaded into. Any errors will prevent
the application from starting.
The `Shutdown()` method is called when the application is shutting down.
The `CallableByJS()` method returns a list of exported functions that can be called from
the frontend. These method names must exactly match the names of the methods exported
by the plugin.
The `InjectJS()` method returns JavaScript that should be injected into all windows as they are created. This is useful for adding custom JavaScript functions that complement the plugin.

View File

@ -90,7 +90,7 @@ Webview Window Interface Methods
| Feature | Windows | Linux | Mac | Notes |
|---------|---------|-------|-----|-------|
| Quit | Y | Y | Y | |
| Quit | Y | Y | Y | |
| Hide | Y | | Y | |
| Show | Y | | Y | |
@ -214,7 +214,6 @@ An 'X' indicates that the option is not supported by the platform.
| Zoom | | | | |
| ZoomControlEnabled | | | | |
### Log
To log or not to log? System logger vs custom logger.
@ -223,7 +222,7 @@ To log or not to log? System logger vs custom logger.
| Event | Windows | Linux | Mac | Notes |
|--------------------------|---------|-------|-----|-------|
| Default Application Menu | Y | Y | Y | |
| Default Application Menu | Y | Y | Y | |
## Tray Menus
@ -320,11 +319,10 @@ TODO:
## Frameless Windows
| Feature | Windows | Linux | Mac | Notes |
|---------|---------|-------|-----|-----------------------------------------------|
| Resize | | | | |
| Drag | | Y | | Linux - can always drag with `Meta`+left mouse |
| Feature | Windows | Linux | Mac | Notes |
|---------|---------|-------|-----|------------------------------------------------|
| Resize | Y | | Y | |
| Drag | Y | Y | Y | Linux - can always drag with `Meta`+left mouse |
## Mac Specific
@ -359,7 +357,6 @@ TODO:
## Linux Specific
Implementation details for the functions utilized by the `*_linux.go` files are located in the following files:
- linux_cgo.go: CGo implementation
@ -367,7 +364,8 @@ Implementation details for the functions utilized by the `*_linux.go` files are
### CGO
By default CGO is utilized to compile the Linux port. This prevents easy cross-compilation and so the PureGo implementation is also being simultaneously developed.
By default CGO is utilized to compile the Linux port. This prevents easy cross-compilation and so the PureGo
implementation is also being simultaneously developed.
### Purego

View File

@ -14,7 +14,7 @@ Application events are events that are emitted by the application. These events
### Window Events
Window events are events that are emitted by a window. These events include native events such as `WindowDidBecomeMain` on macOS.
Window events are events that are emitted by a window. These events include native events such as `WindowDidBecomeMain` on macOS. Common events are also defined, so they work cross-platform, e.g. `WindowClosing`.
### Custom Events
@ -46,6 +46,8 @@ When emitting an event in JS, it now sends the event to the application. This wi
The Window API has largely remained the same, however the methods are now on an instance of a window rather than the runtime.
Some notable differences are:
- Windows now have a Name that identifies them. This is used to identify the window when emitting events.
- Windows have even more methods on the that were previously unavailable, such as `AbsolutePosition` and `ToggleDevTools`.
- Windows can now accept files via native drag and drop. See the Drag and Drop section for more details.
## ClipBoard

View File

@ -7,6 +7,7 @@ require github.com/wailsapp/wails/v3 v3.0.0-alpha.0
require (
github.com/bep/debounce v1.2.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/ebitengine/purego v0.4.0-alpha.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
@ -22,7 +23,7 @@ require (
github.com/samber/lo v1.37.0 // indirect
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6 // indirect
github.com/wailsapp/wails/v2 v2.5.1 // indirect
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.7.0 // indirect

View File

@ -5,6 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/ebitengine/purego v0.4.0-alpha.4 h1:Y7yIV06Yo5M2BAdD7EVPhfp6LZ0tEcQo5770OhYUVes=
github.com/ebitengine/purego v0.4.0-alpha.4/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=

View File

@ -39,15 +39,16 @@ func main() {
// When true, the original app will be activated when a second instance is launched
ActivateAppOnSubsequentLaunch: true,
}),
"start_at_login": start_at_login.NewPlugin(),
"start_at_login": start_at_login.NewPlugin(start_at_login.Config{}),
},
Assets: application.AssetOptions{
FS: assets,
},
})
window := app.NewWebviewWindow()
window.ToggleDevTools()
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
DevToolsEnabled: true,
})
err := app.Run()

225
v3/pkg/w32/guid.go Normal file
View File

@ -0,0 +1,225 @@
//go:build windows
package w32
// This code has been adapted from: https://github.com/go-ole/go-ole
/*
The MIT License (MIT)
Copyright © 2013-2017 Yasuhiro Matsumoto, <mattn.jp@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the Software), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
const hextable = "0123456789ABCDEF"
const emptyGUID = "{00000000-0000-0000-0000-000000000000}"
// GUID is Windows API specific GUID type.
//
// This exists to match Windows GUID type for direct passing for COM.
// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx.
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
// NewGUID converts the given string into a globally unique identifier that is
// compliant with the Windows API.
//
// The supplied string may be in any of these formats:
//
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
//
// The conversion of the supplied string is not case-sensitive.
func NewGUID(guid string) *GUID {
d := []byte(guid)
var d1, d2, d3, d4a, d4b []byte
switch len(d) {
case 38:
if d[0] != '{' || d[37] != '}' {
return nil
}
d = d[1:37]
fallthrough
case 36:
if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' {
return nil
}
d1 = d[0:8]
d2 = d[9:13]
d3 = d[14:18]
d4a = d[19:23]
d4b = d[24:36]
case 32:
d1 = d[0:8]
d2 = d[8:12]
d3 = d[12:16]
d4a = d[16:20]
d4b = d[20:32]
default:
return nil
}
var g GUID
var ok1, ok2, ok3, ok4 bool
g.Data1, ok1 = decodeHexUint32(d1)
g.Data2, ok2 = decodeHexUint16(d2)
g.Data3, ok3 = decodeHexUint16(d3)
g.Data4, ok4 = decodeHexByte64(d4a, d4b)
if ok1 && ok2 && ok3 && ok4 {
return &g
}
return nil
}
func decodeHexUint32(src []byte) (value uint32, ok bool) {
var b1, b2, b3, b4 byte
var ok1, ok2, ok3, ok4 bool
b1, ok1 = decodeHexByte(src[0], src[1])
b2, ok2 = decodeHexByte(src[2], src[3])
b3, ok3 = decodeHexByte(src[4], src[5])
b4, ok4 = decodeHexByte(src[6], src[7])
value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4)
ok = ok1 && ok2 && ok3 && ok4
return
}
func decodeHexUint16(src []byte) (value uint16, ok bool) {
var b1, b2 byte
var ok1, ok2 bool
b1, ok1 = decodeHexByte(src[0], src[1])
b2, ok2 = decodeHexByte(src[2], src[3])
value = (uint16(b1) << 8) | uint16(b2)
ok = ok1 && ok2
return
}
func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) {
var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool
value[0], ok1 = decodeHexByte(s1[0], s1[1])
value[1], ok2 = decodeHexByte(s1[2], s1[3])
value[2], ok3 = decodeHexByte(s2[0], s2[1])
value[3], ok4 = decodeHexByte(s2[2], s2[3])
value[4], ok5 = decodeHexByte(s2[4], s2[5])
value[5], ok6 = decodeHexByte(s2[6], s2[7])
value[6], ok7 = decodeHexByte(s2[8], s2[9])
value[7], ok8 = decodeHexByte(s2[10], s2[11])
ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8
return
}
func decodeHexByte(c1, c2 byte) (value byte, ok bool) {
var n1, n2 byte
var ok1, ok2 bool
n1, ok1 = decodeHexChar(c1)
n2, ok2 = decodeHexChar(c2)
value = (n1 << 4) | n2
ok = ok1 && ok2
return
}
func decodeHexChar(c byte) (byte, bool) {
switch {
case '0' <= c && c <= '9':
return c - '0', true
case 'a' <= c && c <= 'f':
return c - 'a' + 10, true
case 'A' <= c && c <= 'F':
return c - 'A' + 10, true
}
return 0, false
}
// String converts the GUID to string form. It will adhere to this pattern:
//
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
//
// If the GUID is nil, the string representation of an empty GUID is returned:
//
// {00000000-0000-0000-0000-000000000000}
func (guid *GUID) String() string {
if guid == nil {
return emptyGUID
}
var c [38]byte
c[0] = '{'
putUint32Hex(c[1:9], guid.Data1)
c[9] = '-'
putUint16Hex(c[10:14], guid.Data2)
c[14] = '-'
putUint16Hex(c[15:19], guid.Data3)
c[19] = '-'
putByteHex(c[20:24], guid.Data4[0:2])
c[24] = '-'
putByteHex(c[25:37], guid.Data4[2:8])
c[37] = '}'
return string(c[:])
}
func putUint32Hex(b []byte, v uint32) {
b[0] = hextable[byte(v>>24)>>4]
b[1] = hextable[byte(v>>24)&0x0f]
b[2] = hextable[byte(v>>16)>>4]
b[3] = hextable[byte(v>>16)&0x0f]
b[4] = hextable[byte(v>>8)>>4]
b[5] = hextable[byte(v>>8)&0x0f]
b[6] = hextable[byte(v)>>4]
b[7] = hextable[byte(v)&0x0f]
}
func putUint16Hex(b []byte, v uint16) {
b[0] = hextable[byte(v>>8)>>4]
b[1] = hextable[byte(v>>8)&0x0f]
b[2] = hextable[byte(v)>>4]
b[3] = hextable[byte(v)&0x0f]
}
func putByteHex(dst, src []byte) {
for i := 0; i < len(src); i++ {
dst[i*2] = hextable[src[i]>>4]
dst[i*2+1] = hextable[src[i]&0x0f]
}
}
// IsEqualGUID compares two GUID.
//
// Not constant time comparison.
func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool {
return guid1.Data1 == guid2.Data1 &&
guid1.Data2 == guid2.Data2 &&
guid1.Data3 == guid2.Data3 &&
guid1.Data4[0] == guid2.Data4[0] &&
guid1.Data4[1] == guid2.Data4[1] &&
guid1.Data4[2] == guid2.Data4[2] &&
guid1.Data4[3] == guid2.Data4[3] &&
guid1.Data4[4] == guid2.Data4[4] &&
guid1.Data4[5] == guid2.Data4[5] &&
guid1.Data4[6] == guid2.Data4[6] &&
guid1.Data4[7] == guid2.Data4[7]
}

View File

@ -9,6 +9,7 @@ package w32
import (
"errors"
"fmt"
"golang.org/x/sys/windows"
"syscall"
"unsafe"
)
@ -82,6 +83,124 @@ const (
NOTIFYICON_VERSION = 4
)
var (
FOLDERID_AccountPictures = NewGUID("{008CA0B1-55B4-4C56-B8A8-4DE4B299D3BE}")
FOLDERID_AddNewPrograms = NewGUID("{DE61D971-5EBC-4F02-A3A9-6C82895E5C04}")
FOLDERID_AdminTools = NewGUID("{724EF170-A42D-4FEF-9F26-B60E846FBA4F}")
FOLDERID_ApplicationShortcuts = NewGUID("{A3918781-E5F2-4890-B3D9-A7E54332328C}")
FOLDERID_AppsFolder = NewGUID("{1E87508D-89C2-42F0-8A7E-645A0F50CA58}")
FOLDERID_AppUpdates = NewGUID("{A305CE99-F527-492B-8B1A-7E76FA98D6E4}")
FOLDERID_CDBurning = NewGUID("{9E52AB10-F80D-49DF-ACB8-4330F5687855}")
FOLDERID_ChangeRemovePrograms = NewGUID("{DF7266AC-9274-4867-8D55-3BD661DE872D}")
FOLDERID_CommonAdminTools = NewGUID("{D0384E7D-BAC3-4797-8F14-CBA229B392B5}")
FOLDERID_CommonOEMLinks = NewGUID("{C1BAE2D0-10DF-4334-BEDD-7AA20B227A9D}")
FOLDERID_CommonPrograms = NewGUID("{0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8}")
FOLDERID_CommonStartMenu = NewGUID("{A4115719-D62E-491D-AA7C-E74B8BE3B067}")
FOLDERID_CommonStartup = NewGUID("{82A5EA35-D9CD-47C5-9629-E15D2F714E6E}")
FOLDERID_CommonTemplates = NewGUID("{B94237E7-57AC-4347-9151-B08C6C32D1F7}")
FOLDERID_ComputerFolder = NewGUID("{0AC0837C-BBF8-452A-850D-79D08E667CA7}")
FOLDERID_ConflictFolder = NewGUID("{4BFEFB45-347D-4006-A5BE-AC0CB0567192}")
FOLDERID_ConnectionsFolder = NewGUID("{6F0CD92B-2E97-45D1-88FF-B0D186B8DEDD}")
FOLDERID_Contacts = NewGUID("{56784854-C6CB-462B-8169-88E350ACB882}")
FOLDERID_ControlPanelFolder = NewGUID("{82A74AEB-AEB4-465C-A014-D097EE346D63}")
FOLDERID_Cookies = NewGUID("{2B0F765D-C0E9-4171-908E-08A611B84FF6}")
FOLDERID_Desktop = NewGUID("{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}")
FOLDERID_DeviceMetadataStore = NewGUID("{5CE4A5E9-E4EB-479D-B89F-130C02886155}")
FOLDERID_Documents = NewGUID("{FDD39AD0-238F-46AF-ADB4-6C85480369C7}")
FOLDERID_DocumentsLibrary = NewGUID("{7B0DB17D-9CD2-4A93-9733-46CC89022E7C}")
FOLDERID_Downloads = NewGUID("{374DE290-123F-4565-9164-39C4925E467B}")
FOLDERID_Favorites = NewGUID("{1777F761-68AD-4D8A-87BD-30B759FA33DD}")
FOLDERID_Fonts = NewGUID("{FD228CB7-AE11-4AE3-864C-16F3910AB8FE}")
FOLDERID_Games = NewGUID("{CAC52C1A-B53D-4EDC-92D7-6B2E8AC19434}")
FOLDERID_GameTasks = NewGUID("{054FAE61-4DD8-4787-80B6-090220C4B700}")
FOLDERID_History = NewGUID("{D9DC8A3B-B784-432E-A781-5A1130A75963}")
FOLDERID_HomeGroup = NewGUID("{52528A6B-B9E3-4ADD-B60D-588C2DBA842D}")
FOLDERID_HomeGroupCurrentUser = NewGUID("{9B74B6A3-0DFD-4F11-9E78-5F7800F2E772}")
FOLDERID_ImplicitAppShortcuts = NewGUID("{BCB5256F-79F6-4CEE-B725-DC34E402FD46}")
FOLDERID_InternetCache = NewGUID("{352481E8-33BE-4251-BA85-6007CAEDCF9D}")
FOLDERID_InternetFolder = NewGUID("{4D9F7874-4E0C-4904-967B-40B0D20C3E4B}")
FOLDERID_Libraries = NewGUID("{1B3EA5DC-B587-4786-B4EF-BD1DC332AEAE}")
FOLDERID_Links = NewGUID("{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}")
FOLDERID_LocalAppData = NewGUID("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}")
FOLDERID_LocalAppDataLow = NewGUID("{A520A1A4-1780-4FF6-BD18-167343C5AF16}")
FOLDERID_LocalizedResourcesDir = NewGUID("{2A00375E-224C-49DE-B8D1-440DF7EF3DDC}")
FOLDERID_Music = NewGUID("{4BD8D571-6D19-48D3-BE97-422220080E43}")
FOLDERID_MusicLibrary = NewGUID("{2112AB0A-C86A-4FFE-A368-0DE96E47012E}")
FOLDERID_NetHood = NewGUID("{C5ABBF53-E17F-4121-8900-86626FC2C973}")
FOLDERID_NetworkFolder = NewGUID("{D20BEEC4-5CA8-4905-AE3B-BF251EA09B53}")
FOLDERID_OriginalImages = NewGUID("{2C36C0AA-5812-4B87-BFD0-4CD0DFB19B39}")
FOLDERID_PhotoAlbums = NewGUID("{69D2CF90-FC33-4FB7-9A0C-EBB0F0FCB43C}")
FOLDERID_Pictures = NewGUID("{33E28130-4E1E-4676-835A-98395C3BC3BB}")
FOLDERID_PicturesLibrary = NewGUID("{A990AE9F-A03B-4E80-94BC-9912D7504104}")
FOLDERID_Playlists = NewGUID("{DE92C1C7-837F-4F69-A3BB-86E631204A23}")
FOLDERID_PrintersFolder = NewGUID("{76FC4E2D-D6AD-4519-A663-37BD56068185}")
FOLDERID_PrintHood = NewGUID("{9274BD8D-CFD1-41C3-B35E-B13F55A758F4}")
FOLDERID_Profile = NewGUID("{5E6C858F-0E22-4760-9AFE-EA3317B67173}")
FOLDERID_ProgramData = NewGUID("{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}")
FOLDERID_ProgramFiles = NewGUID("{905E63B6-C1BF-494E-B29C-65B732D3D21A}")
FOLDERID_ProgramFilesCommon = NewGUID("{F7F1ED05-9F6D-47A2-AAAE-29D317C6F066}")
FOLDERID_ProgramFilesCommonX64 = NewGUID("{6365D5A7-0F0D-45E5-87F6-0DA56B6A4F7D}")
FOLDERID_ProgramFilesCommonX86 = NewGUID("{DE974D24-D9C6-4D3E-BF91-F4455120B917}")
FOLDERID_ProgramFilesX64 = NewGUID("{6D809377-6AF0-444B-8957-A3773F02200E}")
FOLDERID_ProgramFilesX86 = NewGUID("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}")
FOLDERID_Programs = NewGUID("{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}")
FOLDERID_Public = NewGUID("{DFDF76A2-C82A-4D63-906A-5644AC457385}")
FOLDERID_PublicDesktop = NewGUID("{C4AA340D-F20F-4863-AFEF-1F769F2BE730}")
FOLDERID_PublicDocuments = NewGUID("{ED4824AF-DCE4-45A8-81E2-FC7965083634}")
FOLDERID_PublicDownloads = NewGUID("{3D644C9B-1FB8-4F30-9B45-F670235F79C0}")
FOLDERID_PublicGameTasks = NewGUID("{DEBF2536-E1A8-4C59-B6A2-414586476AEA}")
FOLDERID_PublicLibraries = NewGUID("{48DAF80B-E6CF-4F4E-B800-0E69D84EE384}")
FOLDERID_PublicMusic = NewGUID("{3214FAB5-9757-4298-BB61-92A9DEAA44FF}")
FOLDERID_PublicPictures = NewGUID("{B6EBFB86-6907-413C-9AF7-4FC2ABF07CC5}")
FOLDERID_PublicRingtones = NewGUID("{E555AB60-153B-4D17-9F04-A5FE99FC15EC}")
FOLDERID_PublicUserTiles = NewGUID("{0482af6c-08f1-4c34-8c90-e17ec98b1e17}")
FOLDERID_PublicVideos = NewGUID("{2400183A-6185-49FB-A2D8-4A392A602BA3}")
FOLDERID_QuickLaunch = NewGUID("{52a4f021-7b75-48a9-9f6b-4b87a210bc8f}")
FOLDERID_Recent = NewGUID("{AE50C081-EBD2-438A-8655-8A092E34987A}")
FOLDERID_RecordedTVLibrary = NewGUID("{1A6FDBA2-F42D-4358-A798-B74D745926C5}")
FOLDERID_RecycleBinFolder = NewGUID("{B7534046-3ECB-4C18-BE4E-64CD4CB7D6AC}")
FOLDERID_ResourceDir = NewGUID("{8AD10C31-2ADB-4296-A8F7-E4701232C972}")
FOLDERID_Ringtones = NewGUID("{C870044B-F49E-4126-A9C3-B52A1FF411E8}")
FOLDERID_RoamingAppData = NewGUID("{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}")
FOLDERID_RoamingTiles = NewGUID("{AAA8D5A5-F1D6-4259-BAA8-78E7EF60835E}")
FOLDERID_SampleMusic = NewGUID("{B250C668-F57D-4EE1-A63C-290EE7D1AA1F}")
FOLDERID_SamplePictures = NewGUID("{C4900540-2379-4C75-844B-64E6FAF8716B}")
FOLDERID_SamplePlaylists = NewGUID("{15CA69B3-30EE-49C1-ACE1-6B5EC372AFB5}")
FOLDERID_SampleVideos = NewGUID("{859EAD94-2E85-48AD-A71A-0969CB56A6CD}")
FOLDERID_SavedGames = NewGUID("{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}")
FOLDERID_SavedPictures = NewGUID("{3B193882-D3AD-4EAB-965A-69829D1FB59F}")
FOLDERID_SavedPicturesLibrary = NewGUID("{E25B5812-BE88-4BD9-94B0-29233477B6C3}")
FOLDERID_SavedSearches = NewGUID("{7D1D3A04-DEBB-4115-95CF-2F29DA2920DA}")
FOLDERID_SEARCH_CSC = NewGUID("{ee32e446-31ca-4aba-814f-a5ebd2fd6d5e}")
FOLDERID_SEARCH_MAPI = NewGUID("{98ec0e18-2098-4d44-8644-66979315a281}")
FOLDERID_SearchHome = NewGUID("{190337d1-b8ca-4121-a639-6d472d16972a}")
FOLDERID_SendTo = NewGUID("{8983036C-27C0-404B-8F08-102D10DCFD74}")
FOLDERID_SidebarDefaultParts = NewGUID("{7B396E54-9EC5-4300-BE0A-2482EBAE1A26}")
FOLDERID_SidebarParts = NewGUID("{A75D362E-50FC-4fb7-AC2C-A8BEAA314493}")
FOLDERID_SkyDrive = NewGUID("{A52BBA46-E9E1-435f-B3D9-28DAA648C0F6}")
FOLDERID_SkyDriveCameraRoll = NewGUID("{767E6811-49CB-4273-87C2-20F355E1085B}")
FOLDERID_SkyDriveDocuments = NewGUID("{24D89E24-2F19-4534-9DDE-6A6671FBB8FE}")
FOLDERID_SkyDriveMusic = NewGUID("{C3F2459E-80D6-45DC-BFEF-1F769F2BE730}")
FOLDERID_SkyDrivePictures = NewGUID("{339719B5-8C47-4894-94C2-D8F77ADD44A6}")
FOLDERID_StartMenu = NewGUID("{625B53C3-AB48-4EC1-BA1F-A1EF4146FC19}")
FOLDERID_Startup = NewGUID("{B97D20BB-F46A-4C97-BA10-5E3608430854}")
FOLDERID_SyncManagerFolder = NewGUID("{43668BF8-C14E-49B2-97C9-747784D784B7}")
FOLDERID_SyncResultsFolder = NewGUID("{289a9a43-be44-4057-a41b-587a76d7e7f9}")
FOLDERID_SyncSetupFolder = NewGUID("{0F214138-B1D3-4a90-BBA9-27CBC0C5389A}")
FOLDERID_System = NewGUID("{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}")
FOLDERID_SystemX86 = NewGUID("{D65231B0-B2F1-4857-A4CE-A8E7C6EA7D27}")
FOLDERID_Templates = NewGUID("{A63293E8-664E-48DB-A079-DF759E0509F7}")
FOLDERID_UserPinned = NewGUID("{9E3995AB-1F9C-4F13-B827-48B24B6C7174}")
FOLDERID_UserProfiles = NewGUID("{0762D272-C50A-4BB0-A382-697DCD729B80}")
FOLDERID_UserProgramFiles = NewGUID("{5CD7AEE2-2219-4A67-B85D-6C9CE15660CB}")
FOLDERID_UserProgramFilesCommon = NewGUID("{BCBD3057-CA5C-4622-B42D-BC56DB0AE516}")
FOLDERID_UsersFiles = NewGUID("{F3CE0F7C-4901-4ACC-8648-D5D44B04EF8F}")
FOLDERID_UsersLibraries = NewGUID("{A302545D-DEFF-464b-ABE8-61C8648D939B}")
FOLDERID_Videos = NewGUID("{18989B1D-99B5-455B-841C-AB7C74E4DDFC}")
FOLDERID_VideosLibrary = NewGUID("{491E922F-5643-4AF4-A7EB-4E7A138D8174}")
FOLDERID_Windows = NewGUID("{F38BF404-1D43-42F2-9305-67DE0B28FC23}")
)
var (
modshell32 = syscall.NewLazyDLL("shell32.dll")
@ -95,6 +214,7 @@ var (
procExtractIcon = modshell32.NewProc("ExtractIconW")
procGetSpecialFolderPath = modshell32.NewProc("SHGetSpecialFolderPathW")
procShellNotifyIcon = modshell32.NewProc("Shell_NotifyIconW")
procSHGetKnownFolderPath = modshell32.NewProc("SHGetKnownFolderPath")
)
func ShellNotifyIcon(cmd uintptr, nid *NOTIFYICONDATA) bool {
@ -108,6 +228,15 @@ func SHBrowseForFolder(bi *BROWSEINFO) uintptr {
return ret
}
func SHGetKnownFolderPath(rfid *GUID, dwFlags uint32, hToken HANDLE) (string, error) {
var path *uint16
ret, _, _ := procSHGetKnownFolderPath.Call(uintptr(unsafe.Pointer(rfid)), uintptr(dwFlags), hToken, uintptr(unsafe.Pointer(path)))
if ret != uintptr(windows.S_OK) {
return "", fmt.Errorf("SHGetKnownFolderPath failed: %v", ret)
}
return windows.UTF16PtrToString(path), nil
}
func SHGetPathFromIDList(idl uintptr) string {
buf := make([]uint16, 1024)
procSHGetPathFromIDList.Call(

View File

@ -337,14 +337,6 @@ type BROWSEINFO struct {
Image int32
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373931.aspx
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms221627.aspx
type VARIANT struct {
VT uint16 // 2

View File

@ -24,7 +24,7 @@ You can then call the methods from the frontend:
To use this from Go, create a new instance of the plugin, then call the methods on that:
```go
start_at_login := start_at_login.NewPlugin()
start_at_login := start_at_login.NewPlugin(Options)
start_at_login.StartAtLogin(true)
```

View File

@ -7,10 +7,19 @@ import (
type Plugin struct {
app *application.App
disabled bool
options Config
}
func NewPlugin() *Plugin {
return &Plugin{}
type Config struct {
// RegistryKey is the key in the registry to use for storing the start at login setting.
// This defaults to the name of the executable
RegistryKey string
}
func NewPlugin(options Config) *Plugin {
return &Plugin{
options: options,
}
}
// Shutdown is called when the app is shutting down

View File

@ -6,3 +6,11 @@ func (p *Plugin) init() error {
// TBD
return nil
}
func (p *Plugin) StartAtLogin(enabled bool) error {
panic("not implemented")
}
func (p *Plugin) IsStartAtLogin() (bool, error) {
panic("not implemented")
}

View File

@ -2,7 +2,87 @@
package start_at_login
import (
"fmt"
"golang.org/x/sys/windows/registry"
"os"
"path/filepath"
"strings"
)
func (p *Plugin) init() error {
// TBD
return nil
}
func (p *Plugin) getRegistryKey() (string, string, error) {
exePath, err := os.Executable()
if err != nil {
return "", "", fmt.Errorf("failed to get executable path: %s", err)
}
registryKey := p.options.RegistryKey
if p.options.RegistryKey == "" {
registryKey = strings.Split(filepath.Base(exePath), ".")[0]
}
return registryKey, exePath, nil
}
func openRegKey() (registry.Key, error) {
// Open the registry key
return registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.ALL_ACCESS)
}
func (p *Plugin) IsStartAtLogin() (bool, error) {
registryKey, exePath, err := p.getRegistryKey()
if err != nil {
return false, err
}
key, err := openRegKey()
if err != nil {
return false, err
}
defer key.Close()
// Get the registry value
value, _, err := key.GetStringValue(registryKey)
if err != nil {
return false, nil
}
return value == exePath, nil
}
func (p *Plugin) StartAtLogin(enabled bool) error {
registryKey, exePath, err := p.getRegistryKey()
if err != nil {
return err
}
if enabled {
// Open the registry key
key, err := openRegKey()
defer key.Close()
// Set the registry value
err = key.SetStringValue(registryKey, exePath)
if err != nil {
return fmt.Errorf("failed to set registry value: %s", err)
}
} else {
// Remove registry key
key, err := openRegKey()
if err != nil {
return fmt.Errorf("failed to open registry key: %s", err)
}
defer key.Close()
// Remove the registry value
err = key.DeleteValue(registryKey)
if err != nil {
return fmt.Errorf("failed to delete registry value: %s", err)
}
}
return nil
}