90 lines
2.3 KiB
Go
90 lines
2.3 KiB
Go
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build windows
|
|
|
|
package com
|
|
|
|
import (
|
|
"fmt"
|
|
"unsafe"
|
|
)
|
|
|
|
// GenericObject is a struct that wraps any interface that implements the COM ABI.
|
|
type GenericObject[A ABI] struct {
|
|
Pp **A
|
|
}
|
|
|
|
func (o GenericObject[A]) pp() **A {
|
|
return o.Pp
|
|
}
|
|
|
|
// Object is the interface that all garbage-collected instances of COM interfaces
|
|
// must implement.
|
|
type Object interface {
|
|
// IID returns the interface ID for the object. This method may be called
|
|
// on Objects containing the zero value, so its return value must not depend
|
|
// on the value of the method's receiver.
|
|
IID() *IID
|
|
|
|
// Make converts r to an instance of a garbage-collected COM object. The type
|
|
// of its return value must always match the type of the method's receiver.
|
|
Make(r ABIReceiver) any
|
|
}
|
|
|
|
// EmbedsGenericObject is a type constraint matching any struct that embeds
|
|
// a GenericObject[A].
|
|
type EmbedsGenericObject[A ABI] interface {
|
|
Object
|
|
~struct{ GenericObject[A] }
|
|
pp() **A
|
|
}
|
|
|
|
// As casts obj to an object of type O, or panics if obj cannot be converted to O.
|
|
func As[O Object, A ABI, PU PUnknown[A], E EmbedsGenericObject[A]](obj E) O {
|
|
o, err := TryAs[O, A, PU](obj)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("wingoes.com.As error: %v", err))
|
|
}
|
|
return o
|
|
}
|
|
|
|
// TryAs casts obj to an object of type O, or returns an error if obj cannot be
|
|
// converted to O.
|
|
func TryAs[O Object, A ABI, PU PUnknown[A], E EmbedsGenericObject[A]](obj E) (O, error) {
|
|
var o O
|
|
|
|
iid := o.IID()
|
|
p := (PU)(unsafe.Pointer(*(obj.pp())))
|
|
|
|
i, err := p.QueryInterface(iid)
|
|
if err != nil {
|
|
return o, err
|
|
}
|
|
|
|
r := NewABIReceiver()
|
|
*r = i.(*IUnknownABI)
|
|
|
|
return o.Make(r).(O), nil
|
|
}
|
|
|
|
// IsSameObject returns true when both l and r refer to the same underlying object.
|
|
func IsSameObject[AL, AR ABI, PL PUnknown[AL], PR PUnknown[AR], EL EmbedsGenericObject[AL], ER EmbedsGenericObject[AR]](l EL, r ER) bool {
|
|
pl := (PL)(unsafe.Pointer(*(l.pp())))
|
|
ul, err := pl.QueryInterface(IID_IUnknown)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
defer ul.Release()
|
|
|
|
pr := (PR)(unsafe.Pointer(*(r.pp())))
|
|
ur, err := pr.QueryInterface(IID_IUnknown)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
defer ur.Release()
|
|
|
|
return ul.(*IUnknownABI) == ur.(*IUnknownABI)
|
|
}
|