win icon indicating copy to clipboard operation
win copied to clipboard

pdh.go alway get incorrect data

Open oliveagle opened this issue 10 years ago • 3 comments

#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 avatar Feb 11 '15 07:02 oliveagle


/*
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

oliveagle avatar Feb 11 '15 07:02 oliveagle

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.

krpors avatar Feb 11 '15 09:02 krpors

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.

douglaswth avatar Feb 26 '15 18:02 douglaswth