How to properly initialize a COM object? - go

I'm trying to get the quota state of a volume in Windows using the win32 api through IDiskQuotaControl interface
The problem that I got into seems to be related the initialization. Please see the code below.
//go:build windows && amd64
package main
import (
"flag"
"fmt"
"runtime"
"sync"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// START OF basic stuff
const COINIT_APARTMENTTHREADED = 0x2
const CLSCTX_INPROC_SERVER = 0x1
const CLSCTX_INPROC_HANDLER = 0x2
const CLSCTX_LOCAL_SERVER = 0x4
var (
modole32 = syscall.NewLazyDLL("ole32.dll")
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
procCoUninitialize = modole32.NewProc("CoUninitialize")
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
)
func CoInitializeEx(pvReserved uintptr, dwCoInit uint32) (r1, r2 uintptr, lastErr error) {
r1, r2, lastErr = procCoInitializeEx.Call(
uintptr(pvReserved),
uintptr(dwCoInit),
)
return
}
func CoUninitialize() (r1, r2 uintptr, lastErr error) {
r1, r2, lastErr = procCoUninitialize.Call()
return
}
func CoCreateInstance(rclsid *GUID, pUnkOuter *byte, dwClsContext uint32, riid *GUID, ppv *uintptr) (r1, r2 uintptr, lastErr error) {
r1, r2, lastErr = procCoCreateInstance.Call(
uintptr(unsafe.Pointer(rclsid)),
uintptr(unsafe.Pointer(pUnkOuter)),
uintptr(dwClsContext),
uintptr(unsafe.Pointer(riid)),
uintptr(unsafe.Pointer(ppv)),
)
return
}
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
type IDiskQuotaControl struct {
lpVtbl *IDiskQuotaControlVtbl
}
type IDiskQuotaControlVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
Initialize uintptr
SetQuotaState uintptr
GetQuotaState uintptr
SetQuotaLogFlags uintptr
GetQuotaLogFlags uintptr
SetDefaultQuotaThreshold uintptr
GetDefaultQuotaThreshold uintptr
GetDefaultQuotaThresholdText uintptr
SetDefaultQuotaLimit uintptr
GetDefaultQuotaLimit uintptr
GetDefaultQuotaLimitText uintptr
AddUserSid uintptr
AddUserName uintptr
DeleteUser uintptr
FindUserSid uintptr
FindUserName uintptr
CreateEnumUsers uintptr
CreateUserBatch uintptr
InvalidateSidNameCache uintptr
GiveUserNameResolutionPriority uintptr
ShutdownNameResolution uintptr
}
func (x *IDiskQuotaControl) AddRef() (r1, r2 uintptr, lastErr error) {
r1, r2, lastErr = syscall.SyscallN(
x.lpVtbl.AddRef,
uintptr(unsafe.Pointer(x)),
)
return
}
func (x *IDiskQuotaControl) Release() (r1, r2 uintptr, lastErr error) {
r1, r2, lastErr = syscall.SyscallN(
x.lpVtbl.Release,
uintptr(unsafe.Pointer(x)),
)
return
}
func (x *IDiskQuotaControl) Initialize(pszPath *uint16, bReadWrite int32) (r1, r2 uintptr, lastErr error) {
r1, r2, lastErr = syscall.SyscallN(
x.lpVtbl.Initialize,
uintptr(unsafe.Pointer(x)),
uintptr(unsafe.Pointer(pszPath)),
uintptr(bReadWrite),
)
return
}
func (x *IDiskQuotaControl) GetQuotaState(pdwState *uint32) (r1, r2 uintptr, lastErr error) {
r1, r2, lastErr = syscall.SyscallN(
x.lpVtbl.GetQuotaState,
uintptr(unsafe.Pointer(x)),
uintptr(unsafe.Pointer(pdwState)),
)
return
}
func (x *IDiskQuotaControl) GetDefaultQuotaLimit(pllLimit *int64) (r1, r2 uintptr, lastErr error) {
r1, r2, lastErr = syscall.SyscallN(
x.lpVtbl.GetDefaultQuotaLimit,
uintptr(unsafe.Pointer(x)),
uintptr(unsafe.Pointer(pllLimit)),
)
return
}
var CLSID_DiskQuotaControl = &GUID{0x7988b571, 0xec89, 0x11cf, [8]byte{0x9c, 0x0, 0x0, 0xaa, 0x0, 0xa1, 0x4f, 0x56}}
var IID_IDiskQuotaControl = &GUID{0x7988b572, 0xec89, 0x11cf, [8]byte{0x9c, 0x0, 0x0, 0xaa, 0x0, 0xa1, 0x4f, 0x56}}
// END OF basic stuff
func getVolumeQuota(wg *sync.WaitGroup, volume string) {
defer wg.Done()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
// Init COM
r1, r2, lastErr := CoInitializeEx(
0, // must be NULL
COINIT_APARTMENTTHREADED,
)
defer CoUninitialize()
fmt.Println("CoInitializeEx", r1, r2, lastErr) // Print results for debug
// Create a COM instance
var ppv uintptr
r1, r2, lastErr = CoCreateInstance(
CLSID_DiskQuotaControl,
nil,
CLSCTX_INPROC_SERVER,
IID_IDiskQuotaControl,
&ppv,
)
fmt.Println("CoCreateInstance", r1, r2, lastErr)
diskQuotaControl := (*IDiskQuotaControl)(unsafe.Pointer(ppv))
defer diskQuotaControl.Release()
pszPath, err := windows.UTF16PtrFromString(volume)
if err != nil {
panic(err)
}
// Initialize seems to fail even the return is a success
r1, r2, lastErr = diskQuotaControl.Initialize(
pszPath,
0, // false => read only
)
fmt.Println("Initialize", r1, r2, lastErr)
var pdwState uint32
r1, r2, lastErr = diskQuotaControl.GetQuotaState(
&pdwState,
)
fmt.Println("GetQuotaState", r1, r2, lastErr)
fmt.Println(pdwState)
var pllLimit int64
r1, r2, lastErr = diskQuotaControl.GetDefaultQuotaLimit(
&pllLimit,
)
fmt.Println("GetDefaultQuotaLimit", r1, r2, lastErr)
fmt.Println(pllLimit)
}
func main() {
volume := flag.String("v", `C:\`, "volume")
flag.Parse()
fmt.Println("Volume", *volume)
var wg sync.WaitGroup
wg.Add(1)
go getVolumeQuota(&wg, *volume)
wg.Wait()
}
The reason why I think the problem is in the Initialize, is that whatever input I put (using the flag -v) I always get a success return.
Results:
Volume C:\
CoInitializeEx 0 0 The operation completed successfully.
CoCreateInstance 0 0 The operation completed successfully.
Initialize 0 9298000 The operation completed successfully.
GetQuotaState 2147942403 9298000 The system cannot find the path specified.
0
GetDefaultQuotaLimit 2147942421 9298000 The operation completed successfully.
0
Volume \\?\C:\
CoInitializeEx 0 0 The operation completed successfully.
CoCreateInstance 0 0 The operation completed successfully.
Initialize 0 9103168 The operation completed successfully.
GetQuotaState 2147942403 9103168 The system cannot find the path specified.
0
GetDefaultQuotaLimit 2147942421 9103168 The operation completed successfully.
0
Volume Invalid:Volume://+
CoInitializeEx 0 0 The operation completed successfully.
CoCreateInstance 0 0 The operation completed successfully.
Initialize 0 8709600 The operation completed successfully.
GetQuotaState 2147942403 8709600 The system cannot find the path specified.
0
GetDefaultQuotaLimit 2147942421 8709600 The operation completed successfully.
0
The error in GetDefaultQuotaLimit (2147942421 = 0x80070015) translates to "This object has not been initialized"

The IDiskQuotaControl interface documentation:
The IDiskQuotaControl interface inherits from the IUnknown interface.
is wrong! IDiskQuotaControl derives from IConnectionPointContainer , not from IUnknown. You can see that in dskquota.h:
DECLARE_INTERFACE_IID_(IDiskQuotaControl, IConnectionPointContainer, "7988B572-EC89-11cf-9C00-00AA00A14F56")
So, your vtable definition is wrong, you must add two methods between your Release and Initialize methods.

Related

Implementation of syscall.Mmap

The source code is here
I commented on what I understand
type mmapper struct {
sync.Mutex
active map[*byte][]byte // active mappings; key is last byte in mapping
mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, error)
munmap func(addr uintptr, length uintptr) error
}
func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
if length <= 0 {
return nil, EINVAL
}
// Map the requested memory using a operating-system dependent function
addr, errno := m.mmap(0, uintptr(length), prot, flags, fd, offset)
if errno != nil {
return nil, errno
}
// Create slice memory layout
var sl = struct {
addr uintptr
len int
cap int
}{addr, length, length}
// Cast it to byte slice
b := *(*[]byte)(unsafe.Pointer(&sl))
// ??
p := &b[cap(b)-1]
m.Lock()
defer m.Unlock()
m.active[p] = b
return b, nil
}
func (m *mmapper) Munmap(data []byte) (err error) {
if len(data) == 0 || len(data) != cap(data) {
return EINVAL
}
// Check if the slice is valid ?
p := &data[cap(data)-1]
m.Lock()
defer m.Unlock()
b := m.active[p]
if b == nil || &b[0] != &data[0] {
return EINVAL
}
// Unmap the memory and update m.
if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != nil {
return errno
}
delete(m.active, p)
return nil
}
So my question is just about the field mmapper.active, I don't understand what it is for.
I have readed about some issue with uintptr and garbage collector, maybe is for avoid them ?
Or maybe it's just for validate the slice when Munmap is call ?
It might be a slice of a slice, then its backed pointer will be different from the original. This map allows us to check whether this slice was sliced.
b := []byte{1,2,3,4}
fmt.Println(&b[0]) // 0x40e020
b = b[1:]
fmt.Println(&b[0]) // 0x40e021
https://play.golang.org/p/WJogLiMOfj7

Is this because the go compiler optimized the code?

package main
import "time"
func main() {
i := 1
go func() {
for {
i++
}
}()
<-time.After(1 * time.Second)
println(i)
}
The output is always 1.
However it's absolutely that 1s is enough for the for loop to go over many many times.
I think the i in the closure is the i in the main func.
See the code below.
package main
import "time"
func main() {
i := 1
go func() {
for {
i++
println("+1")
}
}()
<-time.After(1 * time.Second)
println(i)
}
After many lines of "+1", the output is exactly a great number as expected.
The Go Memory Model
Version of May 31, 2014
Introduction
The Go memory model specifies the conditions under which reads of a
variable in one goroutine can be guaranteed to observe values produced
by writes to the same variable in a different goroutine.
Advice
Programs that modify data being simultaneously accessed by multiple
goroutines must serialize such access.
To serialize access, protect the data with channel operations or other
synchronization primitives such as those in the sync and sync/atomic
packages.
If you must read the rest of this document to understand the behavior
of your program, you are being too clever.
Don't be clever.
Synchronization
var a string
func hello() {
go func() { a = "hello" }()
print(a)
}
the assignment to a is not followed by any synchronization event, so
it is not guaranteed to be observed by any other goroutine. In fact,
an aggressive compiler might delete the entire go statement.
The assignment to i, via increment i++ (i = i + 1), is not followed by any synchronization event, so it is not guaranteed to be observed by any other goroutine. In fact, an aggressive compiler might delete the entire i++ statement.
For example,
package main
import "time"
func main() {
i := 1
go func() {
for {
i++
}
}()
<-time.After(1 * time.Millisecond)
println(i)
}
Output:
1
The goroutine reduces to:
"".main.func1 STEXT nosplit size=2 args=0x8 locals=0x0
0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), NOSPLIT, $0-8
0x0000 00000 (elide.go:7) FUNCDATA $0, gclocals·2a5305abe05176240e61b8620e19a815(SB)
0x0000 00000 (elide.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (elide.go:9) JMP 0
To the compiler,
for {
i++
}
can be implemented by incrementing a register forever, essentially a no-op for loop.
for { }
After inserting a print statement,
package main
import "time"
func main() {
i := 1
go func() {
for {
i++
println("+1")
}
}()
<-time.After(1 * time.Millisecond)
println(i)
}
Output:
+1
+1
<< SNIP >>
+1
+1
432
The goroutine expands to,
"".main.func1 STEXT size=81 args=0x8 locals=0x18
0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), $24-8
0x0000 00000 (elide.go:7) MOVQ (TLS), CX
0x0009 00009 (elide.go:7) CMPQ SP, 16(CX)
0x000d 00013 (elide.go:7) JLS 74
0x000f 00015 (elide.go:7) SUBQ $24, SP
0x0013 00019 (elide.go:7) MOVQ BP, 16(SP)
0x0018 00024 (elide.go:7) LEAQ 16(SP), BP
0x001d 00029 (elide.go:7) FUNCDATA $0, gclocals·a36216b97439c93dafebe03e7f0808b5(SB)
0x001d 00029 (elide.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (elide.go:8) MOVQ "".&i+32(SP), AX
0x0022 00034 (elide.go:9) INCQ (AX)
0x0025 00037 (elide.go:10) PCDATA $0, $0
0x0025 00037 (elide.go:10) CALL runtime.printlock(SB)
0x002a 00042 (elide.go:10) LEAQ go.string."+1\n"(SB), AX
0x0031 00049 (elide.go:10) MOVQ AX, (SP)
0x0035 00053 (elide.go:10) MOVQ $3, 8(SP)
0x003e 00062 (elide.go:10) PCDATA $0, $0
0x003e 00062 (elide.go:10) CALL runtime.printstring(SB)
0x0043 00067 (elide.go:10) PCDATA $0, $0
0x0043 00067 (elide.go:10) CALL runtime.printunlock(SB)
0x0048 00072 (elide.go:9) JMP 29
0x004a 00074 (elide.go:9) NOP
0x004a 00074 (elide.go:7) PCDATA $0, $-1
0x004a 00074 (elide.go:7) CALL runtime.morestack_noctxt(SB)
0x004f 00079 (elide.go:7) JMP 0
The increased complexity of the goroutine means that the compiler no longer considers dedicating a register to the value of i. The in-memory value of i is incremented, which makes the updates visible, with a data race, to the main goroutine.
==================
WARNING: DATA RACE
Read at 0x00c420094000 by
main goroutine:
main.main()
/home/peter/gopath/src/lucky.go:14 +0xac
Previous write at 0x00c420094000 by
goroutine 5:
main.main.func1()
/home/peter/gopath/src/lucky.go:9 +0x4e
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/lucky.go:7 +0x7a
==================
For your expected result, add some synchronization,
package main
import (
"sync"
"time"
)
func main() {
mx := new(sync.Mutex)
i := 1
go func() {
for {
mx.Lock()
i++
mx.Unlock()
}
}()
<-time.After(1 * time.Second)
mx.Lock()
println(i)
mx.Unlock()
}
Output:
41807838
Concurrent access to the variable i need to be synchronized:
synchronization is better done with channels or the facilities of the
sync package. Share memory by communicating; don't communicate by
sharing memory.
ref: https://golang.org/pkg/sync/atomic/
This is interesting, so I'm sharing my experiments:
0- Your code using time.Sleep(1 * time.Second) (not recommended-not synchronized):
package main
import "time"
func main() {
i := 1
go func() {
for {
i++
}
}()
time.Sleep(1 * time.Second)
println(i)
}
output:
1
1- Using i := new(int) (not recommended-not synchronized):
package main
import "time"
func main() {
i := new(int)
go func() {
for {
*i++
}
}()
time.Sleep(1 * time.Second)
println(*i)
}
output(CPU: i7-7700K # 4.2GHz):
772252413
2- Synchronizing using atomic.AddInt64(&i, 1)(Package atomic provides low-level atomic memory primitives useful for implementing synchronization algorithms):
package main
import (
"sync/atomic"
"time"
)
func main() {
i := int64(1)
go func() {
for {
atomic.AddInt64(&i, 1) // free running counter
}
}()
time.Sleep(1 * time.Second)
println(atomic.LoadInt64(&i)) // sampling the counter
}
output:
233008800
3- Synchronizing using chan int:
package main
import "time"
func main() {
ch := make(chan int)
go func() {
timeout := time.NewTimer(1 * time.Second)
defer timeout.Stop()
i := 1
for {
select {
case <-timeout.C:
ch <- i
return
default:
i++
}
}
}()
//time.Sleep(1 * time.Second)
println(<-ch)
}
output:
272702341
4- Synchronizing using sync.WaitGroup:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var done sync.WaitGroup
done.Add(1)
i := 1
go func() {
defer done.Done()
timeout := time.NewTimer(1 * time.Second)
defer timeout.Stop()
for {
select {
case <-timeout.C:
return
default:
i++
}
}
}()
done.Wait()
fmt.Println(i)
}
output:
261459418
5- Synchronizing using quit channel:
package main
import (
"fmt"
"time"
)
func main() {
quit := make(chan struct{})
i := 1
go func() {
for {
i++
select {
case <-quit:
return
default:
}
}
}()
time.Sleep(1 * time.Second)
quit <- struct{}{}
fmt.Println(i)
}
output:
277366276
6- Synchronizing using sync.RWMutex:
package main
import (
"sync"
"time"
)
func main() {
var i rwm
go func() {
for {
i.inc() // free running counter
}
}()
time.Sleep(1 * time.Second)
println(i.read()) // sampling the counter
}
type rwm struct {
sync.RWMutex
i int
}
func (l *rwm) inc() {
l.Lock()
defer l.Unlock()
l.i++
}
func (l *rwm) read() int {
l.RLock()
defer l.RUnlock()
return l.i
}
output:
24271318
related topics:
The Go Memory Model
There is no equivalent to volatile and register in Go
Does Go support volatile / non-volatile variables?

Casting a int to a pointer?

in C/C++ I can cast int to a int* like this
int i = 0x1040c108;
int *p = (int*)i; // compiles
but why can't I do this in Go?
addr := 0x1040c108
p := (*int)(addr) // Error: cannot convert addr (type int) to type *int
What is the way to achieve this in golang ??
Go through the unsafe package. Please note that it is called "unsafe" for a reason and probably shouldn't be used unless you REALLY need to do operations that bypass type safeties, or operate on memory directly.
https://play.golang.org/p/WUavNAlyVP
package main
import (
"fmt"
"unsafe"
)
func main() {
fmt.Println("Hello, playground")
var intaddr int = 0x1040c108
var addr uintptr = uintptr(intaddr)
ptr := unsafe.Pointer(addr)
p := (*int)(ptr)
fmt.Printf("Type: %T, Ptr: %v, Val: %d", p, p, *p)
}

convert string to uint in go lang

I am trying to convert the string to uint on 32-bit ubuntu using the following code. But it always convert it in uint64 despite explicitly passing 32 as the argument in the function. Below in the code mw is the object of the image magick library. Which returns uint when mw.getImageWidth() and mw.getImageHeight() is called. Also, it accepts the uint type argument in the resize function.
width := strings.Split(imgResize, "x")[0]
height := strings.Split(imgResize, "x")[1]
var masterWidth uint = mw.GetImageWidth()
var masterHeight uint = mw.GetImageHeight()
mw := imagick.NewMagickWand()
defer mw.Destroy()
err = mw.ReadImageBlob(img)
if err != nil {
log.Fatal(err)
}
var masterWidth uint = mw.GetImageWidth()
var masterHeight uint = mw.GetImageHeight()
wd, _ := strconv.ParseUint(width, 10, 32)
ht, _ := strconv.ParseUint(height, 10, 32)
if masterWidth < wd || masterHeight < ht {
err = mw.ResizeImage(wd, ht, imagick.FILTER_BOX, 1)
if err != nil {
panic(err)
}
}
Error is :
# command-line-arguments
test.go:94: invalid operation: masterWidth < wd (mismatched types uint and uint64)
goImageCode/test.go:94: invalid operation: masterHeight < ht (mismatched types uint and uint64)
goImageCode/test.go:100: cannot use wd (type uint64) as type uint in argument to mw.ResizeImage
goImageCode/AmazonAWS.go:100: cannot use ht (type uint64) as type uint in argument to mw.ResizeImage
Package strconv
func ParseUint
func ParseUint(s string, base int, bitSize int) (n uint64, err error)
ParseUint is like ParseInt but for unsigned numbers.
func ParseInt
func ParseInt(s string, base int, bitSize int) (i int64, err error)
ParseInt interprets a string s in the given base (2 to 36) and returns
the corresponding value i. If base == 0, the base is implied by the
string's prefix: base 16 for "0x", base 8 for "0", and base 10
otherwise.
The bitSize argument specifies the integer type that the result must
fit into. Bit sizes 0, 8, 16, 32, and 64 correspond to int, int8,
int16, int32, and int64.
The errors that ParseInt returns have concrete type *NumError and
include err.Num = s. If s is empty or contains invalid digits, err.Err
= ErrSyntax and the returned value is 0; if the value corresponding to s cannot be represented by a signed integer of the given size, err.Err
= ErrRange and the returned value is the maximum magnitude integer of the appropriate bitSize and sign.
The bitSize argument specifies the integer type that the result must
fit into. The uint type size is implementation defined, either 32 or 64 bits. The ParseUint return type is always uint64. For example,
package main
import (
"fmt"
"strconv"
)
func main() {
width := "42"
u64, err := strconv.ParseUint(width, 10, 32)
if err != nil {
fmt.Println(err)
}
wd := uint(u64)
fmt.Println(wd)
}
Output:
42
Here is short way to convert string to uint.
import (
"strconv"
)
...
func StringToUint(s string) uint {
i, _ := strconv.Atoi(s)
return uint(i)
}

Byte slice manipulation

I'm writing a program that should parse and reply to network packets but I'm a bit annoyed because I can't do simple C style return (int)buffer[at]; with an array of bytes. Is there any better way to retrieve 4 bytes from byte[] as int32 than the following?
func (packet *Packet) GetInt32(at int) int32 {
return int32(packet.buffer[at]) << 24 +
int32(packet.buffer[at+1]) << 16 +
int32(packet.buffer[at+2]) << 8 +
int32(packet.buffer[at+3])
}
It works correctly but I was thinking if there was a better way to do this.
package main
import (
"encoding/binary"
"fmt"
"math"
)
type Packet struct {
buffer []byte
}
func (p *Packet) Int32(i int) int32 {
return int32(binary.BigEndian.Uint32(p.buffer[i : i+4]))
}
func (p *Packet) Float32(i int) float32 {
return math.Float32frombits(binary.BigEndian.Uint32(p.buffer[i : i+4]))
}
func main() {
p := &Packet{buffer: []byte{0x01, 0x02, 0x00, 0x00, 0xFF, 0xFF, 0x07}}
fmt.Println(p.Int32(2), p.Float32(2))
}
Output: 65535 9.1834e-41

Resources