Files
2024-11-01 17:43:06 +00:00

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)
}