icns

Easily create .icns files (Mac Icons) with this Go library or the included CLI.
Log | Files | Refs | LICENSE

main.go (2747B)


      1 //go:build windows
      2 
      3 // Command shell-extension builds a Windows shell extension DLL that lets
      4 // Explorer render thumbnails for .icns files.
      5 //
      6 // The DLL is an in-process COM server implemented entirely in Go. cgo is used
      7 // only to export the four entry points COM requires from any such server; the
      8 // COM objects themselves live in the internal/provider and internal/com
      9 // packages and contain no C.
     10 //
     11 // Build and register (per-user, no elevation needed):
     12 //
     13 //	go build -buildmode=c-shared -o icns-shellext.dll .
     14 //	regsvr32 icns-shellext.dll
     15 //
     16 // Unregister with `regsvr32 /u icns-shellext.dll`.
     17 package main
     18 
     19 import "C"
     20 
     21 import (
     22 	"unsafe"
     23 
     24 	"github.com/jackmordaunt/icns/cmd/shell-extension/internal/com"
     25 	"github.com/jackmordaunt/icns/cmd/shell-extension/internal/provider"
     26 )
     27 
     28 // main is required by -buildmode=c-shared but never runs.
     29 func main() {}
     30 
     31 // factory builds thumbnail providers for the shell.
     32 var factory = com.NewClassFactory(provider.Create)
     33 
     34 // DllGetClassObject hands out the class factory for our CLSID.
     35 //
     36 //export DllGetClassObject
     37 func DllGetClassObject(rclsid, riid unsafe.Pointer, ppv *unsafe.Pointer) uint32 {
     38 	return uint32(com.Guard("DllGetClassObject", func() com.HRESULT {
     39 		if ppv == nil {
     40 			return com.E_POINTER
     41 		}
     42 		*ppv = nil
     43 		if !com.IsEqualGUID((*com.GUID)(rclsid), provider.CLSID) {
     44 			return com.CLASS_E_CLASSNOTAVAILABLE
     45 		}
     46 		return factory.QueryInterface((*com.GUID)(riid), ppv)
     47 	}))
     48 }
     49 
     50 // DllCanUnloadNow always refuses: a Go runtime cannot be torn down and
     51 // unloaded from a host process, so the DLL must stay resident until the host
     52 // exits. Thumbnail providers run in a dedicated surrogate process that exits
     53 // when idle, so this costs nothing in Explorer itself.
     54 //
     55 //export DllCanUnloadNow
     56 func DllCanUnloadNow() uint32 {
     57 	return uint32(com.S_FALSE)
     58 }
     59 
     60 // DllRegisterServer registers the provider for the current user.
     61 //
     62 //export DllRegisterServer
     63 func DllRegisterServer() uint32 {
     64 	return uint32(com.Guard("DllRegisterServer", func() com.HRESULT {
     65 		if err := register(); err != nil {
     66 			logError("registering", err)
     67 			return com.E_FAIL
     68 		}
     69 		return com.S_OK
     70 	}))
     71 }
     72 
     73 // DllUnregisterServer removes the current user's registration.
     74 //
     75 //export DllUnregisterServer
     76 func DllUnregisterServer() uint32 {
     77 	return uint32(com.Guard("DllUnregisterServer", func() com.HRESULT {
     78 		if err := unregister(); err != nil {
     79 			logError("unregistering", err)
     80 			return com.E_FAIL
     81 		}
     82 		return com.S_OK
     83 	}))
     84 }
     85 
     86 // DllInstall supports `regsvr32 /n /i[:cmdline]`. Registration is always
     87 // per-user, so the command line is ignored.
     88 //
     89 //export DllInstall
     90 func DllInstall(install int32, cmdline unsafe.Pointer) uint32 {
     91 	if install != 0 {
     92 		return DllRegisterServer()
     93 	}
     94 	return DllUnregisterServer()
     95 }