在Go中调用EnumServicesStatusEx,内存分配?

时间:2016-05-16 09:39:33

标签: windows go advapi32

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

经过@chowey here的大量帮助后,我得到了一些东西并开始了一个基础库,我已经把它放在GitHub上here

我现在转到“服务”,要求在计算机上列出所有Windows服务,启动,停止,重新启动它们。一旦你有一个服务句柄可以使用,启动/停止/重启看起来非常直接,但我很难获得已安装服务的列表。

Advapi32.dll中的

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

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

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

有谁知道如何实现这一点,非常好吗? :)

1 个答案:

答案 0 :(得分:0)

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

看起来我们创建了一个正确大小的字节片,然后使用unsafe类转换为我们的struct类型。

    _, _, _ = 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.")
    }