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

воскресенье, 17 мая 2009 г.

UAC для разработчика 2

Сегодня наконец-то нашел решение проблемы с повышением привилегий для приложения в runtime, которую я озвучивал в одном из своих предыдущих постов. Решение крылось в создании специального COM объекта, который будет содержать весь привилегированный код, требующий повышенных привилегий.
Не буду полностью рассказывать эту возможность, поскольку это все уже описано в блоге Alexander Bozhko Bold&ECO blog
Цикл статей посвященный UAC:
Использование Vista UAC в Delphi: Часть 1
Использование Vista UAC в Delphi: Часть 2
Использование Vista UAC в Delphi: Часть 3

По всей видимости эти же методы будут работать и в Windows 7 - поскольку Microsoft обещает практически полную совместимость с программами, разработанными для Vista.




суббота, 11 апреля 2009 г.

Простой пример клиента для WebService

Одному моему коллеге понадобилось написать небольшого клиента для сервера контента системы электронного документооборота. Собственно сам сервер контента реализован в виде WEB-сервиса, предоставляющего некоторые функции для доступа к документам. Что он только не пробовал и ClientSocket и TCPConnection и HTTPRIO. Но на самом деле все достаточно просто, и не нужно изобретать велосипед :)

Сразу же оговорюсь , что данный пост не претендует на полноту изложения данной темы, здесь я просто хочу показать в каком направлении следует «копать» и покажу как обойти один подводный камень, на который можно наткнуться при создании клиента для WEB-сервисов.

Итак, приступим … У нас есть некоторый WEB-сервис, предоставляющий некоторые функции. Пусть это будет некоторый ASP.NET WebService запущенный на соседней машине с именем Athlon, URI данного WEB-сервиса выглядит следующим образом: http://athlon/test/service.asmx . Набрав данный URI в браузере получаем тестовую страничку, сгенерированную IIS для проверки работоспособности WEB-сервиса:


Из тестовой страницы видно, что WEB-сервис поддерживает одну единственную процедуру с именем HelloWorld. Попробуем ее вызвать из клиентского приложения на Delphi. Естественно создадим новый проект, сохраним куда-нибудь по-удобнее, и займемся самым интересным – автоматической генерацией кода для доступа к WEB-сервису. Для этого в среде Delphi есть специальный мастер который вызвается следующей командой: File -> New -> Other … -> WebServices -> WSDL Importer:



В мастер вводится один единственный параметр – это URI нашего WEB-сервиса:



Но при попытке перейти на следующий шаг мастера появится сообщение об ошибке, примерно следующего содержания, толком ничего не говорящего:



Давайте разберемся, что это такое и почему это происходит. Еще раз наберем URI WEB-сервиса в строке адреса браузера – на экране отобразится тестовая страничка. Тоже самое происходит когда по данному URI обращается мастер – в соответствии с протоколом SOAP он ждет XML описания WEB-сервиса, а ему вместо этого выдают обычную html-страницу. Именно по этому он вываливается с ошибкой невразумительного содержания, поскольку пытается разобрать html и ему это не удается и он сообщает на каких именно тэгах разметки он вы вывалился. Чтобы этого не происходило, мы должны явно указать WEB-сервису, что мы ждем от него WSDL описание сервиса. Делается это очень просто, просто добавляется параметр ?WSDL в URI WEB-сервиса:


http://athlon/test/service.asmx?WSDL



Теперь мастер получает именно те данные, которые ожидает и генерация кода происходит без ошибок:



В результате сгенерировался довольно большой код, ключевой фрагмент которого выглядит так:


  ServiceSoap = interface(IInvokable)
['{77573149-9C57-FA51-F11F-EFD527C91BD9}']
function HelloWorld: string; stdcall;
end;

function GetServiceSoap(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): ServiceSoap;

Здесь объявляется интерфейс нашего WEB-сервиса и функция для получения этого интерфейса, именно с ними мы и будем дальше работать. Просто для примера вызовем в обаботчике события OnClick для кнопки процедуру и выведем сообщение с результатом


procedure TForm1.Button1Click(Sender: TObject);
var
ws: ServiceSoap;
begin
// получаем интерфейс
ws := GetServiceSoap();
// вызываем процедуру сервиса
with ws do
ShowMessage(HelloWorld);
end;

Вот собственно и все - клиент для WEB-сервиса готов. На его создание потрачено совсем немного времени и написано совсем небольшое количество кода.


понедельник, 9 февраля 2009 г.

Windows 7 и Delphi

Поддавшись случившейся не так давно в Интернете истерии по поводу выхода первой публичной беты Windows 7, скачал себе 64-разрядную версию и на некоторое время пропал в ней. :) Это было мое первое знакомство с 64-разрядной операционной системой (не говоря уже о Windows 7), хотя мой компьютер уже давным-давно поддерживает их. Все установилось и заработало на удивление гладко, за исключением дурацкого 3G модема от билайна (обыкновенный GPRS-модем от мегафона даже нисколечко не сопротивлялся). Возможно, Microsoft более серьезно подошла к разработке своей новой операционки, а возможно рассказы о проблемах 64-битных систем оказали такими же «сказками на ночь», как и большинство сказок про Vista. Мне почему-то кажется что второе. :)

