如何正确初始化 COM 对象?

huangapple go评论93阅读模式
英文:

How to properly initialize a COM object?

问题

我正在尝试使用win32 API通过IDiskQuotaControl接口来获取Windows中卷的配额状态。

我遇到的问题似乎与初始化有关。请参考下面的代码。

//go:build windows && amd64

package main

import (
	"flag"
	"fmt"
	"runtime"
	"sync"
	"syscall"
	"unsafe"

	"golang.org/x/sys/windows"
)

// 基本内容开始

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

// 基本内容结束

func getVolumeQuota(wg *sync.WaitGroup, volume string) {
	defer wg.Done()

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	// 初始化COM
	r1, r2, lastErr := CoInitializeEx(
		0, // 必须为NULL
		COINIT_APARTMENTTHREADED,
	)
	defer CoUninitialize()
	fmt.Println("CoInitializeEx", r1, r2, lastErr) // 用于调试的打印结果

	// 创建COM实例
	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似乎总是失败,即使返回值是成功的
	r1, r2, lastErr = diskQuotaControl.Initialize(
		pszPath,
		0, // false => 只读
	)
	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()
}

我认为问题在于Initialize函数,因为无论我输入什么(使用-v标志),我总是得到一个成功的返回值。

结果:

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

GetDefaultQuotaLimit中的错误(2147942421 = 0x80070015)转换为"This object has not been initialized"(该对象尚未初始化)。

英文:

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"

答案1

得分: 1

IDiskQuotaControl接口的文档:

> IDiskQuotaControl接口继承自IUnknown接口。

这是错误的!IDiskQuotaControl是从IConnectionPointContainer接口派生的,而不是从IUnknown派生的。你可以在dskquota.h中看到:

DECLARE_INTERFACE_IID_(IDiskQuotaControl, IConnectionPointContainer, "7988B572-EC89-11cf-9C00-00AA00A14F56")

所以,你的虚函数表定义是错误的,你必须在Release方法和Initialize方法之间添加两个方法。

英文:

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.

huangapple
  • 本文由 发表于 2022年9月22日 05:37:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/73807191.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定