Skip to content
This repository has been archived by the owner on Jun 17, 2022. It is now read-only.

Commit

Permalink
Listen for printer changes and refresh the printers list when it happ…
Browse files Browse the repository at this point in the history
…ens (#361)

* Listen for printer changes and refresh the printers list when such happen.

This reduces the delay after new printer is added until it shows up
as a printer avaiable for printing in Goolge Cloud Print on Windows.

Timer based refreshes still need to happen due to other sources of
printers eg Privet etc.

* Adjust the code to the commited version of golang.org/x/sys/windows/svc.
  • Loading branch information
pastarmovj authored and jay0lee committed May 26, 2017
1 parent cd1943f commit 7f96e58
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 34 deletions.
19 changes: 19 additions & 0 deletions gcp-windows-connector/gcp-windows-connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ func (service *service) Execute(args []string, r <-chan svc.ChangeRequest, s cha
}
defer pm.Quit()

statusHandle := svc.StatusHandle()
if statusHandle != 0 {
err = ws.StartPrinterNotifications(statusHandle)
if err != nil {
log.Error(err)
} else {
log.Info("Successfully registered for device notifications.")
}
}

if config.CloudPrintingEnable {
if config.LocalPrintingEnable {
log.Infof("Ready to rock as proxy '%s' and in local mode", config.ProxyName)
Expand Down Expand Up @@ -198,6 +208,15 @@ func (service *service) Execute(args []string, r <-chan svc.ChangeRequest, s cha

return false, 0

case svc.DeviceEvent:
log.Infof("Printers change notification received %d.", request.EventType)
// Delay the action to let the OS finish the process or we might
// not see the new printer. Even if we miss it eventually the timed updates
// will pick it up.
time.AfterFunc(time.Second*5, func() {
pm.SyncPrinters(false)
})

default:
log.Errorf("Received unsupported service command from service control manager: %d", request.Cmd)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func FilterBlacklistPrinters(printers []Printer, list map[string]interface{}) []

func FilterWhitelistPrinters(printers []Printer, list map[string]interface{}) []Printer {
if len(list) == 0 {
return printers; // Empty whitelist means don't use whitelist
return printers // Empty whitelist means don't use whitelist
}

return filterPrinters(printers, list, true)
Expand Down
14 changes: 7 additions & 7 deletions lib/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@ https://developers.google.com/open-source/licenses/bsd
package lib

import (
"testing"
"reflect"
"testing"
)

func TestFilterBlacklistPrinters(t *testing.T) {
printers := []Printer {
printers := []Printer{
{Name: "Stay1"},
{Name: "Go1"},
{Name: "Go2"},
{Name: "Stay2"},
}
blacklist := map[string]interface{} {
blacklist := map[string]interface{}{
"Go1": "",
"Go2": "",
}
correctFilteredPrinters := []Printer {
correctFilteredPrinters := []Printer{
{Name: "Stay1"},
{Name: "Stay2"},
}
Expand All @@ -37,17 +37,17 @@ func TestFilterBlacklistPrinters(t *testing.T) {
}

func TestFilterWhitelistPrinters(t *testing.T) {
printers := []Printer {
printers := []Printer{
{Name: "Stay1"},
{Name: "Go1"},
{Name: "Go2"},
{Name: "Stay2"},
}
whitelist := map[string]interface{} {
whitelist := map[string]interface{}{
"Stay1": "",
"Stay2": "",
}
correctFilteredPrinters := []Printer {
correctFilteredPrinters := []Printer{
{Name: "Stay1"},
{Name: "Stay2"},
}
Expand Down
8 changes: 4 additions & 4 deletions manager/printermanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func NewPrinterManager(native NativePrintSystem, gcp *gcp.GoogleCloudPrint, priv
// Sync once before returning, to make sure things are working.
// Ignore privet updates this first time because Privet always starts
// with zero printers.
if err = pm.syncPrinters(true); err != nil {
if err = pm.SyncPrinters(true); err != nil {
return nil, err
}

Expand Down Expand Up @@ -146,7 +146,7 @@ func (pm *PrinterManager) syncPrintersPeriodically(interval time.Duration) {
for {
select {
case <-t.C:
if err := pm.syncPrinters(false); err != nil {
if err := pm.SyncPrinters(false); err != nil {
log.Error(err)
}
t.Reset(interval)
Expand All @@ -158,7 +158,7 @@ func (pm *PrinterManager) syncPrintersPeriodically(interval time.Duration) {
}()
}

func (pm *PrinterManager) syncPrinters(ignorePrivet bool) error {
func (pm *PrinterManager) SyncPrinters(ignorePrivet bool) error {
log.Info("Synchronizing printers, stand by")

// Get current snapshot of native printers.
Expand Down Expand Up @@ -442,7 +442,7 @@ func (pm *PrinterManager) printJob(nativePrinterName, filename, title, user, job
}
}

func (pm *PrinterManager)releaseJob(printerName string, nativeJobID uint32, jobID string) {
func (pm *PrinterManager) releaseJob(printerName string, nativeJobID uint32, jobID string) {
if err := pm.native.ReleaseJob(printerName, nativeJobID); err != nil {
log.ErrorJob(jobID, err)
}
Expand Down
94 changes: 72 additions & 22 deletions winspool/win32.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,37 @@ import (
"strings"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

var (
gdi32 = syscall.MustLoadDLL("gdi32.dll")
kernel32 = syscall.MustLoadDLL("kernel32.dll")
ntoskrnl = syscall.MustLoadDLL("ntoskrnl.exe")
winspool = syscall.MustLoadDLL("winspool.drv")

abortDocProc = gdi32.MustFindProc("AbortDoc")
closePrinterProc = winspool.MustFindProc("ClosePrinter")
createDCProc = gdi32.MustFindProc("CreateDCW")
deleteDCProc = gdi32.MustFindProc("DeleteDC")
deviceCapabilitiesProc = winspool.MustFindProc("DeviceCapabilitiesW")
documentPropertiesProc = winspool.MustFindProc("DocumentPropertiesW")
endDocProc = gdi32.MustFindProc("EndDoc")
endPageProc = gdi32.MustFindProc("EndPage")
enumPrintersProc = winspool.MustFindProc("EnumPrintersW")
getDeviceCapsProc = gdi32.MustFindProc("GetDeviceCaps")
getJobProc = winspool.MustFindProc("GetJobW")
openPrinterProc = winspool.MustFindProc("OpenPrinterW")
resetDCProc = gdi32.MustFindProc("ResetDCW")
rtlGetVersionProc = ntoskrnl.MustFindProc("RtlGetVersion")
setGraphicsModeProc = gdi32.MustFindProc("SetGraphicsMode")
setJobProc = winspool.MustFindProc("SetJobW")
setWorldTransformProc = gdi32.MustFindProc("SetWorldTransform")
startDocProc = gdi32.MustFindProc("StartDocW")
startPageProc = gdi32.MustFindProc("StartPage")
user32 = syscall.MustLoadDLL("user32.dll")

abortDocProc = gdi32.MustFindProc("AbortDoc")
closePrinterProc = winspool.MustFindProc("ClosePrinter")
createDCProc = gdi32.MustFindProc("CreateDCW")
deleteDCProc = gdi32.MustFindProc("DeleteDC")
deviceCapabilitiesProc = winspool.MustFindProc("DeviceCapabilitiesW")
documentPropertiesProc = winspool.MustFindProc("DocumentPropertiesW")
endDocProc = gdi32.MustFindProc("EndDoc")
endPageProc = gdi32.MustFindProc("EndPage")
enumPrintersProc = winspool.MustFindProc("EnumPrintersW")
getDeviceCapsProc = gdi32.MustFindProc("GetDeviceCaps")
getJobProc = winspool.MustFindProc("GetJobW")
openPrinterProc = winspool.MustFindProc("OpenPrinterW")
resetDCProc = gdi32.MustFindProc("ResetDCW")
rtlGetVersionProc = ntoskrnl.MustFindProc("RtlGetVersion")
setGraphicsModeProc = gdi32.MustFindProc("SetGraphicsMode")
setJobProc = winspool.MustFindProc("SetJobW")
setWorldTransformProc = gdi32.MustFindProc("SetWorldTransform")
startDocProc = gdi32.MustFindProc("StartDocW")
startPageProc = gdi32.MustFindProc("StartPage")
registerDeviceNotificationProc = user32.MustFindProc("RegisterDeviceNotificationW")
)

// System error codes.
Expand Down Expand Up @@ -798,8 +802,8 @@ func (hPrinter HANDLE) SetJobUserName(jobID int32, userName string) error {
return err
}

ji1.pUserName = pUserName;
ji1.position = 0; // To prevent a possible access denied error (0 is JOB_POSITION_UNSPECIFIED)
ji1.pUserName = pUserName
ji1.position = 0 // To prevent a possible access denied error (0 is JOB_POSITION_UNSPECIFIED)
err = hPrinter.SetJobInfo1(jobID, ji1)
if err != nil {
return err
Expand Down Expand Up @@ -1196,3 +1200,49 @@ func GetWindowsVersion() string {

return version
}

type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}

var PRINTERS_DEVICE_CLASS = GUID{
0x4d36e979,
0xe325,
0x11ce,
[8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18},
}

type DevBroadcastDevinterface struct {
dwSize uint32
dwDeviceType uint32
dwReserved uint32
classGuid GUID
szName uint16
}

const (
DEVICE_NOTIFY_SERVICE_HANDLE = 1
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 4

DBT_DEVTYP_DEVICEINTERFACE = 5
)

func RegisterDeviceNotification(handle windows.Handle) error {

var notificationFilter DevBroadcastDevinterface
notificationFilter.dwSize = uint32(unsafe.Sizeof(notificationFilter))
notificationFilter.dwDeviceType = DBT_DEVTYP_DEVICEINTERFACE
notificationFilter.dwReserved = 0
// BUG(pastarmovj): This class is ignored for now. Figure out what the right GUID is.
notificationFilter.classGuid = PRINTERS_DEVICE_CLASS
notificationFilter.szName = 0

r1, _, err := registerDeviceNotificationProc.Call(uintptr(handle), uintptr(unsafe.Pointer(&notificationFilter)), DEVICE_NOTIFY_SERVICE_HANDLE|DEVICE_NOTIFY_ALL_INTERFACE_CLASSES)
if r1 == 0 {
return err
}
return nil
}
6 changes: 6 additions & 0 deletions winspool/winspool.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"github.com/google/cloud-print-connector/cdd"
"github.com/google/cloud-print-connector/lib"
"golang.org/x/sys/windows"
)

// winspoolPDS represents capabilities that WinSpool always provides.
Expand Down Expand Up @@ -918,6 +919,11 @@ func (ws *WinSpool) ReleaseJob(printerName string, jobID uint32) error {
return nil
}

func (ws *WinSpool) StartPrinterNotifications(handle windows.Handle) error {
err := RegisterDeviceNotification(handle)
return err
}

// The following functions are not relevant to Windows printing, but are required by the NativePrintSystem interface.

func (ws *WinSpool) RemoveCachedPPD(printerName string) {}

0 comments on commit 7f96e58

Please sign in to comment.