Но это не очередной хвалебный пост в пользу Microsoft, так что на этом закругляюсь. Остановлюсь только на работе Delphi в Windows 7. Delphi 2009 устанавливается и работает без проблем – так как ей и положено. Про Delphi 7 пока ничего толком сказать не могу, поскольку еще не устанавливал ее, но мне кажется, что также проблем не будет.

Удивительно, но весь негатив сопутствующий появлению Windows Vista, превратился в позитивные настроения всей «экосистемы» (как это называет Microsoft) перед выходом Windows 7. Это касается Delphi-сообщества, вот например Daniel Wischnewski уже создал прототип нескольких компонентов, позволяющих уже сейчас добавлять в приложения поддержку новых возможностей Windows 7 (в первую очередь визуальных).


Естественно, что все эти новые возможности будут работать только в Windows 7. И еще важное замечание. Хотя у компонентов и присутствует инсталлятор, он просто копирует необходимые файлы, устанавливать их придется вручную.


суббота, 31 января 2009 г.

UAC для разработчика

UAC (User account control) впервые появился в Windows Vista, и сразу же, не заслуженно, получил отрицательную репутацию, а в Интернете появилось полным полно статей как его отключить. Доходит даже до откровенного маразма, что эту функцию ставят как основной недостаток этой операционной системы. Не буду здесь подробно на нем останавливаться, скажу лишь, что у меня он включен по умолчанию, и я нахожу его очень даже полезным для себя, тем более что в Windows 7 он был значительно переработан, хотя на вкус и цвет – все фломастеры разные.

Хотим мы этого или нет UAC есть в операционной системе, и будет дальше, а разработчик программного обеспечения должен учитывать все возможные конфигурации системы, в том числе и с включенным UAC.

Несмотря на то, что UAC обладает некоторой "интеллектуальностью" и при запуске может определить нужно ли приложению увеличение привилегий или нет – возможности его ограничены, и он не всегда корректно работает, что приводит к тихому (или не всегда к тихому) «помиранию» запущенной программы, которой необходимы права администратора, но которые ей никто не дал. Поэтому перед разработчиком стоит задача явно указать необходимый уровень привилегий для своего приложения. Делается это через все тот же файл-манифест приложения, который появился в Windows XP и который используется для отрисовки интерфейса "в стиле XP".

Для явного указания необходимого уровня привилегий используется раздел Security:

<security>
<requestedprivileges>
<requestedexecutionlevel level="requireAdministrator">
</requestedprivileges>
</security>

Полностью файл-манифест приложения может выглядеть, например следующим образом:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" processorArchitecture="*"
name="UAC_Elevation_Prompt" type="win32" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" />
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="Microsoft.Windows.Common-Controls" version="6.0.0.0"
publicKeyToken="6595b64144ccf1df" language="*" processorArchitecture="*" />
</dependentAssembly>
</dependency>
</assembly>

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

После включения манифеста и сборки проекта при запуске приложения появится окно UAС, запрашивающее необходимые привилегии – теперь приложение уже не помрет молча.

Но не все так просто в Delphi 2009. По каким-то своим, никому не известным причинам, разработчики решили добавлять манифест во все проекты по умолчанию. С одной стороны – довольно удобно, но с другой получается наоборот больше работы. Вот, например, так же по умолчанию в ресурсы добавляется иконка приложения и информация о версии. Это не плохо – потому, что существуют инструменты для их быстрого и безболезненного изменения. А манифест – не очень хорошо, мягко говоря, потому как он жестко задан в файле WindowsXP.res в наиболее общей форме и не учитывает специфики конкретного приложения, и нет инструментов для его изменения.

Так вот, при попытке добавления описанного выше манифеста в проект Delphi 2009 получаем предупреждение компилятора о дублировании ресурсов и наш манифест не включается в приложение. Можно перейти в исходный код проекта и убрать директиву компилятора {$R *.res}, но в этом случае мы лишимся иконки приложения и информации о версии. Можно убрать файл WindowsXP.res куда подальше, чтобы Delphi его не нашла – но это тоже неудачный способ выхода из положения – поскольку нарушается работа VCL.

Пока я не нашел способа грамотно подменить манифест приложения, так же остается открытым вопрос повышения привилегий во время выполнения программы, поскольку приложению могут понадобится права администратора только для выполнения какой либо одной операции, которая еще и не всегда используется и нет смысла его постоянно гонять под админом.

Оригинальный способ для решения проблем использован в файловом менеджере Total Commander – для выполнения операций требующих повышения привилегий используется отдельное приложение TCADMIN.EXE, которому передается команда в случае невозможности выполнения операции с правами текущего пользователя.

Интересно, а как еще можно организовать повышение привилегий в Runtime и решить проблемы с манифестом в Delphi 2009?

суббота, 24 января 2009 г.

Установка FIBPlus на Delphi 2009 Trial

Установка обновленных компонентов FIBPlus, о релизе которых я писал ранее, не обошлась без пляски с бубном.

Началось все с того, что инсталлятор не смог корректно установить компоненты на пробную версию Delphi 2009. В результате я получил оошибку:

!!! Unsucessfuly compile package FibPlus2009.dpk!!!


