Как программно определить, работает ли код в общей DLL или EXE? - PullRequest
4 голосов
/ 27 октября 2009

A имеет класс C #, который упрощает обработку глобальных горячих клавиш. Этот класс использует функцию Win32-API RegisterHotKey() для регистрации горячих клавиш.

В соответствии с MSDN для этой функции требуется значение идентификатора в диапазоне от 0x0000 до 0xBFFF, когда вызов из приложения и значение идентификатора в диапазоне от 0xC000 до 0xFFFF, когда вызов из общей библиотеки DLL. GlobalAddAtom() можно использовать для получения идентификатора в случае запуска в DLL.

Чтобы скрыть это различие от пользователя класса, сам класс должен решить, какой Диапазон идентификаторов должен использоваться при регистрации горячей клавиши. Ну, а для этого класс должен быть в состоянии определить, работает ли его код внутри приложения или внутри общая DLL.

Но как это сделать? Какой лучший способ C # /. NET сделать это?

Ответы [ 3 ]

3 голосов
/ 27 октября 2009

Это ваш класс - вы знаете , куда вы его кладете.

Если вы им не делитесь, просто выберите идентификатор ниже 0xBFFF и покончите с этим.

Если ваш класс принадлежит DLL, которая может использоваться несколькими приложениями ... Или просто может использоваться кодом, который вы не контролируете и, следовательно, не можете отсортировать идентификаторы для ..., тогда используйте GlobalAddAtom(), чтобы получить идентификатор (и не забудьте позвонить GlobalDeleteAtom() после того, как вы отмените регистрацию горячей клавиши ).


Объяснение

Вероятно, стоит потратить минуту на то, чтобы подумать о том, почему существуют два разных диапазона идентификаторов и почему в документации по API рекомендуется использовать GlobalAddAtom() для получения идентификатора в последнем диапазоне для общих библиотек DLL. Начнем с документации по параметру RegisterHotKey():

ID
[in] Определяет идентификатор горячей клавиши. Если параметр hWnd имеет значение NULL, тогда горячая клавиша связана с текущим потоком, а не с конкретным окном. Если горячая клавиша уже существует с такими же параметрами hWnd и id, см. Замечания о предпринятых действиях.

Исходя из этого, мы можем предположить, что горячие клавиши однозначно идентифицируются одной из двух потенциальных пар информации: дескриптор потока или окна и произвольное 16-битное число. Если вы укажете дескриптор окна (HWND), тогда сообщение будет отправлено в это окно; в противном случае он отправляется в ветку.

Итак ... Если вы зарегистрируете только одну горячую клавишу для данного окна, идентификатор на самом деле не имеет значения 1 ; никто не может зарегистрировать горячую клавишу для этого окна, и события горячих клавиш для других окон будут публиковаться в этих окнах. Точно так же, если вы зарегистрируете только одну горячую клавишу без окон для данного потока, вы получите только сообщения для этой горячей клавиши. Если вы контролируете весь код для своего приложения, вы можете выбрать любые идентификаторы для своих горячих клавиш , используя любую технику, которую хотите назначить; никто больше не наступит на них, потому что у вас есть весь код , который сможет наступить на них!

Но что, если вы пишете подпрограмму общего назначения, которая может быть вызвана другим кодом? Тогда вы не сможете надежно выбрать постоянный идентификатор, так как вызывающий абонент может уже использовать этот идентификатор, и если они также используют то же окно или поток, вы в конечном итоге переопределите их горячие клавиши. Или что если (как в вашем случае) вы не знаете, сколько горячих клавиш будет зарегистрировано до времени выполнения?

Вам нужен способ гарантировать, что идентификатор, выбранный вами во время выполнения, будет тем, который никто другой не использует. Здесь GlobalAddAtom() вступает в игру: вы передаете ему строку, и она дает вам идентификатор, который гарантированно соответствует этой строке, и никакой другой; это фактически уникально для системы, если кто-то еще не пропустит ту же строку - и вы, вероятно, можете придумать уникальную строку; просто используйте название вашей компании или номер социального страхования, а также префикс, который вы увеличиваете для каждого нового атома, который вам нужен. Или, если вы действительно параноик, используйте GUID.

