2010-07-12 2 views
0

Моя цель концептуально проста: я хочу установить глобальную функцию GetMessage, использующую общий дескриптор файла. Проблема возникает из-за того, что, насколько мне известно, DLL, содержащая функцию hook, загружается несколько раз для каждого процесса, каждый со своим «адресным пространством». По этой причине я убежден, что не могу просто обработать DLL_PROCESS_ATTACH DllMain для создания нужного файла, так как будет создано несколько файлов с разными дескрипторами.Приложение к Global Hook DLL

Решение, которое было доведено до моего сведения, это именованные трубы. В основном приложение будет действовать как конец сервера; он однажды создаст файл, а затем предоставит дескриптор файла для DLL-клиентов, поэтому каждый глобальный крючок будет использовать тот же файл.

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

while(1) 
{ 
    HANDLE hPipe = CreateNamedPipe("\\\\.\\pipe\\pipename", PIPE_ACCESS_OUTBOUND, 
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 32, 32, 5000, NULL); 
    if(hPipe == INVALID_HANDLE_VALUE) 
    return 42; 
    if(!ConnectNamedPipe(hPipe, NULL)) 
    return 43; 
    DWORD dwWritten; 
    WriteFile(hPipe, logFile, sizeof(logFile), &dwWritten, NULL); 
    FlushFileBuffers(hPipe); 
    DisconnectNamedPipe(hPipe); 
    CloseHandle(hPipe); 
} 

Тогда я обрабатывать DLL_PROCESS_ATTACH в DllMain как так:

case DLL_PROCESS_ATTACH: 
{ 
    HANDLE hPipe; 
    while(1) 
    { 
    hPipe = CreateFile("\\\\.\\pipe\\pipename", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); 
    if(hPipe != INVALID_HANDLE_VALUE) 
     break; 
    WaitNamedPipe("\\\\.\\pipe\\pipename", NMPWAIT_USE_DEFAULT_WAIT); 
    } 
    DWORD dwRead; 
    ReadFile(hPipe, logFile, sizeof(logFile), &dwRead, NULL); 
    CloseHandle(hPipe); 
    break; 
} 

Проще говоря, это не работает, и я не могу понять, почему. Есть ли что-то, что мне не хватает или что-то не так в моем коде?

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

Спасибо за любую помощь.

ответ

1

Существуют сильные ограничения в отношении того, какие системные API вы можете назвать в течение DLL_PROCESS_ATTACH или DLL_THREAD_ATTACH. Из функции MSDN documentation.

точки входа должны выполнять только простую инициализацию или задач терминации. Он не должен вызывать функцию LoadLibrary или LoadLibraryEx (или функцию, вызывающую эти функции), так как это может создавать циклы зависимостей в заказе загрузки DLL . Это может привести к тому, что DLL используется до того, как система выполнит свой код инициализации . Аналогично, функция точки входа не должен вызывать FreeLibrary функции (или функцию, которая вызывает FreeLibrary) во время окончания процесса, потому что это может привести к DLL используется после того, как система выполнила свою завершения кода.

Поскольку Kernel32.dll гарантированно быть загружена в адресное пространство процесса когда функция начального точка называется, вызывая функции в Kernel32.dll не приводит к DLL используется до ее инициализации код был выполнен. Следовательно, функция входа может вызывать функции в Kernel32.dll , которые не загружают другие DLL.Для примера DllMain может создавать объекты синхронизации , такие как критические разделы и мьютексы, и использовать TLS. К сожалению, в Kernel32.dll нет исчерпывающего списка безопасных функций .

Windows 2000: Не создать именованный объект синхронизации в DllMain , так как система будет загружать дополнительные DLL.

Вызов функции, которые требуют DLL, кроме Kernel32.dll может привести к проблемам, которые трудно диагностировать. Например, , вызывающий функции пользователя, оболочки и COM , может привести к ошибкам нарушения доступа, , поскольку некоторые функции загружают другие компоненты системы . И наоборот, при вызове такие функции, которые выполняются при завершении , могут привести к нарушению доступа ошибок, поскольку соответствующий компонент , возможно, уже был разгружен или неинициализирован.

Для экспериментального класса рассмотрите возможность использования события присоединения к потоку, чтобы увидеть, что происходит. Для производственных работ вам понадобится полностью пересмотренный подход, который не будет работать в DllMain. Вы можете видеть выше, что будущая история будет включать больше ошибок в этой ОС.

+0

Есть ли у вас какие-либо предложения относительно того, какой другой метод я могу использовать, который не связан с DllMain? – kaykun

+0

Я не знаю достаточно о том, что вы пытаетесь выполнить с сообщением, отправленным по именованному каналу. Какова ваша общая цель? –

+0

Основное приложение создает файл и устанавливает глобальный крючок GetMessage, используя SetWindowsHookEx.Каждый процесс (или поток, я честно не знаю, который) вводится с помощью функции hook, расположенной в DLL. Предполагается, что функция hook используется для записи в файл, который был создан в основном приложении. Поэтому мне нужно каким-то образом передать дескриптор файла в приложении ко всем экземплярам функции hook. – kaykun

1

Мне кажется, что основной проблемой может быть последний параметр функции CreateNamedPipe (SECURITY_ATTRIBUTES) или другие проблемы безопасности (см. Ниже).

Я действительно не понимаю, какую информацию вы планируете разыграть в logFile, которая может быть не более 32 байт (16 WCHAR). Использование sizeof() в CreateNamedPipe было бы также немного лучше (подумайте также о 64-битных операционных системах). Вы хотите отправить дескриптор в файл журнала, открытый в одном процессе, на другие процессы? Если вы это сделаете, вы должны использовать такие функции, как DuplicateHandle (см. http://msdn.microsoft.com/en-us/library/ms724251.aspx). В общем, пример кода для связи по именованному каналу, который вы опубликовали, мне кажется не совсем хорошим. Я рекомендую вам в первую очередь отлаживать именованную связь связи между DLL (с ​​помощью двух отдельных клиентских процессов, которые лучше работают под разными учетными данными пользователя и серверного процесса, который создал этот канал).

Существует ограничение по причине использования API Windows внутри DLL_THREAD_ATTACH, но при использовании Kernel32.dll в вашем случае кажется мне безопасным.

Я не знаю, какие связи вы хотите реализовать, но в целом использование разблокированного режима, как в использовании завершения процедур или других асинхронных операций (см http://msdn.microsoft.com/en-us/library/aa365788.aspxhttp://msdn.microsoft.com/en-us/library/aa365788.aspx и http://msdn.microsoft.com/en-us/library/aa365601.aspx) внутри DllMain может быть лучше.

Еще одна небольшая рекомендация: вы должны использовать DisableThreadLibraryCalls() в случае DLL_PROCESS_ATTACH и попытаться выбрать разумный базовый адрес DLL (коммутатора компоновщика), который уменьшит перемещение DLL во время его загрузки в разных процессах. Это ускорит работу программы и сохранит память.

Смежные вопросы