Открыв лог-файл, созданный в результате установки, я обнаружил истинную причину:

Generate DCC32 config file: C:\Program Files\Devrace\FIBPlus_Unicode\7.0.1076\sources\dcc32.cfg
Initial directory = C:\Program Files\Devrace\FIBPlus_Unicode\7.0.1076\sources\
Command Line = "C:\Program Files\CodeGear\RAD Studio\6.0\bin\dcc32.exe" FibPlus2009.dpk

Command line tools are not supported in the trial version.

Замечательно! Ну ладно, всегда можно скомпилировать и установить пакеты через IDE Delphi. Но эта попытка тоже не увенчалась успехом. Компилятор отказался найти файлы библиотеки FIBPlus, не смотря на то что при установке было указано добавить все пути в нужное место. Даже после того как я прописал все необходимые пути (Tools->Options->Library Win32), один из пакетов (FIBDBMidas2009.dpk) все равно наотрез отказался компилироваться. Но зато другие компоненты и мастеры установились и наконец-то заработали.


Интересный момент: готовилась к выходу именно новая версия компонентов FIBPlus с индексом 7.0, но что-то изменило планы разработчиков. :) В пользу этой версии говорит инсталлятор и файл readme.txt с описанием нововведений:

7.1076
1. Уникодные поля теперь могут маппироваться не на TFIBWideStringField а на
TFIBStringField
2 Добавлено свойство TFIBXSQLVAR.AsAnsiString: AnsiString ;

7.1019
1. В скриптере ошибка при обработке содержимого блоб-полей
2. Ошибка при работе строковых полей не являющихся FIBStringField
3. Подключен быстрый Locate
4. Восстановлена работоспособность IB_Services
5. Восстановлена работоспособность SQLEditor
6. Добавлен обработчик pFIBDataSet.OnLockSQLText. Позволяет заменить генерацию лок-сиквела своей.

и версия в Object Inspector :)


Ну как же так! Проверять программы надо перед релизом! ;-)

понедельник, 19 января 2009 г.

Осторожно! Помощники класса!

В продолжение темы помощников классов и записей. Я провел небольшое исследование в результате которого выяснилось несколько неприятных моментов применения хелперов. На первый взгляд все прекрасно! Мы можем взять и дополнить любой класс практически любым функционалом. Но не все так радужно.

Самая большая обнаруженная мной проблема состоит в том, что для класса может существовать только ОДИН хелпер. Для иллюстрации этого создадим один небольшой проект, содержащий один класс и два хелпера к нему, и посмотрим, что же у нас получилось.

program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils;

type
TSomeObject = class
function X: integer;
end;

TSomeObjectHelper = class helper for TSomeObject
function y: integer;
end;

TSomeObjectHelper2 = class helper for TSomeObject
function z: integer;
end;

{ TSomeObjectHelper }

function TSomeObjectHelper.y: integer;
begin
result := 1;
end;

{ TSomeObject }

function TSomeObject.X: integer;
begin
result := 0;
end;

{ TSomeObjectHelper2 }

function TSomeObjectHelper2.z: integer;
begin
result := 2;
end;

begin
try
with TSomeObject.Create do
begin
writeln(z);
writeln(y);
end;
readln;
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.

Мы получим ошибку компиляции. Потому что компилятор, видит только один хелпер - TSomeClassHelper2. Если закомнтировать его описание и реализацию, то компилятор увидит второй хелпер.

К чему это может привести? Это может привести к трудноуловимым ошибкам, особенно в больших проектах, разрабатываемых не одним разработчиком. Или, как вариант, представим такую ситуацию: в какой нибудь библиотеке - например FIBPlus или DevExpress вдруг используют хелперы, вы снова добавили хелпер уже в своем коде. Компилятор увидел ВАШ хелпер, а исконный хелпер разработчиков - нет, и все - проект уже не скомпилируется. Ситуация еще больше усугубится если у библиотеки закрыты исходные коды.

Поэтому, применять хелперы следует с большой осторожностью, 100 раз подумав.

воскресенье, 18 января 2009 г.

Помощники записей

Сегодня, разбираясь с проектом, который я пытаюсь перевести на Delphi 2009, натолкнулся на одну забавную недокументированную возможность. Наряду с помощниками классов (Class Helpers) введенными в последних версиях Delphi, разработчику доступны и помощники записей!

Стоит сразу отметить, что данная возможность не поддерживается редактором кода и Code Insight. Так в редакторе код подчеркивается красным, а в Code Insight добавленное недоступно.

НО, с точки зрения компилятора, следующий код абсолютно рабочий:

type
TRectHelper = record helper for TRect
function Width: integer;
end;

{ TRectHelper }

function TRectHelper.Width: integer;
begin
Result := Self.Right - Self.Left;
end;

Использовать новый метод можно, например, следующим образом:

var
p: TRect;
begin
p.Top := 0;
p.Left := 0;
p.Bottom := 12;
p.Right := 12;
ShowMessage(inttostr(p.width));
end;

Не знаю, на сколько эта возможность может быть востребована, но она есть.

P.S. К слову сказать, что Class Helpers разработчики не рекомендуют использовать постоянно, а про Record Helpers они и вообще умолчали. :)

