2014-09-19 2 views
6

Я использую стили Delphi XE6 и VCL. У меня есть основное приложение и dll. Мое основное приложение включило темы времени выполнения, и я использую файлы стиля vcl. Я очень похож на свои DLL. Я включил темы времени выполнения и добавил VCL.Themes, VCL.Styles в области использования и файл ресурсов с файлом стиля VCL внутри него. Когда DLL загружается, я загружаю VCL-стиль из ресурсов и устанавливаю его для DLL-gui. Основное приложение и DLL не построены с пакетами времени выполнения.VCL Style из DLL влияет на TMenuItem в приложении

Теперь у меня есть основной графический интерфейс приложения, оформленный в собственном стиле и DLL-стиле с собственным стилем. Кажется, что это хорошо работает до тех пор, пока ...

Когда я нажимаю кнопку в главном приложении, событие, которое открывает TPopupMenu, оно оформлено в том же стиле, что и графический интерфейс DLL вместо основного стиля приложения. Если я перемещаюсь по меню, я тоже получаю AV и сбой программы. Взгляните на прикрепленное изображение.

Что я делаю неправильно? Единственным обходным решением, которое я сейчас вижу, было бы создание собственного настраиваемого TPopupMenu, полученного из какого-либо другого элемента управления. enter image description here


Как я обещал, что я подготовил простую демонстрационную программу, которая похожа на мое приложение. Он состоит из хост-приложения с собственным стилем и DLL со стилем, добавленным к ресурсу. Запустите его и нажмите кнопку «Всплывающее окно», затем попробуйте выбрать что-нибудь из всплывающего окна. Он будет разбиваться и останавливаться в каком-то StdWindowProc или что-то в этом роде. Также, если вы перейдете в меню оконной системы (левый верхний угол), когда вы попытаетесь выбрать что-то из этого меню, вы заметите, что системное меню оформлено как DLL gui и сбой. Ссылка на файл RAR: dropbox.com/sh/f2jmbsmw18akpyg/AAA6SWdBmVhf6n6K-mvYLLmua?dl=0

enter image description here

Спасибо за вашу помощь.

+0

Наиболее вероятным объяснением является то, что вы пропускаете объекты VCL через границу между вашими модулями, что запрещено, если вы не используете пакеты времени исполнения. –

+1

Другая возможность, о которой я могу думать, это то, что код стилей VCL, возможно, включает в себя перечисление ресурсов и перечисляет ресурсы во всем процессе. Быстрое сканирование кода предполагает, что это может быть проблемой, если 'AutoDiscoverStyleResources' является' True'. Этот вызов для 'EnumModules' выглядит немного для меня. –

+1

Если вы не можете отладить это, я думаю, вы просите нас вникать в него. Для этого нам может потребоваться код, демонстрирующий проблему. –

ответ

6

Это фундаментальная проблема с стилями VCL и тем, как они стилят меню. Стилирование осуществляется с помощью крючка с широким спектром. В частности, крюк CBT установлен по вызову SetWindowsHookEx от TCustomStyleEngine.CreateSysHook в блоке Vcl.Themes. Фактически, крючок применяется только к потоку GUI, но это процесс широкий в том смысле, что в этом процессе есть только один поток GUI.

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

Нет простого способа обойти это и полностью поддерживать стили в обоих модулях. То, что мы имеем здесь, является фундаментальным следствием конструкции. Система не была предназначена для поддержки нескольких экземпляров VCL. Если вы хотите использовать стили VCL в нескольких модулях, дизайнеры ожидают, что вы будете использовать пакеты времени исполнения.

Я полагаю, что вы могли бы получить некоторую тягу, управляя DLL из совершенно другой нити. Это связано с загрузкой DLL из этого другого потока, чтобы VCL инициализировался в потоке. И все вызовы в DLL должны быть из этого потока. И вам нужно будет запустить цикл сообщений в этом потоке. Возможно, вы сможете сделать эту работу, но я в этом сомневаюсь. Даже при всех упомянутых оговорках вам все равно придется справляться с тем фактом, что у вас есть два потока GUI, которые представляют всевозможные проблемы с обработкой очереди ввода.

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

Эта версия вашей DLL (после того, как я упростил ее также), удаляет крючок.

library VCLStyleDLL; 

{$R 'Style.res' 'Style.rc'} 

uses 
    VCL.Styles, 
    VCL.Themes, 
    VCL.SysStyles; // to gain access to TSysPopupStyleHook 

{$R *.res} 

begin 
    TStyleManager.TrySetStyle('Glossy', false); 
    TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook); 
end. 

С этой версией библиотеки DLL исполняемый файл хоста не страдает проблемами, описанными в вашем вопросе.

+0

Спасибо, Дэвид за ваше объяснение. С тех пор мы использовали сторонние AlphaControls для скина нашего приложения, но теперь мы решили использовать стили Delphi VCL, и я просто открываю для себя технологии. Спасибо также за редактирование/просмотр моего сообщения и сделать эту тему более читаемой. Я очень ценю это. – Nix

+0

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

+0

+1 отличное объяснение :) – 2014-09-19 18:22:34

7

Как говорит Дэвид, это вызвано тем, что каждый экземпляр VCL устанавливает крючок, чтобы обнаружить, когда создано всплывающее меню (# 32768). Таким образом, одновременно работают два экземпляра крючка.

В качестве обходного пути вы можете отключить крюк стиля popupmenu в dll (или в приложении), используя функцию UnRegisterSysStyleHook, определенную в модуле Vcl.SysStyles.

TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook); 
+0

+1 за показ официального способа отменить регистрацию этого крючка –

+0

Я не знал об этом, но потому, что я только начал с стилей VCL, и я еще не знаком с ними. С тех пор мы использовали сторонние элементы управления Alpha, но мы решили использовать стили Delphi VCL. Спасибо. – Nix