Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Visual C++/MFC/WTL > Как в CPropertySheet добавить CPropertyPage из DLL |
Автор: Bukmop 1.6.2006, 12:17 | ||||||||
Помогите в CPropertySheet добавить CPropertyPage из DLL TestEXE.cpp
TestDLL.h
TestDLL.cpp
TestDLL.rc
|
Автор: DeadSoul 1.6.2006, 12:23 | ||
Единственная проблема там - ресурсы. Вот пример для родительского диалога в ехе, а child-ового в dll
Делай аналогично |
Автор: Bukmop 1.6.2006, 13:09 |
Не совсем понял, куда это девать. Поэтому уточнил задачу. |
Автор: Earnest 1.6.2006, 17:13 | ||||
DeadSoul имел в виду случай, когда ресурс страницы загружается из другого модуля, у тебя же там весь код, как я понимаю. Нет практически никакой разницы, где определен класс-страница - в другой DLL или в том модуле, где создается PropertySheet. Достаточно просто сделать класс экспортируемым. Можно не весь класс, а только конструктор и деструктор. Дальше все, как у тебя в коде. Экспортировать можно 2 способами: 1) __declspec( dllimport )\__declspec( dllexport ): экспортируемая сущность должна быть объявлена в своем модуле как __declspec( dllexport ), а в чужом - как __declspec( dllimport ) Проще всего это достигается макросом:
Макросимвол _TESTDLL нужно определить для всего проекта TestDll (в свойствах проекта) 2) def - файл. Просто поместить имена всех нужных функций в def-файл. На самом деле это не очень просто, т.к. имена должны быть декорированными, т.е. такими, как их видит линкер. Но можно сделать так: сначала ничего никуда не пишем, а просто пытаемся собрать приложение. Линкер будет орать насчет неразрешенных ссылок. Акуратно копируем имена, которые он хочет, прямо из сообщения об ошибке, и помещаем их в def- файл. Выглядеть будет примерно так:
Второй способ плох тем, что при малейшем изменении интерфейса экспортируемых функций нужно менять запись в def-файле. Даже просто при изменении доступа с public на, скажем, protected... Сдругой стороны, есть сущности, которые иначе как через def-файл не проэкспортируешь. |
Автор: Bukmop 1.6.2006, 18:09 |
Подобное, я уже пробовал, используя встроенный в MFC макрос AFX_EXT_CLASS. Но я понял так, что это для экспорта класса на стадии компиляции, а не выполнения. Короче, линкер по-прежнему ругался. |
Автор: Earnest 1.6.2006, 18:16 |
Значит, что-то ты сделал неправильно. Если DLL одна, то вполне можно обойтись макросом AFX_EXT_CLASS, только чтобы он заработал, DLL должна быть создана как ExtensionDLL (кроме всего прочего, в проект добавляется макросимвол _AFXEXT, а без него, конечно, не получится). В общем, опиши, что ты сделал, и какие ошибки дает линкер. Добавлено @ 18:19 Нет, как я понимаю, код Property Sheet находится в EXE. |
Автор: Bukmop 1.6.2006, 18:32 | ||
Earnest, ты как всегда всё правильно понимаешь, а я очень извиняюсь за ложную информацию (сам уже начал путаться в результатах попыток, голова уже плохо соображает). В том случае всё скомпилировалось, но вытащить страницу не получилось. Делал так:
Получал p1=NULL; А если бы делал всё правильно – то всё бы работало (чудес-то не бывает). |
Автор: Earnest 1.6.2006, 18:55 |
Да уж, зову не знамо кого... ![]() Вообще-то при такой связи с DLL ее лучше не динамически подключать, а прямо к проекту, чтобы еще линкер все ссылки разрешил. Так ты разобрался в конце концов? |
Автор: Bukmop 1.6.2006, 19:07 | ||||
Я тычусь как слепой котёнок, т.к. не понимаю, как правильно сделать, сижу и перебираю все мыслимые и немыслимые варианты (пока впустую). Вот последний: DLL
EXE
Благодарю за терпение, по себе знаю как трудно с такими... Но мне уже начинает казаться, что я хочу невозможного. |
Автор: DeadSoul 1.6.2006, 19:31 |
Bukmop, так ответь на простые вопросы: 1. Где находится твой ProperySheet( dll\exe )? 2. Где находится PropertPage( dll\exe )? 3. Где ты создаешь твой PropertySheet( dll\exe )? P.S. LoadLibrary тут не нужен |
Автор: Bukmop 1.6.2006, 19:35 |
DeadSoul 1. exe 2. dll 3. exe P.S. Смысл: Есть dll-ки - есть странички. Нет dll-ек - нет страничек. Изменились свойства dll-ек - не нужно трогать exe-ник. |
Автор: DeadSoul 1.6.2006, 20:35 |
4. Как ты получаешь доступ к классам\функция в dll - прописываешь в exe все необходимые *.h+*.lib - LoadLibrary+GetProcAddress |
Автор: Bukmop 1.6.2006, 20:59 |
4. Пытался - LoadLibrary+GetProcAddress. lib мне не подходит, т.к. при изменении dll-ки придётся перекомпилировать exe-шник. |
Автор: Earnest 2.6.2006, 06:30 |
Так бы сразу и сказал, что тебе обязательно нужно загружать DLL динамически. Тогда действительно, LoadLibrary + GetProcAddress. НО: получить через GetProcAddress можно только то, что есть в таблице экспорта. Поскольку экспортировать придется через def-файл, то с классами возиться сложно (надо ведь декорированные имена записывать для всех функций), Поэтому делаем так: 1) К EXE ничего не подключаем (h-файла с описанием страницы), придется ему довольствоваться интерфейсом CPropertyPage. 2) В DLL создаем функцию CPropertyPage* CreatePage(/* нужные параметры */). Причем объявляем ее как extern "C", чтобы не мучиться с именами. И записываем в def-файл. Вот только не помню, это будет просто CreatePage или _CreatePage, нужно уточнить. Впрочем, с неправильной записью в def-файле DLL не соберется.Еще можно уточнить имя с помощью map-файла. 3) Дальше все как ты делал, GetProcAddress c тем именем, что записано в def. 4) Только теперь у тебя выделена память под страницу, поэтому не забудь ее удалить после DoModal. 5) Не уверена, что по delete будет вызван правильный деструктор. Я бы проверила, поставив туда точку прерывания... |
Автор: DeadSoul 2.6.2006, 10:19 | ||||
Достаточно второго.
Нужно добавить вторую функцию DestroyPage Вчера вечером попробывал. Там действительно неслабые траблы(похоже, с ресурсами). Сегодня еще поковыряюсь |
Автор: Earnest 2.6.2006, 11:51 | ||
Не совсем. Запись в def - необходима, без extern "C" можно обойтись, но имена C++ функций довольно сложно декорируются, именно декорированные имена и надо указывать в def (а потом в GetProcAddress). Если в def написать просто CreatePage, DLL не соберется (с точки зрения линкера, такой функции нет).
Хоть и написала сама, что проверить надо, но как-то это странно: ведь деструктор-то виртуальный. Тогда что, и другие виртуальные ф-и работать не будут? Как же интересно COM работает на одних виртуальных вызовах без всяких экспортов... Наверняка "трабла" не в этом. И не в ресурсах. Не может быть там никаких проблем, если все правильно делать. |
Автор: DeadSoul 2.6.2006, 11:56 | ||||||||
У меня почему-то собралось и успшно работало....
Выделять\освобождать память нужно внутри одного модуля, т.е. не рекомендуется выделять память в exe, а осовобождать в dll(и наоборот)
Это абсолютно другая история.
В них. Только в них. Готов показать кодом. |
Автор: Earnest 2.6.2006, 12:32 | ||
Это может быть, но все равно не совсем понятно... Там действительно есть какие-то проблемы, но мы их как-то обошли... уже не помню... ясно, что хип должен быть общий, тогда проблем нет. В чем это другая? Виртуальные функции и в африке виртуальные... Проблема с удалением страницы действительно может быть не в вызове деструктора (там как раз все должно сойтись), а в освобождении памяти. Давай, только желательно покороче. ![]() |
Автор: DeadSoul 2.6.2006, 12:44 | ||||||
Насколько помню все зависит от линковки рантайма(динамическая\статическая)
Так у меня все падает при попытке создания страницы ![]()
Сейчас нарисуем |
Автор: DeadSoul 2.6.2006, 13:40 |
Вот пример(Компилятор - 8-ая студия). P.S. Заметь extern "C" нигде нет |
Автор: Bukmop 2.6.2006, 16:15 |
Нашёл два отличия с тем, что делал я: 1. Мой EXE-шник - Use MFC in a Static Library 2. Моя DLL-ка - MFC extension DLL Но и после всего этого выдаёт - "A required resource was unavailable". |
Автор: DeadSoul 2.6.2006, 16:25 | ||
Bukmop, я знаю. Я и хотел показать, что там проблема ровно одна - ресурсы. Добавлено @ 16:25 Сейчас покопаюсь еще. Может смогу ресурсы победить. Добавлено @ 16:33 В общем, выход ровно один. Отнаследоватся от CPropertySheet и переопределить BuildPropPageArray |
Автор: Bukmop 2.6.2006, 16:44 | ||
СПАСИБО! Твой вариант я работать заставил.
И он таки нашёл ресурс. Теперь буду воевать со своим Static Library. |
Автор: Earnest 2.6.2006, 17:10 | ||||
DeadSoul, не могу загрузить твой проект, т.к. у меня нет 8й студии, а в 7 эта зараза грузиться не желает... Так что придется всухую...
Это неправильно, все должно использовать Shared MFC DLL, это приведет к Shared RunTime DLL. Тогда, по идее проблем с памятью не должно быть. Кроме того, динамически загружаемая DLL лучше делать Regular, хотя все равно не понимаю, откуда проблемы с ресурсами. ... Посмотрела код ... Как насчет вот этого?
В смысле, в начало кода CreatePage нужно вставить AFX_MANAGE_STATE(AfxGetStaticModuleState()); Не знаю, почему extern "C" перестал быть нужен... раньше (в 6-ке) было нужно ... |
Автор: DeadSoul 2.6.2006, 17:22 | ||||
Посмотри просто cpp\h\def файлы. Там добавления к стандартным проектам минимальны.
Не спасет. Доступ к ресурсу нужен в момент создания окна, а окно создается совсем не там Рад за тебя. Я-то слона не заметил.... |
Автор: Earnest 2.6.2006, 17:29 | ||
Была не права: лучше делать Extension DLL из-за проблем со картой окон... Тогда, конечно, AFX_MANAGE_STATE(AfxGetStaticModuleState()) не надо. Руками сделала проекты в 7 студии (диалоговое приложение + extension DLL), добавила туда файлы, все нормально трудится. Желаете проект? Добавлено @ 17:30
Меня спасло, но потом пошли ASSERTы из-за WndMap, и пришлось переделывать на Extension DLL. |
Автор: Bukmop 2.6.2006, 17:31 |
Earnest, у меня тоже 7.1, но т.к. в проекте нет ничего навороченного я просто в файлах vcproj исправил Version="8,00" на "7.10", а в sln ...Format Version 9.00 на 8.00 И VS это всё прекрасно переварила. А проблема с ресурсами видимо из-за того, что сама Page крейтится уже в Sheet-е. (Безобразие!!! Мало того, что до четырёх часов вообще не мог достучаться до форума. И сейчас перебои. Не успеваю за жизнью.) Проект конечно желаю! |
Автор: Earnest 2.6.2006, 17:48 |
Bukmop, нет там проблем: я просто сделала свой проект и добавила туда файлы, все получилось. Вот проект, под 7.1. А насет экспорта я тоже поняла: DeadSoul использовал нумерацию (@1). Действительно, в этом случае линкер как-то без декораций обходится, но я так никогда не делала, поэтому и забыла ![]() Упс! Не глядя заархивировала прямо с объектниками... То смотрю никак загрузиться не может. |
Автор: Bukmop 2.6.2006, 21:35 |
Ужас! Это получается, что я почти неделю стоял на ушах, дошёл до полного маразма и Вас на два дня озадачил. И всё только потому, что у меня основной проект - Static (так уж давно повелось, таковы были требования). И что теперь делать? Я так понял, что в Static - это без шансов. Спасибо всем за помощь и глубокие знания. Надеюсь, я Вам не мешал (ШУТКА). |
Автор: DeadSoul 2.6.2006, 21:44 | ||
Не только. С моей точки зрения основная трабла была в ресурсах. ![]() |
Автор: Bukmop 2.6.2006, 22:02 |
В Static она так и осталась. |
Автор: DeadSoul 2.6.2006, 22:11 | ||
При статической линковки MFC-ей? Добавлено @ 22:18 Bukmop, ты можешь показать как победил ресурсы? Что и куда ты вписал? |
Автор: Bukmop 2.6.2006, 22:40 |
DeadSoul, именно при статической линковке MFC-ей я проблему так и не решил. Теперь буду решать, продолжать биться как рыба об лёд или переписывать все ранее созданные библиотеки этого проекта на Shared DLL. О ресурсах: В твоём примере (как я уже упоминал) после получения в EXE-ке указателя на "CPropertyPage" из DLL-ки, принудительно поменял HINSTANCE в структуре PROPSHEETPAGE на DLL-ный. А пример Earnest и так работает. |
Автор: Earnest 3.6.2006, 06:16 |
Не знаю, получится ли победить ситуацию, если оставить MFC DLL статически прилинкованной. Думаю, что нет. Там ведь в чем дело-то: в MFC есть объект (какой-то трам-пам-пам-state, который синхронизирует доступ к различным ресурсам, в том числе к ресурсам-RC, а также картам окон (т.е. HWND-CWnd) и т.д. Чтобы все работало, нужно, чтобы эта штука была в приложениии одна. Если каждый модуль будет использовать свой экземляр статически прилинкованной MFC DLL, то и этот менеджер у всех будет свой... В общем, не договорятся. Кроме того, использование статической MFC влечет за собой статический RunTime, т.е. у каждого модуля будет свой распределитель памяти, и отсюда проблемы с освобождением "чужой" памяти. Таким образом, статическая линковка MFC подходит только для приложения, которое состоит из одного EXE. |
Автор: DeadSoul 3.6.2006, 11:53 | ||
Earnest, почему в твоем примере нет проблем с ресурсами?
В каком месте ты это менял? |
Автор: Bukmop 3.6.2006, 12:47 | ||
Строка - 21. |
Автор: DeadSoul 3.6.2006, 13:18 |
Bukmop, thanks. Как-то не посмотрел, что этот мембер открытый. |
Автор: Earnest 5.6.2006, 08:14 |
![]() ![]() ![]() |
Автор: DeadSoul 5.6.2006, 21:33 |
Earnest, честно говоря, хотеось бы более развернутый ответ |
Автор: Bukmop 6.6.2006, 09:37 |
DeadSoul, там всё оказалось просто. Earnest использовала MFC extension DLL, а там создаётся CDynLinkLibrary и по этому AfxFindResourceHandle находит нужный ресурс. |
Автор: Earnest 6.6.2006, 13:13 |
Извини, я просто не очень поняла, вопрос это или наезд ![]() В дополнение к тому, что уже сказал Bukmop: WinAPI, чтобы найти ресурс, нужно указать точно в каком модуле он лежит. MFC это дело слегка скрывает: ExtensionDLL связываются в цепочку, которая хранится во внутренней глобальной переменной. Когда ты запрашиваешь ресурс, MFC проходит по цепочке, опрашивая все библиотеки по очереди, пока не найдет нужный. |
Автор: DeadSoul 6.6.2006, 20:30 |
Bukmop, Earnest, спасибо. |