【问题标题】:Calling EnumServicesStatusEx in Go, memory allocation?在 Go 中调用 EnumServicesStatusEx,内存分配?
【发布时间】:2016-09-12 01:29:28
【问题描述】:

我正在编写一个与 Windows 服务中的 Windows API 交互的应用程序。

在@chowey here 提供大量帮助后,我有点掌握了窍门,并开始了一个基本库,我已将它放在 GitHub here 上。

我现在转到“服务”,要求列出机器上的所有 Windows 服务,启动、停止、重新启动它们。一旦你有一个服务句柄可以使用,启动/停止/重启看起来很简单,但我正在努力获取已安装服务的列表。

Advapi32.dll 中的EnumServicesStatusEx 是我需要调用的函数,但它需要一个指向为ENUM_SERVICE_STATUS_PROCESS 结构数组预分配内存的指针。

您可以使用空指针调用该函数,它会返回所需的内存分配大小,但我不相信 Go 中有直接分配内存的方法。

起初我以为我可以得到内存分配要求,使用 unsafe 包将它除以结构的 SizeOf,创建一个包含该数量元素的切片,然后将指向第一个元素的指针传递给函数,但它表示内存需要为字符串数据包含空间,而这不会。

有没有人知道这是如何实现的,谢谢? :)。

【问题讨论】:

  • 来自 EnumServicesStatusEx 文档:lpServices [out, optional] A pointer to the buffer that receives the status information. ... To determine the required size, specify NULL for this parameter and 0 for the cbBufSize parameter. The function will fail and GetLastError will return ERROR_MORE_DATA. The pcbBytesNeeded parameter will receive the required size.。知道缓冲区需要多大后,使用该大小的缓冲区再次调用 EnumServicesStatusEx。
  • 你好@alex,如上所述,我不知道有一种方法可以让 Go 直接分配内存。你必须给它一个数据结构让它分配内存,然后通过 unsafe 包暴露该内存。
  • 你自己说的:create a slice containing that number of elements, then pass a pointer to the first element to the function。来自 EnumServicesStatusEx doco:cbBufSize [in] The size of the buffer pointed to by the lpServices parameter, in bytes. 因此,您可以创建 cbBufSize 字节数组,并将该数组的第一个元素的地址传递给 EnumServicesStatusEx。例如,请参阅github.com/golang/go/blob/master/src/internal/syscall/windows/…。与示例不同,您应该从按照 EnumServicesStatusEx doco 传递 lpServices=nil 开始。
  • 是的,我是这么认为的,但是如果我创建了一个结构切片,这些结构将包含指针。分配的内存足以存储指针地址,但实际上不会为 API 调用分配任何内存来存储 MSDN 文档所要求的指针应引用的字符串数据。虽然如果所需的大小以字节为单位,我可以创建一个该大小的字节切片,然后尝试使用 unsafe 将其转换为正确的类型。必须假设内存布局是兼容的。我试试看,谢谢!
  • 您应该分配请求大小的字节片。由您调用的 API 在字节缓冲区内布局所需的所有内容。您的工作将是使用 unsafe 将地址转换为您的第一个字节的正确类型。就像你说的那样。

标签: windows go advapi32


【解决方案1】:

根据@alex 的建议,我得到了以下示例代码。

看起来我们创建了一个大小合适的字节切片,然后使用 unsafe 类转换为我们的结构类型。

    _, _, _ = svcEnumServicesStatusEx.Call(
        uintptr(handle),
        uintptr(uint32(SVC_SC_ENUM_PROCESS_INFO)),
        uintptr(uint32(SVC_SERVICE_WIN32)),
        uintptr(uint32(SVC_SERVICE_STATE_ALL)),
        uintptr(0),
        0,
        uintptr(unsafe.Pointer(&bytesReq)),
        uintptr(unsafe.Pointer(&numReturned)),
        uintptr(unsafe.Pointer(&resumeHandle)),
        uintptr(0),
    )

    if bytesReq > 0 {
        var buf []byte = make([]byte, bytesReq)

        ret, _, _ := svcEnumServicesStatusEx.Call(
            uintptr(handle),
            uintptr(uint32(SVC_SC_ENUM_PROCESS_INFO)),
            uintptr(uint32(SVC_SERVICE_WIN32)),
            uintptr(uint32(SVC_SERVICE_STATE_ALL)),
            uintptr(unsafe.Pointer(&buf[0])),
            uintptr(bytesReq),
            uintptr(unsafe.Pointer(&bytesReq)),
            uintptr(unsafe.Pointer(&numReturned)),
            uintptr(unsafe.Pointer(&resumeHandle)),
            uintptr(0),
        )

        if ret > 0 {
            var sizeTest ENUM_SERVICE_STATUS_PROCESS
            iter := uintptr(unsafe.Pointer(&buf[0]))

            for i := uint32(0); i < numReturned; i++ {
                var data *ENUM_SERVICE_STATUS_PROCESS = (*ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(iter))

                fmt.Printf("Service Name: %s - Display Name: %s - %#v\r\n", syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(data.lpServiceName))[:]), syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(data.lpDisplayName))[:]), data.ServiceStatusProcess)

                iter = uintptr(unsafe.Pointer(iter + unsafe.Sizeof(sizeTest)))
            }
        } else {
            return nil, fmt.Errorf("Failed to get Service List even with allocated memory.")
        }
    } else {
        return nil, fmt.Errorf("Unable to get size of required memory allocation.")
    }

【讨论】:

    猜你喜欢
    • 2015-10-11
    • 2012-04-20
    • 1970-01-01
    • 1970-01-01
    • 2019-08-09
    • 1970-01-01
    • 1970-01-01
    • 2010-09-30
    • 1970-01-01
    相关资源
    最近更新 更多