Files
windows_exporter/internal/ole/ole.go
T
2026-10-08 19:15:23 +02:00

118 lines
3.3 KiB
Go

// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package ole provides native COM initialization, interface calls, strings,
// variants, and iteration shared by the taskschd and wuapi bindings. Native
// interfaces and strings have explicit ownership.
package ole
import (
"fmt"
"iter"
"math"
"time"
)
// HRESULT preserves the 32-bit COM status code, including when wrapped.
type HRESULT uint32
func (h HRESULT) Error() string { return formatHRESULT(h) }
// ResultError returns an error only for failed HRESULTs.
func ResultError(result uintptr) error {
if int32(result) < 0 {
return HRESULT(uint32(result))
}
return nil
}
// DATE is an OLE Automation date: days since 1899-12-30, with the absolute
// fractional part representing the time of day, including for negative dates.
type DATE float64
func (d DATE) Time() (time.Time, error) {
value := float64(d)
if math.IsNaN(value) || math.IsInf(value, 0) || value <= -657435 || value >= 2958466 {
return time.Time{}, fmt.Errorf("invalid OLE date: %g", value)
}
days, fraction := math.Modf(value)
date := time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC).AddDate(0, 0, int(days))
millis := int64(math.Round(math.Abs(fraction) * 86400000))
date = date.Add(time.Duration(millis) * time.Millisecond)
if date.Year() > 9999 {
return time.Time{}, fmt.Errorf("invalid OLE date: %g", value)
}
return date, nil
}
// CollectionItemError identifies an unreadable index within a COM collection.
// The iterator can still read later indices; a count failure is not an item error.
type CollectionItemError struct {
Index int32
Err error
}
func (e *CollectionItemError) Error() string {
return fmt.Sprintf("get collection item %d: %v", e.Index, e.Err)
}
func (e *CollectionItemError) Unwrap() error { return e.Err }
// BorrowedItems releases each item even when yield stops early or panics. Items
// are valid only during yield; callers must not retain them or release them.
// Item errors are yielded as CollectionItemError, and iteration continues if
// yield returns true. Count errors stop iteration because there are no indices.
func BorrowedItems[T any](count func() (int32, error), item func(int32) (T, error), release func(T)) iter.Seq2[T, error] {
return func(yield func(T, error) bool) {
var zero T
n, err := count()
if err != nil {
yield(zero, fmt.Errorf("get collection count: %w", err))
return
}
if n < 0 {
yield(zero, fmt.Errorf("invalid collection count: %d", n))
return
}
for i := range n {
value, err := item(i)
if err != nil {
if !yield(zero, &CollectionItemError{Index: i, Err: err}) {
return
}
continue
}
if !func() bool {
defer release(value)
return yield(value, nil)
}() {
return
}
}
}
}