Электронная библиотека » Александр Чиртик » » онлайн чтение - страница 16


  • Текст добавлен: 10 ноября 2013, 00:08


Автор книги: Александр Чиртик


Жанр: Программирование, Компьютеры


сообщить о неприемлемом содержимом

Текущая страница: 16 (всего у книги 24 страниц)

Шрифт:
- 100% +
Программа просмотра реестра

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

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

Рис. 7.11. Программа для просмотра реестра


Далее будут рассмотрены в порядке использования функции и процедуры, формирующие основу этого приложения. Итак, при запуске формы составляется список корневых разделов реестра (листинг 7.23).

Листинг 7.23. Первоначальная инициализация дерева разделов реестра

procedure TForm1.FormCreate(Sender: TObject);

var

item: TTreeNode;

begin

//Формирование списка корневых разделов реестра

item:= keys.Items.AddChild(nil, 'HKEY_CLASSES_ROOT');

item.Data:= Pointer(HKEY_CLASSES_ROOT);

CheckSubKeys(item);

item:= keys.Items.AddChild(nil, 'HKEY_CURRENT_USER');

item.Data:= Pointer(HKEY_CURRENT_USER);

CheckSubKeys(item);

item:= keys.Items.AddChild(nil, 'HKEY_LOCAL_MACHINE');

item.Data:= Pointer(HKEY_LOCAL_MACHINE);

CheckSubKeys(item);

item:= keys.Items.AddChild(nil, 'HKEY_USERS');

item.Data:= Pointer(HKEY_USERS);

CheckSubKeys(item);

item:= keys.Items.AddChild(nil, 'HKEY_CURRENT_CONFIG');

item.Data:= Pointer(HKEY_CURRENT_CONFIG);

CheckSubKeys(item);

end;


Вызываемая для каждого нового элемента дерева (листинг 7.23) процедура CheckSubKeys реализована следующим образом (листинг 7.24).

Листинг 7.24. Оформление элемента дерева в зависимости от наличия вложенных разделов

procedure TForm1.CheckSubKeys(item: TTreeNode);

var

reg: TRegistry;

begin

reg:= TRegistry.Create();

//Проверка, есть ли в разделе реестра вложенные подразделы

reg.RootKey:= GetRootKey(item);

if reg.OpenKeyReadOnly(GetKeyPath(item)) then

begin

if reg.HasSubKeys() then

begin

//Добавляем фиктивный элемент (чтобы показывался "+" для

//разворачивания раздела). Одновременно помечаем фиктивный элемент

keys.Items.AddChild(item,'').Data:= Pointer(-1);

end;

reg.CloseKey();

end;

reg.Free();

end;


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

Важно, что фиктивный элемент помечается значением -1. Именно по наличию дочернего элемента с полем Data, равным -1, можно определить, зачитывалось ли содержимое раздела, соответствующего определенному элементу дерева. Содержимое раздела зачитывается при разворачивании элемента дерева (листинг 7.25).

Листинг 7.25. Составление списка дочерних разделов

procedure TForm1.keysExpanding(Sender: TObject; Node: TTreeNode;

var AllowExpansion: Boolean);

var

reg: TRegistry;

subkeys: TStrings;

i: Integer;

begin

if Integer(Node.getFirstChild.Data) <> -1 then

//Список подразделов был зачитан ранее

Exit;

Node.DeleteChildren(); //Удаление фиктивного элемента дерева

reg:= TRegistry.Create();

//Загрузка списка подразделов выбранного раздела

reg.RootKey:= GetRootKey(Node);

if reg.OpenKey(GetKeyPath(Node), False) then

begin

//Получение списка подразделов

subkeys:= TStringList.Create();

reg.GetKeyNames(subkeys);

for i:= 0 to subkeys.Count – 1 do

begin

//Добавление элемента для дочернего раздела (не забываем

//проверять подразделы у каждого дочернего раздела)

 CheckSubKeys(keys.Items.AddChild(Node, subkeys[i]));

