Почему вызов обратного вызова ProcessGroupPolicyEx вызывает нарушение прав доступа?

Я пытаюсь помочь коллеге с кодом в расширении на стороне клиента. После добавления вызова к обратному вызову функция, похоже, завершается нормально, но событие в журнале событий Windows сообщает о нарушении доступа при обработке объекта групповой политики.

После удаления существующего кода, только с добавленным вызовом обратного вызова, он по-прежнему сообщает об этом нарушении доступа.

Не могли бы вы помочь определить, что нам может не хватать?

//
// Entry point for processing group policy objects.
//
// For full details, see http://msdn.microsoft.com/en-    us/library/windows/desktop/aa374383(v=vs.85).aspx.
//
extern "C" DWORD CALLBACK ProcessGroupPolicyEx (
  __in   DWORD dwFlags,
  __in   HANDLE hToken,
  __in   HKEY hKeyRoot,
  __in   PGROUP_POLICY_OBJECT pDeletedGPOList,
  __in   PGROUP_POLICY_OBJECT pChangedGPOList,
  __in   ASYNCCOMPLETIONHANDLE pHandle,
  __in   BOOL *pbAbort,
  __in   PFNSTATUSMESSAGECALLBACK pStatusCallback,
  __in   IWbemServices *pWbemServices,
  __out  HRESULT *pRsopStatus)
{

 if(pStatusCallback)
   pStatusCallback (FALSE, L"Aaaaargh!");

   return (0);
}

Этот код был опробован с использованием статической строки, массива байтов в стеке, массива байтов, которые были добавлены и намеренно утекли - на случай, если метод перешел во владение памяти. Также был CoTaskMemAlloc'd, на всякий случай. Все создают одну и ту же проблему.

(Отредактированная) ошибка в журнале событий:

Windows не может обработать исключение расширения на стороне клиента групповой политики 0xc0000005.

Windows не может обработать исключение расширения на стороне клиента групповой политики 0xc0000005.

Чтобы сделать вещи интересными, это только на некоторых ОС, полностью пропатченная 32-битная XP является одной из определенных проблем. 2008R2 работает нормально.

Да, он нужен нам для работы на XP 32bit.

Другое странное поведение, которое может иметь здесь значение: если мы вызываем эту функцию несколько раз, она терпит неудачу при третьем вызове. Никаких исключений не генерируется, текст не отображается, никакой наш код после выполнения вызова, никаких дополнительных ошибок в журнале событий. Время здесь не имеет значения: это происходит, если вы звоните 3 раза подряд или 3 раза за 5 минут. Этого не произойдет, если мы заключим вызовы в общий блок try / catch. Никаких исключений не обнаружено - отображается весь текст. Весь код запущен. Однако мы по-прежнему получаем ошибку в журнале событий.


person Scott Langham    schedule 16.03.2012    source источник
comment
Возможно, это долгий путь, но второй параметр обратного вызова статуса - это LPWSTR (в отличие от LPCWSTR). Возможно, он действительно пытается каким-то образом изменить строку (а-ля CreateProcessW). Попробуйте передать в стек локальный буфер вместо строкового литерала и посмотрите, что это такое.   -  person Luke    schedule 16.03.2012
comment
Этот код был опробован с использованием статической строки, массива байтов в стеке, массива байтов, которые были добавлены и намеренно утекли - на случай, если метод перешел во владение памяти. Также был CoTaskMemAlloc'd, на всякий случай. Все создают одну и ту же проблему.   -  person Jim T    schedule 19.03.2012


Ответы (1)


Похоже, мы нашли проблему с этим.

Проблема в том, что обратный вызов должен выполняться с использованием соглашения о вызовах __stdcall. По умолчанию Visual Studio создает проекты с соглашением о вызовах __cdecl. Если вы добавите в проект флаг / Gz, по умолчанию он будет использовать __stdcall. Однако мы не могли этого сделать, так как втягиваем другие модули с другими соглашениями о вызовах.

Основная проблема заключается в том, что UserEnv.h определяет обратный вызов следующим образом:

typedef DWORD (*PFNSTATUSMESSAGECALLBACK)(__in BOOL bVerbose, __in LPWSTR lpMessage);

Это странное определение. Все остальные обратные вызовы Windows определяются следующим образом:

typedef INT_PTR (CALLBACK* DLGPROC)(HWND, UINT, WPARAM, LPARAM);

Этот ОБРАТНЫЙ ЗВОНОК важен, он расширяется следующим образом:

#define CALLBACK    __stdcall

Это означает, что по умолчанию все обратные вызовы Windows по какой-то причине определены для использования соглашений о вызовах __stdcall, кроме этого.

Если мы создадим собственное определение обратного вызова:

typedef DWORD (CALLBACK *PFNSTATUSMESSAGECALLBACK_STDCALL)(__in BOOL bVerbose, __in LPWSTR lpMessage);

И присваиваем ему указатель на нашу функцию:

PFNSTATUSMESSAGECALLBACK_STDCALL pStatusCallback = (PFNSTATUSMESSAGECALLBACK_STDCALL)pRawStatusCallback;

Затем мы можем использовать указатель на функцию pStatusCallback с соглашением о вызовах __stdcall, и все будет работать правильно.

person Jim T    schedule 20.03.2012