суббота, 17 января 2009 г.

Неизвестные возможности стандартных компонентов

Одной строкой: Сегодня Bose сделал уникальное открытие. Оказывается, что стандартный TDbGrid умеет рисовать многострочные заголовки с группировкой. Подробнее можно прочитать и посмотреть в его блоге.

Что самое смешное, что данная возможность не является недокументированной, по крайней мере в справке Delphi 2009 можно найти информацию о ней, но кто бы читал эту самую справку. :)

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

5 книг по Delphi которые следует прочитать

Все мы когда-то только начинали и учились программировать. Кто-то сидел и сам разбирался со всеми возможными проблемами, кто- сразу лез на форум и пачками создавал темы вроде "АААААА! пАмАгИтЕ!". Но так или иначе все начинали с азов, которые описаны в литературе. (Хотя не все из второй категории :)).

Далее приведу небольшой ТОП5 книг которые помогли мне избежать многих вопросов, некоторые из них я перечитывал в разной последовательности и несколько раз.

Перво-наперво, из разряда "книги для начинающих" - это Стив Тейксейра, Ксавье Пачеко. Delphi 5, Руководство разработчика. В двух томах. - Очень удачная, на мой взгляд, книга все грамотно и понятно написано, начальные сведения я черпал именно из нее.

Еще одна очень хорошая книга, на этот раз отечественного автора - Архангельский А.Я. Программирование Delphi 5. - Достаточно объемная книга, в которой есть практически все, что нужно начинающему.

Небольшое отступление. Во всех приводимых книгах в основном описываются довольно старые версии Delphi, но это не так важно, поскольку в них описываются именно основы - так необходимые начинающему. А про все новомодные добавления в виде хелперов и анонимных методов на начальном этапе знать попросту вредно. Когда будет опыт - с ними разобраться не составит труда, и литература не понадобится.

Продолжу импровизированный TOP книг.

На начальном этапе также полезно будет почитать книгу Рода Стивенса - Delphi. Готовые алгоритмы. В ней рассматривается создание и эффективное использование алгоритмов для работы со стеками, списками, очередями - вещами хоть и не всегда применяемыми при реальном программировании, но знание которых очень полезно.

В переходном этапе - когда уже и не новичек, но все же еще чего то не хватает, я нашел для себя книгу П.Дарахвелидзе Е.Маркова. Программирование в Delphi 7

Ну и самая лучшее оставил на последок. Самой лучшей книгой я считаю книгу Марко Кэнту - Программирование в Delphi 7 для профессионалов. В которой рассматриваются практически все темы, связанные с программированием в Delphi.

Это книги из серии "то что читать полезно", но существует еще целый пласт книг на которых недобросовестные авторы просто делают деньги. Я их не осуждаю - каждый зарабатывает так как считает так как считает нужным, но читать я их не советую. В эту категорию для меня попадает довольно большое количество книг начиная от всевозможных "Библий" и "освой за 5-10-15 минут" и заканчивая чьими-то глазами. Поскольку в этих книгах довольно часто отражается не истинное положение вещей, а видение автора, причем не всегда правильное.

В прошлом году разгорелся даже довольно большой спор по поводу одной "статьи" одного такого автора на блоге Delphist.ru. Я абсолютно согласен с aktuba, в одной из приведенных мной книг даже подробно описано ПОЧЕМУ так делать нельзя, но принять участие в споре не смог, поскольку комментарии не добавлялись. Но вроде бы сейчас эта ошибка исправлена.

среда, 14 января 2009 г.

Работа с XML

Несмотря на то, что тема работы с XML в Delphi довольно широко обсуждалась в Интернете, вопросы на эту тему довольно часто возникают на всевозможных форумах.

Я тоже уже писал по этому поводу, но хотел бы вернуться к реальному случаю быстрого разбора XML файла и извлечения данных, который я сегодня проделал на работе. Получение необходимых данных у меня заняло не более 5 минут времени.

Предыстория. Сегодня понадобилось обработать данные об установленных программах на компьютерах пользователей (да, да, пиратов выявляем :)). Технический отдел предоставил мне такую информацию содраную с ничего не подозревающих пользователей по сети с использованием WMI. Программа, которой они пользовались выдает отчеты в формате XML. Соответственно - мне притащили гору XML файлов с довольно сложной структурой из которых мне необходимо было вытащить только название установленных программных продуктов.

Обработка. Просмотрев пару файлов вручную, понял что так и состариться не долго, и решил написать небольшой конвертер. Запустив Delphi - выбрал в репозитарии объект XML DataBinding и скормил ему один из файлов. Все настройки и параметры я оставил по умолчанию и в результате у меня сформировался модуль с большим количеством классов и интерфейсов для доступа к элементам этого XML файла. Я не стал долго разбираться со структурой классов, сразу же перешел к написанию конвертера.

В новом консольном приложении написал довольно простой код:


program XML2TXT;

uses
Forms,
Classes, SysUtils,
SoftwareXML in 'SoftwareXML.pas';

