Skip to content
This repository was archived by the owner on Feb 11, 2025. It is now read-only.

Commit 932d6b9

Browse files
authored
Refactor (#1)
* refactor * tidy go mod * Add stubs * Update readme
1 parent d1182f5 commit 932d6b9

File tree

11 files changed

+276
-161
lines changed

11 files changed

+276
-161
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# go-regwatch
2+
3+
Package regwatch provides a tiny wrapper that allows to track registry key changes
4+
in Windows operating system.
5+
6+
This library wraps `RegNotifyChangeKeyValue` Windows syscall.
7+
8+
## Usage
9+
10+
See `example_test.go`

example_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package regwatch
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
"os"
7+
"os/signal"
8+
"context"
9+
"runtime"
10+
"testing"
11+
"time"
12+
)
13+
14+
const keyPath = `SOFTWARE\SAAZOD\ManagedPosix`
15+
16+
func TestExample(t *testing.T) {
17+
runtime.LockOSThread()
18+
runtime.GOMAXPROCS(runtime.NumCPU())
19+
ctx, cancelFn := cancelHandler()
20+
wg := &sync.WaitGroup{}
21+
wg.Add(2)
22+
w, err := NewWatcher(HKeyLocalMachine, keyPath, 1000)
23+
must(err)
24+
25+
go func() {
26+
<-time.After(5 * time.Second)
27+
fmt.Printf("INFO:\tstopping...\n")
28+
cancelFn()
29+
os.Exit(1)
30+
}()
31+
32+
updates := make(chan string)
33+
go func() {
34+
fmt.Printf("INFO:\tconsumer: start...\n")
35+
for {
36+
kp, ok := <-updates
37+
if !ok {
38+
fmt.Printf("INFO:\tconsumer: shutdown...\n")
39+
return
40+
}
41+
42+
fmt.Printf("INFO:\t consumer: Received update msg - '%s'\n", kp)
43+
}
44+
}()
45+
46+
go func() {
47+
fmt.Printf("INFO:\tLooking for changes in '%s'...\n", keyPath)
48+
defer func() {
49+
if err := w.Destroy(); err != nil {
50+
fmt.Printf("ERROR:\tWatcher.Destroy - %s\n", err)
51+
}
52+
53+
close(updates)
54+
wg.Done();
55+
}()
56+
57+
for {
58+
select {
59+
case <-ctx.Done():
60+
fmt.Printf("INFO:\tGot shutdown signal...\n")
61+
return
62+
default:
63+
}
64+
65+
changed, err := w.Await()
66+
if err != nil {
67+
fmt.Printf("ERROR:\tWatcher.Await - %s\n", err)
68+
return
69+
}
70+
71+
if !changed {
72+
continue
73+
}
74+
75+
fmt.Printf("INFO:\t'%s' changed\n", keyPath)
76+
updates <- keyPath
77+
}
78+
}()
79+
80+
81+
fmt.Println("Waiting...")
82+
wg.Wait()
83+
fmt.Println("Goodbye")
84+
}
85+
86+
// cancelHandler returns cancellation context and function for graceful shutdown
87+
func cancelHandler() (context.Context, context.CancelFunc) {
88+
ctx, cancelFn := context.WithCancel(context.Background())
89+
90+
signals := make(chan os.Signal, 1)
91+
signal.Notify(signals, os.Interrupt)
92+
93+
go func() {
94+
<-signals
95+
cancelFn()
96+
signal.Stop(signals)
97+
}()
98+
99+
return ctx, cancelFn
100+
}
101+
102+
func must(err error) {
103+
if err != nil {
104+
panic(err)
105+
}
106+
}
107+
108+
func try(err error) {
109+
if err != nil {
110+
_, _ = fmt.Fprintf(os.Stderr, "ERR: %s\n", err)
111+
}
112+
}

funcs.go

Lines changed: 0 additions & 18 deletions
This file was deleted.

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
module regwatch
1+
module github.com/x1unix/go-regwatch
22

3-
go 1.12
3+
go 1.11

go.sum

Whitespace-only changes.

main.go

Lines changed: 0 additions & 99 deletions
This file was deleted.

watch.c

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,61 +4,60 @@
44
#include <tchar.h>
55
#include <stdio.h>
66

7-
boolean reg_global_listener(char* regPath, long lEvent);
8-
97
const DWORD dwFilter = REG_NOTIFY_CHANGE_NAME |
108
REG_NOTIFY_CHANGE_ATTRIBUTES |
119
REG_NOTIFY_CHANGE_LAST_SET |
1210
REG_NOTIFY_CHANGE_SECURITY;
1311

14-
typedef struct Watcher {
15-
HKEY hKey;
16-
HANDLE hEvent;
17-
} Watcher;
1812