end;

subkeys.Free();

reg.CloseKey();

end;

reg.Free();

end;


В листинге 7.25 используются две дополнительные функции: для определения полного пути раздела, соответствующего элементу дерева (без имени коневого раздела), и для получения дескриптора корневого раздела (хранится в поле Data корневого элемента каждой ветви дерева).

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

Листинг 7.26. Определение пути раздела в дереве

function GetKeyPath(item: TTreeNode): String;

var

temp: TTreeNode;

path: String;

begin

temp:= item;

while temp.Parent <> nil do

begin

path:= temp.Text + ''+ path;

temp:= temp.Parent;

end;

GetKeyPath:= path;

end;


Аналогично, даже проще, определяется дескриптор корневого раздела определенной ветви реестра: для этого достаточно добраться до корня ветви дерева и прочитать значение поля Data корневого элемента (листинг 7.27).

Листинг 7.27. Определение дескриптора корневого раздела ветви

function GetRootKey(item: TTreeNode): HKEY;

var

temp: TTreeNode;

begin

temp:= item;

while temp.Parent <> nil do

temp:= temp.Parent;

GetRootKey:= HKEY(temp.Data);

end;


При выделении элемента дерева список в правой части формы заполняется параметрами соответствующего раздела. Порядок заполнения списка продемонстрирован в листинге 7.28.

Листинг 7.28. Составление списка параметров раздела реестра

procedure TForm1.keysChange(Sender: TObject; Node: TTreeNode);

var

reg: TRegistry;

valueItem: TListItem;

item: TTreeNode;

valueNames: TStrings;

i: Integer;

begin

item:= keys.Selected;

if item <> nil then

begin

//Зачитаем содержимое выбранного раздела в ListView (values)

values.Clear;

reg:= TRegistry.Create();

reg.RootKey:= GetRootKey(item);

if reg.OpenKeyReadOnly(GetKeyPath(item)) then

begin

valueNames:= TStringList.Create();

//Получение списка названий параметров

reg.GetValueNames(valueNames);

//Добавление каждого параметра в список

for i:= 0 to valueNames.Count – 1 do

begin

valueItem:= values.Items.Add();

if valueNames[i] = '' then

valueItem.Caption:= '< По умолчанию >'

else

valueItem.Caption:= valueNames[i];

//Получение типа и значения параметра

case reg.GetDataType(valueNames[i]) of

rdUnknown:

valueItem.Sub Items.Add('Неизвестно');

 rdString, rdExpandString:

begin

valueItem.Sub Items.Add(' Строка');

valueItem.SubItems.Add(reg.ReadString(valueNames[i]));

end;

rdInteger:

begin

valueItem.Sub Items.Add(' Число');

valueItem.SubItems.Add(IntToStr(

reg.ReadInteger(valueNames[i])));

end;

rdBinary:

valueItem.Sub Items.Add(' Двоичные еданные');

end;

end;

valueNames.Free();

reg.CloseKey();

end;

reg.Free();

end;

end;


Процедура, приведенная в листинге 7.28, не считывает значения двоичных параметров. Это сделано для упрощения и без того громоздкого фрагмента кода. В реализации считывания значений двоичных параметров на самом деле нет ничего сложного: нужно лишь заранее определить размер данных (метод GetDataSize) и создать буфер соответствующего размера.

Глава 8
Обмен данными между приложениями

• Сообщение WM_COPYDATA

• Использование буфера обмена

• Проецируемые в память файлы


