Показаны сообщения с ярлыком delphi. Показать все сообщения
Показаны сообщения с ярлыком delphi. Показать все сообщения

воскресенье, 13 февраля 2011 г.

У Delphi будет новый VCL?

Встретилась в блоге Ника Ходжеса интересная новость - разработчик http://www.ksdev.com/ вместе со своими наработками (VGScene, DXScene, or ImageFX) переходит в Embarcadero.

Похоже, после стольких лет, у Delphi наконец появится какой-то вариант векторного UI фреймворка.

пятница, 21 января 2011 г.

Как правильно заблокировать окно от перерисовки

Иногда это может понадобиться, если что-то активно добавляется, открывается и т.п.
Типичный способ, рекомендуемый первым, это вызов LockWindowUpdate. Что есть в корне неверно - в Майкрософт не очень удобно выбрали название функции.
http://www.transl-gunsmoker.ru/2010/12/lockwindowupdate_29.html
http://msdn.microsoft.com/en-us/library/dd145034%28v=vs.85%29.aspx

Правильный пример:
procedure SetWindowLock(AHandle: THandle);
begin
  SendMessage(AHandle, WM_SETREDRAW, 0, 0);
end;

procedure EndWindowLock(AHandle: THandle);
begin
  SendMessage(AHandle, WM_SETREDRAW, 1, 0);
  RedrawWindow(AHandle, nil, 0, RDW_ERASE or RDW_FRAME or
    RDW_INVALIDATE or RDW_ALLCHILDREN);
end;

Хитрость в том, что работает только RedrawWindow, а Invalidate нет.

UPD
На практике оказало не все так весело - окно просто прячется, а не перестает отрисовываться. У нас решилось точечным применением, только к изменяемым элементам - панелям, таб сетам и т.п.

понедельник, 29 марта 2010 г.

Вывод лога DUnit в файл

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

Для решения на скорую руку был набросан простенький класс (чуть более чем полностью слизанный с TextTestRunner), позволяющий логгировать все в файл - FileTestRunner

Проверен на D2009.

среда, 24 марта 2010 г.

Упрощение CheckException в DUnit с помощью анонимных функций

К сожалению, для проверки функции на возврат исключения возможен только для процедуры типа:
TTestMethod  = procedure of object;
procedure CheckException(AMethod: TTestMethod; AExceptionClass: TClass; msg :string = '');
Легким движением руки(наследником или правкой оригинального класса) для свежих версий Delphi упрощаем написание таких тестов:

TTestProc2  = reference to procedure;
TTestCase2 = class(TTestCase)
public
  procedure CheckException2(AMethod: TTestProc2; AExceptionClass: TClass; msg :string = '');
end;
Пример использования:
...
CheckException2(procedure begin DirDelete(TestDir); end, EInOutError);

суббота, 13 февраля 2010 г.

Desktop-интерфес на html?

На днях, долго выбираемй мной домашний антивирус avast!, заполучил довольно симпатичный новый внешний вид(откровенно говоря старый был жутковат). Проходясь по UI в поисках возможности временного отключения резидентной защиты(кстати эта возможность появилась - правда не так удобно как в Касперском) я набрел на форму "О программе", где вычитал, что построен новый интерфейс с использованием библиотеки HTMLayout.

Вспоминая мучения во время разработки пользовательского интерфейса на Delphi - интересная это в общем штука, позволяет привнести в мир desktop разработки все прелести html/css:
HTMLayout – это, грубо говоря, веб-браузер, который Windows-программист может встроить в свою программу и, таким образом, построить интерфейс программы на базе DHTML

Использует ее уже достаточно много крупных компаний, так что для приложений, требующих развитого UI, можно пробовать. Пока хорошей/популярной обертки для Delphi нет, но начало уже положено.

Подробнее, в интервью с разработчиком, можно почитать здесь

пятница, 25 сентября 2009 г.

Динамически загружаемые bpl , их выгрузка и интерфейсы

Очевидная, но съевшая кучу времени ошибка.

Если в проекте используются динамически загружаемые/выгружаемые bpl, следует помнить, что при выгрузке информации о классах, реализованных в этих bpl в памяти не остается.

Предположим есть некий список, в котором регистрируются _интерфейсы_, имплиментирующие классы которых реализованы в динамически загружаемых bpl(к примеру в плагинах). Сам список живет в базовом приложении, которое и загружает эти пакеты.

Если оставить разрушение этих объектов на совести основного кода, что при использовании интерфейсов довольно очевидно, но делать это _после_ выгрузки bpl с кодом класса реализующего интерфейс, например в секции finalization, - будем получать маловразумительные AV.

Интерфейсы должны быть интерфейсами!

