Как работать с массивами «без заданного размера» в структурах pinvoke? - PullRequest
0 голосов
/ 15 июня 2019

Как бы вы написали этот тип структуры в c #?

struct _JOBOBJECT_BASIC_PROCESS_ID_LIST {
  DWORD     NumberOfAssignedProcesses;
  DWORD     NumberOfProcessIdsInList;
  ULONG_PTR ProcessIdList[1];
}

грехи, нет установленного размера для массива ProcessIdList, что вы делаете? Вы просто напишите это так:

[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
   int NumberOfAssignedProcesses;
   int NumberOfProcessIdsInList;
   IntPtr ProcessIdList; //Must point to a allocated array, thanks jdweng for letting me know.
}

или вы просто назначаете размер, который достаточно велик, например ::10000

[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
   int NumberOfAssignedProcesses;
   int NumberOfProcessIdsInList;
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATH)]
   UIntPtr[] ProcessIdList; //Works just fine, but is limited to the SizeConst.
}

Ответы [ 2 ]

0 голосов
/ 15 июля 2019

Такая структура обычно объявляется (например, в WLan API есть и другие подобные):

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct JOBOBJECT_BASIC_PROCESS_ID_LIST
    {
        public int NumberOfAssignedProcesses;
        public int NumberOfProcessIdsInList;
        public IntPtr[] ProcessIdList;
        public JOBOBJECT_BASIC_PROCESS_ID_LIST(IntPtr pList)
        {
            int nIntSize = Marshal.SizeOf<int>(); // 4
            NumberOfAssignedProcesses = Marshal.ReadInt32(pList, 0);
            NumberOfProcessIdsInList = Marshal.ReadInt32(pList, nIntSize);
            ProcessIdList = new IntPtr[NumberOfProcessIdsInList];
            for (int i = 0; i < NumberOfProcessIdsInList; i++)
            {
                IntPtr pItemList = IntPtr.Zero;
                if (Marshal.SizeOf<IntPtr>() == 4)
                    pItemList = new IntPtr(pList.ToInt32() + (i * Marshal.SizeOf<IntPtr>()) + (nIntSize * 2));
                else
                    pItemList = new IntPtr(pList.ToInt64() + (i * Marshal.SizeOf<IntPtr>()) + (nIntSize * 2));
                IntPtr nPID = new IntPtr();
                nPID = Marshal.ReadIntPtr(pItemList, 0);
                ProcessIdList[i] = nPID;
            }
        }
    }

