Question

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        const int SystemPowerInformation = 11;
        const uint STATUS_SUCCESS = 0;

        [StructLayout(LayoutKind.Sequential)]
        struct PROCESSOR_POWER_INFORMATION
        {
            public uint  Number;
            public uint MaxMhz;
            public uint CurrentMhz;
            public uint MhzLimit;
            public uint MaxIdleState;
            public uint CurrentIdleState;
        }

        [DllImport("powrprof.dll")]
        static extern uint CallNtPowerInformation(
            int InformationLevel,
            IntPtr lpInputBuffer,
            int nInputBufferSize,
            [MarshalAs(UnmanagedType.LPArray)]
            out byte[]  lpOutputBuffer,
            int nOutputBufferSize
        );

        static void Main(string[] args)
        {


            byte[] buffer = new byte[4 * Marshal.SizeOf(typeof(PROCESSOR_POWER_INFORMATION))];
            uint retval = CallNtPowerInformation(
                SystemPowerInformation,
                IntPtr.Zero,
                0,
                out  buffer,
                4 * Marshal.SizeOf(typeof(PROCESSOR_POWER_INFORMATION))
            );

            if (retval == STATUS_SUCCESS)
                Console.WriteLine(buffer);
       }
    }
}

I am trying to get some data out of CallNtPowerInformation. I tried to create a struct and call CallNtPowerInformation and marshal the data from it, but that didn't work. So I am trying to see if I can get the data into a byte array, but I get the following:

Object reference not set to an instance of an object.

I believe I am allocating the memory to the buffer.

I am not sure why. Any pointers would be helpful.

Was it helpful?

Solution

Your constant named SystemPowerInformation with value 11 has the wrong name. It should be named ProcessorInformation.

You should declare the p/invoke like this:

[DllImport("powrprof.dll")]
static extern uint CallNtPowerInformation(
    int InformationLevel,
    IntPtr lpInputBuffer,
    int nInputBufferSize,
    [Out] PROCESSOR_POWER_INFORMATION[] processorPowerInformation,
    int nOutputBufferSize
);

In order to call the function you need to allocate a suitably sized array of PROCESSOR_POWER_INFORMATION structs. Like this:

PROCESSOR_POWER_INFORMATION[] powerInfo = 
    new PROCESSOR_POWER_INFORMATION[procCount];

The documentation for CallNtPowerInformation tells you to use GetSystemInfo to work out how many processors you have. You can use Environment.ProcessorCount.

Then you call the function like this:

uint retval = CallNtPowerInformation(
    ProcessorInformation,
    IntPtr.Zero,
    0,
    powerInfo,
    powerInfo.Length*Marshal.SizeOf(typeof(PROCESSOR_POWER_INFORMATION))
);

Here's a complete program:

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        const int ProcessorInformation = 11;
        const uint STATUS_SUCCESS = 0;

        [StructLayout(LayoutKind.Sequential)]
        struct PROCESSOR_POWER_INFORMATION
        {
            public uint Number;
            public uint MaxMhz;
            public uint CurrentMhz;
            public uint MhzLimit;
            public uint MaxIdleState;
            public uint CurrentIdleState;
        }

        [DllImport("powrprof.dll")]
        static extern uint CallNtPowerInformation(
            int InformationLevel,
            IntPtr lpInputBuffer,
            int nInputBufferSize,
            [Out] PROCESSOR_POWER_INFORMATION[] lpOutputBuffer,
            int nOutputBufferSize
        );

        static void Main(string[] args)
        {
            int procCount = Environment.ProcessorCount;
            PROCESSOR_POWER_INFORMATION[] procInfo =
                new PROCESSOR_POWER_INFORMATION[procCount]; 
            uint retval = CallNtPowerInformation(
                ProcessorInformation,
                IntPtr.Zero,
                0,
                procInfo,
                procInfo.Length * Marshal.SizeOf(typeof(PROCESSOR_POWER_INFORMATION))
            );
            if (retval == STATUS_SUCCESS)
            {
                foreach (var item in procInfo)
                {
                    Console.WriteLine(item.CurrentMhz);
                }
            }
        }
    }
}

OTHER TIPS

Change the parameter type of your umnanaged call to IntPtr:

[DllImport("powrprof.dll")]
    static extern uint CallNtPowerInformation(
        int InformationLevel,
        IntPtr lpInputBuffer,
        int nInputBufferSize,
        IntPtr lpOutputBuffer,
        int nOutputBufferSize
    );

And use this before calling it:

GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();

Then call it passing that IntPtr as the parameter.

Don't forget to release after use!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top