lxn / win

A Windows API wrapper package for the Go Programming Language
Other
1.19k stars 313 forks source link

pdh.go alway get incorrect data #12

Open oliveagle opened 9 years ago

oliveagle commented 9 years ago
#python pseudocode
hQuery = win32pdh.OpenQuery()
hCounter = win32pdh.AddCounter(hQuery, "\System\Processes")
win32pdh.CollectQueryData(hQuery)
win32pdh.CollectQueryData(hQuery)
_, val = win32pdh.GetFormattedCounterValue(hCounter, win32pdh.PDH_FMT_DOUBLE)
print val

Code above will get processes count of windows. which usually in my system is around 60

but with pdh.go version I always get meaningless data. here is my code:

// +build windows
package main

import (
    "fmt"
    "github.com/kr/pretty"
    "github.com/lxn/win"
)

func main() {
    var handle win.PDH_HQUERY
    var counterHandle win.PDH_HCOUNTER
    ret := win.PdhOpenQuery(0, 0, &handle)
    ret = win.PdhAddEnglishCounter(handle, "\\System\\Processes", 0, &counterHandle)

    var derp win.PDH_FMT_COUNTERVALUE_DOUBLE

    ret = win.PdhCollectQueryData(handle)

    var lpdwType *uint32

    fmt.Printf("Collect return code is %x\n", ret) // return code will be PDH_CSTATUS_INVALID_DATA
    ret = win.PdhGetFormattedCounterValueDouble(counterHandle, lpdwType, &derp)
    pretty.Println(derp)

    ret = win.PdhCollectQueryData(handle)
    fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS
    ret = win.PdhGetFormattedCounterValueDouble(counterHandle, lpdwType, &derp)
    pretty.Println(derp)
}

here is the output qq20150211-1

after some dig on this. I found out a solution: pass in a c struct instead of go struct

here is the code:


/*
typedef struct _PDH_FMT_COUNTERVALUE_DOUBLE
{
    int CStatus;
    double DoubleValue;
}PDH_FMT_COUNTERVALUE_DOUBLE;
*/
import "C"

func main(){
    // ...

    var pValue C.PDH_FMT_COUNTERVALUE_DOUBLE

    r1, r2, err = syscall.Syscall6(uintptr(PdhGetFormattedCounterValue), 4,
        uintptr(phCounter),
        uintptr(PDH_FMT_DOUBLE),
        uintptr(lpdwType),
        uintptr(unsafe.Pointer(&pValue)),
        0, 0)
    fmt.Println(r1, r2, err)
    fmt.Println(lpdwType, pValue, pValue.DoubleValue)
    pretty.Println(pValue)
}

qq20150211-3

I checked these counters they all give me correct data

    // path := syscall.StringToUTF16Ptr("\\System\\Processes")
    // path := syscall.StringToUTF16Ptr("\\LogicalDisk(C:)\\% Free Space")
    // path := syscall.StringToUTF16Ptr("\\Memory\\% Committed Bytes In Use")
    path := syscall.StringToUTF16Ptr("\\Memory\\Available MBytes")
oliveagle commented 9 years ago

/*
typedef long LONG;
typedef unsigned long DWORD;
typedef struct _PDH_FMT_COUNTERVALUE_DOUBLE
{
    DWORD CStatus;
    double DoubleValue;
}PDH_FMT_COUNTERVALUE_DOUBLE;
*/
import "C"

this struct will be better

krpors commented 9 years ago

That's weird. When I created the code (+ the example in the comments) it worked fine. When I try it right now myself, I also get garbled data.

douglaswth commented 9 years ago

I'm also running into this using \System\System Up Time on all of the types. Using the CGo double struct that @oliveagle proposed seems to be working fine. Perhaps Go structs no longer line up correctly with Windows C structs or it is also possible that this only worked/works correctly with 32bit as I'm running into the problem on 64bit.