Skip to content

Commit

Permalink
delegates: allocate VTable in heap
Browse files Browse the repository at this point in the history
The VTable is a field all WinRT types have and it is used to store
pointers to all the class functions. This field is usually created by
WinRT, but for delegates we need to provide our own VTable instance.

We were previously using a Go struct pointer as our VTable, but Go
structs can be randomly moved or GCed by the Go runtime, and since we
are pasing the VTable to WinRT (out of the Go runtime), we were getting
random invalid memory errors.

This was addressed in a previous PR (#64) but somehow we missed this.

fixes #94
  • Loading branch information
jagobagascon committed May 9, 2024
1 parent 45c2d7a commit d40e892
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 30 deletions.
21 changes: 15 additions & 6 deletions internal/codegen/templates/delegate.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,29 @@ var releaseChannels{{.Name}} = &{{.Name | toLower}}ReleaseChannels {
}

func New{{.Name}}(iid *ole.GUID, callback {{.Name}}Callback) *{{.Name}} {
// create type instance
size := unsafe.Sizeof(*(*{{.Name}})(nil))
instPtr := kernel32.Malloc(size)
inst := (*{{.Name}})(instPtr)

// get the callbacks for the VTable
callbacks := delegate.RegisterCallbacks(instPtr, inst)

// Initialize all properties: the malloc may contain garbage
inst.RawVTable = (*interface{})(unsafe.Pointer(&{{.Name}}Vtbl{
IUnknownVtbl: ole.IUnknownVtbl{
// the VTable should also be alocated in the heap
sizeVTable := unsafe.Sizeof(*(*{{.Name}}Vtbl)(nil))
vTablePtr := kernel32.Malloc(sizeVTable)

inst.RawVTable = (*interface{})(vTablePtr)

vTable := (*{{.Name}}Vtbl)(vTablePtr)
vTable.IUnknownVtbl = ole.IUnknownVtbl{
QueryInterface: callbacks.QueryInterface,
AddRef: callbacks.AddRef,
Release: callbacks.Release,
},
Invoke: callbacks.Invoke,
}))
}
vTable.Invoke = callbacks.Invoke

// Initialize all properties: the malloc may contain garbage
inst.IID = *iid // copy contents
inst.Mutex = sync.Mutex{}
inst.refs = 0
Expand Down Expand Up @@ -119,6 +127,7 @@ func (instance *{{.Name}}) Release() uint64 {
// https://github.com/golang/go/issues/55015
releaseChannels{{.Name}}.release(instancePtr)

kernel32.Free(unsafe.Pointer(instance.RawVTable))
kernel32.Free(instancePtr)
}
return rem
Expand Down
25 changes: 17 additions & 8 deletions windows/foundation/asyncoperationcompletedhandler.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 17 additions & 8 deletions windows/foundation/deferralcompletedhandler.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 17 additions & 8 deletions windows/foundation/typedeventhandler.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d40e892

Please sign in to comment.