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

Commit d1182f5

Browse files
committed
kinda works, but blocks the main thread
0 parents  commit d1182f5

File tree

6 files changed

+209
-0
lines changed

6 files changed

+209
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.exe
2+
.vscode/
3+
.idea/

funcs.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
)
7+
8+
func must(err error) {
9+
if err != nil {
10+
panic(err)
11+
}
12+
}
13+
14+
func try(err error) {
15+
if err != nil {
16+
_, _ = fmt.Fprintf(os.Stderr, "ERR: %s\n", err)
17+
}
18+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module regwatch
2+
3+
go 1.12

main.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package main
2+
3+
/*
4+
#cgo CFLAGS: -I.
5+
#include "watch.h"
6+
*/
7+
import "C"
8+
9+
import (
10+
"fmt"
11+
"sync"
12+
"syscall"
13+
"os"
14+
"os/signal"
15+
"context"
16+
"runtime"
17+
"time"
18+
)
19+
20+
const keyPath = `SOFTWARE\SAAZOD\ManagedPosix`
21+
22+
type listener func(regPath string, event uint32) bool
23+
24+
var listeners = map[string]listener{}
25+
26+
func main() {
27+
runtime.GOMAXPROCS(runtime.NumCPU())
28+
ctx, _ := cancelHandler()
29+
wg := &sync.WaitGroup{}
30+
wg.Add(1)
31+
go func(wg *sync.WaitGroup) {
32+
time.Sleep(time.Millisecond)
33+
fmt.Printf("INFO:\tLooking for changes in '%s'...\n", keyPath)
34+
defer wg.Done();
35+
select {
36+
case <-ctx.Done():
37+
fmt.Printf("INFO:\tGot shutdown signal...\n")
38+
default:
39+
}
40+
err := RegListen(C.HKEY_LOCAL_MACHINE, keyPath, func(kp string, event uint32) bool {
41+
fmt.Printf("INFO:\t'%s' changed (%d)\n", kp, event)
42+
select {
43+
case <-ctx.Done():
44+
fmt.Printf("DEBUG:\t context is dead\n")
45+
return false
46+
default:
47+
return true
48+
}
49+
})
50+
if err != nil {
51+
fmt.Printf("ERR:\t%s", err)
52+
}
53+
}(wg)
54+
55+
fmt.Println("Waiting...")
56+
wg.Wait()
57+
fmt.Println("Goodbye")
58+
}
59+
60+
func RegListen(hKey C.HKEY, regPath string, cb listener) error {
61+
cstr := C.CString(regPath)
62+
listeners[regPath] = cb
63+
result := C.reg_listen(hKey, cstr)
64+
if result == C.ERROR_SUCCESS {
65+
return nil
66+
}
67+
68+
return syscall.Errno(result)
69+
}
70+
71+
//export reg_global_listener
72+
func reg_global_listener(cstr *C.char, dwEvent uint32) bool {
73+
keyPath := C.GoString(cstr)
74+
handler, ok := listeners[keyPath]
75+
if !ok {
76+
fmt.Printf("ERROR:\tno listener for '%s'\n", keyPath)
77+
return false;
78+
}
79+
80+
result := handler(keyPath, dwEvent)
81+
return result
82+
}
83+
84+
85+
// cancelHandler returns cancellation context and function for graceful shutdown
86+
func cancelHandler() (context.Context, context.CancelFunc) {
87+
ctx, cancelFn := context.WithCancel(context.Background())
88+
89+
signals := make(chan os.Signal, 1)
90+
signal.Notify(signals, os.Interrupt)
91+
92+
go func() {
93+
<-signals
94+
cancelFn()
95+
signal.Stop(signals)
96+
}()
97+
98+
return ctx, cancelFn
99+
}

watch.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#ifdef _WIN32
2+
#include "watch.h"
3+
#include <windows.h>
4+
#include <tchar.h>
5+
#include <stdio.h>
6+
7+
boolean reg_global_listener(char* regPath, long lEvent);
8+
9+
const DWORD dwFilter = REG_NOTIFY_CHANGE_NAME |
10+
REG_NOTIFY_CHANGE_ATTRIBUTES |
11+
REG_NOTIFY_CHANGE_LAST_SET |
12+
REG_NOTIFY_CHANGE_SECURITY;
13+
14+
typedef struct Watcher {
15+
HKEY hKey;
16+
HANDLE hEvent;
17+
} Watcher;
18+
19+
long reg_add_listener(callback cb, HKEY hMainKey, char* regPath) {
20+
HKEY hKey;
21+
22+
DEBUG_PRINT("RegOpenKeyEx '%s'\n", regPath);
23+
LONG lErrorCode = RegOpenKeyEx(hMainKey, regPath, 0, KEY_NOTIFY, &hKey);
24+
if (lErrorCode != ERROR_SUCCESS)
25+
return lErrorCode;
26+
27+
DEBUG_PRINT("CreateEvent\n");
28+
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
29+
if (hEvent == NULL)
30+
return GetLastError();
31+
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+
}
49+
50+
lErrorCode = dwEvent;
51+
break;
52+
}
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;
59+
}
60+
61+
long reg_listen(HKEY hMainKey, char* regPath) {
62+
return reg_add_listener(reg_global_listener, hMainKey, regPath);
63+
}
64+
#endif

watch.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef WATCH_H
2+
#define WATCH_H
3+
4+
#define DEBUG 3
5+
#include <windows.h>
6+
#include <stdlib.h>
7+
8+
#if defined(DEBUG) && DEBUG > 0
9+
#define DEBUG_PRINT(fmt, ...) fprintf(stderr, "DEBUG: %s:%d:%s(): " fmt, \
10+
__FILE__, __LINE__, __func__, ##__VA_ARGS__);
11+
12+
#else
13+
#define DEBUG_PRINT(fmt, ...) /* Don't do anything in release builds */
14+
#endif
15+
16+
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+
22+
#endif

0 commit comments

Comments
 (0)