Организация обмена данными между приложениями, а именно между процессами этих приложений, является достаточно трудоемкой задачей. Архитектура Win32 подразумевает максимальную изоляцию исполняющихся приложений друг от друга. Каждое приложение исполняется в своем виртуальном адресном пространстве, которое изолированно и не имеет доступа к памяти других процессов приложений. Однако довольно часто возникает необходимость перевать данные из одного исполняющегося процесса в другой. Она вызвана тем, что функциональные приложения и пакеты программ исполняются не в одном процессе, а в нескольких. Поэтому для нормальной работы используются основные возможности межпроцессного взаимодействия. Наиболее простой, понятной, но не всегда удобной является передача данных с использованием сообщения WM_COPYDATA. Также для передачи данных между приложениями широко используются проецируемые в память файлы (Mapping Files). Существуют и такие высокоуровневые средства, как буфер обмена или уже рассмотренная технология COM. Перечисленные способы будут подробно рассмотрены в этой главе. За рамки этой книги выходит рассмотрение способа передачи данных через каналы (трубы, или «Pipe»), который считается устаревшим и по этой причине не будет столь интересным.

Сообщение WM_COPYDATA

Сообщение WM_COPYDATA позволяет приложениям копировать данные между их адресными пространствами. Для передачи сообщения должна использоваться функция синхронной отправки сообщения SendMessage, а не PostMessage, которая асинхронным образом передает сообщение. Данные, предназначенные для передачи, не должны содержать указателей или других ссылок на объекты, недоступные программе, принимающей эти данные. Рассмотрим параметры, передаваемые с сообщением WM_COPYDATA:


//дескриптор передающего окна

wParam = (WPARAM) (HWND) hwnd;

//указатель на структуру с данными

lParam = (LPARAM) (PCOPYDATASTRUCT) pcds;


На использование сообщения налагаются следующие ограничения:

• принимаемые данные должны быть доступны только для чтения, так как изменение их структуры может привести к непредсказуемым последствиям;

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

Приступим к созданию приложения, демонстрирующего работу WM_COPYDATA. Для создания примера необходимо создать два приложения. Первое будет отправлять данные, например, строку текста, другое же приложение будет их получать. На главной форме первого приложения необходимо поместить элемент управления TextBox, в который будет записываться передаваемая строка, и кнопка, нажатие которой будет инициировать передачу данных. Для второго приложения достаточно элемента, отображающего текстовую информацию типа Label. Перейдем к рассмотрению исходных текстов разрабатываемых приложений.

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


const

CMD_SETLABELTEXT = 1; //Задаем ID Dкоманды


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

Листинг 8.1. Отправка данных другому приложению

procedureTDataSender.bnSendClick(Sender: TObject);

var

CDS: TCopyDataStruct;

begin

//Устанавливаем тип команды

CDS.dwData:= CMD_SETLABELTEXT;

//Устанавливаем длин у передаваемых данных

CDS.cbData:= Length (StringEdit.Text) + 1;

//Выделяем память буфера для передачи данных

GetMem(CDS.lpData, CDS.cbData);

try

//Копируем данные в буфер

StrPCopy (CDS.lpData, StringEdit.Text);

//Отсылаем сообщение в окно с заголовком «StringReciever»

SendMessage(FindWindow(NIL, 'StringReciever'),

WM_COPYDATA, Handle, Integer(@CDS));

Finally

//Высвобождаем буфер

FreeMem(CDS.lpData, CDS.cbData);

end;

end;


Подробного комментария данный листинг не требует. Обратите внимание на вызов функции SendMessage, которая использует процедуру FindWindow для установки одного из своих параметров. Процедура FindWindow в случае успешного выполнения возвращает HWND окна, заголовок которого задается в параметре этой функции (строка StringReciever из предыдущего примера). Синхронная отправка сообщения WM_COPYDATA с набором данных, которые помещены в структуру CDS, осуществляется вызовом SendMessage.

Теперь рассмотрим второе приложение, которое принимает строку и отображает ее в надписи (Label). Для начала в блок объявления необходимо поместить обработчик сообщения и объявить само сообщение WM_COPYDATA:


type

TStringReciever = class(TForm)

LabelStr: TLabel;

private

//Обработчик сообщения WM_COPYDATA

procedure WMCopyData(var MessageData: TWMCopyData);

message WM_COPYDATA;


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


