Pull to refresh

Comments 6

Комментарии? Их есть у меня.
  1. Каждое создание функции приводит к линейному поиску среди списка модулей. unordred_map?
  2. Кажется, это всё не потокобезопасно
  3. По процедуре нельзя получить её модуль. Это не даёт возможности управлять вызовами LoadLibrary/FreeLibrary, а это может быть важно.
  4. FreeLibrary очень уж далеко от LoadLibrary
  5. Почему-то, если FreeLibrary упало, то assert(), а LoadLibrary проверяется, судя по всему, в момент вызова процедуры. Если LoadLibrary вернул NULL, получится вызов нулевого указателя. Это не UB, случаем?


А в целом — идея интересная, особенно оператор приведения типа — в голову такое не приходило.
Справедливости ради, стоит отметить, что кроме двух упомянутых в статье способов работы с dll, есть еще один, так называемый delay load режим. По сути это нечто среднее между load-time и run-time режимами.
А как он выглядит? Как человеку, работавшему в основном в Linux'е мне бы хотелось видеть что-нибудь подобное вот этому:
extern void __cxa_finalize (void *) TARGET_ATTRIBUTE_WEAK;
...
if (__cxa_finalize)
    __cxa_finalize (__dso_handle);
Всё. Если функция нашлась — её можно вызвать. Напрямую и без всяких обёрток. Не нашлась — точно так же можно проверить, что это случилось — и тоже напрямую и без всякого C++.

P.S. Вообще же меня поражает насколько Linux и Windows протовиположны: в Linux «внизу» — чёткая архитектура, всё удобно, красиво и причёсано, но чем ближе «к человеку» тем более растрёпанным всё выглядит — CLI уже не так удобны как API, а GUI часто вообще что-то с чем-то. В Windows — всё равно наоборот: «сверху» всё красиво и причёсано, но стоит копнуть глубже, так обнаруживаются такие многоуровныевые «костыли» на «костылях», что волосы дыбом встают. Нет в жизни совершенства, увы :-(
К сожалению не так удобно.
Фактически в данном режиме в месте вызова импортируемой функции будет вызываться хелпер-функция __delayLoadHelper или __delayLoadHelper2 (зависит от версии ms studio). Их реализация содержится в библиотеке delayimp.lib (потребуется с ней слинковаться), ее код лежит здесь — vc/include/delayhlp.cpp.
По нему видно, что данный хелпер осуществляет вызов LoadLibrary/GetProcAddress (это делается только один раз).
В случае неудачи — не найден модуль или не найдена фунция — будет брошено исключение посредством RaiseException.
Соответственно его можно перехватить и определить причину ошибки.
Ясно. Да, во многих случаях это будет работать, но во многих — приведёт к видимым для человека проблемам. Типичный пример — какой-нибудь TWAIN: если у нас установлены соответствующие библиотеки, то хочется показать соответствующую кнопочки, но если в момент загрузки программы начать TWAIN инициализировать, то там может пойти деятельность не то, что просто замедляющая работу, а вообще нервирующая: сканер включится, что-то делать начнёт, etc.

Но да, во многих случаях такой подход годится. Будем иметь ввиду :-)
1. Не потокобезопасно, причем существование общего кэша не очевидно из интерфейса библиотеки.
2. Указание имени модуля рядом с именем процедуры, по моим ощущуениям, не совсем C++ style — избыточный runtime overhead. Если используются несколько процедур из одного модуля, то имя модуля все равно будет задаваться переменной или константой, так почему бы эту переменную не сделать схожей с ntmodule? Таким образом убирается поиск в кэше и добавляется управление временем жизни хэндла.
Sign up to leave a comment.

Articles