Сбой P/Invoking CreateToolhelp32Snapshot в Compact Framework

Эй, я делаю небольшое приложение для своего смартфона, используя Windows Mobile 6. Я пытаюсь получить все запущенные в настоящее время процессы, но метод CreateToolhelp32Snapshot всегда возвращает -1. Так что теперь я застрял. Я пытался получить ошибку при вызове метода GetLastError(), но этот метод возвращает значение 0. Вот фрагмент моего кода.

private const int TH32CS_SNAPPROCESS = 0x00000002;
[DllImport("toolhelp.dll")]
public static extern IntPtr CreateToolhelp32Snapshot(uint flags, 
                                                     uint processid);

public static Process[] GetProcesses()
    {
        ArrayList procList = new ArrayList();
        IntPtr handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        if ((int)handle > 0)
        {
            try
            {
                PROCESSENTRY32 peCurr;
                PROCESSENTRY32 pe32 = new PROCESSENTRY32();

                // get byte array to pass to API call
                byte[] peBytes = pe32.ToByteArray();
                // get the first process
                int retval = Process32First(handle, peBytes);

3 ответа

Решение
  • Во-первых, проверка вашего дескриптора неверна. Обычно старший бит включен в дескриптор, поэтому при приведении к целому числу со знаком он выглядит как отрицательное число. Вы должны проверить, что это не NULL (0) или INVALID_HANDLE_VALUE (-1 / 0xffffffff).
  • Вы не должны "вызывать GetLastError", но вызывать Marshal.GetLastWin32Error()
  • Вы не установили атрибут SetLastError в объявлении P/Invoke. В C# по умолчанию используется значение false, в VB по умолчанию используется значение true.
  • Где ваша реализация PROCESS32? В документах четко указано, что член dwLength должен быть установлен до вызова, и здесь неясно, происходит ли это.

В качестве примечания, в пространстве имен OpenNETCF.ToolHelp в http://www.smartdeviceframework.com/ все это реализовано и работает (на случай, если вы не захотите изобретать велосипед).

Вместо

CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

использование

private const int TH32CS_SNAPNOHEAPS = 0x40000000;
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPNOHEAPS, 0);

По умолчанию CreateToolhelp32Snapshot пытается сделать снимок кучи, что может вызвать ошибку нехватки памяти.

Нашел это по адресу https://social.msdn.microsoft.com/Forums/en-US/e91d845d-d51e-45ad-8acf-737e832c20d0/createtoolhelp32snapshot-windows-mobile-5?forum=vssmartdevicesnative и это решило мою проблему.

Это правильная реализация, основанная на документации MSDN.

private const int INVALID_HANDLE_VALUE = -1;

[Flags]
private enum SnapshotFlags : uint
{
    HeapList = 0x00000001,
    Process = 0x00000002,
    Thread = 0x00000004,
    Module = 0x00000008,
    Module32 = 0x00000010,
    Inherit = 0x80000000,
    All = 0x0000001F,
    NoHeaps = 0x40000000
}

[DllImport("toolhelp.dll"]
private static extern IntPtr CreateToolhelp32Snapshot(SnapshotFlags dwFlags, int th32ProcessID);

[StructLayout(LayoutKind.Sequential)]
public struct PROCESSENTRY32
{
    public uint dwSize;
    public uint cntUsage;
    public uint th32ProcessID;
    public IntPtr th32DefaultHeapID;
    public uint th32ModuleID;
    public uint cntThreads;
    public uint th32ParentProcessID;
    public int pcPriClassBase;
    public uint dwFlags;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szExeFile;
};


IntPtr hSnap = CreateToolhelp32Snapshot(SnapshotFlags.Process, 0);

if (hSnap.ToInt64() != INVALID_HANDLE_VALUE)
{
    PROCESSENTRY32 procEntry = new PROCESSENTRY32();
    procEntry.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));

    if (Process32First(hSnap, ref procEntry))
    {
        do
        {

            //do whatever you want here

        } while (Process32Next(hSnap, ref procEntry));
    }
}

CloseHandle(hSnap);

Наиболее важна эта строка, потому что вы должны установить размер procEntry:

procEntry.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));

Если вы не видите действительной информации о "последней ошибке", возможно, вам может понадобиться добавить атрибут "SetLastError" в атрибут API DllImport ( ссылка на MSDN с примерами кода). Согласно документации этого атрибута, вы должны установить SetLastError в...

... true, чтобы указать, что вызываемый будет вызывать SetLastError; иначе ложно. По умолчанию установлено значение false.

Маршалер среды выполнения вызывает GetLastError и кэширует возвращаемое значение, чтобы предотвратить его перезапись другими вызовами API. Вы можете получить код ошибки, вызвав GetLastWin32Error

Что касается сбоя API, который вы видите, я не вижу ничего очевидного; код, который вы имеете, кажется очень похожим на пример кода здесь.

Другие вопросы по тегам