Истина за истиной

В связи с этим позвольте мне попытаться устранить некоторую путаницу: на самом деле Windows не волнует, находится ли код, вызывающий RegisterHotKey(), в DLL. Это не может . Рассмотрим следующую процедуру:

void RegisterSuperHappyFunHotKey(HWND hWnd, int id, 
                                 unsigned int fsModifiers, unsigned int vk)
{
   RegisterHotKey(hWnd, id, fsModifiers, vk);
}

Эта подпрограмма ничего не делает , но перенаправляет свои параметры в функцию WinAPI, ни одна из которых не идентифицирует вызывающий модуль.Если бы он жил в DLL, он вел себя так же, как если бы он был скомпилирован в само приложение.У Windows нет надежного способа узнать, откуда происходит вызов, а сама горячая клавиша привязана к окну и потоку (или одному потоку), любой из которых может управляться кодом внутри или из DLL.Теперь, конечно, у вас могут быть собственные требования к приложениям или библиотекам: если ваша DLL создает окно и устанавливает для него горячую клавишу, тогда вы захотите отменить регистрацию этой горячей клавиши при уничтожении окна... Но это ваша собственная задача - справиться с ней по своему усмотрению.

MSDN определяет два диапазона идентификаторов по одной уважительной причине: чтобы вы, автор библиотеки DLL, избегали наступления на идентификаторы, используемые приложениемавтор.Если вы автор приложения, тогда мир - это ваша устрица - вы контролируете (по большей части), какой код загружается и выполняется в процессе вашего приложения, и, следовательно, можете принимать решения относительно того, какие идентификаторывы используете , но считаете нужным : начиная с 0 и увеличивая значение для каждой новой горячей клавиши, вполне допустимо.Но как только вы зайдете в верхний диапазон идентификаторов, вам придется использовать GlobalAddAtom(), как если бы вы были DLL - или вы рискуете столкнуться с идентификатором, сгенерированным таким образом сторонним кодом, загруженным изDLL.Это ... социальный контракт сортов.

Сводка:

Бит "общая DLL" - здесь красная сельдь;если вы можете знать идентификаторы всех горячих клавиш, зарегистрированных вашим приложением, просто выберите число ниже 0xBFFF и используйте его.Если вы не можете этого сделать, поскольку ваш код будет использоваться несколькими абонентами (как и ваш ...), получите идентификатор, используя GlobalAddAtom(), и используйте его.

Рекомендация

ДляПо этим причинам я рекомендую вам do использовать GlobalAddAtom() для класса, который вы разрабатываете, просто потому, что это звучит так, как будто вы не знаете, будете ли вы встраивать его в данный момент или нетприложение вашего собственного дизайна (где вы управляете используемыми идентификаторами) или DLL, которая будет загружена каким-либо другим приложением (где вы не ).Не беспокойтесь - вы не нарушаете контракт, притворяясь библиотекой DLL, если вы следуете правилам, установленным для вызывающих DLL.


1 хорошо, так что есть пара системных идентификаторов горячих клавиш, которые вы должны остерегаться ...

3 голосов
/ 27 октября 2009

Попробуйте это:

bool isDll = this.GetType().Assembly.EntryPoint == null;

MSDN:

Assembly.EntryPoint Property

"Значение свойства Объект MethodInfo которая представляет собой точку входа эта сборка. Если нет точки входа найдено (например, сборка является DLL), нулевая ссылка (ничего в Visual Basic) возвращается. «

1 голос
/ 28 октября 2009

Завершение ответа Филиппа:

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

Assembly assembly = Assembly.GetCallingAssembly();
Boolean isDll = assembly.EntryPoint == null;

Надеюсь, это поможет.

Рикардо Ласерда Каштелу-Бранку

...