procedure CovertXML2Text;
var
softbase : IXMLSTDSoftwareType;
i: integer;
sr: TSearchRec;
CurDir: string;
ExportFile: TStringList;
begin
CurDir := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName));
if FindFirst(CurDir+'*.xml', faAnyFile, sr) = 0 then
repeat
ExportFile := TStringList.Create;
softbase := LoadSTDSoftware(Pchar(CurDir+sr.Name));
for i := 0 to softbase.InstalledSoftware.source.software.Count - 1 do
ExportFile.Add(softbase.InstalledSoftware.source.software[i].DisplayName);
ExportFile.Sort;
ExportFile.SaveToFile(CurDir + softbase.InstalledSoftware.Source.servername+'.txt');
ExportFile.Free;
until FindNext(sr) <> 0;
end;

begin
Application.Initialize;
CovertXML2Text;
end.

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

Чувствую что данный код потребует пояснений. Например, зачем я в консольном приложении использовал модуль Forms и вызывал процедуру Application.Initialize;?

На самом деле все просто - это небольшой хак, позволяющий использовать XML Data Binding в консольном приложении. Потому как в нем упорно отказывался инициализироваться класс для работы с XML. В истинных причинах пока не разбирался - сегодня было важно время, я и так 4 из 5 минут потратил на борьбу с этой ошибкой. :) Думаю позже разобраться с этой проблемой и написать в чем истинная причина.

Странный класс softbase был создан на основе XML файла - так назывался корневой элемент, а softbase.InstalledSoftware.source.software[i].DisplayName - просто навигация по вложенным элементам до нужного и получение его значения.

Вот собственно так выглядит один из самых быстрых способов работы с XML в Delphi.

понедельник, 12 января 2009 г.

Новое в Delphi 2009: работа с ресурсами

Чуть ранее я уже писал о работе с ресурсами в Delphi. Там был описан абсолютно универсальный способ, работающий в любой версии Delphi. Существует еще одна возможность прикрепления ресурсов. Но сегодня я расскажу о том, какие новые возможности по работе с ресурсами нам предлагает Delphi 2009.

Если раньше можно было добавить файл с описаниями ресурсов (*.rc) в проект, и он автоматически скомпилируется и привяжется, то в Delphi 2009 разработчики пошли еще дальше. Теперь с помощью менеджера проектов (Project Manager) в проект можно добавлять сами ресурсы!


Так же с помощью меню Project -> Resources можно вызвать менеджер ресурсов:


Единственное, чем мне не понравились эти нововведения - это не очень удобный менеджер ресурсов. Мне кажется в нем будет сложно работать при большом количестве подключаемых ресурсов. Так же удивило малое количество поддерживаемых типов ресурсов, поддерживаются только Bitmap, RCData и None. На самом деле тип ресурса определяется по формату файла. Так например при добавлении курсора в выпадающем списке становятся доступны варианты - Cursor, RCData, None, при выборе иконки - Icon, RCData и None, а при выборе JPEG изображения - только RCData И None.

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

воскресенье, 11 января 2009 г.

Технологии интеллектуального редактирования (Часть III)

В этой заметке я продолжу обзор наиболее продвинутых возможностей редактирования исходного кода в Delphi. Напомню, что несколько ранее в Части I рассматривались возможности интеллектуального поиска, а в Части II - возможности по быстрому созданию и переименованию конструкций кода.

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



Так же есть возможность быстро перемещать члены классов от дочерних классов к одному из родительских или наоборот - от родительского класса - к одному из классов-наследников. Если данная команда применима к выделенному члену класса, то на экране появится следующее диалоговое окно для выбора:


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

Еще одной немаловажной особенностью является то, что перед применением выполнения рефакторинга можно оценить те изменения в коде которые получатся. А в том случае, если вдруг в результате затяжной операции рефакторинга произойдет какая либо ошибка - код вернется в исходное состояние.

Ну и еще - рефакторини можно отменять так же как и любые другие действия в редакторе кода - с помощью клавиш Ctrl+Z, что делает их очень мощным и простым инструментом.

суббота, 10 января 2009 г.

Технологии интеллектуального редактирования (Часть II)

Продолжим начатый ранее разговор о рефакторинге. Сегодня рассмотрим более продвинутые возможности по интеллектуальному редактированию.

Как обычно начнем с более простых возможностей, первой из которых рассмотрим быструю декларацию переменной и поля. В заметке посвященной LiveTemplates я уже рассказывал как можно быстро объявить переменную. Но есть еще один вариант. Представим себе ситуацию, когда вы уже пытаетесь скомпилировать проект с не объявленной переменной, естественно компилятор выдаст ошибку:

[DCC Error] Unit2.pas(94): E2003 Undeclared identifier: 's'

Так вот, если теперь встать на эту ошибку и из контекстного меню выбрать пункт Declare Variable то в диалоговом окне можно выбрать тип определяемой переменной и при необходимости даже инициализировать ее:

Этим средством удобно пользоваться когда необходимо быстро задействовать новые локальные переменны - например при добавлении циклов.

Аналогично можно поступить если требуется не локальная переменная, а поле класса - Declare Field. Единственное отличие заключается в том, что для поля класса можно дополнительно выбрать область видимости.

Следующая замечательная возможность называется визуальное создание метода (Extract Method). Она позволяет выделить в редакторе кода произвольную часть кода, и организовать ее в виде метода. На рисунке я выделил код, отвечающий за удаление временных файлов в отдельный метод:

При выделении более сложного участка кода в процессе рефакторинга автоматически создадутся параметры метода и его локальные переменные.

Пожалуй самая используемая мной возможность рефакторинга - это визуальное изменение параметров (Change Parameters). Очень удобно при большом количестве параметров, часть из которых задана со значениями по умолчанию. Вызывается из контекстного меню редактора кода, если предварительно выделить имя необходимой процедуры или метода.

Ну и в завершение еще одна из часто используемых и полезных функций рефакторинга - глобальное переименование идентификатора (Rename). Сколько хлопот раньше приносило переименование какого-либо визуального контрола (скажем TLabel) если он использовался коде. Все вхождения можно было отловить только с помощью компилятора и спотыкания на каждом вхождении. Но теперь с этим покончено. Необходимо всего лишь встать на необходимый идентификатор и дать команду Refactor -> Rename. Данная операция применима к названиям переменных, методов, полей, классов, структуур, интерфейсов и параметров.

Перед переименованием можно просмотреть все идентификаторы которые будут изменены.

В следующей части речь пойдет перемещениях частей кода. Не переключайтесь. :)

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

Технологии интеллектуального редактирования (Часть I)

Продолжаю публиковать серию заметок начатую мной в заметке "Отличия новых версий Delphi от Delphi 7". Сегодня речь пойдет о технологиях интеллектуального редактирования (в англоязычной терминологии Refactoring). Взамен такого длинного русскоязычного названия я буду применять более короткое – "рефакторинг".

Итак, рефакторинг является одним из наиболее значимых нововведений последних версий Delphi. Ключевой особенностью рефакторинга является то, что перед применением изменений их можно просмотреть, а при необходимости легко отменить.

Начнем, пожалуй, с самых простых сценариев применения рефакторинга с поиска. Рассмотрим первую возможность - "Поиск модуля". Данная возможность позволяет найти необходимый модуль и добавить его к списку подключенных модулей в разделе Interface или Implementation. Кстати сказать, все команды рефакторинга вызываются либо из главного меню среды – Refactorings, либо через контекстное меню редактора кода. Причем можно производить поиск не только по имени модуля, но и по имении функций входяших в него (!!!). На рисунке приведен пример поиска модуля по функции ShellExecute:

При нажатии кнопки OK название модуля ShellAPI добавится в раздел подключенных модулей раздела Implementation.

Еще одна прекрасная возможность поиска - поиск ссылок (FindReference), позволяющаяя найти все упоминания о каком либо идентификаторе во всех связанных файлах проекта.

При двойном щелчке на элементах дерева в редакторе кода будет подсвечиваться соответствующая строчка кода.

Для поиска локальных идентификаторов можно воспользоваться вариантом этой функции - Find Local Reference. Результат ее действия аналогичен.

Рассмотрим еще одну возможность поиска. Новая возможность называется "Поиск класса" (FindClass), позволяющая находить определения классов по их именам.

Нажав кнопку OK мы перейдем к описанию класса.

На сегодня, пожалуй, все, продолжение в следующих заметках.

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

Перевод проекта на Delphi 2009 – первые грабли

Сегодня выдалось немного свободного времени и решил я поковырять свой проект на предмет совместимости с Delphi 2009. В одной из моих предыдущих заметок я уже говорил, что проект отказался компилироваться. Начнем разбираться почему.

Первым делом компилятор споткнулся о «мертвую» (нигде не использующуюся) процедуру RunDosAndWait которая запускала консольное приложение фоном и ждала его выполнения. Честно говоря, я намеренно не стал убирать эту процедуру и берег ее для такого случая, как она мне сегодня пригодится – речь пойдет немного позже.

Итак, не работает функция CreateProcess(nil, PChar(CommandLine), nil, nil, true, 0, nil, nil, StartupInfo, ProcessInfo). Попробовал поискать в инете, толком решения проблемы не нашел. На одном из форумов тоже сталкивались с этой проблемой, но решения предложено не было.

Пробуем просто создать процесс:

Получаем следующую картинку:



Поскольку новая Delphi 2009 это один большой Юникод, то думаю следует поискать причину в этом. Попробуем разобраться почему код отлично работавший в предыдущих версиях резко перестал работать. В предыдущих версиях (не Юникод) при вызове функции CreateProcess фактически вызывалась ANSI-версия этой функции – CreateProcessA, в новой Юникод-версии фактически вызывается CreateProcessW и по всей видимости в этом то и кроется основная проблема. А так как CreateProcess это самая что ни на есть WinAPI функция, попробуем поискать ответы на наши вопросы в MSDN.

Вот собственно, что и следовало доказать. Функции CreateProcessA и CreaterocessW ведут себя по-разному:

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

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


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


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

function RunDosAndWait(const CommandLine: string; OutSrings: TStrings): boolean;

написать

function RunDosAndWait(var CommandLine: string; OutSrings: TStrings): boolean;.

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

понедельник, 29 декабря 2008 г.

Использование собственных курсоров в Delphi