const CMD_SETLABELTEXT = 1;


Далее рассмотрим тело функции обработчика сообщения WM_COPYDATA (листинг 8.2).

Листинг 8.2. Обработка сообщения WMCOPYDATA

proceduree TStringReciever.WMCopyData (varrrMessageData: TWMCopyData);

begin

//Устанавливаем свойства метки, если заданная команда совпадает

if MessageData.CopyDataStruct.dwData = CMD_SETLABELTEXT then begin

//Устанавливаем текст из полученных данных

LabelStr.Caption:= PChar(MessageData.CopyDataStruct.lpData);

MessageData.Result:= 1;

end else

MessageData.Result:= 0;

end;


Если окну второго приложения, которое носит название StringReciver («получатель строки»), приходит сообщение WM_COPYDATA, происходит вызов процедуры WMCopyData. В качестве параметра эта процедура получает структуру данных MessageData типа TWMCopyData, содержащую идентификатор операции и данные (передаваемую строку). После проверки типа операции, в случае совпадения его с константой CMD_SETLABELTEXT, полученные данные преобразуются в строку. Преобразование происходит с помощью функции PChar. Полученная строка устанавливается в качестве заголовка для метки с именем LabelStr. Затем полю Result структуры MessageData присваивается значение 1 или 0 в зависимости от успешности выполненной операции (если успешно, то функция возвращает 1).

Таким образом, для передачи данных (строки) необходимо записать передаваемую строку в текстовое поле первой формы и нажать кнопку Отправить. Результат работы приложений показан на рис. 8.1.

Рис. 8.1. Вид приложений посылки и получения строки


Необходимо отметить, что передача данных посредством сообщения WM_COPYDATA является удобным и простым способом, однако WM_COPYDATA можно передавать только функцией SendMessage, что представляет существенный недостаток такого метода. SendMessage «замораживает» работу приложения отправителя, поэтому такой способ применяется для передачи небольших объемов данных, которые не требуют сложной обработки на стороне программы приемника. К тому же на использование сообщения WM_CO PYDATA налагаются некоторые существенные ограничения, о которых говорилось выше.

Использование буфера обмена

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

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

Для выполнения операции обмена данными через буфер в Delphi предназначен специальный класс TClipboard. В Delphi также имеется глобальный объект Clipboard, который является экземпляром класса TClipboard и представляет буфер обмена Windows.

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

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


//В разделе uses указываем модуль Clipboard

InformationClipLabel.Caption:= IntToString(Clipboard.FormatCount);


Буфер обмена поддерживает самые разнообразные типы данных. Список поименованных констант некоторых форматов представлен ниже.

• CF_TEXT – обычный текст (коды ANSI). Символы окончания строки – #10 и #13, окончания текста – #0.

• CF_BITMAP – рисунок BMP-формата.

• CF_MetaFilePic – рисунок WMF-формата.

• CF_TIFF – рисунок TIFF-формата.

• CF_OEMTEXT – текст.

• CF_DIB – рисунок DIB-формата.

• CF_Wave – звуковые данные.

• CF_UniCodeText – текст (коды Unicode).

• CF_Picture – объект типа TPicture.

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

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

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

Листинг 8.3. Отображение значений форматов буфера

procedureTFormClipboard.bnInfoCipClick(Sender: TObject);

var i: integer;

begin

ListBoxInfo.Clear;

for i:= 0 to Clipboard.FormatCount – 1 do

ListBoxInfo.Items.Add(IntToStr(Clipboard.Formats[i]));

end;


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

Перед осуществлением доступа к данным, содержащимся в объекте Clipboard, может потребоваться анализ формата данных, для чего служит метод HasFormat. Процедура HasFormat (Format: Word): Boolean используется для запроса к буферу обмена и позволяет узнать, можно ли извлечь хранимые в нем данные в формате, указанном параметром Format. При положительном результате возвращаемое значение равно True, в противном случае – False.