Встретился интересный баг связанные с тем, что смешивается интерфейс и обычный объект. Вернее интерфейс кастрируется до некого альтернативного указателя на экземпляр объекта, без использования замечательного механизма подсчета ссылок.

Есть объект, унаследованный от TComponent и поддерживающий какой-то интерфейс. Временем жизни этого объекта управляет некая фабрика, честно убивающая его при закрытии приложения вручную, как обычный объект - т.е. посредством вызова Free.

А в совершенно другой части приложения есть ссылка на этот объект, но в виде глобальной переменной модуля. Некрасиво, но пока приложение живет проблем нет.

А при завершении работы - фабрика все честно чистит и умирает сама, выполняется весь пользовательский код. И начинают финализироваться модули, обнуляя интерфейсы и соотв. дергая TComponent._Release у давно умершего объекта...

Обнаружить сию неприятность удалось только благодаря FastMM, который заполняет память под разрушенными объектами по специфическому шаблону:

Q: My program used to work fine, but if I enable "FullDebugMode" and run it I get an access violation at address $8080xxxx. Why?
A: You are attempting to access properties of a freed object. When you free a block in "FullDebugMode", FastMM fills the freed memory area with a pattern of $80 bytes. If there were any pointers, long strings or object references inside the freed object they will now point to $80808080 which is in a reserved address space.

четверг, 19 марта 2009 г.

Указатель на метод интерфейса

Понадобилось на днях коллегам передать в качестве callback-функции метод объекта, доступ к которому есть только через интерфейс. Показалось, что проблемы особой нет - интерфейс "знает" о своем объекте, осталось вычислить адрес объекта да самого метода.

Но реальность оказалась не столь оптимистичной - на текущий момент Delphi такого функционала не поддерживает:
  • http://groups.google.com.ua/group/borland.public.delphi.objectpascal/browse_thread/thread/4c5dcb094b82522f?pli=1
  • http://qc.embarcadero.com/wc/qcmain.aspx?d=941 (а судя по датам и не будет поддерживать)
Самому раньше в таком функционале необходимости не было - проще передавать весь интерфейс, но если необходимо взаимодействие с чужим кодом, где могут требовать именно указатель на функцию, то прийдется изобретать не очень стройные решения:
  • метод в интерфейсе напрямую возвращающий адрес метода объекта
IAnIntf = interface
function GetPointerToProc: TAnObjectProc:
end
  • объект-обертку над интерфейсом
TWrapObj = class
private
AnIntf: ITheIntf;
protected
procedure AnProc(const Value: string);
end;

procedure TWrapObj.AnProc(const Value: string);
begin
AnIntf.AnProc(Value);
end;
  • возможно как-то можно будет выкрутиться через анонимные функции? нельзя, ибо
    TAnProc = procedure (S: string) of object;
    TAnProc2 = reference to procedure (S: string);
    это разные типы
Хотя и в реализации проблем более чем - вычислять статический адрес, на этапе компиляции, для такого указателя не получится, т.к. один и тот же интерфейс может реализовываться разными объектами.

> Still I think making (procedure of object) and (procedure of Interface) equivalent
> typeswould be the better solution.

Like I said, the semantics for the compiler would be quite different, so
it would make sense to make them different types (even if both
represented internally by a TMethod). Actually, if the interface
instance variable is nil, it is impossible to get the method address.
This is not a problem for objects, since there the static address is
taken.

пятница, 9 января 2009 г.

Borland, умолчания и поддержка старого

Компания меняет не только названия, но и простым разработчикам не дает скучать.
Вводная - при неизменных внешних условиях перестала выполняться сборка релиза из командной строки при переходе с 2007 на 2009 - упорно попадал туда код дебага. В то время как из среды - все ок.

Проведенное в два этапа(первый был еще на 2007 и текущей проблемы не касался см.) расследование показало, что:

- в импортируемом проектами файле CodeGear.Delphi.Targets дефолтный таргет изменен с Build на Make. А последний при смене конфигурации Debug|Release не пересобирает dcu

- группы проектов, в отличии от проектов, не обновляются до соотв. версии и импортируют $(MSBuildBinPath)\Borland.Group.Targets, который существует только при инсталяции 2007(или когда там msbuild прикрутили?)

- таргет Build для группы проектов не задает таргет для конечных проектов(т.е. используется умолчательный, который теперь Make, да)

Выходом может быть использование таргета BuildAll для группы проектов.

Delphi 2009 и приведение к PAnsiString

Часть winapi функций требуют передачи себе в кач-ве параметров структур с PAnsiChar строками (например mapi). В предыдущих версиях приведение строк посредством PChar(SomeString) работало, используя область памяти выделенную под SomeString, а в 2009 результат PAnsiString(AnsiString(SomeString)), уже как вычисление обычной строки из юникодной, размещается в локальной области памяти, что чревато тем, что старый код может перестать работать на, казалось бы, ровном месте (хотя конечно прямое приведение к pchar тоже имеет кучу особенностей).