На днях мне задали довольно тривиальный вопрос: "Как сделать собственные курсоры в Delphi?". Что, думаю, в сети разве нет информации по этому поводу? Методом "гугления" обнаружил статью на DelphiSources - была первая в списке. Но это не совсем то, просмотрев еще несклько ресурсов, бросил это дело. И хотя вся процедура создания и подключения курсоров, да и других ресурсов довольно проста, но она не прозрачна и содержит много подводных камней, и может вызвать затруднения у начинающего программиста.

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

Собственно курсоры создаются в программе ImageEditor входящей в поставку Delphi 7 (именно для этой версии прозвучал порос). Процесс рисования курсора опустим - там все достаточно просто. Первые проблемы могут возникнуть при попытке подключения курсоров к программе.

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

Для создания ресурсов нам понадобится утилита brcc32.exe (Borland Resource Compiler) - которая распологается в каталоге с Delphi. Я рекомендую создать отдельный подкаталог "resources" в каталоге с проектом и разместить там все необходимые файлы для создания файла с собственными ресурсами. Туда же скопировать и эту утилиту.


Теперь необходимо подготовить файлик для компиляции ресурсов. Неоходимо открыть блокнот и описать каждый курсор в следующем формате:

ИМЯ_РЕСУРСА ТИП_РЕСУРСА ПУТЬ_К_РЕСУРСУ

Для исключения возможных неточностей следует имя ресурса сразу же писать заглавными латинскими буквами. Чтобы мои ресурсы можно было легко увидеть в одном месте я именую их все с заглавной буквы Z - так проще. Реальный файл с описаниями ресурсов одного из моих проектов представлен ниже:

В нашем случае необходимо написать следующее:

ZCURSOR_PEN CURSOR pen.cur
ZCURSOR_ERR CURSOR err.cur

и сохранить в файле cursors.rc. Теперь остается только выполнить из командной строки команду brcc32 cursors.rc в результате чего создастся файл с ресурсами cursors.res, содержащий в себе наши курсоры.

Теперь остается только подключить этот файл к проекту, делается это следующей директивой {$R cursors.res}. В моем проекте это сделано следующим образом:

Затем объявляется числовая константа на каждый курсор, например:

crPen = 100;
crErraser = 101;

и далее при инициализации формы следует подгрузить эти ресурсы:

Screen.Cursors[crPen] := LoadCursor(HInstance, 'ZCURSOR_PEN');
Screen.Cursors[crErraser] := LoadCursor(HInstance, 'ZCURSOR_ERR CURSOR');

Ну и теперь, наконец-то, можно с ними работать как с обычными курсорами. Кстати, тут можно посмотреть описание функций для работы с курсорами.

Вот собственно и все, довольно объемный пост получился, даже сам не ожидал :)

понедельник, 27 октября 2008 г.

Подозрительный файл в проекте

Сегодня выдалась свободная минутка, и решил продолжить работу над одним из своих проектов. Сходу решил откомпилировать проект, чтобы вспомнить, на чем я остановился в прошлый раз. О-па! А проект-то не работает и IDE выдает следующую ошибку:

«Странно» - подумал я, припоминая, что когда то я уже боролся с этой проблемой … Первой шальной мыслью было, что перевод часов стал причиной сбоя. :) Но эту мысль я очень быстро откинул, однако сменил дату всех файлов проекта – так, на всякий случай.

Стал мыслить дальше, поскольку даже запуск приложения просто из-под Windows выдал ошибку – «Не удалось запустить приложение, поскольку его параллельная конфигурация неправильна». Открыл папку проекта и стал на нее смотреть, вскоре заметил один подозрительный файл, которого быть не должно! (он выделен на рисунке ниже)

Ума не приложу, откуда он нарисовался. Причем его содержимое было идентично файлу mntrng.config в котором у меня содержатся настройки приложения – поэтому система и не могла запустить приложение. Просто удалил файл – и все заработало в штатном режиме. Даже несколько раз перекомпилировал и собрал проект – файл пока больше не появился, откуда он только взялся ….

понедельник, 20 октября 2008 г.

Упрощаем себе жизнь

Да, именно так. И я не побоюсь этого громкого названия поста. :) Речь, наконец-то, пойдет о создании Live Templates, о которых я говорил несколько ранее. Давно хотел написать обзор по этому поводу, но все руки не доходили.

Итак. LiveTemplates представляют собой обычный XML файл следующей структуры:

<?xml version="1.0" encoding="utf-8" ?>

<codetemplate xmlns=http://schemas.borland.com/Delphi/2005/codetemplates version="1.0.0">

<template name="" invoke="manual">

<description>


</description>

<author>


</author>

<code language=""><![CDATA[]]>

</code>

</template>

</codetemplate>

Это общий скелет шаблона, который генерируется средой по умолчанию и который подойдет для нашего сегодняшнего примера. Разберем его подробнее.

Собственно шаблон начинается с тега <template name="" invoke="manual">. В атрибуте name указывается произвольное имя шабна, а в атрибуте invoke указывается режим запуска данного шаблона. Данный атрибут может принимать следующие значения: manual и auto.

Далее идет тег краткого описания шаблона, и указания автора.

Ну и далее – наиболее интересный тег – code. Атрибут language может принимать следующие значения: CSharp, Delphi, HTML, IDE, XML. Стоит так же отметить, что приведенный выше скелет шаблона создается также с использованием шаблона с названием code template template, что говорит о большой гибкости LiveTemplates.

