Получение WPARAM в TWndMethod для возврата 4 байтов - PullRequest
1 голос
/ 03 декабря 2011

Я использую AllocateHWnd в классе, который я пишу, чтобы получать системные сообщения с TWndMethod, и сообщения, которые я получаю, должны обрабатывать 4-байтовый WPARAM, который конкретно ссылается на указатель. Но я получаю только 2 байта взамен. Как мне все настроить, чтобы я мог правильно получать эти сообщения в классе?

Редактировать: конкретный код. Я настраиваю событие сообщения с помощью SHChangeNotifyRegister, основываясь на загруженном образце Microsoft. Proc работает достаточно, чтобы откатить события (в lEvent), на которые я могу купить, но код, используемый Microsoft, определяет WParam как Thandle, а LParam как DWord. Конкретная проблема, с которой я столкнулся, заключается в том, что когда функция IsItemNotificationEvent имеет значение true, SHGetPathFromIDList выполняет AVing или возвращает мусор. Я продолжал просматривать это и не вижу проблемы, кроме того, что указано в моих документах, в том, что WParam является Word (возможно, старым) и что GetLastError в точке, которую я вставил в код, возвращает «Дескриптор недействителен».

function IsItemNotificationevent(lEvent: Longint): boolean;
  var
    flagval: Longint;
  begin
    flagval := (lEvent and (SCHNE_UPDATEIMAGE or SHCNE_ASSOCCHANGED
            or SHCNE_EXTENDED_EVENT or SHCNE_FREESPACE
            or SHCNE_DRIVEADDGUI or SHCNE_SERVERDISCONNECT));
    Result := (flagval > 0);
  end;

procedure TShellNotifyHandler.WindowProc(Var msg: TMessage);
  var
    hNotifyLock: THandle;
    lEvent: Longint;
    pgpidl: PitemIDList;
    psi1: array[1..MAX_PATH] of Char;
  begin
    if Msg.Msg = FShellMsg then
      begin
        hNotifyLock := SHChangeNotification_Lock(THandle(Msg.WParam),DWord(Msg.LParam),
                 pgpidl, lEvent);
        writeln(SysErrorMessage(GetLastError));
        if (hNotifyLock > 0) then              
          begin
            if IsItemNotificationEvent(lEvent) then 
  // this limits events for this to what Microsoft defined in their example
               begin
                 if (pgpidl <> nil) then
                   SHGetPathFromIDList(pgpidl, @psi1);
                 Writeln('Path #1: ', String(psi1));
               end;
            SHChangeNotification_Unlock(hNotifyLock);
          end;
        if Assigned(FOnShellNotify) then
          FOnShellNotify(Self, LEvent);
      end
   else
      FWndProc(Msg);
  end;

Ответы [ 2 ]

2 голосов
/ 03 декабря 2011

Главное, что я вижу неправильно в этом коде, и я действительно изучил вызов SHChangeNotification_Lock, это то, что вы безоговорочно звоните GetLastError.

Документация для этой функции API неадекватна, поскольку в ней не указано, как сообщать об ошибках. Тем не менее, я бы сильно ожидал, что об ошибках сообщит функция, возвращающая NULL. Поскольку в документации ничего не говорится о вызове GetLastError, вполне возможно, что функция API не устанавливает последнее значение ошибки. Неважно, даже если вы можете быть уверены, что GetLastError может быть вызван, вы должны делать это только после сбоя, т.е. если вызов SHChangeNotification_Lock возвращает NULL. Если вы позвоните GetLastError после успешного вызова API, вы получите код ошибки для самого последнего неудачного вызова API, который не связан с текущим вызовом.

Суть в том, что я уверен, что WParam содержит все 4 байта, и что ваша проблема не в этой части процесса.


Результатом всего этого является то, что я твердо верю, что SHChangeNotification_Lock успешен, но вызов SHGetPathFromIDList терпит неудачу. Вы не проверяете возвращаемое значение для этого. Бьюсь об заклад, он возвращает FALSE.

Взгляните на объявления C ++ для двух функций.

SHChangeNotification_Lock возвращает список идентификаторов в параметре, набранном вот так:

PIDLIST_ABSOLUTE **pppidl

SHGetPathFromIDList получает список идентификаторов в параметре, набранном вот так:

PCIDLIST_ABSOLUTE pidl

Я не знаю, как выглядит ваше объявление SHChangeNotification_Lock, но тот, который представлен в моей версии Delphi (XE2), выглядит неправильно. Этот параметр объявлен так:

out pppidl: array of PItemIDList

Честно говоря, я не вижу, как функция Windows API может возвращать открытый массив Delphi в качестве параметра out. Я думаю, что это должно быть объявлено так:

out pppidl: PPItemIDList

и вам может потребоваться объявить PPItemIDList равным ^PItemIDList.

Теперь pppidl - это массив. Он указывает на первый элемент массива PItemIDList. Таким образом, вы получите путь к первому элементу, вызвав:

SHGetPathFromIDList(pppidl^, @psi1);

Я полагаю, это реальная проблема, с которой вы столкнулись.


Наконец, я не могу понять, почему вы проверяли бы успех с hNotifyLock > 0. Правильный тест - hNotifyLock <> 0. Теперь я знаю, что некоторые типы Delphi изменились в последних версиях, но если бы THandle было значением со знаком в вашей версии Delphi, то ваш код был бы неправильным. Независимо от того, что правильный логический тест - <>0.

0 голосов
/ 05 декабря 2011

Хорошо, я получил ответ.На самом деле, существует целый ряд проблем:

1) У меня были проблемы, когда дело касалось IsItemNotificationEvent.Чтобы иметь действительные PIDL, мне нужно было убедиться, что событие НЕ БЫЛО одним из них, потому что ни один PIDL не подходит для обработки против них.

if IsItemNotificationEvent(lEvent) then 

2) "out" был необходим в определении дляSHChangeNotification_Lock, а не «var» или простая ссылка на указатель.У меня нет ничего, что бы указывало на то, что «вне» делает конкретно, поэтому, если кто-то может помочь, пожалуйста, сделайте.Ниже приведено фиксированное определение.

function SHChangeNotification_Lock(hChangeNotification: THandle; dwProcessID: DWord;
     out pppidl: PSHNotifyStruct; out plEvent: Longint): THandle; stdcall;

3) В моей документации (включая исходные примеры) указано, что для некоторых типов событий возможно несколько pidls.Что делает предложенную коррекцию недействительной в отчете о контроле качества.Проблема с использованием оригинального определения, вероятно, как предложено.Это не совсем верно.Ссылка на определение выше, и вы увидите другой тип.Это определение ниже.Ни в одном событии не может быть больше двух парм, поэтому этого будет достаточно.

TSHNotifyStruct = packed record
  dw1: PItemIDList;
  dw2: PItemIDList;
end;
PSHNotifyStruct = ^TSHNotifyStruct;

Получилось так, как я ожидаю.Мне просто нужно найти действительный список двух событий parm и код, чтобы сделать его немного чище (т.е. не ссылаться на второй pitemid, если известно, что он недействителен).Ниже приведены некоторые примеры результатов моей тестовой программы:

 Event received: $00001000 Parm 1: (C:) Local Disk  // update directory
 Event received: $00000008 Parm 1: ChangeNotifyWatcher // make directory
 Event received: $00000002 Parm 1: ChangeNotifyWatcher // create file
 Event received: $00000010 Parm 1: ChangeNotifyWatcher Parm 2: RECYCLER // remove directory

Спасибо всем за помощь!

...