Например код приведенный ниже возвращает разные результаты на 2007 и 2009 Делфи

program array_error_test;

{$APPTYPE CONSOLE}

uses
SysUtils,
StrUtils,
Classes;

procedure DoMain;
const
Count = 5;

type
RecType = record
AnString: PAnsiChar;
end;
var
RecArr: array of RecType;
I: Integer;
SomeStrings: TStringList;
begin

SetLength(RecArr, Count);

SomeStrings := TStringList.Create;
try
for I := 0 to Length(RecArr) - 1 do
begin
SomeStrings.Add(DupeString(IntToStr(I), 10));
RecArr[I].AnString := PAnsiChar(AnsiString(SomeStrings[I]));
end;

for I := 0 to Length(RecArr) - 1 do
WriteLn(RecArr[I].AnString);
finally
SomeStrings.Free;
end;
end;

begin
DoMain;
ReadLn;
end.

четверг, 8 января 2009 г.

Delphi 2009 и InternetQueryOption

Переносится в одном из проектов код на 2009 Делфи и соотвественно кушаются все прелести первопроходчества.

Для получения адреса прокси сервера пользовалась функция InternetQueryOption. После сборки в 2009 стала она возвращать мусор. Ничего криминального замечено не было, замена на InternetQueryOptionW не помогала.

Решение нашлось в
http://akirabbq.spaces.live.com/blog/cns!CEB8A04DC43BCEE9!264.entry
кто виноват не совсем понятно, но неприятно :(

По сути - в структуре INTERNET_PROXY_INFO lpszProxy должно быть не LPCTSTR(которое в 2009 уже PWideChar), а просто LPCSTR, хотя в исходных сишных заголовках она объявлена как LPCTSTR.

Решить проблему можно создав необходимую структуру у себя.

четверг, 4 сентября 2008 г.

Microsoft runtime и Firebird Embeded 2.1.1

Для возможности x64 билдов разработчики Firebird перешли на vc8, что потянуло за собой, в принципе уже привычную, проблему распространения vc runtime.

Но учитывая теперешний зоопарк ОС, технологий совместимости 32/64, попыток решения dll хелл, и вообще постижения Дао от МС, деплоймент этого runtime становится очень непростым.

Он, рантайм 8-ой версии, не идет, пока, в поставки никакх ОС - и Висте его тоже нет(по меньшей мере до SP).

Первое - для правильной установки, как это рекомендует МС, нужны права Администратора. А для установки на win2k до SP1 - обновление инсталятора МС до 3.1

Для интеграции этого redist себе в инсталятор - нужно делать msi инсталяторы, создание которых в корректном виде не самое простое занятие, для котрого хороших
свободных инструментов вроде как и нет, и опять таки необходимы права администратора.

В принципе на первый вгляд проблем быть не должно - что стоит поставить этот рантайм,
когда разворачивается сервер СУБД. Но тем, кто пытается распространять shareware системы, особенно с FB embeded, приходится хуже - необходимо тянуть этот рантайм со своим приложением.

Есть три пути его поставки:
  1. Официальный.
    Устанавливаем инсталятором от MS и все работает. Не подходит, если нет гарантии наличия админских прав у пользователей, наличия Windows Installer 3.0 or later, portable решения.
  2. Экстенсивный.
    Поставляем со своим приложением просто необходимые бинарники рантайма. Так делает, впрочем, большинство софта - стоит только поискать msvcr80.dll по жесткому диску :)). Но, поставлять нужно ТРИ копии рантайма, одну для работы самого fbembed, вторую в каталоге intl для fbintl и третью в для подключаемых функций в каталоге udf.
  3. Хитро-экстенсивный.
    Пользуясь текстовой природой манифестов, в каталогах intl и udf оставить только манифест, с измененными путями к файлам вида:

    <file name="..\msvcr80.dll">
    <file name="..\msvcp80.dll">

    ВАЖНО, в таком случае указать в манифесте своего приложения зависимость от Microsoft.VC80.CRT.

    такой манифест представляет собой текстовый файл <FullAppName>.manifest с содержимым:
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <dependency>
    <dependentAssembly>
    <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
    </dependency>
    </assembly>

    Этот манифест можно включить в ресурсы самого приложения, см. например


Ссылки:

вторник, 2 сентября 2008 г.

Не используйте сlass helper's?

Не сказать, что я их когда-то использовал, но вот встретился материал:

http://www.deltics.co.nz/blog/?p=282

Суть в том, что если в проекте объявлено два хелпера к одному классу, то использоваться будет ТОЛЬКО один, но какой именно - не известно(зависит от порядка юнитов и т.п.).

Так что можно поиметь плавающих ошибок на ровном месте...