Тест с 5 блокнотами запущен и назначен на работу с JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE , затем QueryInformationJobObject для перечисления идентификаторов PID с использованием этой структуры =>

        private IntPtr hJob = IntPtr.Zero;

        bool bRet = false;
        hJob  = CreateJobObject(IntPtr.Zero, "Test Job Object");
        JOBOBJECT_EXTENDED_LIMIT_INFORMATION jbeli = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
        jbeli.BasicLimitInformation.LimitFlags |= (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_BREAKAWAY_OK);
        int nLength = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
        IntPtr pJobInfo = Marshal.AllocHGlobal(nLength);
        Marshal.StructureToPtr(jbeli, pJobInfo, false);           
        SetInformationJobObject(hJob, JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation, pJobInfo, (uint)nLength);
        Marshal.FreeHGlobal(pJobInfo);

        int nNbProcesses = 5;
        for (int i = 0; i < nNbProcesses; i++)
        {
            using (Process exeProcess = new Process())
            {
                exeProcess.StartInfo.FileName = "notepad";
                exeProcess.Start();
                exeProcess.WaitForInputIdle();
                IntPtr hProcess = exeProcess.Handle;
                bRet = AssignProcessToJobObject(hJob, hProcess);
            }
        }

        JOBOBJECT_BASIC_PROCESS_ID_LIST jobpil = new JOBOBJECT_BASIC_PROCESS_ID_LIST();
        jobpil.NumberOfAssignedProcesses = nNbProcesses;
        int nSize = Marshal.SizeOf<JOBOBJECT_BASIC_PROCESS_ID_LIST>() + (nNbProcesses - 1) * Marshal.SizeOf<IntPtr>();
        IntPtr pJobpil = Marshal.AllocHGlobal(nSize);
        Marshal.StructureToPtr(jobpil, pJobpil, false);
        int nReturnLength = 0;
        bRet = QueryInformationJobObject(hJob, JOBOBJECTINFOCLASS.JobObjectBasicProcessIdList,  pJobpil, nSize, out nReturnLength);
        if (bRet)
        {
            var processidlist = new JOBOBJECT_BASIC_PROCESS_ID_LIST(pJobpil);
            foreach (var pid in processidlist.ProcessIdList)
            {
                Console.WriteLine("PID: {0}", pid.ToString());
            }
        }
        else
        {
            int nErr = Marshal.GetLastWin32Error();
            Win32Exception win32Exception = new Win32Exception(nErr);
            this.Activate();
            MessageBox.Show("Error: " + win32Exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        Marshal.FreeHGlobal(pJobpil);


    // CloseHandle can be added in Form1_FormClosed :

    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
        CloseHandle(hJob);
    }

Объявления =>

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string lpName);

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool SetInformationJobObject(IntPtr hJob, JOBOBJECTINFOCLASS JobObjectInfoClass, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess);

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool CloseHandle(IntPtr hObject);

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool QueryInformationJobObject(IntPtr hJob, JOBOBJECTINFOCLASS JobObjectInformationClass, [Out, MarshalAs(UnmanagedType.SysUInt)] IntPtr lpJobObjectInformation, int cbJobObjectInformationLength, out int lpReturnLength);

        [StructLayout(LayoutKind.Sequential)]
        struct JOBOBJECT_BASIC_LIMIT_INFORMATION
        {
            public ulong PerProcessUserTimeLimit;
            public ulong PerJobUserTimeLimit;
            public int LimitFlags;
            public IntPtr MinimumWorkingSetSize;
            public IntPtr MaximumWorkingSetSize;
            public int ActiveProcessLimit;
            public IntPtr Affinity;
            public int PriorityClass;
            public int SchedulingClass;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct IO_COUNTERS
        {
            public ulong ReadOperationCount;
            public ulong WriteOperationCount;
            public ulong OtherOperationCount;
            public ulong ReadTransferCount;
            public ulong WriteTransferCount;
            public ulong OtherTransferCount;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
        {
            public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
            public IO_COUNTERS IoInfo;
            public IntPtr ProcessMemoryLimit;
            public IntPtr JobMemoryLimit;
            public IntPtr PeakProcessMemoryUsed;
            public IntPtr PeakJobMemoryUsed;
        }

        //
        // Basic Limits
        //
        public const int JOB_OBJECT_LIMIT_WORKINGSET = 0x00000001;
        public const int JOB_OBJECT_LIMIT_PROCESS_TIME = 0x00000002;
        public const int JOB_OBJECT_LIMIT_JOB_TIME = 0x00000004;
        public const int JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 0x00000008;
        public const int JOB_OBJECT_LIMIT_AFFINITY = 0x00000010;
        public const int JOB_OBJECT_LIMIT_PRIORITY_CLASS = 0x00000020;
        public const int JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 0x00000040;
        public const int JOB_OBJECT_LIMIT_SCHEDULING_CLASS = 0x00000080;

        //
        // Extended Limits
        //
        public const int JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x00000100;
        public const int JOB_OBJECT_LIMIT_JOB_MEMORY = 0x00000200;
        public const int JOB_OBJECT_LIMIT_JOB_MEMORY_HIGH = JOB_OBJECT_LIMIT_JOB_MEMORY;
        public const int JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400;
        public const int JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800;
        public const int JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000;
        public const int JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000;
        public const int JOB_OBJECT_LIMIT_SUBSET_AFFINITY = 0x00004000;
        public const int JOB_OBJECT_LIMIT_JOB_MEMORY_LOW = 0x00008000;

        public enum JOBOBJECTINFOCLASS
        {
            JobObjectBasicAccountingInformation = 1,
            JobObjectBasicLimitInformation,
            JobObjectBasicProcessIdList,
            JobObjectBasicUIRestrictions,
            JobObjectSecurityLimitInformation,  // deprecated
            JobObjectEndOfJobTimeInformation,
            JobObjectAssociateCompletionPortInformation,
            JobObjectBasicAndIoAccountingInformation,
            JobObjectExtendedLimitInformation,
            JobObjectJobSetInformation,
            JobObjectGroupInformation,
            JobObjectNotificationLimitInformation,
            JobObjectLimitViolationInformation,
            JobObjectGroupInformationEx,
            JobObjectCpuRateControlInformation,
            JobObjectCompletionFilter,
            JobObjectCompletionCounter,
            JobObjectReserved1Information = 18,
            JobObjectReserved2Information,
            JobObjectReserved3Information,
            JobObjectReserved4Information,
            JobObjectReserved5Information,
            JobObjectReserved6Information,
            JobObjectReserved7Information,
            JobObjectReserved8Information,
            JobObjectReserved9Information,
            JobObjectReserved10Information,
            JobObjectReserved11Information,
            JobObjectReserved12Information,
            JobObjectReserved13Information,
            JobObjectReserved14Information = 31,
            JobObjectNetRateControlInformation,
            JobObjectNotificationLimitInformation2,
            JobObjectLimitViolationInformation2,
            JobObjectCreateSilo,
            JobObjectSiloBasicInformation,
            JobObjectReserved15Information = 37,
            JobObjectReserved16Information,
            JobObjectReserved17Information,
            JobObjectReserved18Information,
            JobObjectReserved19Information = 41,
            JobObjectReserved20Information,
            MaxJobObjectInfoClass
        }
0 голосов
/ 16 июня 2019

Я думаю, что любой из упомянутых вами способов должен работать.

Кроме того, в c # есть соответствующая функция: определить массив с фиксированным ключевым словом:

struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
   int NumberOfAssignedProcesses;
   int NumberOfProcessIdsInList;
   fixed IntPtr ProcessIdList[1];
}

См. Документацию: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/fixed-size-buffers

Также нет проверки границ, поэтому вы сможете легко читать за концом удара:

Примечание

За исключением созданной памятис помощью stackalloc компилятор C # и общеязыковая среда выполнения (CLR) не выполняют никаких проверок переполнения буфера безопасности.Как и во всех небезопасных кодах, соблюдайте осторожность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...