19-
long reg_add_listener(callback cb, HKEY hMainKey, char* regPath) {
13+
#define HANDLE_ERR(val) if (val != ERROR_SUCCESS) return val
14+
15+
long watcher_create(void* hive, char* regPath, Watcher* out) {
16+
HKEY hMainKey = (HKEY) hive;
2017
HKEY hKey;
2118

2219
DEBUG_PRINT("RegOpenKeyEx '%s'\n", regPath);
2320
LONG lErrorCode = RegOpenKeyEx(hMainKey, regPath, 0, KEY_NOTIFY, &hKey);
24-
if (lErrorCode != ERROR_SUCCESS)
25-
return lErrorCode;
21+
HANDLE_ERR(lErrorCode);
2622

2723
DEBUG_PRINT("CreateEvent\n");
2824
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
2925
if (hEvent == NULL)
3026
return GetLastError();
3127

32-
while(TRUE) {
33-
DEBUG_PRINT("RegNotifyChangeKeyValue\n");
34-
lErrorCode = RegNotifyChangeKeyValue(hKey, TRUE, dwFilter, hEvent, TRUE);
35-
if(lErrorCode != ERROR_SUCCESS) break;
36-
37-
DEBUG_PRINT("WaitForSingleObject\n");
38-
DWORD dwEvent = WaitForSingleObject(hEvent, INFINITE);
39-
ResetEvent(hEvent);
40-
if (dwEvent == WAIT_OBJECT_0) {
41-
boolean result = cb(regPath, dwEvent);
42-
DEBUG_PRINT("Callback returned %d\n", result);
43-
if (!result) {
44-
DEBUG_PRINT("break\n");
45-
break;
46-
}
47-
continue;
48-
}
28+
out->hKey = hKey;
29+
out->hEvent = hEvent;
30+
return 0;
31+
}
4932

50-
lErrorCode = dwEvent;
51-
break;
33+
long watcher_await(Watcher* out, long timeout, boolean* changed) {
34+
LONG lErrorCode;
35+
DEBUG_PRINT("RegNotifyChangeKeyValue\n");
36+
lErrorCode = RegNotifyChangeKeyValue(out->hKey, TRUE, dwFilter, out->hEvent, TRUE);
37+
HANDLE_ERR(lErrorCode);
38+
DEBUG_PRINT("WaitForSingleObject\n");
39+
DWORD dwEvent = WaitForSingleObject(out->hEvent, timeout);
40+
ResetEvent(out->hEvent);
41+
switch (dwEvent) {
42+
case WAIT_TIMEOUT:
43+
*changed = FALSE;
44+
break;
45+
case WAIT_OBJECT_0:
46+
*changed = TRUE;
47+
break;
48+
default:
49+
return dwEvent;
5250
}
53-
54-
DEBUG_PRINT("Close watcher\n");
55-
RegCloseKey(hKey);
56-
CloseHandle(hEvent);
57-
free(regPath); // Allocated in heap by C.GoString()
58-
return lErrorCode;
51+
52+
return 0;
5953
}
6054

61-
long reg_listen(HKEY hMainKey, char* regPath) {
62-
return reg_add_listener(reg_global_listener, hMainKey, regPath);
55+
long watcher_close(Watcher* out) {
56+
DEBUG_PRINT("RegCloseKey\n");
57+
LONG lErrorCode = RegCloseKey(out->hKey);
58+
HANDLE_ERR(lErrorCode);
59+
DEBUG_PRINT("CloseHandle\n");
60+
CloseHandle(out->hEvent);
6361
}
62+
6463
#endif

watch.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#ifndef WATCH_H
22
#define WATCH_H
33

4-
#define DEBUG 3
54
#include <windows.h>
65
#include <stdlib.h>
76

@@ -13,10 +12,12 @@
1312
#define DEBUG_PRINT(fmt, ...) /* Don't do anything in release builds */
1413
#endif
1514

15+
typedef struct Watcher {
16+
HKEY hKey;
17+
HANDLE hEvent;
18+
} Watcher;
1619

17-
typedef boolean (*callback)(char*, long);
18-
19-
long reg_add_listener(callback cb, HKEY hMainKey, char* regPath);
20-
long reg_listen(HKEY hMainKey, char* regPath);
21-
20+
long watcher_create(void* hive, char* regPath, Watcher* out);
21+
long watcher_await(Watcher* out, long timeout, boolean* changed);
22+
long watcher_close(Watcher* out);
2223
#endif

watcher.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Package regwatch provides a tiny wrapper that allows to track registry key changes
2+
// in Windows operating system.
3+
//
4+
// See "example_test.go" for usage example.
5+
package regwatch
6+
7+
// Timeout represents event timeout in milliseconds
8+
type Timeout uint32
9+
10+
11+
// Watcher tracks registry changes
12+
type Watcher interface {
13+
// Destroy stops
14+
Destroy() error
15+
16+
// Await waits until key change event occurs
17+
Await() (bool, error)
18+
}

0 commit comments

Comments
 (0)