Как правило, буфер обмена одновременно используется различными приложениями, однако в случае, когда необходимо получить монопольный доступ к буферу, приложение должно открыть его для себя в специальном режиме. Для этого необходимо вызвать метод Open, позволяющий программе получить полный (исключительный) доступ к общей области обмена. После вызова метода Open содержимое буфера не может быть изменено другими приложениями, поэтому, после того как необходимость в монопольном использовании буфера отпадет, приложение должно вызвать метод Close объекта Clipboard. Если открытый буфер не будет закрыт с помощью метода Close, то он будет автоматически закрыт системой после завершения программы, открывшей буфер обмена.

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

Класс TClipboard используется многими классами и компонентами, которые поддерживают обмен данными через буфер обмена. К примеру, компоненты Memo и Edit содержат специальные методы для обмена текстовой информацией посредством буфера. Методы CopyToClipBoard и CutToClipBoard помещают текстовые данные в буфер обмена, копируя и вырезая их из источника (компонента) соответственно, а метод PasteFromClipBoard вставляет текстовый фрагмент из буфера в текстовое поле.

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

Листинг 8.4. Копирование текста из поля редактора Memo в буфер обмена

procedure TFormClipboard.bnCopyTextClick(Sender: TObject);

begin

//Выделяем весь текст в поле редактора

MemoText.SelectAll;

//Копируем текст

MemoText.CopyToClipBoard;

end;


Буфер обмена часто используется для хранения текста, поэтому объект Clipboard имеет специальное свойство AsText типа String, предназначенное для обработки содержимого буфера как данных текстового формата. Свойство AsText предназначено как для чтения, так и для записи. При чтении свойства данные извлекаются, а при записи заносятся в буфер обмена (листинг 8.5).

Листинг 8.5. Копирование текстовой информации

procedureTFormClipboard.bnCopyTextAsTextClick(Sender: TObject);

begin

//Если в буфере текст, то выводим его в поле редактора

if Clipboard.HasFormat(CF_Text)

then MemoText:= Clipboard.AsText

end;


При работе с графическими компонентами для операций, связанных с обменом информацией через общую область, удобно использовать метод Assi gn. Процедура Assign (Source: TPersistent) присваивает буферу обмена объект, указанный параметром Source. Если объект является изображением и принадлежит к таким графическим классам, как TBitmap, TPicture или TMetafile, то в буфер обмена копируется изображение установленного формата. Для извлечения изображения из буфера также может использоваться метод Assign.

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

Листинг 8.6. Обмен изображением через буфер обмена

procedure TFormClipboard.bnCopyImageClick(Sender: TObject);

begin

//Открываем монопольный доступ

Clipboard.Open;

//Заносим изображение в буфер

Clipboard.Assign(ImageMyPic1.Picture);

//Проверям формат находящихся в буфере данных

if Clipboard.HasFormat(CF_Picture)

then ImageMyPic2.Picture.Assign(Clipboard);

//Закрываем монопольный доступ к буферу

Clipboard.Close;

end;


Изображение, находящееся в образе ImageMyPic1, помещается в буфер обмена, откуда затем копируется в образ ImageMyPic2. Для выполнения этих операций устанавливается монопольный доступ к объекту Clipboard.

Таким образом, использование объекта Clipboard широко примененяется при разработке приложений, которым необходим обмен данными с другими программами. Необходимо отметить, что буфер обмена ориентирован на работу с пользователем (пользователь инициирует обмен данными между приложениями), поэтому такой способ обмена данными наиболее удобен с точки зрения пользователя. К тому же, буфер обмена поддерживает множество форматов представления информации, что позволяет сделать обмен данными более гибким и эффективным.


Страницы книги >> Предыдущая | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | Следующая
  • 0 Оценок: 0

Правообладателям!

Это произведение, предположительно, находится в статусе 'public domain'. Если это не так и размещение материала нарушает чьи-либо права, то сообщите нам об этом.


Популярные книги за неделю


Рекомендации