如何正确初始化 COM 对象?

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

How to properly initialize a COM object?

问题

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

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

  1. //go:build windows && amd64
  2. package main
  3. import (
  4. "flag"
  5. "fmt"
  6. "runtime"
  7. "sync"
  8. "syscall"
  9. "unsafe"
  10. "golang.org/x/sys/windows"
  11. )
  12. // 基本内容开始
  13. const COINIT_APARTMENTTHREADED = 0x2
  14. const CLSCTX_INPROC_SERVER = 0x1
  15. const CLSCTX_INPROC_HANDLER = 0x2
  16. const CLSCTX_LOCAL_SERVER = 0x4
  17. var (
  18. modole32 = syscall.NewLazyDLL("ole32.dll")
  19. procCoInitializeEx = modole32.NewProc("CoInitializeEx")
  20. procCoUninitialize = modole32.NewProc("CoUninitialize")
  21. procCoCreateInstance = modole32.NewProc("CoCreateInstance")
  22. )
  23. func CoInitializeEx(pvReserved uintptr, dwCoInit uint32) (r1, r2 uintptr, lastErr error) {
  24. r1, r2, lastErr = procCoInitializeEx.Call(
  25. uintptr(pvReserved),
  26. uintptr(dwCoInit),
  27. )
  28. return
  29. }
  30. func CoUninitialize() (r1, r2 uintptr, lastErr error) {
  31. r1, r2, lastErr = procCoUninitialize.Call()
  32. return
  33. }
  34. func CoCreateInstance(rclsid *GUID, pUnkOuter *byte, dwClsContext uint32, riid *GUID, ppv *uintptr) (r1, r2 uintptr, lastErr error) {
  35. r1, r2, lastErr = procCoCreateInstance.Call(
  36. uintptr(unsafe.Pointer(rclsid)),
  37. uintptr(unsafe.Pointer(pUnkOuter)),
  38. uintptr(dwClsContext),
  39. uintptr(unsafe.Pointer(riid)),
  40. uintptr(unsafe.Pointer(ppv)),
  41. )
  42. return
  43. }
  44. type GUID struct {
  45. Data1 uint32
  46. Data2 uint16
  47. Data3 uint16
  48. Data4 [8]byte
  49. }
  50. type IDiskQuotaControl struct {
  51. lpVtbl *IDiskQuotaControlVtbl
  52. }
  53. type IDiskQuotaControlVtbl struct {
  54. QueryInterface uintptr
  55. AddRef uintptr
  56. Release uintptr
  57. Initialize uintptr
  58. SetQuotaState uintptr
  59. GetQuotaState uintptr
  60. SetQuotaLogFlags uintptr
  61. GetQuotaLogFlags uintptr
  62. SetDefaultQuotaThreshold uintptr
  63. GetDefaultQuotaThreshold uintptr
  64. GetDefaultQuotaThresholdText uintptr
  65. SetDefaultQuotaLimit uintptr
  66. GetDefaultQuotaLimit uintptr
  67. GetDefaultQuotaLimitText uintptr
  68. AddUserSid uintptr
  69. AddUserName uintptr
  70. DeleteUser uintptr
  71. FindUserSid uintptr
  72. FindUserName uintptr
  73. CreateEnumUsers uintptr
  74. CreateUserBatch uintptr
  75. InvalidateSidNameCache uintptr
  76. GiveUserNameResolutionPriority uintptr
  77. ShutdownNameResolution uintptr
  78. }
  79. func (x *IDiskQuotaControl) AddRef() (r1, r2 uintptr, lastErr error) {
  80. r1, r2, lastErr = syscall.SyscallN(
  81. x.lpVtbl.AddRef,
  82. uintptr(unsafe.Pointer(x)),
  83. )
  84. return
  85. }
  86. func (x *IDiskQuotaControl) Release() (r1, r2 uintptr, lastErr error) {
  87. r1, r2, lastErr = syscall.SyscallN(
  88. x.lpVtbl.Release,
  89. uintptr(unsafe.Pointer(x)),
  90. )
  91. return
  92. }
  93. func (x *IDiskQuotaControl) Initialize(pszPath *uint16, bReadWrite int32) (r1, r2 uintptr, lastErr error) {
  94. r1, r2, lastErr = syscall.SyscallN(
  95. x.lpVtbl.Initialize,
  96. uintptr(unsafe.Pointer(x)),
  97. uintptr(unsafe.Pointer(pszPath)),
  98. uintptr(bReadWrite),
  99. )
  100. return
  101. }
  102. func (x *IDiskQuotaControl) GetQuotaState(pdwState *uint32) (r1, r2 uintptr, lastErr error) {
  103. r1, r2, lastErr = syscall.SyscallN(
  104. x.lpVtbl.GetQuotaState,
  105. uintptr(unsafe.Pointer(x)),
  106. uintptr(unsafe.Pointer(pdwState)),
  107. )
  108. return
  109. }
  110. func (x *IDiskQuotaControl) GetDefaultQuotaLimit(pllLimit *int64) (r1, r2 uintptr, lastErr error) {
  111. r1, r2, lastErr = syscall.SyscallN(
  112. x.lpVtbl.GetDefaultQuotaLimit,
  113. uintptr(unsafe.Pointer(x)),
  114. uintptr(unsafe.Pointer(pllLimit)),
  115. )
  116. return
  117. }
  118. var CLSID_DiskQuotaControl = &GUID{0x7988b571, 0xec89, 0x11cf, [8]byte{0x9c, 0x0, 0x0, 0xaa, 0x0, 0xa1, 0x4f, 0x56}}
  119. var IID_IDiskQuotaControl = &GUID{0x7988b572, 0xec89, 0x11cf, [8]byte{0x9c, 0x0, 0x0, 0xaa, 0x0, 0xa1, 0x4f, 0x56}}
  120. // 基本内容结束
  121. func getVolumeQuota(wg *sync.WaitGroup, volume string) {
  122. defer wg.Done()
  123. runtime.LockOSThread()
  124. defer runtime.UnlockOSThread()
  125. // 初始化COM
  126. r1, r2, lastErr := CoInitializeEx(
  127. 0, // 必须为NULL
  128. COINIT_APARTMENTTHREADED,
  129. )
  130. defer CoUninitialize()
  131. fmt.Println("CoInitializeEx", r1, r2, lastErr) // 用于调试的打印结果
  132. // 创建COM实例
  133. var ppv uintptr
  134. r1, r2, lastErr = CoCreateInstance(
  135. CLSID_DiskQuotaControl,
  136. nil,
  137. CLSCTX_INPROC_SERVER,
  138. IID_IDiskQuotaControl,
  139. &ppv,
  140. )
  141. fmt.Println("CoCreateInstance", r1, r2, lastErr)
  142. diskQuotaControl := (*IDiskQuotaControl)(unsafe.Pointer(ppv))
  143. defer diskQuotaControl.Release()
  144. pszPath, err := windows.UTF16PtrFromString(volume)
  145. if err != nil {
  146. panic(err)
  147. }
  148. // Initialize似乎总是失败,即使返回值是成功的
  149. r1, r2, lastErr = diskQuotaControl.Initialize(
  150. pszPath,
  151. 0, // false => 只读
  152. )
  153. fmt.Println("Initialize", r1, r2, lastErr)
  154. var pdwState uint32
  155. r1, r2, lastErr = diskQuotaControl.GetQuotaState(
  156. &pdwState,
  157. )
  158. fmt.Println("GetQuotaState", r1, r2, lastErr)
  159. fmt.Println(pdwState)
  160. var pllLimit int64
  161. r1, r2, lastErr = diskQuotaControl.GetDefaultQuotaLimit(
  162. &pllLimit,
  163. )
  164. fmt.Println("GetDefaultQuotaLimit", r1, r2, lastErr)
  165. fmt.Println(pllLimit)
  166. }
  167. func main() {
  168. volume := flag.String("v", `C:\`, "volume")
  169. flag.Parse()
  170. fmt.Println("Volume", *volume)
  171. var wg sync.WaitGroup
  172. wg.Add(1)
  173. go getVolumeQuota(&wg, *volume)
  174. wg.Wait()
  175. }

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

结果:

  1. Volume C:\
  2. CoInitializeEx 0 0 The operation completed successfully.
  3. CoCreateInstance 0 0 The operation completed successfully.
  4. Initialize 0 9298000 The operation completed successfully.
  5. GetQuotaState 2147942403 9298000 The system cannot find the path specified.
  6. 0
  7. GetDefaultQuotaLimit 2147942421 9298000 The operation completed successfully.
  8. 0
  9. Volume \\?\C:\
  10. CoInitializeEx 0 0 The operation completed successfully.
  11. CoCreateInstance 0 0 The operation completed successfully.
  12. Initialize 0 9103168 The operation completed successfully.
  13. GetQuotaState 2147942403 9103168 The system cannot find the path specified.
  14. 0
  15. GetDefaultQuotaLimit 2147942421 9103168 The operation completed successfully.
  16. 0
  17. Volume Invalid:Volume://+
  18. CoInitializeEx 0 0 The operation completed successfully.
  19. CoCreateInstance 0 0 The operation completed successfully.
  20. Initialize 0 8709600 The operation completed successfully.
  21. GetQuotaState 2147942403 8709600 The system cannot find the path specified.
  22. 0
  23. GetDefaultQuotaLimit 2147942421 8709600 The operation completed successfully.
  24. 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.

  1. //go:build windows && amd64
  2. package main
  3. import (
  4. "flag"
  5. "fmt"
  6. "runtime"
  7. "sync"
  8. "syscall"
  9. "unsafe"
  10. "golang.org/x/sys/windows"
  11. )
  12. // START OF basic stuff
  13. const COINIT_APARTMENTTHREADED = 0x2
  14. const CLSCTX_INPROC_SERVER = 0x1
  15. const CLSCTX_INPROC_HANDLER = 0x2
  16. const CLSCTX_LOCAL_SERVER = 0x4
  17. var (
  18. modole32 = syscall.NewLazyDLL("ole32.dll")
  19. procCoInitializeEx = modole32.NewProc("CoInitializeEx")
  20. procCoUninitialize = modole32.NewProc("CoUninitialize")
  21. procCoCreateInstance = modole32.NewProc("CoCreateInstance")
  22. )
  23. func CoInitializeEx(pvReserved uintptr, dwCoInit uint32) (r1, r2 uintptr, lastErr error) {
  24. r1, r2, lastErr = procCoInitializeEx.Call(
  25. uintptr(pvReserved),
  26. uintptr(dwCoInit),
  27. )
  28. return
  29. }
  30. func CoUninitialize() (r1, r2 uintptr, lastErr error) {
  31. r1, r2, lastErr = procCoUninitialize.Call()
  32. return
  33. }
  34. func CoCreateInstance(rclsid *GUID, pUnkOuter *byte, dwClsContext uint32, riid *GUID, ppv *uintptr) (r1, r2 uintptr, lastErr error) {
  35. r1, r2, lastErr = procCoCreateInstance.Call(
  36. uintptr(unsafe.Pointer(rclsid)),
  37. uintptr(unsafe.Pointer(pUnkOuter)),
  38. uintptr(dwClsContext),
  39. uintptr(unsafe.Pointer(riid)),
  40. uintptr(unsafe.Pointer(ppv)),
  41. )
  42. return
  43. }
  44. type GUID struct {
  45. Data1 uint32
  46. Data2 uint16
  47. Data3 uint16
  48. Data4 [8]byte
  49. }
  50. type IDiskQuotaControl struct {
  51. lpVtbl *IDiskQuotaControlVtbl
  52. }
  53. type IDiskQuotaControlVtbl struct {
  54. QueryInterface uintptr
  55. AddRef uintptr
  56. Release uintptr
  57. Initialize uintptr
  58. SetQuotaState uintptr
  59. GetQuotaState uintptr
  60. SetQuotaLogFlags uintptr
  61. GetQuotaLogFlags uintptr
  62. SetDefaultQuotaThreshold uintptr
  63. GetDefaultQuotaThreshold uintptr
  64. GetDefaultQuotaThresholdText uintptr
  65. SetDefaultQuotaLimit uintptr
  66. GetDefaultQuotaLimit uintptr
  67. GetDefaultQuotaLimitText uintptr
  68. AddUserSid uintptr
  69. AddUserName uintptr
  70. DeleteUser uintptr
  71. FindUserSid uintptr
  72. FindUserName uintptr
  73. CreateEnumUsers uintptr
  74. CreateUserBatch uintptr
  75. InvalidateSidNameCache uintptr
  76. GiveUserNameResolutionPriority uintptr
  77. ShutdownNameResolution uintptr
  78. }
  79. func (x *IDiskQuotaControl) AddRef() (r1, r2 uintptr, lastErr error) {
  80. r1, r2, lastErr = syscall.SyscallN(
  81. x.lpVtbl.AddRef,
  82. uintptr(unsafe.Pointer(x)),
  83. )
  84. return
  85. }
  86. func (x *IDiskQuotaControl) Release() (r1, r2 uintptr, lastErr error) {
  87. r1, r2, lastErr = syscall.SyscallN(
  88. x.lpVtbl.Release,
  89. uintptr(unsafe.Pointer(x)),
  90. )
  91. return
  92. }
  93. func (x *IDiskQuotaControl) Initialize(pszPath *uint16, bReadWrite int32) (r1, r2 uintptr, lastErr error) {
  94. r1, r2, lastErr = syscall.SyscallN(
  95. x.lpVtbl.Initialize,
  96. uintptr(unsafe.Pointer(x)),
  97. uintptr(unsafe.Pointer(pszPath)),
  98. uintptr(bReadWrite),
  99. )
  100. return
  101. }
  102. func (x *IDiskQuotaControl) GetQuotaState(pdwState *uint32) (r1, r2 uintptr, lastErr error) {
  103. r1, r2, lastErr = syscall.SyscallN(
  104. x.lpVtbl.GetQuotaState,
  105. uintptr(unsafe.Pointer(x)),
  106. uintptr(unsafe.Pointer(pdwState)),
  107. )
  108. return
  109. }
  110. func (x *IDiskQuotaControl) GetDefaultQuotaLimit(pllLimit *int64) (r1, r2 uintptr, lastErr error) {
  111. r1, r2, lastErr = syscall.SyscallN(
  112. x.lpVtbl.GetDefaultQuotaLimit,
  113. uintptr(unsafe.Pointer(x)),
  114. uintptr(unsafe.Pointer(pllLimit)),
  115. )
  116. return
  117. }
  118. var CLSID_DiskQuotaControl = &GUID{0x7988b571, 0xec89, 0x11cf, [8]byte{0x9c, 0x0, 0x0, 0xaa, 0x0, 0xa1, 0x4f, 0x56}}
  119. var IID_IDiskQuotaControl = &GUID{0x7988b572, 0xec89, 0x11cf, [8]byte{0x9c, 0x0, 0x0, 0xaa, 0x0, 0xa1, 0x4f, 0x56}}
  120. // END OF basic stuff
  121. func getVolumeQuota(wg *sync.WaitGroup, volume string) {
  122. defer wg.Done()
  123. runtime.LockOSThread()
  124. defer runtime.UnlockOSThread()
  125. // Init COM
  126. r1, r2, lastErr := CoInitializeEx(
  127. 0, // must be NULL
  128. COINIT_APARTMENTTHREADED,
  129. )
  130. defer CoUninitialize()
  131. fmt.Println("CoInitializeEx", r1, r2, lastErr) // Print results for debug
  132. // Create a COM instance
  133. var ppv uintptr
  134. r1, r2, lastErr = CoCreateInstance(
  135. CLSID_DiskQuotaControl,
  136. nil,
  137. CLSCTX_INPROC_SERVER,
  138. IID_IDiskQuotaControl,
  139. &ppv,
  140. )
  141. fmt.Println("CoCreateInstance", r1, r2, lastErr)
  142. diskQuotaControl := (*IDiskQuotaControl)(unsafe.Pointer(ppv))
  143. defer diskQuotaControl.Release()
  144. pszPath, err := windows.UTF16PtrFromString(volume)
  145. if err != nil {
  146. panic(err)
  147. }
  148. // Initialize seems to fail even the return is a success
  149. r1, r2, lastErr = diskQuotaControl.Initialize(
  150. pszPath,
  151. 0, // false => read only
  152. )
  153. fmt.Println("Initialize", r1, r2, lastErr)
  154. var pdwState uint32
  155. r1, r2, lastErr = diskQuotaControl.GetQuotaState(
  156. &pdwState,
  157. )
  158. fmt.Println("GetQuotaState", r1, r2, lastErr)
  159. fmt.Println(pdwState)
  160. var pllLimit int64
  161. r1, r2, lastErr = diskQuotaControl.GetDefaultQuotaLimit(
  162. &pllLimit,
  163. )
  164. fmt.Println("GetDefaultQuotaLimit", r1, r2, lastErr)
  165. fmt.Println(pllLimit)
  166. }
  167. func main() {
  168. volume := flag.String("v", `C:\`, "volume")
  169. flag.Parse()
  170. fmt.Println("Volume", *volume)
  171. var wg sync.WaitGroup
  172. wg.Add(1)
  173. go getVolumeQuota(&wg, *volume)
  174. wg.Wait()
  175. }

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:

  1. Volume C:\
  2. CoInitializeEx 0 0 The operation completed successfully.
  3. CoCreateInstance 0 0 The operation completed successfully.
  4. Initialize 0 9298000 The operation completed successfully.
  5. GetQuotaState 2147942403 9298000 The system cannot find the path specified.
  6. 0
  7. GetDefaultQuotaLimit 2147942421 9298000 The operation completed successfully.
  8. 0
  9. Volume \\?\C:\
  10. CoInitializeEx 0 0 The operation completed successfully.
  11. CoCreateInstance 0 0 The operation completed successfully.
  12. Initialize 0 9103168 The operation completed successfully.
  13. GetQuotaState 2147942403 9103168 The system cannot find the path specified.
  14. 0
  15. GetDefaultQuotaLimit 2147942421 9103168 The operation completed successfully.
  16. 0
  17. Volume Invalid:Volume://+
  18. CoInitializeEx 0 0 The operation completed successfully.
  19. CoCreateInstance 0 0 The operation completed successfully.
  20. Initialize 0 8709600 The operation completed successfully.
  21. GetQuotaState 2147942403 8709600 The system cannot find the path specified.
  22. 0
  23. GetDefaultQuotaLimit 2147942421 8709600 The operation completed successfully.
  24. 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中看到:

  1. 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:

  1. 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:

确定