doumao1047 2018-05-01 22:03
浏览 146
已采纳

使用go读取ETW提供程序

I'm trying to access the EnumerateTraceGuids function from Advapi32.dll in go. I'm at the very early stage and still trying to decipher what is it that I must do. I have the following code that keeps giving me Error: 87, meaning ERROR_INVALID_PARAMETER.

I've used this file as a starting point though it's only writing and not reading : https://github.com/moby/moby/blob/master/daemon/logger/etwlogs/etwlogs_windows.go

Official documentation for the function I'm trying to call is here : https://msdn.microsoft.com/en-us/library/windows/desktop/aa363713(v=vs.85).aspx

It requires GuidPropertiesArray [in, out] An array of pointers to TRACE_GUID_PROPERTIES structures. This structure is the following (https://msdn.microsoft.com/en-us/library/windows/desktop/aa364143(v=vs.85).aspx)

typedef struct _TRACE_GUID_PROPERTIES {
  GUID    Guid;
  ULONG   GuidType;
  ULONG   LoggerId;
  ULONG   EnableLevel;
  ULONG   EnableFlags;
  BOOLEAN IsEnable;
} TRACE_GUID_PROPERTIES, *PTRACE_GUID_PROPERTIES;

I have the following code to try and do this :

package main
import (
    "errors"
    "fmt"
    "syscall"
    "unsafe"
    "github.com/sirupsen/logrus"
    "golang.org/x/sys/windows"
)

const (
    win32CallSuccess = 0
    MaxProv = 50
    nbProviders = 50
)

var (
    modAdvapi32          = windows.NewLazySystemDLL("Advapi32.dll")
    procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
)

type ulong int32

type TRACE_GUID_PROPERTIES struct {
    Guid syscall.GUID
    GuidType ulong
    LoggerId ulong
    EnableLevel ulong
    EnableFlags ulong
    IsEnable bool
}

func callEnumerateTraceGuids() error {
    GuidPropertiesArray:= make([]TRACE_GUID_PROPERTIES, 1)
    ptr := &GuidPropertiesArray[0]
    ret, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(&ptr)), MaxProv, nbProviders)
    if ret != win32CallSuccess {
        errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret)
        logrus.Error(errorMessage)
        return errors.New(errorMessage)
    }
    return nil
}

func main() {
    callEnumerateTraceGuids()
}

At this point I'm not sure what is it that I must do. I've tried a lot of variation of initializing the array without success. Hoping someone can point me in the right direction. Thanks !

Edit : Changed code based on comments but still getting the same error.

PS : This is my first time posting to stackoverflow and I've already been told that I'm lazy less than 12 hours after posting my question (yay!) so not sure I'm asking this right...I am not too familiar with go and never called windows DLL from go before and since I keep hitting that ERROR_INVALID_PARAMETER I thought of reaching out to try and pass this first wall to be able to grasp some concepts at the same time. Hope this helps understands my request (ie. I come in peace).

  • 写回答

1条回答 默认 最新

  • douji8549 2018-05-02 15:32
    关注

    OK, I had a bit of free time and an access to a Windows XP box, so I've decided to dust off my Windows programming skills and wrote a working solution:

    package main
    
    import (
        "golang.org/x/sys/windows"
    
        "log"
        "syscall"
        "unsafe"
    )
    
    var (
        modAdvapi32             = windows.NewLazySystemDLL("advapi32")
        procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
    )
    
    type traceGuidProperties struct {
        guid        syscall.GUID
        guidType    uint32
        loggerId    uint32
        enableLevel uint32
        enableFlags uint32
        isEnable    uint32
    }
    
    func enumerateTraceGuids(ptr **traceGuidProperties, count uint32, out *uint32) error {
        rc, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(ptr)),
            uintptr(count), uintptr(unsafe.Pointer(out)))
        if rc != 0 {
            return syscall.Errno(rc)
        }
        return nil
    }
    
    func enumTraceGuids() ([]*traceGuidProperties, error) {
        var errMoreData = syscall.Errno(234)
    
        var (
            dummyProps traceGuidProperties
            dummyPtr   = &dummyProps
    
            count uint32
        )
    
        err := enumerateTraceGuids(&dummyPtr, 0, &count)
        if err != errMoreData {
            return nil, err
        }
    
        items := make([]*traceGuidProperties, count)
        for i := range items {
            items[i] = new(traceGuidProperties)
        }
    
        for {
            err = enumerateTraceGuids(&items[0], count, &count)
            if err == nil {
                break
            }
            if err != errMoreData {
                return nil, err
            }
            for i := 0; i < int(count)-len(items); i++ {
                items = append(items, new(traceGuidProperties))
            }
        }
    
        return items[:count], nil
    }
    
    func main() {
        log.SetFlags(0)
    
        data, err := enumTraceGuids()
        if err != nil {
            log.Fatal(err)
        }
        log.Printf("len(data)=%d
    ", len(data))
        for i := range data {
            log.Println(*(data[i]))
        }
    }
    

    The key points:

    • I was wrong when I told you that «you … should allocate an array of structs (not pointers)»—in fact EnumerateTraceGuids indeed expects an array of pointers.

    • As hinted here, there are two subtleties with how EnumerateTraceGuids works:

      • Contrary to what its documentation states, it actually supports being called with its PropertyArrayCount parameter set to 0, in which case it's expected to return ERROR_MORE_DATA while having set GuidCount to the number of elements of the input array required for the (next) call to complete successfully. IOW, that way we know how many trace GUIDs the system currently "knows about".
      • Still, even in this case, the function performs validity check on the input array (see below).
    • As it turns out, the function expects an array of pointers to TRACE_GUID_PROPERTIES blocks allocated by you.

      In other words, if it says you it knows about 10 trace GUIDs, you have to allocate 10 values of type TRACE_GUID_PROPERTIES, then make an array of 10 pointers to those values and pass a pointer to the 1st element of that array to the function.

    • Notice that there's an inherent race between changes occuring in the system (those traces added or removed for any number of reasons) and the calls to EnumerateTraceGuids.

      This means if the first call to this function told you it "knows" about 10 trace GUIDs, on the next call it may turn out there's already 20 trace GUIDs, or 5 GUIDs (or any other number of them FWIW).

      So we account for both of these possibilities in the following way:

      1. First we do a call with a pointer to a single (but valid) TRACE_GUID_PROPERTIES value, allocated statically (hence the function "sees" what looks like an array of a single element), while telling the function the input "array" has zero elements.

        We expect the function to fail with ERROR_MORE_DATA and put the actual number of trace GUIDs it "knows" about into the variable we've supplied it a pointer to.

      2. We allocate that much TRACE_GUID_PROPERTIES memory blocks the function indicated on the first call. For this, we use the new() built-in function which behaves somewhat like malloc() in the standard C library—it allocates the memory for a value of the specified type and returns a pointer to the allocated memory block.

      3. We create an array of pointers to these allocated memory blocks and call EnumerateTraceGuids again.

      4. If it succeeds, we handle the possibility it returned less elements than we've allocated, and reslice our slice.

      5. If it fails with ERROR_MORE_DATA, we extend our slice with whatever the number of elements is needed (allocating memory for their TRACE_GUID_PROPERTIES blocks first), and try calling the function again.

    • The "magic number" 234 is the actual code for the ERROR_MORE_DATA value.

    Sorry for the initial confusion.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器