Во вложенном теге CDATA собственно и размещается сам код шаблона.

Теперь, когда разобрались со структурой шаблона, попробуем создать шаблон который будет автоматически исправлять опечатку в операторе присваивания, которая иногда может вывести из себя :). Я имею ввиду опечатку в виде оператора ;= вместо необходимого :=.

Установим атрибут имени шаблона в «;=», а атрибут invoke в auto поскольку нам необходимо чтобы шаблон применялся автоматически. Далее установим значения тегов description и autor по своему усмотрению и перейдем к коду самого шаблона.

В атрибуте language следует написать Delphi, поскольку именно для этого языка мы создаем шаблон. Так же следует добавить еще один атрибут, отсутствующий в скелете по умолчанию – delimeter раный значению «|», который определяет разделитель шаблона, а в CDATA следует написать следующее: <![CDATA[:= |end|]]>, где |end| указывает положение курсора после применения шаблона. Осталось только сохранить шаблон в специальной папке, чтобы IDE могла их найти. По умолчанию эта папка находится c:\Documents and Settings\[USERNAME]\Local Settings\Application Data\Borland\BDS\4.0\code_templates. Вот и все – ничего сложного, а сколько нервов теперь мы себе сэкономим :).

Полный текст шаблона получившийся у меня выглядит следующим образом:

<?xml version="1.0" encoding="utf-8" ?>

<codetemplate xmlns="http://schemas.borland.com/Delphi/2005/codetemplates"

version="1.0.0">

<template name=";=" invoke="auto">

<description>

typo fix

</description>

<author>

Valeriy

</author>

<code language="Delphi" delimiter="|">

<![CDATA[:= |end|]]>

</code>

</template>

</codetemplate>

Теперь, если вдруг набрать в редакторе кода ;= то данный оператор автоматически заменится на правильный :=. Это пока все, подробнее о создании более сложных и полезных шаблонов я напишу в следующих своих постах.

суббота, 13 сентября 2008 г.

Tiburon

Наконец-то дождались! Вышла новая версия Delphi - Delphi 2009 и доступна для скачивания как в виде вэб-инсталлятора, так и в виде ISO-образа. К сожалению у меня нет возможности сейчас скачать и попробовать новую версию, а пре-релиз который у меня был - очень жестко глючил: показывал ошибку в пакете coreide120.bpl и отказывался загружаться в принципе :( Что я только не пробовал ...

Надеюсь получится скачать в течении нескольких дней и "пощупать" своими руками ... Кстати ISO-образ "весит" почти 2 ГБ. Можно впринципе заказать по файловой почте, но кто знает когда он придет. Что-то почта в последнее время стала совсем отвратительно работать. Короче говоря пока не знаю, постараюсь на днях как-нибудь решить этот вопрос.

суббота, 19 июля 2008 г.

Увеличиваем скорость написания кода

Продолжу описание новых возможностей среды и языка Delphi, начатое мной пару дней назад. Сегодня речь пойдет о Live Templates. В русскоязычной литературе их называют «шаблоны кода», что несколько неточно (на мой взгляд) отражает их сущность, более к ним подошел бы дословный перевод – «живые шаблоны». Но оно несколько режет слух, поэтому я всегда буду упоминать их англоязычное название. В этой заметке я расскажу о существующих шаблонах и как ими пользоваться, а темой одной из следующих заметок будет создание собственных шаблонов.

Итак, Live Templates – это своего рода макросы, позволяющие при вводе ключевой комбинации символов подставлять шаблоны кода. Причем эти шаблоны параметризованы, т.е. вы можете перемещаться по частям шаблона при помощи клавиш Tab и Shift+Tab. Например, вводим в редакторе кода слово for нажимаем клавишу Tab в редакторе кода появляется следующий текст:

Причем, после того как будут заполнены все параметры в раздел описания локальных переменных автоматически добавится переменная счетчик цикла:

Для цикла FOR есть и другое ключевое слово – forb, его действие аналогично, но результат несколько другой – в шаблоне присутствует еще и блок begin … end;

Кстати, раз уж речь зашла о блоках кода следует упомянуть об еще одном улучшении – это автозавершение блоков кода (Block Completion). Если буквально в двух словах – это подстановка логически необходимого ключевого слова в блоке кода, т.е. когда вы набираете в редакторе begin нажимаете Enter, то редактор сам подставляет блок end; и устанавливает курсор между этими словами со сдвигом на 2 позиции курсора. Это же касается и других блоков кода, таких как case, type, try и многих других

Но вернемся к Live Templates. В контекстном меню редактора кода появился новый пункт меню – Surround, с помощью которого можно обернуть шаблоном любой выделенный фрагмент кода. Очень удобно когда необходимо некоторую часть кода поместить в блок try … finally;. В том же пункте можно посмотреть весь список встроенных в среду шаблонов кода.

Ну и напоследок расскажу о самом моем любимом шаблоне. Это шаблон var. Достаточно написать в любом месте тела процедуры слово var и нажать Tab, сработает шаблон, с помощью которого можно добавить любую новую переменную в раздел описания локальных переменных. Очень удобно в случае с большими процедурами.