Статьи по Delphi (019)…
Работа с последовательными портами 2.
Если вам нужно что-то РЕАЛЬНОЕ, то попробуйте это. Можете только добавить проверку на ошибки. <<Книги>> Serial Communications: A C++ Developer's Guide by Mark Nelson, M&T Books.Правда, по большей части это про DOS, а Windows посвящена только одна глава. Проверьте это.
unit Comm;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Forms;
type
TCommEvent = procedure(Sender: TObject; Data: Char) of object;
TCommErrEvent = procedure(Sender: TObject; Error: Integer) of object;
TComm = class(TComponent)
private
Wnd: HWND;
DCB: TDCB;
CommID: Integer;
Buf: array[0..2048] of char;
NumChars: Integer;
FOnCommErr: TCommErrEvent;
FOnCommRecvd: TCommEvent;
procedure CommWndProc(var Message: TMessage);
public
function Send(data: Char): Boolean;
function Connect: Boolean;
constructor Create(AOwner: TComponent); override;
destructor destroy; override;
published
property OnCommErr: TCommErrEvent read FOnCommErr write FOnCommErr;
property OnCommRecvd: TCommEvent read FOnCommRecvd write FOnCommRecvd;
end;
procedure Register;
implementation
constructor TComm.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Wnd := AllocateHwnd(CommWndProc);
end;
procedure TComm.CommWndProc(var Message: TMessage);
var
Error, count: Integer;
Stat: TComStat;
begin
if Message.Msg = WM_COMMNOTIFY then
begin
Message.Result := 0;
GetCommEventMask(CommId, $3FFF);
NumChars := ReadComm(CommID, @Buf, 2048);
Error := GetCommError(CommId, Stat);
if Error = 0 then
begin
if Assigned(FOnCommRecvd) then
begin
for count := 0 to NumChars - 1 do
FOnCommRecvd(Self, Buf[count]);
end;
end
else
begin
if Assigned(FOnCommErr) then
begin
FOnCommErr(Self, Error);
end;
end;
end;
end;
function TComm.Send(data: Char): Boolean;
var
Error: Integer;
begin
Error := TransmitCommChar(CommId, data);
if Error < 0 then
Result := False
else
Result := True;
end;
function TComm.Connect: Boolean;
var
Config: array[0..20] of Char;
begin
CommId := OpenComm('COM2', 2048, 2048);
StrCopy(Config, 'com2:96,n,8,1'); {Здесь меняем настройки порта}
BuildCommDCB(Config, DCB);
DCB.ID := CommId;
SetCommState(DCB);
EnableCommNotification(CommID, Wnd, 1, -1);
SetCommEventMask(CommId, ev_RXChar);
Result := True;
end;
destructor TComm.destroy;
begin
CloseComm(CommID);
DeallocateHwnd(Wnd);
inherited destroy;
end;
procedure Register;
begin
RegisterComponents('Samples', [TComm]);
end;
end.
|
Как работать со всеми ячейками таблицы в WebBrowsere.
| - Избавиться от интернет-зависимости очень легко! У нас в чате все проделывали это по нескольку раз. |
Пример показывает как добавить содержимое каждой ячейки в TMemo:
procedure TForm1.Button1Click(Sender: TObject);
var
i, j: integer;
ovTable: OleVariant;
begin
// Use first table
ovTable := WebBrowser1.OleObject.Document.all.tags('TABLE').item(0);
for i := 0 to (ovTable.Rows.Length - 1) do
begin
for j := 0 to (ovTable.Rows.Item(i).Cells.Length - 1) do
begin
Memo1.Lines.Add(ovTable.Rows.Item(i).Cells.Item(j).InnerText;
end;
end;
end;
|
Не работает функция Writeln.
Для того, чтобы Delphi "увидела" функцию Writeln, включите WinCrt в список используемых модулей.
Вывод надписи поверх всех окон.
Автор: Vit WEB-сайт: http://forum.vingrad.ru
procedure WriteDC(s: string); var c: TCanvas; begin c := TCanvas.Create; c.Brush.Color := clBlue; c.Font.color := clYellow; c.Font.name := 'Fixedsys'; c.Handle := GetDC(GetWindow(GetDesktopWindow, GW_OWNER)); c.TextOut(screen.Width - c.TextWidth(s) - 2, screen.Height - 43, s); c.free; end;
Пишем свой текст в Меню.
Когда играешь во встроенную в Windows игру freecell, то справа в меню постоянно пишется сколько осталось карт. Давайте посмотрим, как это делается.
Перво наперво положим компонент главного меню на форму. Теперь установим свойство OwnerDraw в true. Далее создайте, то что Вы хотите вырисовывать в меню и создайте OnDrawItem. И добавьте в него следующую строку:
ACanvas.TextOut(1, ARect.Top + 1, 'I''m in the MainMenuDrawbar'); |
Не забудьте, если Вы используете изменяющуюся переменную, то измените её в другой функции и всё что надо будет сделать - это вызвать API функцию DrawMenuBar.
Если Вы используете Delphi 2,3 пользуйтесь сообщениями WM_MESUREITEM и WM_DRAWITEM, чтобы сделать данный эффект.
Сохранение и чтение из потока данных объекта.
Для чтения и записи на диск большого количества записей данных объекта я использовал TFileStream и TMemoryStream. Ниже я привел пример, сохраняющий на диске c помощью потока содержание контейнера DataArray, насчитывающего несколько тысяч записей объектов:
DataStream := TFileStream.Create('vccmem.dat', fmCreate);
MakeDate := FormatDateTime('mmmm d, yyyy',date);
DataStream.Write(MakeDate, SizeOf(MakeDate));
RCount := DataArray.Size;
DataStream.Write(RCount,SizeOf(RCount));
for q := 1 to DataArray.Size do
begin
Application.ProcessMessages;
ARecord := DataArray[q] as TNameRecord;
DataStream.Write(ARecord.id, SizeOf(ARecord.id));
DataStream.Write(ARecord.first_name, SizeOf(ARecord.first_name));
DataStream.Write(ARecord.last_name, SizeOf(ARecord.last_name));
DataStream.Write(ARecord.title, SizeOf(ARecord.title));
DataStream.Write(ARecord.address, SizeOf(ARecord.address));
DataStream.Write(ARecord.city, SizeOf(ARecord.city));
DataStream.Write(ARecord.state, SizeOf(ARecord.state));
DataStream.Write(ARecord.zip, SizeOf(ARecord.zip));
DataStream.Write(ARecord.phone, SizeOf(ARecord.phone));
DataStream.Write(ARecord.fax, SizeOf(ARecord.fax));
DataStream.Write(ARecord.e_mail, SizeOf(ARecord.e_mail));
end;
DataStream.Destroy;
|
А вот код, но на этот раз загружающий данные в память при запуске приложения и создающие контейнер данных с именем NameArray:
DataStream := TMemoryStream.Create;
DataStream.LoadFromFile('vccmem.dat');
DataStream.Read(MakeDate, SizeOf(MakeDate));
DataStream.Read(RCount, SizeOf(RCount));
for q := 1 to RCount do
begin
NameRecord := TNameRecord.Create;
DataStream.Read(NameRecord.id, SizeOf(NameRecord.id));
DataStream.Read(NameRecord.first_name,
SizeOf(NameRecord.first_name));
DataStream.Read(NameRecord.last_name,
SizeOf(NameRecord.last_name));
DataStream.Read(NameRecord.title, SizeOf(NameRecord.title));
DataStream.Read(NameRecord.address, SizeOf(NameRecord.address));
DataStream.Read(NameRecord.city, SizeOf(NameRecord.city));
DataStream.Read(NameRecord.state, SizeOf(NameRecord.state));
DataStream.Read(NameRecord.zip, SizeOf(NameRecord.zip));
DataStream.Read(NameRecord.phone, SizeOf(NameRecord.phone));
DataStream.Read(NameRecord.fax, SizeOf(NameRecord.fax));
DataStream.Read(NameRecord.e_mail, SizeOf(NameRecord.e_mail));
NameArray.Insert(NameRecord);
end;
|
Пишем ScreenSaver в Delphi.
| Программеры квасят на природе. Идут разговоры, трали-вали... Один рассказывает: - Познакомился тут с девушкой на дискотеке! Бесподобная деваха! И фигурка, и мозги есть, и танцует хорошо! Так вот, решил рiскuр"нуть ее к себе домой. Пригласил, она согласилась. Пришли, выпили винца, поговорили о том, о сем, музыку послушали. Потом я ее поцеловал, приподнял аккуратно, посадил задницей на клавиатуру, поднял юбку... Остальные: - Так у тебя компутер дома стоит?!?! А какой??? |
В примере описывается создание простейшего скринсейвера, а так же его установка и запуск.
Для написания скринсейвера нам необходимо включить следующие процедуры:
FormShow скрыть курсор, установка обработки сообщений, начало отображения скринсейвера FormHide окончание отображения скринсейвера, отображение курсора DeactivateScrSaver обработка сообщений, деактивирование, если нажата мышка или клавиатураТипичный код для этих процедур показан ниже.
Вы должны быть уверены, что Ваша форма создана со стилем fsStayOnTop. Вы так же должны быть уверены, что только один экземпляр Вашей программы будет запущен в системе. И в заключении Вам необходимо включить директиву компилятора {$D "Programname Screensaver"} в Ваш проект (*.dpr).
После того, как Вы скомпилируете программу, измените расширение файла на SCR и скопируйте его в Вашу системную папку \WINDOWS\SYSTEM .
var
crs : TPoint; {первоначально расположение курсора мышки}
procedure TScrForm.FormShow(Sender: TObject);
{starts the screensaver}
begin
WindowState := wsMaximized; {окошко будет на полный экран}
GetCursorPos(crs); {получаем позицию курсора}
Application.OnMessage := DeactivateScrSaver; {проверяем мышку/клавиатуру}
ShowCursor(false); {скрываем курсор}
{начинаем отображение скринсейвера...}
//
end; {процедура TScrForm.FormShow}
procedure TScrForm.FormHide(Sender: TObject);
{возвращаем управление пользователю}
begin
Application.OnMessage := nil; {запрещаем сообщения}
{останавливаем скринсейвер...}
//
ShowCursor(true); {возвращаем курсор назад}
end; {procedure TScrForm.FormHide}
procedure TScrForm.DeactivateScrSaver(var Msg : TMsg; var Handled : boolean);
{определение движения мышки или нажатия на клавиатуре}
var
done: boolean;
begin
if Msg.message = WM_MOUSEMOVE then {сдвинулась мышка}
done := (Abs(LOWORD(Msg.lParam) - crs.x) > 5) or
(Abs(HIWORD(Msg.lParam) - crs.y) > 5)
else {key / mouse нажаты?}
done := (Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP) or
(Msg.message = WM_SYSKEYDOWN) or (Msg.message = WM_SYSKEYUP) or
(Msg.message = WM_ACTIVATE) or (Msg.message = WM_NCACTIVATE) or
(Msg.message = WM_ACTIVATEAPP) or (Msg.message = WM_LBUTTONDOWN) or
(Msg.message = WM_RBUTTONDOWN) or (Msg.message = WM_MBUTTONDOWN);
if done then
Close;
end; {procedure TScrForm.DeactivateScrSaver}
|
Написание программ на чистом API.
program SmallPrg;
uses
Windows, Messages;
const
WinName = 'MainWClass';
function MainWndProc(Window: HWnd; AMessage, WParam, LParam: Longint): Longint; stdcall;
begin
//подпрограмма обработки сообщений
case AMessage of
WM_DESTROY:
begin
PostQuitMessage(0);
Result := 0;
Exit;
end;
else
Result := DefWindowProc(Window, AMessage, WParam, LParam);
end;
end;
function InitApplication: Boolean;
var
wcx: TWndClass;
begin
//Заполняем структуру TWndClass
// перерисовываем, если размер изменяется
wcx.style := CS_HREDRAW or CS_VREDRAW;
// адрес оконной процедуры
wcx.lpfnWndProc := @MainWndProc;
wcx.cbClsExtra := 0;
wcx.cbWndExtra := 0;
// handle to instance
wcx.hInstance := hInstance;
// загружаем стандандартную иконку
wcx.hIcon := LoadIcon(0, IDI_APPLICATION);
// загружаем стандартный курсор
wcx.hCursor := LoadCursor(0, IDC_ARROW);
// делаем светло-серый фон
wcx.hbrBackground := COLOR_WINDOW;
// пока нет главного меню
wcx.lpszMenuName := nil;
// имя класса окна
wcx.lpszClassName := PChar(WinName);
// Регистрируем наш класс окна.
Result := RegisterClass(wcx) <> 0;
end;
function InitInstance: HWND;
begin
// Создаем главное окно.
Result := CreateWindow(
// имя класса окна
PChar(WinName),
// заголовок
'Small program',
// стандартный стиль окна
WS_OVERLAPPEDWINDOW,
// стандартные горизонтальное, вертикальное положение, ширина и высота
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
0,//нет родительского окна
0,//нет меню
hInstance, // handle to application instance
nil); // no window-creation data
end;
var
hwndMain: HWND;
AMessage: msg;
begin
if (not InitApplication) then
MessageBox(0, 'Ошибка регистрации окна', nil, mb_Ok)
else
begin
hwndMain := InitInstance;
if (hwndMain = 0) then
MessageBox(0, 'Ошибка создания окна', nil, mb_Ok)
else
begin
// Показываем окно и посылаем сообщение WM_PAINT оконной процедуре
ShowWindow(hwndMain, CmdShow);
UpdateWindow(hwndMain);
while (GetMessage(AMessage, 0, 0, 0)) do
begin
TranslateMessage(AMessage);
DispatchMessage(AMessage);
end;
end;
end;
end.
|
Написание программ на чистом API 2.
В этом выпуске мы попробуем написать с Вами программу, которая не будет пользоваться VCL, а будет использовать вызовы функций Windows API. Приложения такого типа нужны, когда размер исполняемого файла является критичным. Например, в инсталяторах, деинсталяторах, самораспаковывающихся архивах и т.п. В крайнем случае, для того чтобы посмотреть какую работу выполняет за нас VCL, и что из себя представляет Windows-программа. На самом деле все очень просто...Для этого нам необходимо:
// 1. Зарегистрировать класс окна для окна главной формы.
function InitApplication: Boolean;
var
wcx: TWndClass;
begin
//Заполняем структуру TWndClass
// перерисовываем, если размер изменяется
wcx.style := CS_HREDRAW or CS_VREDRAW;
// адрес оконной процедуры
wcx.lpfnWndProc := @MainWndProc;
wcx.cbClsExtra := 0;
wcx.cbWndExtra := 0;
// handle to instance
wcx.hInstance := hInstance;
// загружаем стандандартную иконку
wcx.hIcon := LoadIcon(0, IDI_APPLICATION);
// загружаем стандартный курсор
wcx.hCursor := LoadCursor(0, IDC_ARROW);
// делаем светло-серый фон
wcx.hbrBackground := COLOR_WINDOW;
// пока нет главного меню
wcx.lpszMenuName := nil;
// имя класса окна
wcx.lpszClassName := PChar(WinName);
// Регистрируем наш класс окна.
Result := RegisterClass(wcx) <> 0;
end;
// 2. Написать подпрограмму обработки оконных сообщений.
function MainWndProc(Window: HWnd; AMessage, WParam,
LParam: Longint): Longint; stdcall; export;
begin
//подпрограмма обработки сообщений
case AMessage of
WM_DESTROY: begin
PostQuitMessage(0);
Exit;
end;
else
Result := DefWindowProc(Window, AMessage, WParam, LParam);
end;
end;
// 3. Создать главное окно приложения.
function InitInstance: HWND;
begin
// Создаем главное окно.
Result := CreateWindow(
// имя класса окна
PChar(WinName),
// заголовок
'Small program',
// стандартный стиль окна
WS_OVERLAPPEDWINDOW,
// стандартные горизонтальное, вертикальное положение, ширина и высота
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
0,//нет родительского окна
0,//нет меню
hInstance, // handle to application instance
nil); // no window-creation data
end;
// 4. Написать тело программы.
var
hwndMain: HWND;
AMessage: msg;
begin
if (not InitApplication) then
begin
MessageBox(0, 'Ошибка регистрации окна', nil, mb_Ok);
Exit;
end;
hwndMain := InitInstance;
if (hwndMain = 0) then
begin
MessageBox(0, 'Ошибка создания окна', nil, mb_Ok);
Exit;
end
else
begin
// Показываем окно и посылаем сообщение WM_PAINT оконной процедуре
ShowWindow(hwndMain, CmdShow);
UpdateWindow(hwndMain);
end;
while (GetMessage(AMessage, 0, 0, 0)) do
begin
//Запускаем цикл обработки сообщений
TranslateMessage(AMessage);
DispatchMessage(AMessage);
end;
Halt(AMessage.wParam);
end.
// 5. Запустить программу на исполнение. ;)
|
Здесь находится полный текст программы.
program SmallPrg;
uses Windows, Messages;
const
WinName = 'MainWClass';
function MainWndProc(Window: HWnd; AMessage, WParam,
LParam: Longint): Longint; stdcall; export;
begin
//подпрограмма обработки сообщений
case AMessage of
WM_DESTROY: begin
PostQuitMessage(0);
Exit;
end;
else
Result := DefWindowProc(Window, AMessage, WParam, LParam);
end;
end;
function InitApplication: Boolean;
var
wcx: TWndClass;
begin
//Заполняем структуру TWndClass
// перерисовываем, если размер изменяется
wcx.style := CS_HREDRAW or CS_VREDRAW;
// адрес оконной процедуры
wcx.lpfnWndProc := @MainWndProc;
wcx.cbClsExtra := 0;
wcx.cbWndExtra := 0;
// handle to instance
wcx.hInstance := hInstance;
// загружаем стандандартную иконку
wcx.hIcon := LoadIcon(0, IDI_APPLICATION);
// загружаем стандартный курсор
wcx.hCursor := LoadCursor(0, IDC_ARROW);
// делаем светло-серый фон
wcx.hbrBackground := COLOR_WINDOW;
// пока нет главного меню
wcx.lpszMenuName := nil;
// имя класса окна
wcx.lpszClassName := PChar(WinName);
// Регистрируем наш класс окна.
Result := RegisterClass(wcx) <> 0;
end;
function InitInstance: HWND;
begin
// Создаем главное окно.
Result := CreateWindow(
// имя класса окна
PChar(WinName),
// заголовок
'Small program',
// стандартный стиль окна
WS_OVERLAPPEDWINDOW,
// стандартные горизонтальное, вертикальное положение, ширина и высота
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
0,//нет родительского окна
0,//нет меню
hInstance, // handle to application instance
nil); // no window-creation data
end;
var
hwndMain: HWND;
AMessage: msg;
begin
if (not InitApplication) then
begin
MessageBox(0, 'Ошибка регистрации окна', nil, mb_Ok);
Exit;
end;
hwndMain := InitInstance;
if (hwndMain = 0) then
begin
MessageBox(0, 'Ошибка создания окна', nil, mb_Ok);
Exit;
end
else
begin
// Показываем окно и посылаем сообщение WM_PAINT оконной процедуре
ShowWindow(hwndMain, CmdShow);
UpdateWindow(hwndMain);
end;
while (GetMessage(AMessage, 0, 0, 0)) do
begin
//Запускаем цикл обработки сообщений
TranslateMessage(AMessage);
DispatchMessage(AMessage);
end;
Halt(AMessage.wParam);
end.
|
Писать в порт принтера.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure Out32(portadresse: Word; wert: Byte);
var
val: Byte;
begin
val := Byte(wert);
asm
push dx
mov dx,portadresse
mov al,val
out dx,al
pop dx
end;
end;
function Inp32(portadresse: Word): Byte;
var
val: Byte;
begin
asm
push dx
mov dx,portadresse
in al,dx
mov val,al
pop dx
end;
Inp32 := Byte(val) and $00ff;
end;
// Example/Beispiel:
Out32($378, 0) {= Dataport all 8 Bit Low}
Out32($378, (Inp32($378) or 1); {Dataport Bit 1 High}
Out32($378, (Inp32($378) and 254); {Datap. Bit 1, Low}
Как писать Win32API приложения на Delphi.
| В 3 часа ночи она тихо прокралась в соседнюю комнату. Разбила 17 дюймовый монитор, дискеты залила клеем, системный блок выбросила в окно 12 этажа, принтер утопила в ванне. Потом она вернулась в теплую пастель и прижалась к плечу нечего неподозревающего спящего мужа. Жить ей осталось не больше 3 часов. |
Главная пробема, возникающая при написании WinAPI приложений - это неудобство ручного создания всех окон приложения. Требуется вызывать функцию CreateWindow для каждого (в том числе и дочернго) окна программы, а затем еще и менять шрифт в некоторых из них. Лучшим на мой взгляд выходом из этой ситуации является использование ресурсов диалоговых окон (dialog box resources) для соэдания всех окон приложения. В этой статье я расскажу как это делается в Delphi на примере простоо приложения с одним главным и двумя (модальными) окнами.
Шаг 1. Создание ресурсов диалоговых окон
Для создания ресурсов я использовал редактор ресурсов из состава Borland C++ 5.02, и поэтому все скриншоты сделаны с него. В Borland Resource Workshop 4.5 все почти аналогично. Создаем главное окно, вот его код:
500 DIALOGEX 0, 0, 240, 117
EXSTYLE WS_EX_DLGMODALFRAME | WS_EX_APPWINDOW | WS_EX_CLIENTEDGE
STYLE DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU | WS_MINIMIZEBOX
class "WndClass1"
CAPTION "Главное окно приложения"
MENU 300
FONT 8, "MS Sans Serif", 400, 0
LANGUAGE LANG_RUSSIAN , 0
{
CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
19, 94, 50, 14, WS_EX_CLIENTEDGE
CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
96, 94, 50, 14, WS_EX_CLIENTEDGE
CONTROL "Help", IDHELP, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
172, 94, 50, 14, WS_EX_CLIENTEDGE
CONTROL "Группа", -1, "button", BS_GROUPBOX | BS_RIGHT | WS_CHILD | WS_VISIBLE | WS_GROUP,
20, 9, 100, 76
CONTROL "Кнопка 1", 105, "button", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
28, 21, 60, 12
CONTROL "Кнопка 2", 106, "button", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
28, 37, 60, 12
CONTROL "Кнопка 3", 107, "button", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
28, 53, 60, 12
CONTROL "ListBox1", 108, "listbox", LBS_NOTIFY | LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_CHILD |
WS_VISIBLE | WS_BORDER | WS_TABSTOP,
132, 13, 92, 72
}
|
Обратите внимание на поле CLASS. В нем должно стоять то же значение, что и в поле lpszClassName записи TWndClassEx основной программы. В редакторе ресурсов значение этого поля можно изменить в окне свойств ресурса. В Borland C++ оно выглядит так:
Два других окна создаются как обычные ресурсы, поэтому их код я здесь не привожу - вы можете его посмотреть самостоятельно в исходниках (см. в конце статьи).
Шаг 2. Основная программа
Текст программы в нашем случае несколько отличается от текста, например, winmin. Регистрация оконного класса:
wc.cbSize:=sizeof(wc); wc.style:=cs_hredraw or cs_vredraw; wc.lpfnWndProc:=@WindowProc; wc.cbClsExtra:=0; wc.cbWndExtra:=DLGWINDOWEXTRA; wc.hInstance:=HInstance; wc.hIcon:=LoadIcon(hInstance, 'MAINICON'); wc.hCursor:=LoadCursor(0,idc_arrow); wc.hbrBackground:=COLOR_BTNFACE+1; wc.lpszMenuName:=nil; wc.lpszClassName:='WndClass1'; RegisterClassEx(wc); |
Обратите внимание, что в поле cbWindowExtra стоит константа DLGWINDOWEXTRA, если бы её там не было, нам не удалось бы создать главное окно, основанное на ресурсе Dialog Box. Кроме того, в поле lpszClassName стоит то же значение, что и в соответствующем поле описания ресурса окна.
Итак, класс создан и зарегистрирован, теперь создаем главное окно из ресурса:
MainWnd:=CreateDialog(hInstance, '#500', 0, nil); |
Напоминаю, что '#500' значит имя ресурса окна. Не забудьте подключить откомпилированный файл сценария ресурса к программе при помощи директивы {$r ...}.
Шаг 3. Оконная функция
Оконная функция ничем не отличается от обычной:
function WindowProc(wnd:HWND; Msg : Integer; Wparam:Wparam;
Lparam: Lparam): Lresult; stdcall;
var
nCode, ctrlID, size: word;
pt: TPoint;
s: string;
begin
case msg of
wm_command:
begin
nCode:=hiWord(wParam);
ctrlID:=loWord(wParam);
case ctrlID of
IDHELP:
begin
DialogBox(hInstance,'#501',wnd,@DialogFunc);
end;
IDOK:
begin
DialogBoxParam(hInstance,'#503',wnd,@DialogFunc2, Integer(pd));
s := 'Login: '+pd^.login;
s := s + ' ' + 'Pass: '+pd^.pass;
ListBox_AddString(lb, s);
end;
IDCANCEL:
begin
DestroyWindow(wnd);
end;
end;
end;
wm_destroy :
begin
Dispose(pd);
postquitmessage(0); exit;
Result:=0;
end;
else
Result := DefWindowProc(wnd, msg, wparam, lparam);
end;
end;
|
Шаг 4. Цикл сбора сообщений
Цикл сбора сообщений следует изменить следующим образом, как при использовании немодальных диалоговых окон. Можно, правда, оставить все как есть, но тогда вы не сможете использовать клавиатуру для перемещения между дочерними окнами, использования кнопок "по умолчанию" и т.д.
while GetMessage(Mesg, 0, 0, 0) do
begin
if mainWnd<>0 then
if IsDialogMessage(mainWnd,Mesg) then
continue;
TranslateMessage(Mesg);
DispatchMessage(Mesg);
end;
|
После нажатия кнопки "ОК" появляется еще одно окно. Если в нем ввести текст и нажать "ОК", этот текст будет добавлен в Listbox.
Что нужно давать WSAAsyncSelect в качестве параметра handle.
Автор: NomadicЧто нужно давать WSAAsyncSelect в качестве параметра handle, если тот запускается и используется в dll (init), и никакой формы (у которой можно было бы взять этот handle) в этой dll не создается?
const
WM_ASYNCSELECT = WM_USER + 0;
type
TNetConnectionsManager = class(TObject)
protected
FWndHandle: HWND;
procedure WndProc(var MsgRec: TMessage);
...
end;
constructor TNetConnectionsManager.Create
begin
inherited Create;
FWndHandle := AllocateHWnd(WndProc);
...
end;
destructor TNetConnectionsManager.Destroy;
begin
...
if FWndHandle <> 0 then
DeallocateHWnd(FWndHandle);
inherited Destroy;
end;
procedure TNetConnectionsManeger.WndProc(var MsgRec: TMessage);
begin
with MsgRec do
if Msg = WM_ASYNCSELECT then
WMAsyncSelect(MsgRec)
else
DefWindowProc(FWndHandle, Msg, wParam, lParam);
end;
|
Но рекомендую посмотреть WinSock2, в котором можно:
WSAEventSelect( FSocket, FEventHandle, FD_READ or FD_CLOSE ); WSAWaitForMultipleEvents( ... ); WSAEnumNetworkEvents( FSocket, FEventHandle, lpNetWorkEvents ); |
То есть, обойтись без окон и без очереди сообщений windows, а заодно иметь возможность работать и с IPX/SPX, и с netbios.
Использование функции wsprintf.
Оформил: DeeCo
Автор: Виктор Кода.
Данная заметка возможно, будет интересна для тех, кто программирует на Object Pascal с использованием только API-функций Windows.Часто необходимо вывести (к примеру, даже в окошко с MB_OK) какое-то значение, будь то код ошибки типа HRESULT или результат работы какой-либо функции. Простейшее решение - использовать IntToStr() из модуля sysutils.pas. Однако в этом случае размер минимальной программы на Delphi увеличится с 16 до 41 кб, что лично меня очень неудовлетворяет. Хочется получить реальное преимущество в размерах, раз уж не используется VCL. Есть и другое объяснение - хотя мы и живём в эпоху быстрых процессоров и ёмких HDD, следить за качеством кода программ и его размерами обязывает культура программирования. В конце-концов это просто неразумно - ради одной функции тащить весь код из sysutils.pas и sysconst.pas. Складывается глупейшая ситуация - ради преобразования чисел в строку приходится мириться с тем, что к готовому exe-модулю подключаются многочисленные resourcestring и хлам в виде кучи ненужного кода.
Для некоторых преобразований можно использовать API-функцию wsprintf() из модуля windows.pas. Она позволяет произвести форматированную запись в буфер последовательности символов и значений аргументов. Вот как она описана:function wsprintf(Output: PChar; Format: PChar): Integer; stdcall;А вот так она описана в файле winuser.h:
WINUSERAPI int WINAPIV wsprintfA(LPSTR, LPCSTR, ...);Как видно, осутствует третий параметр. Он подразумевает передачу значения произвольного типа - например, char* или int (при желании его можно даже опустить). Причина, по которой функция документирована неправильно - ограничение, накладываемое языком Object Pascal - он не позволяет передать в параметр функции значение произвольного типа (хотя, например, SizeOf() позволяет, но реализована она на системном уровне).
Я всё-таки нашёл способ приспособить wsprintf() для своих нужд. Выход - описать её самостоятельно в своей программе так:
function wsprintf(lpOut: LPSTR; lpFmt: LPCTSTR; p: Pointer): Integer; stdcall; external 'user32.dll' name 'wsprintfA';Теперь для вывода в буфер достаточно написать так:
var szMessage: PChar; GetMem(szMessage, 256); // Определяем размер строки в памяти wsprintf(szMessage, 'Number %d', Pointer(899034)); MessageBox(0, szMessage, 'Сообщение', MB_OK);Предварительно надо выделить память для буфера, иначе форматирование не произойдёт.
Полную документацию по функции wsprintf() можно найти в справке Delphi. Добавлю, что для корректности можно приводить передаваемые значения не к типу Pointer, а к Pinteger - для типа Integer, PUINT - для целых беззнаковых и т. д.
Скачать demo-проект WsPrintf.zip (0.67 K)Примеры форматирования строк с использованием API-функции wvsprintf.
Автор: Внук WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Примеры форматирования строк с использованием API-функции wvsprintf
Примеры показывают приемы использования функции wvsprintf в Delphi для
форматирования строк. Эта функция входит в состав Windows API
(реализована в user32.dll), поэтому не требует подключения модуля
SysUtils по сравнению с аналогичной функцией Format.
Зависимости: Windows
Автор: Внук, Zhigalin@poria.ru, ICQ:60571446, Самара
Copyright: Внук
Дата: 29 мая 2002 г.
***************************************************** }
// Пример использования функции wvsprintf с двумя параметрами разного типа
function WinFormat(out TargetString: AnsiString): Boolean;
const
FormatStr = 'String Parameter:"%s", Number Parameter:%05d';
var
StrParam: AnsiString;
NumberParam, FormatResult: Integer;
ParamArray: array[0..1] of Pointer;
begin
NumberParam := 105; // Задаем значение первого параметра
StrParam := 'Hello'; // Задаем значение второго параметра
SetLength(TargetString, 50); // Должно быть достаточно места для результата.
// Без этой строки работать не будет
ParamArray[0] := @StrParam[1];
ParamArray[1] := Pointer(NumberParam);
FormatResult := wvsprintf(PChar(TargetString), FormatStr, PChar(@ParamArray));
if FormatResult < Length(FormatStr) then // В случае ошибки значение функции
// меньше длины шаблонной строки
begin
TargetString := '';
Result := False;
end
else
result := True;
end;
// Пример использования функции wvsprintf с одним параметром
function SimpleWinFormat(out TargetString: AnsiString): Boolean;
const
FormatStr = 'Number Parameter:%05d';
var
NumberParam, FormatResult: Integer;
begin
NumberParam := 112;
SetLength(TargetString, 50); // Должно быть достаточно места для результата.
// Без этой строки работать не будет
FormatResult := wvsprintf(PChar(TargetString), FormatStr,
PChar(@NumberParam));
if FormatResult < Length(FormatStr) then // В случае ошибки значение функции
// меньше длины шаблонной строки
begin
TargetString := '';
Result := False;
end
else
result := True;
end;
Пример использования:
uses Dialogs, ...;
...
var
S: AnsiString;
...
begin
...
if WinFormat(S) then
ShowMessage(S)
else
ShowMessage('An error has been occured!');
if SimpleWinFormat(S) then
ShowMessage(S)
else
ShowMessage('An error has been occured!');
...
end;
...
Как импортировать данные из Excel в StringGrid.
Автор: http://www.swissdelphicenter.ch
uses
ComObj;
function Xls_To_StringGrid(AGrid: TStringGrid; AXLSFile: string): Boolean;
const
xlCellTypeLastCell = $0000000B;
var
XLApp, Sheet: OLEVariant;
RangeMatrix: Variant;
x, y, k, r: Integer;
begin
Result := False;
// Create Excel-OLE Object
XLApp := CreateOleObject('Excel.Application');
try
// Hide Excel
XLApp.Visible := False;
// Open the Workbook
XLApp.Workbooks.Open(AXLSFile);
// Sheet := XLApp.Workbooks[1].WorkSheets[1];
Sheet := XLApp.Workbooks[ExtractFileName(AXLSFile)].WorkSheets[1];
// In order to know the dimension of the WorkSheet, i.e the number of rows
// and the number of columns, we activate the last non-empty cell of it
Sheet.Cells.SpecialCells(xlCellTypeLastCell, EmptyParam).Activate;
// Get the value of the last row
x := XLApp.ActiveCell.Row;
// Get the value of the last column
y := XLApp.ActiveCell.Column;
// Set Stringgrid's row &col dimensions.
AGrid.RowCount := x;
AGrid.ColCount := y;
// Assign the Variant associated with the WorkSheet to the Delphi Variant
RangeMatrix := XLApp.Range['A1', XLApp.Cells.Item[X, Y]].Value;
// Define the loop for filling in the TStringGrid
k := 1;
repeat
for r := 1 to y do
AGrid.Cells[(r - 1), (k - 1)] := RangeMatrix[K, R];
Inc(k, 1);
AGrid.RowCount := k + 1;
until k > x;
// Unassign the Delphi Variant Matrix
RangeMatrix := Unassigned;
finally
// Quit Excel
if not VarIsEmpty(XLApp) then
begin
// XLApp.DisplayAlerts := False;
XLApp.Quit;
XLAPP := Unassigned;
Sheet := Unassigned;
Result := True;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if Xls_To_StringGrid(StringGrid1, 'C:\Table1.xls') then
ShowMessage('Table has been exported!');
end;
Обмен информацией в формате XML.
Обмен информацией в формате XML - это чрезвычайно удобный механизм, позволяющий свести к минимуму проблемы внутрифирменных форматов данных. Эти проблемы сводятся к сложностям при обмене информацией с контрагентами фирмы. Зачастую проблема стоит не только в невозможности других воспринимать предлагаемый вами формат (DBF, ASCII и т.п.), сколько в нежелании приспосабливаться к ним. Эти форматы не очень удобны. К тому же у вашего партнера наверняка уже есть наработки в этой области. Так почему бы вам не приспособится к его формату обмена данными? Язык XML позволяет разрабатывать форматы описания данных, которые могут стать тем мостиком, котрый свяжет всех контрагентов и устранит технические и психологические барьеры, неизбежно возникающие при приспособлении к чужим технологиям. Технология XML тоже не назовешь родной, однако ее возможности и универсальность при описании текстовых данных перекрывают все сложности, связанные с ее освоением. Формировать документы в формате XML достаточно просто. Следует лишь познакомится с конкретным DTD и образцами корректных документов. А вот загрузка может быть достаточно трудна, если не прибегать к помощи готовых решений в виде XML парсеров. Их довольно много для разных платформ и при желании можно найти их описания в WWW. Одним из наиболее распространенным на платформе Windows является Microsoft XML Parser. Дело в том, что он входит в состав Microsoft Explorer 5.0 и более позние версии. Он доступен в виде объекта ActiveX. Данный парсер является верифицирующим, то есть проверяет не только синтаксическую проверку документа, но и семантическую корректность в соответствии с заданным DTD.Для загрузки и анализа документа в Delphi рассмотрим следующий код.
procedure LoadOnixDoc(const FileName: string);
var
XML: variant;
Node, mainNode, childNodes: variant;
i: integer;
TreeNode: TTreeNode;
procedure LoadItems(TreeNode: TTreeNode; Node: variant);
var
i: integer;
begin
TreeNode := TV.Items.AddChild(TreeNode, Node.nodeName);
TreeNode.ImageIndex := TreeNode.Level;
TreeNode.SelectedIndex := TreeNode.ImageIndex;
if Node.nodeName = '#text' then
begin
TreeNode.Text := Node.nodeValue;
TV.SetNodeBoldState(TreeNode, true);
end;
for i := 0 to Node.childNodes.length - 1 do
LoadItems(TreeNode, Node.childNodes.item[i]);
end;
begin
XML := CreateOleObject('Microsoft.XMLDOM');
XML.load(FileName);
if XML.parseError.reason <> '' then
begin
ShowMessage(XML.parseError.reason);
end
else
begin
mainNode := XML.documentElement;
childNodes := mainNode.childNodes;
LoadItems(nil, mainNode);
TreeNode := TV.Items[1];
while Assigned(TreeNode) do
begin
TreeNode.Expand(false);
TreeNode := TreeNode.GetNextSibling;
end;
if Assigned(TV.Items[0]) then
TV.Items[0].Expand(false);
end;
end;
Пример документа XML Onix.
<?xml version="1.0" encoding="windows-1251"?> <!DOCTYPE ONIXMessage SYSTEM "onix-international.dtd"> <ONIXMessage release="1.1"> <FrоmСоmраnу>ТД Библио-Глобус</FrоmСоmраnу> <FromPerson>Kirillov Alexey kirillov@biblio-globus.u</FromPerson> <ToCompany>EDItEUR</ToCompany> <ToPerson>EDItEUR chief manager</ToPerson> <MessageNumber>1</MessageNumber> <SentDate>23.12.00</SentDate> <DefaultLanguageOfText>rus</DefaultLanguageOfText> <Product> <RecordReference>247825</RecordReference> <NotificationType>3</NotificationType> <ISBN>966-7393-05-4</ISBN> <ProductForm>BB</ProductForm> <DistinctiveTitle>SQL энциклопедия пользователя</DistinсtivеТitlе> <Contributor> <ContributorSequenceNumber>0</ContributorSequenceNumber> <ContributorRole>A01</ContributorRole> <РеrsоnNаmе>Х. Ладани</РеrsоnNаmе> <РеrsоnNаmеInvеrtеd>Ладани, Х.</PersonNameInverted> <ВiоgrарhiсаlNоtе>Книга раскрывает темы, которые часто не описываются или достаточно кратко представлены в руководствах пользователя - подзапросы, структуры данных, представления, производительность, целостность и защита данных. </BiographicalNote> </Contributor> <NumberOfPages>624</NumberOfPages> <ImрrintNаmе>ДиаСофт</ImрrintNаmе> <MediaFile> <MediaFileTypeCode>04</MediaFileTypeCode> <MediaFileFormatCode>03</MediaFileFormatCode> <MediaFileLinkTypeCode>06</MediaFileLinkTypeCode> <MediaFileLink> http://shop.biblio-globus.ru/photos1/05/58983.jpg </MediaFileLink> </MediaFile> <SupplyDetail> <SupplierName></SupplierName> <AvailabilityCode></AvailabilityCode> <Price> <PriceTypeCode>01</PriceTypeCode> <PriceAmount>425</PriceAmount> <CurrencyCode>RUB</CurrencyCode> </Price> </SupplyDetail> </Product> </ONIXMessage>
Для работы Вам понадобятся файлы DTD: ONIX INTERNATIONAL DTD
Динамическое создание меню по XML-файлу.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
The following procedure allows you to build a menu from an XML file.
Special feature: You only need to specify the Name of the procedure which then
will be attached to a OnClick handler.
Note that the procedure must be declared as public.
}
{
Mit folgender Prozedur kann man aus einem XML-File ein Menu
erstellen lassen (einfach im OnCreate aufrufen).
Besonderes Feature: Im XML-File gebt ihr nur den Namen der Prozedur an,
die dem OnClick-Ereignis zugewiesen werden soll.
Die einzige Einschrankung besteht darin, dass diese Prozedur
published sein muss.
Bindet einfach diese Prozedur in euer Hauptformular ein:
}
procedure TMainForm.CreateMenuFromXMLFile;
function Get_Int(S: string): Integer;
begin
Result := 0;
try
Result := StrToInt(S);
except
end;
end;
procedure AddRecursive(Parent: TMenuItem; Item: IXMLNode);
var
I: Integer;
Node: TMenuItem;
Child: IXMLNode;
Address: TMethod;
begin
Node := TMenuItem.Create(Parent);
if (Uppercase(Item.Attributes['CAPTION']) <> 'SEPERATOR') then
begin
Node.Caption := Item.Attributes['CAPTION'];
if (Uppercase(Item.Attributes['ID']) <> 'NONE') then
begin
Address.Code := MethodAddress(Item.Attributes['ID']);
Address.Data := Self;
if (Item.ChildNodes.Count - 1 < 0) then
Node.OnClick := TNotifyEvent(Address);
end;
if (Uppercase(Item.Attributes['SHORTCUT']) <> 'NONE') then
Node.ShortCut := TextToShortCut(Item.Attributes['SHORTCUT']);
Node.Checked := (Item.Attributes['CHECKED'] = '1');
end
else
Node.Caption := '-';
Node.Visible := (Item.Attributes['VISIBLE'] = '1');
if Parent <> nil then
Parent.Add(Node)
else
MainMenu.Items.Add(Node);
for I := 0 to Item.ChildNodes.Count - 1 do
begin
Child := item.ChildNodes[i];
if (Child.NodeName = 'ENTRY') then
AddRecursive(Node, Child);
end;
end;
var
Root: IXMLMENUType;
Parent: TMenuItem;
I: Integer;
Child: IXMLNode;
begin
XMLDocument.FileName := ExtractFilePath(Application.ExeName) + XMLFile;
if not FileExists(XMLDocument.FileName) then
begin
MessageDlg('Menu-XML-Document nicht gefunden!', mtError, [mbOK], 0);
Halt;
end;
XMLDocument.Active := True;
Screen.Cursor := crHourglass;
try
Root := GetXMLMenu(XMLDocument);
Parent := nil;
for I := 0 to Root.ChildNodes.Count - 1 do
begin
Child := Root.ChildNodes[i];
if (Child.NodeName = 'ENTRY') then
AddRecursive(Parent, Child);
end;
finally
Screen.Cursor := crDefault;
end;
end;
{----------------------------------------------------------
You also need the encapsulation of the XML-File.
( Save it as unit and add it to your program.
Created with Delphi6 -> New -> XML Data Binding Wizard )
-----------------------------------------------------------}
{----------------------------------------------------------
Naturlich braucht man auch die Kapselung des XML-Files
(Als Unit speichern und ins Programm einbinden.
Die Datei wurde mit Delphi 6 -> Neu -> XML-Datenbindung erstellt):
-----------------------------------------------------------}
{***************************************************}
{ }
{ Delphi XML-Datenbindung }
{ }
{ Erzeugt am: 27.06.2002 13:25:01 }
{ }
{***************************************************}
unit XMLMenuTranslation;
interface
uses xmldom, XMLDoc, XMLIntf;
type
{ Forward-Deklarationen }
IXMLMENUType = interface;
IXMLENTRYType = interface;
{ IXMLMENUType }
IXMLMENUType = interface(IXMLNode)
['{8F36F5E2-834F-41D9-918F-9B1A441C9074}']
{ Zugriff auf Eigenschaften }
function Get_ENTRY: IXMLENTRYType;
{ Methoden & Eigenschaften }
property ENTRY: IXMLENTRYType read Get_ENTRY;
end;
{ IXMLENTRYType }
IXMLENTRYType = interface(IXMLNode)
['{AD85CD05-725E-40F8-A8D7-D6EC05FD4360}']
{ Zugriff auf Eigenschaften }
function Get_CAPTION: WideString;
function Get_VISIBLE: Integer;
function Get_ID: Integer;
function Get_ENTRY: IXMLENTRYType;
procedure Set_CAPTION(Value: WideString);
procedure Set_VISIBLE(Value: Integer);
procedure Set_ID(Value: Integer);
{ Methoden & Eigenschaften }
property Caption: WideString read Get_CAPTION write Set_CAPTION;
property Visible: Integer read Get_VISIBLE write Set_VISIBLE;
property ID: Integer read Get_ID write Set_ID;
property ENTRY: IXMLENTRYType read Get_ENTRY;
end;
{ Forward-Deklarationen }
TXMLMENUType = class;
TXMLENTRYType = class;
{ TXMLMENUType }
TXMLMENUType = class(TXMLNode, IXMLMENUType)
protected
{ IXMLMENUType }
function Get_ENTRY: IXMLENTRYType;
public
procedure AfterConstruction; override;
end;
{ TXMLENTRYType }
TXMLENTRYType = class(TXMLNode, IXMLENTRYType)
protected
{ IXMLENTRYType }
function Get_CAPTION: WideString;
function Get_VISIBLE: Integer;
function Get_ID: Integer;
function Get_ENTRY: IXMLENTRYType;
procedure Set_CAPTION(Value: WideString);
procedure Set_VISIBLE(Value: Integer);
procedure Set_ID(Value: Integer);
public
procedure AfterConstruction; override;
end;
{ Globale Funktionen }
function GetXMLMENU(Doc: IXMLDocument): IXMLMENUType;
function LoadMENU(const FileName: WideString): IXMLMENUType;
function NewMENU: IXMLMENUType;
implementation
{ Globale Funktionen }
function GetXMLMENU(Doc: IXMLDocument): IXMLMENUType;
begin
Result := Doc.GetDocBinding('MENU', TXMLMENUType) as IXMLMENUType;
end;
function LoadMENU(const FileName: WideString): IXMLMENUType;
begin
Result := LoadXMLDocument(FileName).GetDocBinding('MENU', TXMLMENUType) as IXMLMENUType;
end;
function NewMENU: IXMLMENUType;
begin
Result := NewXMLDocument.GetDocBinding('MENU', TXMLMENUType) as IXMLMENUType;
end;
{ TXMLMENUType }
procedure TXMLMENUType.AfterConstruction;
begin
RegisterChildNode('ENTRY', TXMLENTRYType);
inherited;
end;
function TXMLMENUType.Get_ENTRY: IXMLENTRYType;
begin
Result := ChildNodes['ENTRY'] as IXMLENTRYType;
end;
{ TXMLENTRYType }
procedure TXMLENTRYType.AfterConstruction;
begin
RegisterChildNode('ENTRY', TXMLENTRYType);
inherited;
end;
function TXMLENTRYType.Get_CAPTION: WideString;
begin
Result := ChildNodes['CAPTION'].Text;
end;
procedure TXMLENTRYType.Set_CAPTION(Value: WideString);
begin
ChildNodes['CAPTION'].NodeValue := Value;
end;
function TXMLENTRYType.Get_VISIBLE: Integer;
begin
Result := ChildNodes['VISIBLE'].NodeValue;
end;
procedure TXMLENTRYType.Set_VISIBLE(Value: Integer);
begin
ChildNodes['VISIBLE'].NodeValue := Value;
end;
function TXMLENTRYType.Get_ID: Integer;
begin
Result := ChildNodes['ID'].NodeValue;
end;
procedure TXMLENTRYType.Set_ID(Value: Integer);
begin
ChildNodes['ID'].NodeValue := Value;
end;
function TXMLENTRYType.Get_ENTRY: IXMLENTRYType;
begin
Result := ChildNodes['ENTRY'] as IXMLENTRYType;
end;
end.
{---------------------------------------------------------------------
Finally, I'll show you an example for the XML-File.
The Procedure Name is assigned to the ID which then will be called.
---------------------------------------------------------------------}
{---------------------------------------------------------------------
Als Beispiel fur das XML-File hier noch eines aus
einem meiner Programme.
In ID steht der Name der Prozedur, die man als OnClick aufrufen will
- denkt auch daran, dass diese Prozedur unbedingt als published
deklariert sein muss, sonst liefert MethodAddress() Nil zuruck.
----------------------------------------------------------------------}
{
<?xml version="1.0" encoding="ISO-8859-1"?>
<MENU>
<ENTRY CAPTION="Datei" VISIBLE="1" ID="None" SHORTCUT="None" CHECKED="0">
<ENTRY CAPTION="Beenden" VISIBLE="1" ID="CloseProgram" SHORTCUT="Strg+X" CHECKED="0"></ENTRY>
</ENTRY>
<ENTRY CAPTION="Anzeige" VISIBLE="1" ID="None" SHORTCUT="None" CHECKED="0">
<ENTRY CAPTION="Toolbar" VISIBLE="1" ID="ShowToolbar" SHORTCUT="None" CHECKED="1"></ENTRY>
<ENTRY CAPTION="Seperator" VISIBLE="1"></ENTRY>
<ENTRY CAPTION="Optionen" VISIBLE="1" ID="ShowOptionen" SHORTCUT="Strg+O" CHECKED="0"></ENTRY>
</ENTRY>
<ENTRY CAPTION="News" VISIBLE="1" ID="None" SHORTCUT="None" CHECKED="0">
<ENTRY CAPTION="Refresh" VISIBLE="1" ID="RefreshAll" SHORTCUT="F5" CHECKED="0"></ENTRY>
<ENTRY CAPTION="Seperator" VISIBLE="1"></ENTRY>
<ENTRY CAPTION="Administration" VISIBLE="1" ID="None" SHORTCUT="None" CHECKED="0">
<ENTRY CAPTION="neue Nachricht hinzufugen" VISIBLE="1" ID="NewMarkedNews" SHORTCUT="Strg+N" CHECKED="0"></ENTRY>
<ENTRY CAPTION="markierte Nachricht bearbeiten" VISIBLE="1" ID="EditMarkedNews" SHORTCUT="Strg+E" CHECKED="0"></ENTRY>
<ENTRY CAPTION="markierte Nachricht loschen" VISIBLE="1" ID="DeleteMarkedNews" SHORTCUT="None" CHECKED="0"></ENTRY>
<ENTRY CAPTION="Seperator" VISIBLE="1"></ENTRY>
<ENTRY CAPTION="Film hinzufugen" VISIBLE="1" ID="AddMPG" SHORTCUT="None" CHECKED="0"></ENTRY>
<ENTRY CAPTION="markierten Film loschen" VISIBLE="1" ID="DeleteMPG" SHORTCUT="None" CHECKED="0"></ENTRY>
</ENTRY>
</ENTRY>
<ENTRY CAPTION="Hilfe" VISIBLE="1" ID="None" SHORTCUT="None" CHECKED="0">
<ENTRY CAPTION="LogView" VISIBLE="1" ID="ShowLog" SHORTCUT="Strg+L" CHECKED="0"></ENTRY>
<ENTRY CAPTION="eMail schreiben" VISIBLE="1" ID="WriteEMail" SHORTCUT="None" CHECKED="0"></ENTRY>
<ENTRY CAPTION="Seperator" VISIBLE="1"></ENTRY>
<ENTRY CAPTION="Uber" VISIBLE="1" ID="About" SHORTCUT="None" CHECKED="0"></ENTRY>
</ENTRY>
</MENU>
}
Парсер подавляющего большинства нотаций XML.
Автор: Delirium WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Парсер подавляющего большинства нотаций XML.
Для задачи десериализации мне потребовался парсер.
Основное преимущество - никак не связан с операционной системой
(в отличие от TXMLDocument), ну и разумеется - простота :)
Зависимости: SysUtils, StrUtils
Автор: Delirium, VideoDVD@hotmail.com, ICQ:118395746, Москва
Copyright: Delirium (Master BRAIN) 2003
Дата: 16 сентября 2003 г.
***************************************************** }
unit BNFXMLParser;
interface
uses SysUtils, StrUtils;
type
PXMLNode = ^TXMLNode;
TXMLValues = (TextNode, XMLNode);
TXMLNode = record
Name: string;
Attributes: array of record
Name: string;
Value: string;
end;
SubNodes: array of record
RecType: TXMLValues;
case TXMLValues of
TextNode: (Text: PString);
XMLNode: (XML: PXMLNode);
end;
Parent: PXMLNode;
end;
function BNFXMLTree(var Value: string): PXMLNode;
implementation
function fnTEG(var Node: PXMLNode; var Value: string): boolean; forward;
function fnVAL(var Node: PXMLNode; var Value: string): boolean; forward;
function fnATT(var Node: PXMLNode; var Value: string): boolean; forward;
function fnXML(var Node: PXMLNode; var Value: string): boolean;
var
i: integer;
begin
if (Pos('<', Value) > 0)
and (Pos('>', Value) > Pos('<', Value))
and (Pos('<', Value) <> Pos('</', Value)) then
begin
// Оганизую узел
if Node = nil then
begin
New(Node);
Node.Parent := nil;
end
else
begin
i := length(Node.SubNodes);
Setlength(Node.SubNodes, i + 1);
New(Node.SubNodes[i].XML);
Node.SubNodes[i].RecType := XMLNode;
Node.SubNodes[i].XML.Parent := Node;
Node := Node.SubNodes[i].XML;
end;
Result := fnTEG(Node, Value);
end // '<'
else
Result := True;
end;
function fnTEG(var Node: PXMLNode; var Value: string): boolean;
var
i, i1, i2, i3: integer;
S: string;
begin
Result := False;
i1 := Pos('<', Value);
if i1 > 0 then
begin
i2 := PosEx('/>', Value, i1);
i3 := PosEx('>', Value, i1);
if (i2 > 0) and (i2 < i3) then
begin // <abc/>
// Value
S := Copy(Value, i1 + 1, (i2 - i1) - 1);
Delete(Value, i1, (i2 - i1) + 2);
// TEXT, этот текст пренадлежит предку
if Node.Parent <> nil then
begin // Добавляюсь к предку
i := length(Node.Parent.SubNodes);
Setlength(Node.Parent.SubNodes, i + 1);
New(Node.Parent.SubNodes[i].Text);
Node.Parent.SubNodes[i].RecType := TextNode;
Node.Parent.SubNodes[i].Text^ := Copy(Value, 1, Pos('<', Value) - 1);
end;
Delete(Value, 1, Pos('<', Value) - 1);
//
if fnVAL(Node, S) then
begin // Вложенных тегов не бывает
Node := Node.Parent;
Result := fnXML(Node, Value);
end;
end
else
begin // <abc>...</abc>
// Value
S := Copy(Value, i1 + 1, (i3 - i1) - 1);
Delete(Value, i1, (i3 - i1) + 1);
// TEXT
i := length(Node.SubNodes);
Setlength(Node.SubNodes, i + 1);
New(Node.SubNodes[i].Text);
Node.SubNodes[i].RecType := TextNode;
Node.SubNodes[i].Text^ := Copy(Value, 1, Pos('<', Value) - 1);
Delete(Value, 1, Pos('<', Value) - 1);
//
if fnVAL(Node, S) then
begin // Val
// Проверяю закрытие тега, удаляю хвост и передаю управление предку
if Pos('</' + AnsiLowerCase(Node.Name) + '>', AnsiLowerCase(Value)) = 1
then
begin
Delete(Value, 1, Length('</' + Node.Name + '>'));
// TEXT принадлежащий предку
if Node.Parent <> nil then
begin // Добавляюсь к предку
i := length(Node.Parent.SubNodes);
Setlength(Node.Parent.SubNodes, i + 1);
New(Node.Parent.SubNodes[i].Text);
Node.Parent.SubNodes[i].RecType := TextNode;
Node.Parent.SubNodes[i].Text^ := Copy(Value, 1, Pos('<', Value) -
1);
end;
Delete(Value, 1, Pos('<', Value) - 1);
Node := Node.Parent;
Result := fnXML(Node, Value);
end
else
begin
// Обрабатываю вложенные теги, на выходе мой узел
if fnXML(Node, Value) then
begin
// закрываю его
if Pos('</' + AnsiLowerCase(Node.Name) + '>', AnsiLowerCase(Value))
= 1 then
begin
Delete(Value, 1, Length('</' + Node.Name + '>'));
// TEXT принадлежащий предку
if Node.Parent <> nil then
begin // Добавляюсь к предку
i := length(Node.Parent.SubNodes);
Setlength(Node.Parent.SubNodes, i + 1);
New(Node.Parent.SubNodes[i].Text);
Node.Parent.SubNodes[i].RecType := TextNode;
Node.Parent.SubNodes[i].Text^ := Copy(Value, 1, Pos('<', Value)
- 1);
end;
Delete(Value, 1, Pos('<', Value) - 1);
end;
// Остальной XML - предку
if Node.Parent <> nil then
Node := Node.Parent;
Result := fnXML(Node, Value);
end;
end;
end; // Val
end; // <abc>...</abc>
end; // i1
end;
function fnVAL(var Node: PXMLNode; var Value: string): boolean;
begin
Value := AnsiReplaceStr(Value, '''', '"');
if (Pos(' ', Value) > 0)
and (Pos('="', Value) > Pos(' ', Value)) then
begin
Node.Name := Trim(Copy(Value, 1, Pos(' ', Value) - 1)); // Название тега Name
Delete(Value, 1, Pos(' ', Value));
Result := fnATT(Node, Value);
end // ' ' и ('="'
else
begin
// Название тега Name
Value := Trim(Value);
if Pos(' ', Value) > 0 then
Node.Name := Copy(Value, 1, Pos(' ', Value) - 1)
else
Node.Name := Value;
Value := '';
Result := True;
end;
end;
function fnATT(var Node: PXMLNode; var Value: string): boolean;
begin
Result := True;
Value := Trim(Value);
if Pos('="', Value) > 0 then
begin
Result := False;
SetLength(Node.Attributes, Length(Node.Attributes) + 1);
// Название атрибута
Node.Attributes[Length(Node.Attributes) - 1].Name := Trim(Copy(Value, 1,
Pos('="', Value) - 1));
Delete(Value, 1, Pos('="', Value) + 1);
if Pos('"', Value) > 0 then
begin
// Значение атрибута
Node.Attributes[Length(Node.Attributes) - 1].Value := Copy(Value, 1,
Pos('"', Value) - 1);
Delete(Value, 1, Pos('"', Value));
if Length(Value) > 0 then
Result := fnATT(Node, Value)
else
Result := True;
end;
end;
end;
function BNFXMLTree(var Value: string): PXMLNode;
begin
Result := nil;
fnXML(Result, Value);
end;
end.
Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
Node: PXMLNode;
i: integer;
begin
S := '<A> aaa1 ' + #13 +
' aaa2 aaa3 ' + #13 +
' <B>bbb ' + #13 +
' <C>ccc</C> ' + #13 +
' </B> ' + #13 +
' <D>ddd ' + #13 +
' <E eee="EEE"/> ' + #13 +
' </D> ' + #13 +
'</A> ';
Node := BNFXMLTree(S);
for i := 0 to Length(Node.SubNodes) - 1 do
case Node.SubNodes[i].RecType of
TextNode: ShowMessage('Text = ' + Node.SubNodes[i].Text^);
XMLNode: ShowMessage('XML Node name = ' + Node.SubNodes[i].XML.Name);
end;
end;
Разбор XML.
Автор: Delirium WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Разбор XML
Данный прасер не такой универсальный, как предыдущий,
за то - почти в 1000 раз эффективнее!
Зависимости: Windows, Forms, SysUtils, StrUtils
Автор: Delirium, VideoDVD@hotmail.com, ICQ:118395746, Москва
Copyright: Delirium (Master BRAIN) 2003
Дата: 22 октября 2003 г.
***************************************************** }
unit BNFXMLParser2;
interface
uses Windows, Forms, SysUtils, StrUtils;
type
PXMLNode = ^TXMLNode;
PXMLTree = ^TXMLTree;
TXMLAttr = record
NameIndex, NameSize: integer;
TextIndex, TextSize: integer;
end;
TXMLNode = record
NameIndex, NameSize: integer;
Attributes: array of TXMLAttr;
TextIndex, TextSize: integer;
SubNodes: array of PXMLNode;
Parent: PXMLNode;
Data: PString;
end;
TXMLTree = record
Data: PString;
TextSize: integer;
NodesCount: integer;
Nodes: array of PXMLNode;
end;
function BNFXMLTree(Value: string): PXMLTree;
function GetXMLNodeName(Node: PXMLNode): string;
function GetXMLNodeText(Node: PXMLNode): string;
function GetXMLNodeAttr(AttrName: string; Node: PXMLNode): string;
implementation
function BNFXMLTree(Value: string): PXMLTree;
var
LPos, k, State, CurAttr: integer;
i: integer;
CurNode: PXMLNode;
begin
New(Result);
Result^.TextSize := Pos('<', Value) - 1;
New(Result^.Data);
Result^.Data^ := Value;
k := 0;
State := 0;
CurNode := nil;
CurAttr := -1;
for LPos := Result.TextSize + 1 to Length(Value) do
case State of
0: case Value[LPos] of
'<':
begin
i := length(Result.Nodes);
Setlength(Result.Nodes, i + 1);
New(Result.Nodes[i]);
Inc(k);
if k mod 10 = 0 then
begin
Application.ProcessMessages;
if k mod 100 = 0 then
SleepEx(1, True);
end;
CurNode := Result.Nodes[i];
CurNode^.NameIndex := 0;
CurNode^.NameSize := 0;
CurNode^.TextIndex := 0;
CurNode^.Parent := nil;
CurNode^.Data := Result^.Data;
State := 1;
end;
end;
1: case Value[LPos] of
' ': ;
'>': State := 9;
'/': State := 10;
else
begin
CurNode^.NameIndex := LPos;
CurNode^.NameSize := 1;
State := 2;
end;
end;
2: case Value[LPos] of
' ': State := 3;
'>': State := 9;
'/': State := 10;
else
Inc(CurNode^.NameSize);
end;
3: case Value[LPos] of
' ': ;
'>': State := 9;
'/': State := 10;
else
begin
i := length(CurNode^.Attributes);
Setlength(CurNode^.Attributes, i + 1);
CurNode^.Attributes[i].NameIndex := LPos;
CurNode^.Attributes[i].NameSize := 1;
CurAttr := i;
State := 4;
end;
end;
4: case Value[LPos] of
'=': State := 5;
else
Inc(CurNode^.Attributes[CurAttr].NameSize);
end;
5: case Value[LPos] of
'''': State := 6;
'"': State := 7;
end;
6: case Value[LPos] of
'''':
begin
CurNode^.Attributes[CurAttr].TextIndex := LPos;
CurNode^.Attributes[CurAttr].TextSize := 0;
State := 8;
end;
else
begin
CurNode^.Attributes[CurAttr].TextIndex := LPos;
CurNode^.Attributes[CurAttr].TextSize := 1;
State := 61;
end;
end;
7: case Value[LPos] of
'"':
begin
CurNode^.Attributes[CurAttr].TextIndex := LPos;
CurNode^.Attributes[CurAttr].TextSize := 0;
State := 8;
end;
else
begin
CurNode^.Attributes[CurAttr].TextIndex := LPos;
CurNode^.Attributes[CurAttr].TextSize := 1;
State := 71;
end;
end;
61: case Value[LPos] of
'''': State := 8;
else
Inc(CurNode^.Attributes[CurAttr].TextSize);
end;
71: case Value[LPos] of
'"': State := 8;
else
Inc(CurNode^.Attributes[CurAttr].TextSize);
end;
8: case Value[LPos] of
' ': State := 3;
'>': State := 9;
'/': State := 10;
end;
9: case Value[LPos] of
'>': ;
else
begin
CurNode^.TextIndex := LPos;
CurNode^.TextSize := 1;
State := 11;
end;
end;
10: case Value[LPos] of
'>':
begin
CurNode := CurNode^.Parent;
if CurNode = nil then
State := 0
else
State := 9;
end;
end;
11: case Value[LPos] of
'<': State := 12;
else
Inc(CurNode^.TextSize);
end;
12: case Value[LPos] of
'/': State := 10;
else
begin
i := length(CurNode^.SubNodes);
Setlength(CurNode^.SubNodes, i + 1);
New(CurNode^.SubNodes[i]);
Inc(k);
if k mod 10 = 0 then
begin
Application.ProcessMessages;
if k mod 100 = 0 then
SleepEx(1, True);
end;
CurNode^.SubNodes[i]^.Parent := CurNode;
CurNode^.SubNodes[i]^.Data := Result^.Data;
CurNode^.SubNodes[i].NameIndex := LPos;
CurNode^.SubNodes[i].NameSize := 1;
CurNode^.SubNodes[i].TextIndex := 0;
CurNode := CurNode^.SubNodes[i];
State := 2;
end;
end;
end;
Result^.NodesCount := k;
end;
function GetXMLNodeName(Node: PXMLNode): string;
begin
Result := Copy(Node^.Data^, Node^.NameIndex, Node^.NameSize);
end;
function GetXMLNodeText(Node: PXMLNode): string;
begin
Result := Copy(Node^.Data^, Node^.TextIndex, Node^.TextSize);
end;
function GetXMLNodeAttr(AttrName: string; Node: PXMLNode): string;
var
i: integer;
begin
Result := '';
if Length(Node^.Attributes) = 0 then
exit;
i := 0;
while (i < Length(Node^.Attributes))
and (AnsiLowerCase(AttrName) <> AnsiLowerCase(Trim(Copy(Node^.Data^,
Node^.Attributes[i].NameIndex, Node^.Attributes[i].NameSize)))) do
Inc(i);
Result := Copy(Node^.Data^, Node^.Attributes[i].TextIndex,
Node^.Attributes[i].TextSize);
end;
end.
XML сериализация объекта Delphi.
Оформил: DeeCo
Автор: Чудин Андрей.
В статье рассмотрены возможности прямой загрузки/сохранения XML документов в объекты Dеlрhi/С++Builder и генерации соответствующих DTD. Предлагается оптимизированный компонент для реализации этих возможностей. Язык XML предоставляет нам чрезвычайно удобный и почти универсальный подход к хранению и передаче информации. Существует множество парсеров для разбора XML документов по модели DOM. На платформе Microsoft Windows - это, в первую очередь, парсеры MSXML от Microsoft. Парсеры взаимодействуют с вызывающими приложениями посредством интерфейса SAX (Simple API for XML) и/или DOM (Document Object Model). Во всех анализаторах, за исключением продукта фирмы Microsoft, используется SAX, и почти во всех их возможно применение DOM. Реализация парсера MSXML не плоха, поддерживает проверку семантической корректности документа и с его помощью достаточно удобно загружать небольшие XML документы. Однако для работы с каждым типом документов, реализованном на XML разработчику приходится создавать некий оберточный код для загрузки данных из объекта Microsoft.XMLDOM во внутренние структуры программы или для удобного перемещения по DOM. При изменении формата документа, что часто возможно в части расширения его спецификации, изменения созданного кода могут быть достаточно трудоемкими и требующими тщательной отладки. Возникает вопрос возможности упростить работу с XML документами, интегрировать их обработку в разрабатываемые программы. Для модели DOM наилучшим является непосредственная загрузка XML документа в объект Dеlрhi/С++Builder. И эта возможность есть. Используя RTTI можно загружать данные непосредственно из тегов XML документа в атрибуты заданного объекта. Соответственно, становится возможным и XML-сериализация published интерфейсов объектов любых классов Delphi. Рассматриваемый подход дает возможность наиболее удобно интегрировать обработку XML в среду разработки Delphi и C++Builder. Возможность доступа к свойствам объектов определяется через механизмы RTTI. Его возможности в Delphi очень велики, т.к. среда разработки сама хранит ресурсы объектов в текстовом формате. Очевидно, что за предлагаемыми преимуществами скрываются и ряд ограничений. В первую очередь, это касается атрибутов тегов. У нас нет простых механизмов отличить атрибут от тега при сохранении свойства объекта. Поэтому в предлагаемой реализации мы будем обрабатывать XML документы, не содержащие атрибутов. Это ограничение может стать критическим только если мы хотим поддержать уже существующий тип XML документа. Если же мы разрабатываем формат сами, то вполне можем отказаться от атрибутов. Зато наш парсер будет работать не просто быстро, а очень быстро. ;) Алгоритм XML-сериализации реализуется в виде рекурсивного обхода published интерфейса объекта. Для начала определим ряд простых функций для формирования XML кода. Они позволят нам добавлять открывающие, закрывающие теги и значения в выходной поток.
{ пишет строку в выходящий поток. Исп-ся при сериализации }
procedure WriteOutStream(Value: string);
begin
OutStream.Write(Pchar(Value)[0], Length(Value));
end;
{ Добавляет открывающий тег с заданным именем }
procedure addOpenTag(const Value: string);
begin
WriteOutStream(CR + DupStr(TAB, Level) + '<' + Value + '>');
inc(Level);
end;
{ Добавляет закрывающий тег с заданным именем }
procedure addCloseTag(const Value: string; addBreak: boolean = false);
begin
dec(Level);
if addBreak then
WriteOutStream(CR + DupStr(TAB, Level));
WriteOutStream('</' + Value + '>');
end;
{ Добавляет значение в результирующую строку }
procedure addValue(const Value: string);
begin
WriteOutStream(Value);
end;
Следующее, что предстоит реализовать - это перебор всех свойств объекта и формирование тегов. Сведения о свойствах получаются через интерфейс компонента. Это информация о типе. Для каждого свойства, за исключением классовых получается их имя и текстовое значение, после чего формируется XML-тег. Значение загружается через ф-ию TypInfo.GetPropValue();
procedure TglXMLSerializer.SerializeInternal(Component: TObject; Level: integer
= 1);
var
PropInfo: PPropInfo;
TypeInf, PropTypeInf: PTypeInfo;
TypeData: PTypeData;
i, j: integer;
AName, PropName, sPropValue: string;
PropList: PPropList;
NumProps: word;
PropObject: TObject;
begin
{ Playing with RTTI }
TypeInf := Component.ClassInfo;
AName := TypeInf^.Name;
TypeData := GetTypeData(TypeInf);
NumProps := TypeData^.PropCount;
GetMem(PropList, NumProps * sizeof(pointer));
try
{ Получаем список строк }
GetPropInfos(TypeInf, PropList);
for i := 0 to NumProps - 1 do
begin
PropName := PropList^[i]^.Name;
PropTypeInf := PropList^[i]^.PropType^;
PropInfo := PropList^[i];
case PropTypeInf^.Kind of
tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet,
tkWChar, tkLString, tkWString, tkVariant:
begin
{ Получение значения свойства }
sPropValue := GetPropValue(Component, PropName, true);
{ Перевод в XML }
addOpenTag(PropName);
addValue(sPropValue); { Добавляем значение свойства в результат }
addCloseTag(PropName);
end;
Для классовых типов придется использовать рекурсию для загрузки всех свойств соответствующего объекта.
Более того, для ряда классов необходимо использовать особый подход. Сюда относятся, к примеру, строковые списки и коллекции. Ими и ограничимся.
Для текстового списка TStrings будем сохранять в XML его свойство CommaText, а в случае коллекции после обработки всех ее свойств сохраним в XML каждый элемент TCollectionItem отдельно. При этом в качестве контейнерного тега будем использовать имя класса TCollection(PropObject).Items[j].ClassName.
tkClass: { Для классовых типов рекурсивная обработка }
begin
addOpenTag(PropName);
PropObject := GetObjectProp(Component, PropInfo);
if Assigned(PropObject) then
begin
{ Для дочерних свойств-классов - рекурсивный вызов }
if (PropObject is TPersistent) then
Result := Result + SerializeInternal(PropObject, Level);
{ Индивидуальный подход к некоторым классам }
if (PropObject is TStrings) then { Текстовые списки }
begin
WriteOutStream(TStrings(PropObject).CommaText);
end
else if (PropObject is TCollection) then { Коллекции }
begin
Result := Result + SerializeInternal(PropObject, Level);
for j := 0 to (PropObject as TCollection).Count - 1 do
begin
addOpenTag(TCollection(PropObject).Items[j].ClassName);
SerializeInternal(TCollection(PropObject).Items[j], Level);
addCloseTag(TCollection(PropObject).Items[j].ClassName, true);
end
end;
{ Здесь можно добавить обработку остальных классов: TTreeNodes, TListItems }
end;
addCloseTag(PropName, true);
end;
Описанные функции позволят нам получить XML код для объекта включая все его свойства. Остается только 'обернуть' полученный XML в тег верхнего уровня - имя класса объекта. Если мы поместим вышеприведенный код в функцию SerializeInternal(), то результирующая функция Serialize() будет выглядеть так:
procedure Serialize(Component: TObject; Stream: TStream); ... WriteOutStream(PChar(CR + '<' + Component.ClassName + '>')); SerializeInternal(Component); WriteOutStream(PChar(CR + '</' + Component.ClassName + '>'));К вышеприведенному можно добавить еще ф-ии для форматирования генерируемого XML кода. Также можно добавить возможность пропуска пустых значений и свойств со значениями по умолчанию. Все эти расширения мы реализуем при создании готового компонента. Следует заметить, что при желании можно переписать этот код для генерации также и атрибутов элементов. Для отличия элементов от их атрибутов в интерфейсе сохраняемого объекта можно принять следующее соглашение: элементами являются только классовые типы, все же прочие свойства кодируются как атрибуты соответствующих классов. Соответственно можно модифицировать и парсер. При этом появляется возможность использования XML схем вместо DTD. Тут, однако, возникает проблема описания модели содержания для текста #PCDATA. Для разрешения проблемы придется выделить отдельный класс для хранения подобных данных.
XML сериализация объектов - Заключение.
Оформил: DeeCo
Автор: Чудин Андрей.
После последовательного рассмотрения возможностей прямой загрузки/сохранения XML документов в объекты Dеlрhi/С++Builder стоит подвести некоторые итоги и ответить на вопросы о применимости данного подхода.Применимость.
1. Метод подходит для создания собственных типов XML документов. Когда есть необходимость оперативно разработать свой формат для обмена данными, то проблем возникать не должно. В такой ситуации это может быть оптимальным подходом. Поскольку формат разрабатывается заново, то мы можем учесть все ограничения предложенной реализации и не использовать атрибуты. При этом достаточно спроектировать необходимые нам классы, а вся остальная работа будет проделана автоматически. 2. Метод подходит для обработки XML документов, в которых не используются атрибуты. Подобных типов документов не много, но если они подходят, то такой путь их обработки достаточно удобен. Так автором статьи в рабочем проекте описанным методом обрабатываются документы стандарта ONIX XML. В этом формате присутствует атрибут только у одного элемента, а подобное ограничение можно обойти предварительной обработкой загружаемого документа. Применимость может стать почти универсальной, если доработать код для обработки атрибутов элементов.Производительность.
Код загрузки XML документа в объект дает вполне приемлемую производительность. Тестирование дало следующие результаты.Документ: формат ONIX XML размером 10 мб.
Системная конфигурация: Celeron 450 / 256 / Windows 2000prof парсер приблизительное время загрузки MS XML Parser 2.6 синхронная загрузка без проверки состоятельности ~6 сек MS XML Parser 2.6 синхронная загрузка с проверкой состоятельности ~11 сек Компонент TglXMLSerializer 8,5 секКомпонент TglXMLSerializer загружает данные в синхронном режиме. Он не использует DTD или схемы. При загрузке проводится проверка правильности (well-formed) и частично - состоятельности (valid). При нарушении правильности документа парсер выдаст соответствующее исключение и прекратит загрузку. Проверка состоятельности определяется тем, что при загрузке данные загружаются в свойства объекта. Если одноименное элементу XML документа свойство не найдено, то генерируется исключение. Так как свойства объекта типизированы и при загрузке происходит преобразование текстовых значений, то ошибки, здесь возникающие, говорят о нарушении состоятельности документа. Таким образом, интерфейс нашего объекта играет роль XML схемы, что очень удобно. Более того, эти проверки могут быть расширены дополнительным кодом в обработчиках свойств объекта.Расширяемость.
Приведенная реализация имеет ряд ограничений. Первое и основное - это отказ от использования элементов в атрибутах XML документов. Это ограничение может быть снято переработкой кода парсера и процедур сохранения XML. Для отличия элементов от атрибутов в интерфейсе объектов можно придти к следующему соглашению: Все классовые типы являются элементами Все простые типы являются атрибутами соответствующих объектов Пример.TPerson = class; TMyXMLMessage = class(TPersistent) published property LanguageOfText: WideString; property ToPerson: TPerson; end; TPerson = class(TPersistent) published property FirstName: WideString; property LastName: WideString; end;Таким образом, в первом случае объект приведенного выше класса TMyXMLMessage при сериализации даст следующий XML код:
<TMyXMLMessage> <LanguageOfText>english</LanguageOfText> <ToPerson> <FirstName>Osama</FirstName> <LastName>Unknoun</LastName> </ToPerson> </TMyXMLMessage>При обработке простых типов как атрибутов получим следующий более компактный код:
<TMyXMLMessage LanguageOfText="english"> <ToPerson FirstName="Osama" LastName="Unknoun"/> </TMyXMLMessage>Второй вариант позволяет работать с любыми документами, однако надо решить, каким образом описывать данные #CDDATA. Возможно, для этого придется зарезервировать какой-либо тип. Второе ограничение, которое следует упомянуть, это способ описания коллекций. В приведенной реализации коллекции сохраняются в виде тега свойства, в который вложены описания элементов коллекции. Довольно часто в XML документах повторяющаяся группа тегов не заключается специально в теги, отделяющие эту группу. Это является препятствием для написания классов для обработки уже существующих документов. Поэтому необходимо предусмотреть и такую возможность. Приведенная реализация будет постоянно обновляться, в том числе и на основании Ваших, уважаемый читатель, предложений. Последняя версия компонента с исходными текстами входит в библиотеку Globus VCL Extention Library.
Компонент для XML сериализации.
| Автор: Chudin A.V |
| Встречаются два интернетчика: - Жена меня ну совсем достала! Все время шипит на меня! - А может, она сконнектиться пытается? |
Объединяя сказанное о сериализации, десериализации объектов и создании DTD соберем полноценный компонент для XML сериализации.
Компонент конвертирует компонент в XML и обратно в соответствии с published-интерфейсом класса компонента.
XML формируется в виде пар тегов с вложенными в них значениями. Атрибуты у тегов отсутствуют.
Тег верхнего уровня соответствует классу объекта. Вложенные теги соответствуют именам свойств. Для элементов коллекций контейнерный тег соответствует имени класса.
Вложенность тегов не ограничена и полностью повторяет published интерфейс класса заданного объекта.
Поддерживаются целые типы, типы с плавающей точкой, перечисления, наборы, строки, символы. вариантные типы, классовые типы, стоковые списки и коллекции.
Интерфейс:
procedure Serialize(Component: TObject; Stream: TStream); |
procedure DeSerialize(Component: TObject; Stream: TStream); |
property GenerateFormattedXML; // создавать форматированный XML код property ExcludeEmptyValues; // пропускать пустые значения свойств property ExcludeDefaultValues; // пропускать значения по умолчанию property OnGetXMLHeader; // позволяет указать свой XML заголовок |
Ограничения:
В объекте допустимо использовать только одну коллекцию каждого типа. Для преодоления этого ограничения требуется некоторая доработка.
Наследники класса TStrings не могут иметь published свойств.
Процедурные типы не обрабатываются.
Для генерации DTD у объекта все свойства классовых типов, одноименные со свойствами агрегированных объектов, должны быть одного класса.
Предусловия:
Объект для (де)сериализации должен быть создан до вызова процедуры.
Дополнительно:
При загрузке из XML содержимое коллекций в объекте не очищается, что позволяет дозагружать данные из множества источников в один объект.
unit glXMLSerializer;
{
Globus Delphi VCL Extensions Library ' GLOBUS LIB '
Copyright (c) 2001 Chudin A.V, chudin@yandex.ru
glXMLSerializer Unit 08.2001 component TglXMLSerializer
}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, comctrls, TypInfo;
type
TOnGetXMLHeader = procedure Ошибка! Недопустимый объект гиперссылки.
(Sender: TObject; var Value: string) of object;
XMLSerializerException = class(Exception)
end;
TglXMLSerializer = class(TComponent)
private
{ Private declarations }
Buffer: PChar;
BufferLength: DWORD;
TokenPtr: PChar;
OutStream: TStream;
FOnGetXMLHeader: TOnGetXMLHeader;
FGenerateFormattedXML: boolean;
FExcludeEmptyValues: boolean;
FExcludeDefaultValues: boolean;
FReplaceReservedSymbols: boolean;
procedure check(Expr: boolean; const message: string);
procedure WriteOutStream(Value: string);
protected
procedure SerializeInternal(Component: TObject; Level: integer = 1);
procedure DeSerializeInternal(Component: TObject; const ComponentTagName:
string; ParentBlockEnd: PChar = nil);
procedure GenerateDTDInternal(Component: TObject; DTDList: TStrings;
Stream: TStream; const ComponentTagName: string);
procedure SetPropertyValue(Component: TObject; PropInfo: PPropInfo;
Value, ValueEnd: PChar; ParentBlockEnd: PChar);
public
tickCounter, tickCount: DWORD;
constructor Create(AOwner: TComponent); override;
{ Сериализация объекта в XML }
procedure Serialize(Component: TObject; Stream: TStream);
{ Загрузка XML в объект }
procedure DeSerialize(Component: TObject; Stream: TStream);
{ Генерация DTD }
procedure GenerateDTD(Component: TObject; Stream: TStream);
published
property GenerateFormattedXML: boolean
read FGenerateFormattedXML write FGenerateFormattedXML default true;
property ExcludeEmptyValues: boolean
read FExcludeEmptyValues write FExcludeEmptyValues;
property ExcludeDefaultValues: boolean
read FExcludeDefaultValues write FExcludeDefaultValues;
property ReplaceReservedSymbols: boolean
read FReplaceReservedSymbols write FReplaceReservedSymbols;
property OnGetXMLHeader: TOnGetXMLHeader
read FOnGetXMLHeader write FOnGetXMLHeader;
end;
procedure register;
implementation
uses dsgnintf, glUtils;
const
ORDINAL_TYPES = [tkInteger, tkChar, tkEnumeration, tkSet];
TAB: string = #9;
CR: string = #13#10;
procedure register;
begin
RegisterComponents('Gl Components', [TglXMLSerializer]);
end;
constructor TglXMLSerializer.Create(AOwner: TComponent);
begin
inherited;
//...defaults
FGenerateFormattedXML := true;
end;
{ пишет строку в выходящий поток. Исп-ся при сериализации }
procedure TglXMLSerializer.WriteOutStream(Value: string);
begin
OutStream.write(Pchar(Value)[0], Length(Value));
end;
{
Конвертирует компонент в XML-код в соответствии
с published интерфейсом класса объекта.
Вход:
Component - компонент для конвертации
Выход:
текст XML в поток Stream
}
procedure TglXMLSerializer.Serialize(Component: TObject; Stream: TStream);
var
Result: string;
begin
TAB := IIF(GenerateFormattedXML, #9, '');
CR := IIF(GenerateFormattedXML, #13#10, '');
Result := '';
{ Получение XML заголовка }
if Assigned(OnGetXMLHeader) then
OnGetXMLHeader(self, Result);
OutStream := Stream;
WriteOutStream( PChar(CR + '<' + Component.ClassName + '>') );
SerializeInternal(Component);
WriteOutStream( PChar(CR + '</' ? + Component.ClassNameend;
{
Внутренняя процедура конвертации объекта в XML
Вызывается из:
Serialize()
Вход:
Component - компонент для конвертации
Level - уровень вложенности тега для форматирования результата
Выход:
строка XML в выходной поток через метод WriteOutStream()
}
procedure TglXMLSerializer.SerializeInternal(Component: TObject; Level: integer = 1);
var
PropInfo: PPropInfo;
TypeInf, PropTypeInf: PTypeInfo;
TypeData: PTypeData;
i, j: integer;
AName, PropName, sPropValue: string;
PropList: PPropList;
NumProps: word;
PropObject: TObject;
{ Добавляет открывающий тег с заданным именем }
procedure addOpenTag(const Value: string);
begin
WriteOutStream(CR + DupStr(TAB, Level) + '<' + Value + '>');
inc(Level);
end;
{ Добавляет закрывающий тег с заданным именем }
procedure addCloseTag(const Value: string; addBreak: boolean = false);
begin
dec(Level);
if addBreak then
WriteOutStream(CR + DupStr(TAB, Level));
WriteOutStream('</' ? + Valueend;
{ Добавляет значение в результирующую строку }
procedure addValue(const Value: string);
begin
WriteOutStream(Value);
end;
begin
// Result := '';
{ Playing with RTTI }
TypeInf := Component.ClassInfo;
AName := TypeInf^.name;
TypeData := GetTypeData(TypeInf);
NumProps := TypeData^.PropCount;
GetMem(PropList, NumProps*sizeof(pointer));
try
{ Получаем список свойств }
GetPropInfos(TypeInf, PropList);
for i := 0 to NumProps-1 do
begin
PropName := PropList^[i]^.name;
PropTypeInf := PropList^[i]^.PropType^;
PropInfo := PropList^[i];
{ Хочет ли свойство, чтобы его сохранили ? }
if not IsStoredProp(Component, PropInfo) then
continue;
case PropTypeInf^.Kind of
tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet,
tkWChar, tkLString, tkWString, tkVariant:
begin
{ Получение значения свойства }
sPropValue := GetPropValue(Component, PropName, true);
{ Проверяем на пустое значение и значение по умолчанию }
if ExcludeEmptyValues and (sPropValue = '') then
continue;
if ExcludeDefaultValues and (PropTypeInf^.Kind in ORDINAL_TYPES)
and (sPropValue = IntToStr(PropInfo.default)) then
continue;
{ Замена спецсимволов }
if FReplaceReservedSymbols then
begin
sPropValue := StringReplace(sPropValue, '<', '%lt;', [rfReplaceAll]);
sPropValue := StringReplace(sPropValue, '>', '%gt;', [rfReplaceAll]);
sPropValue := StringReplace(sPropValue, '&', '%', [rfReplaceAll]);
end;
{ Перевод в XML }
addOpenTag(PropName);
addValue(sPropValue); { Добавляем значение свойства в результат }
addCloseTag(PropName);
end;
tkClass: { Для классовых типов рекурсивная обработка }
begin
addOpenTag(PropName);
PropObject := GetObjectProp(Component, PropInfo);
if Assigned(PropObject)then
begin
{ Для дочерних свойств-классов - рекурсивный вызов }
if (PropObject is TPersistent) then
SerializeInternal(PropObject, Level);
{ Индивидуальный подход к некоторым классам }
if (PropObject is TStrings) then { Текстовые списки }
begin
WriteOutStream(TStrings(PropObject).CommaText);
end
else
if (PropObject is TCollection) then { Коллекции }
begin
SerializeInternal(PropObject, Level);
for j := 0 to (PropObject as TCollection).Count-1 do
begin { Контейнерный тег по имени класса }
addOpenTag(TCollection(PropObject).Items[j].ClassName);
SerializeInternal(TCollection(PropObject).Items[j], Level);
addCloseTag(TCollection(PropObject).Items[j].ClassName, true);
end
end;
{ Здесь можно добавить обработку остальных классов: TTreeNodes, TListItems }
end;
{ После обработки свойств закрываем тег объекта }
addCloseTag(PropName, true);
end;
end;
end;
finally
FreeMem(PropList, NumProps*sizeof(pointer));
end;
end;
{
Загружает в компонент данные из потока с XML-кодом.
Вход:
Component - компонент для конвертации
Stream - источник загрузки XML
Предусловия:
Объект Component должен быть создан до вызова процедуры
}
procedure TglXMLSerializer.DeSerialize(Component: TObject; Stream: TStream);
begin
GetMem(Buffer, Stream.Size);
try
{ Получаем данные из потока }
Stream.read(Buffer[0], Stream.Size + 1);
{ Устанавливаем текущий указатель чтения данных }
TokenPtr := Buffer;
BufferLength := Stream.Size-1;
{ Вызываем загрузчик }
DeSerializeInternal(Component, Component.ClassName);
finally
FreeMem(Buffer);
end;
end;
{
Рекурсивная процедура загрузки объекта их текстового буфера с XML
Вызывается из:
Serialize()
Вход:
Component - компонент для конвертации
ComponentTagName - имя XML тега объекта
ParentBlockEnd - указатель на конец XML описания родительского тега
}
procedure TglXMLSerializer.DeSerializeInternal(Component: TObject;
const ComponentTagName: string; ParentBlockEnd: PChar = nil);
var
BlockStart, BlockEnd, TagStart, TagEnd: PChar;
TagName, TagValue, TagValueEnd: PChar;
TypeInf: PTypeInfo;
TypeData: PTypeData;
PropIndex: integer;
AName: string;
PropList: PPropList;
NumProps: word;
{ Поиск у объекта свойства с заданным именем }
function FindProperty(TagName: PChar): integer;
var
i: integer;
begin
Result := -1;
for i := 0 to NumProps-1 do
if CompareStr(PropList^[i]^.name, TagName) = 0 then
begin
Result := i;
break;
end;
end;
procedure SkipSpaces(var TagEnd: PChar);
begin
while TagEnd[0] <= #33 do
inc(TagEnd);
end;
function StrPos2(const Str1, Str2: PChar; Str2Len: DWORD): PChar; assembler;
asm
PUSH EDI
PUSH ESI
PUSH EBX
or EAX,EAX // Str1
JE @@2 // если строка Str1 пуста - на выход
or EDX,EDX // Str2
JE @@2 // если строка Str2 пуста - на выход
MOV EBX,EAX
MOV EDI,EDX // установим смещение для SCASB - подстрока Str2
xor AL,AL // обнулим AL
push ECX // длина строки
MOV ECX,0FFFFFFFFH // счетчик с запасом
REPNE SCASB // ищем конец подстроки Str2
not ECX // инвертируем ECX - получаем длину строки+1
DEC ECX // в ECX - длина искомой подстроки Str2
JE @@2 // при нулевой длине - все на выход
MOV ESI,ECX // сохраняем длину подстроки в ESI
pop ECX
SUB ECX,ESI // ECX == разница длин строк : Str1 - Str2
JBE @@2 // если длина подсроки больше длине строки - выход
MOV EDI,EBX // EDI - начало строки Str1
LEA EBX,[ESI-1] // EBX - длина сравнения строк
@@1: MOV ESI,EDX // ESI - смещение строки Str2
LODSB // загужаем первый символ подстроки в AL
REPNE SCASB // ищем этот символ в строке EDI
JNE @@2 // если символ не обнаружен - на выход
MOV EAX,ECX // сохраним разницу длин строк
PUSH EDI // запомним текущее смещение поиска
MOV ECX,EBX
REPE CMPSB // побайтно сравниваем строки
POP EDI
MOV ECX,EAX
JNE @@1 // если строки различны - ищем следующее совпадение первого символа
LEA EAX,[EDI-1]
JMP @@3
@@2: xor EAX,EAX
@@3: POP EBX
POP ESI
POP EDI
end;
begin
{ Playing with RTTI }
TypeInf := Component.ClassInfo;
AName := TypeInf^.name;
TypeData := GetTypeData(TypeInf);
NumProps := TypeData^.PropCount;
GetMem(PropList, NumProps*sizeof(pointer));
try
GetPropInfos(TypeInf, PropList);
{ ищем открывающий тег }
BlockStart := StrPos2(TokenPtr, PChar('<' + ComponentTagName + '>'), BufferLength);
check(BlockStart <> nil, 'Открывающий тег не найден: ' + '<' + ComponentTagName + '>');
inc(BlockStart, length(ComponentTagName) + 2);
{ ищем закрывающий тег }
BlockEnd := StrPos2(BlockStart, PChar('</' ? + ComponentTagName nil,
'Закрывающий тег не найден: ' + '<' + ComponentTagName + '>');
{ проверка на вхождение закр. тега в родительский тег }
check((ParentBlockEnd = nil)or(BlockEnd { XML парсер }
while TagEnd do
begin
{ быстрый поиск угловых скобок }
asm
mov CL, '<'
mov EDX, Pointer(TagEnd)
dec EDX
@@1: inc EDX
mov AL, byte[EDX]
cmp AL, CL
jne @@1
mov TagStart, EDX
mov CL, '>'
@@2: inc EDX
mov AL, byte[EDX]
cmp AL, CL
jne @@2
mov TagEnd, EDX
end;
GetMem(TagName, TagEnd - TagStart + 1);
try
{ TagName - имя тега }
StrLCopy(TagName, TagStart + 1, TagEnd - TagStart - 1);
{ TagEnd - закрывающий тег }
{ поиск свойства, соответствующего тегу }
TagEnd := StrPos2(TagEnd, PChar('</' ? + TagName
PropIndex := FindProperty(TagName);
check(PropIndex <> -1, 'TglXMLSerializer.DeSerializeInternal: Uncknown property: ' +
TagName);
SetPropertyValue(Component, PropList^[PropIndex], TagValue, TagValueEnd, BlockEnd);
inc(TagEnd, length('</' ? + TagNamefinally
FreeMem(TagName);
end;
end;
finally
FreeMem(PropList, NumProps*sizeof(pointer));
end;
end;
{
Процедура инициализации свойства объекта
Вызывается из:
DeSerializeInternal()
Вход:
Component - инициализируемый объект
PropInfo - информация о типе для устанавливаемого свойства
Value - значение свойства
ParentBlockEnd - указатель на конец XML описания родительского тега
Используется для рекурсии
}
procedure TglXMLSerializer.SetPropertyValue(Component: TObject;
PropInfo: PPropInfo; Value, ValueEnd: PChar; ParentBlockEnd: PChar);
var
PropTypeInf: PTypeInfo;
PropObject: TObject;
CollectionItem: TCollectionItem;
sValue: string;
charTmp: char;
begin
PropTypeInf := PropInfo.PropType^;
case PropTypeInf^.Kind of
tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet,
tkWChar, tkLString, tkWString, tkVariant:
begin
{ имитируем zero terminated string }
charTmp := ValueEnd[0];
ValueEnd[0] := #0;
sValue := StrPas(Value);
ValueEnd[0] := charTmp;
{ Замена спецсимволов. Актуально только для XML,
сохраненного с помощью этого компонента }
if FReplaceReservedSymbols then
begin
sValue := StringReplace(sValue, '%lt;', '<', [rfReplaceAll]);
sValue := StringReplace(sValue, '%gt;', '>', [rfReplaceAll]);
sValue := StringReplace(sValue, '%', '&', [rfReplaceAll]);
end;
{ Для корректного преобразования парсером tkSet нужны угловые скобки }
if PropTypeInf^.Kind = tkSet then
sValue := '[' + sValue + ']';
SetPropValue(Component, PropInfo^.name, sValue);
end;
tkClass:
begin
PropObject := GetObjectProp(Component, PropInfo);
if Assigned(PropObject)then
begin
{ Индивидуальный подход к некоторым классам }
if (PropObject is TStrings) then { Текстовые списки }
begin
charTmp := ValueEnd[0];
ValueEnd[0] := #0;
sValue := StrPas(Value);
ValueEnd[0] := charTmp;
TStrings(PropObject).CommaText := sValue;
end
else
if (PropObject is TCollection) then { Коллекции }
begin
while true do { Заранее не известно число элементов в коллекции }
begin
CollectionItem := (PropObject as TCollection).Add;
try
DeSerializeInternal(CollectionItem, CollectionItem.ClassName,
ParentBlockEnd);
except { Исключение, если очередной элемент не найден }
CollectionItem.Free;
break;
end;
end;
end
else { Для остальных классов - рекурсивная обработка }
DeSerializeInternal(PropObject, PropInfo^.name, ParentBlockEnd);
end;
end;
end;
end;
{
Процедура генерации DTD для заданного объекта в
соответствии с published интерфейсом его класса.
Вход:
Component - объект
Выход:
текст DTD в поток Stream
}
procedure TglXMLSerializer.GenerateDTD(Component: TObject; Stream: TStream);
var
DTDList: TStringList;
begin
DTDList := TStringList.Create;
try
GenerateDTDInternal(Component, DTDList, Stream, Component.ClassName);
finally
DTDList.Free;
end;
end;
{
Внутренняя рекурсивная процедура генерации DTD для заданного объекта.
Вход:
Component - объект
DTDList - список уже определенных элементов DTD
для предотвращения повторений.
Выход:
текст DTD в поток Stream
}
procedure TglXMLSerializer.GenerateDTDInternal(Component: TObject; DTDList:
TStrings; Stream: TStream; const ComponentTagName: string);
var
PropInfo: PPropInfo;
TypeInf, PropTypeInf: PTypeInfo;
TypeData: PTypeData;
i: integer;
AName, PropName, TagContent: string;
PropList: PPropList;
NumProps: word;
PropObject: TObject;
const
PCDATA = '#PCDATA';
procedure addElement(const ElementName: string; Data: string);
var
s: string;
begin
if DTDList.IndexOf(ElementName) <> -1 then
exit;
DTDList.Add(ElementName);
s := 'then Data := PCDATA;
s := s + '(' + Data + ')>'#13#10;
Stream.Write(PChar(s)[0], length(s));
end;
begin
{ Playing with RTTI }
TypeInf := Component.ClassInfo;
AName := TypeInf^.name;
TypeData := GetTypeData(TypeInf);
NumProps := TypeData^.PropCount;
GetMem(PropList, NumProps*sizeof(pointer));
try
{ Получаем список свойств }
GetPropInfos(TypeInf, PropList);
TagContent := '';
for i := 0 to NumProps-1 do
begin
PropName := PropList^[i]^.name;
PropTypeInf := PropList^[i]^.PropType^;
PropInfo := PropList^[i];
{ Пропустить не поддерживаемые типы }
if not (PropTypeInf^.Kind in [tkDynArray, tkArray,
tkRecord, tkInterface, tkMethod]) then
begin
if TagContent <> '' then
TagContent := TagContent + '|';
TagContent := TagContent + PropName;
end;
case PropTypeInf^.Kind of
tkInteger, tkChar, tkFloat, tkString,
tkWChar, tkLString, tkWString, tkVariant, tkEnumeration, tkSet:
begin
{ Перевод в DTD. Для данных типов модель содержания - #PCDATA }
addElement(PropName, PCDATA);
end;
{ код был бы полезен при использовании атрибутов
tkEnumeration:
begin
TypeData:= GetTypeData(GetTypeData(PropTypeInf)^.BaseType^);
s := '';
for j := TypeData^.MinValue to TypeData^.MaxValue do
begin
if s <> '' then s := s + '|';
s := s + GetEnumName(PropTypeInf, j);
end;
addElement(PropName, s);
end;
}
tkClass: { Для классовых типов рекурсивная обработка }
begin
PropObject := GetObjectProp(Component, PropInfo);
if Assigned(PropObject)then
begin
{ Для дочерних свойств-классов - рекурсивный вызов }
if (PropObject is TPersistent) then
GenerateDTDInternal(PropObject, DTDList, Stream, PropName);
end;
end;
end;
end;
{ Индивидуальный подход к некоторым классам }
{ Для коллекций необходимо включить в модель содержания тип элемента }
if (Component is TCollection) then
begin
if TagContent <> '' then
TagContent := TagContent + '|';
TagContent := TagContent + (Component as TCollection).ItemClass.ClassName + '*';
end;
{ Добавляем модель содержания для элемента }
addElement(ComponentTagName, TagContent);
finally
FreeMem(PropList, NumProps*sizeof(pointer));
end;
end;
procedure TglXMLSerializer.check(Expr: boolean; const message: string);
begin
if not Expr then
raise XMLSerializerException.Create ('XMLSerializerException'#13#10#13#10 + message);
end;
end.
//(PShortString(@(GetTypeData(GetTypeData (PropTypeInf)^.BaseType^).NameList)))
//tickCount := GetTickCount();
//inc(tickCounter, GetTickCount() - tickCount);
|
Использование XML в среде Delphi.
Оформил: DeeCo
Автор: А.Календарев.
Данная статья является переработанным вариантом материалов по XML-технологии опубликованных на сайте автора Электронные документы и логистика. Использование СОМ в среде DelphiПоследнее время много внимания уделяется построение систем электронного бизнеса, или как их еще назыают - B2B (business to business). Учитывая рекомендации по построению обменных потоковых систем координирующего интернет-технологий органа - WWW Consortium: акцент сделан в сторону XML-технологий и построение систем обмена XML-документами.
Преимущество использования XML в электронном бизнесе - высокая эффективность B2B систем при низких затратах на ее создание за счет четкого и наглядного представления структурированной информации, возможность использования современных сетевых протоколов и создания бизнес-систем реального времени.
Независимость представления информации в виде XML документов позволяет разным, участвующим в электронном бизнесе, фирмам производить независимое друг от друга ПО.
Во всех системах обмен, как правило, строится по одинаковой схеме, с использованием HTTP запросов. В качестве протокола защиты информации применяется протокол SSL (но это отдельная тема).
Один из возможных вариантов обработки XML сообщения является построение BIN/CGI (ISAPI)-приложений или COM (серверных) компонент, формирующих или обрабатывающих XML-документы.
С одной стороны, приложение выступает в качестве клиента, которое в режиме POST выдает HTTP запрос, с другой стороны, находится WEB сервер на стороне которого осуществляется обработка запроса и выдача ответа. В информационном обмене используются XML-документы.
Один из наиболее эффективных вариантов реализации - использование существующего XML-парсера, поддерживающего DOM модель. Такой парсер является дистрибутивной поставкой Win`98 или составной частью IE 4,7 и выше (для Win`95) и представляет COM сервер, находящийся в библиотеке msxml.dll.
Модель компонентных объектов (COM) - представляет инкапсулированные данные и методы в единую сущность и способ доступа к ним через систему интерфейсов. Средствами Delphi достаточно просто осуществить доступ к классам COM-объекта (в одном COM-сервере может быть включено несколько классов). Доступ к объектам осуществляется путем инициализации экземпляра класса через систему интерфейсов. Описание интерфейсов осуществляется языком определения интерфейсов (IDL), которое возможно осуществить средствами среды автоматически.
Средствами Delphi осуществляется импорт из COM-сервера msxml.dll, строится файлы описания интерфейса IDL и файл бинарноого описания типов библиотеки - TLB. Данная операция осуществляется через системное меню: Project | Type Library Import… (рис 1). Далее появляется диалоговое окно (рис 2), в котором необходимо выбрать COM-объект (в нашем случае объект зарегистрирован под именем "Microsoft.XMLDom (Version 2.0)" ) и создать TLB-файл (кнопка Create Unit). Используя TLB-файл, среда генерирует "паскалевский" файл описания COM-сервера - MSXML_TLB.pas.
|
|
| Рис 1 | Рис 2 |
В файле MSXML_TLB.pas описаны все интерфейсы, константы и соклассы COM-сервера.
Для доступа к объектам COM-элемента, необходимо в директиве USES добавить имя файла описания библиотеки (MSXML_TLB.pas). Ниже представлена простейшая программа, использующая DOM стандартный анализатор msxml.dll, которая загружает XML-документ и отображает его в элементе текстового поля Memo1.
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
OleServer, MSXML_TLB, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: Tobject);
var
coDoc: CoDOMDocument;
/ * объявление сокласса объекта DOMDocument;
* /
var
Doc: IXMLDOMDocument;
/ * класс, согласованный с интерфейсом IDOMDocument;
* /
begin
Doc := coDoc.Create;
/ * создание экземпляра объекта DOMDocument;
* /
Doc.load('data.xml');
/ * вызов метода Load экземпляра объекта DOMDocument;
* /
Memo1.Text := Doc.xml;
/ * доступ к свойстве xml экземпляра объекта DOMDocument;
* /
end;
end.
Концепция DOM - объектная модель документа
Каждый XML документ представляется в виде набора множества объектов (классов), с помощью которых возможен доступ к отдельным элементам (полям объекта). DOM - интерфейс описывает доступ как к простым объектам типа DOMString или CharacterData, так и к частям или отдельным элементам XML документа: DOMFragmentElement, DOMNode, DOMElement.
Ниже приведены самые важные свойства и методы объектов XMLDOMDocument, XMLDOMNode, XMLDOMNodeList. Необходимо отметить, что представленные ниже методы и функции объектов DOM модели (Document Object Model) используются Microsoft XML-анализатором msxml.dll и несколько шире, чем утвержденная W3C Консорциумом DOM модель.
Более полное описание интерфейса DOM объектов можно найти на www.msdn.microsoft.com/xml.
| Объект XMLDOMDocument | |
| Представляет верхний уровень объектной иерархии и содержит методы для работы с документом: его загрузки, анализа, создания в нем элементов, атрибутов, комментариев и т.д. . | |
| Свойства | |
| Async | Свойство идентифицирующее текущий режим обработки |
| ParseError | Возвращает ссылку на объект обработки ошибки XMLDOMParseError |
| validateOnParse | Включение - выключение верификации документа. |
| url | Возвращает URL документа |
| documentElement | Содержит ссылку на корневой элемент документа в виде объекта XMLDOMElement. |
| Методы | |
| load(url) loadXML(xmlString) | Загружает XML документ, |
| save(objTarget) | Сохраняет XML документ в файле |
| abort | Прерывание процесса загрузки и обработки документа. |
| createAttribute (name) | Создает для текущего элемента новый атрибут с указанным именем. |
| createNode(Type, name, nameSpaceURI) | Создает узел указанного типа и названия |
| createElement(tagName) | Создает элемент документа с указанным названием. |
| createTextNode(data) | Создает текст внутри документа |
| getElementsByTagName(tagname) | Возвращает ссылку на коллекцию элементов документа с заданным именем |
| nodeFromID(idString) | Поиск элемента по идентификатору |
| Объект XMLDOMNode | |
| Объект XMLDOMNode, реализующий базовый DOM интерфейс Node , предназначен для манипулирования с отдельным узлом дерева документа. Его свойства и методы позволяют получать и изменять полную информацию о текущем узле - его тип, название, полное название, его содержимое, список дочерних элементов и т.д. | |
| Свойства | |
| nodeName, baseName | Возвращает название текущего узла. |
| prefix | Возвращает Namespace префикс. |
| dataType | Определяет тип содержимого текущего узла |
| nodeType, nodeTypeString | Возвращает тип текущего узла: |
| attributes | Возвращает список атрибутов текущего узла в виде коллекции XMLDOMNamedNodeMap. |
| text | Возвращает содержимое текущего поддерева в виде текста |
| xml | Возвращает XML-представление текущего поддерева. |
| nodeValue | Возвращает содержимое текущего узла. |
| childNodes | Возвращает список дочерних элементов в виде XMLDOMNodeList. |
| firstChild, lastChild | Возвращает первый/последний дочерний элемент |
| previousSibling ,nextSibling | Возвращает предыдущий /следующий сестринский элемент. |
| parentNode | Содержит ссылку на родительский элемент. |
| ownerDocument | Возвращает указатель на документ, в котором находится текущий узел. |
| Методы | |
| appendChild(newChild) | Добавляет текущему узлу новый дочерний элемент. |
| insertBefore(newChild, refChild) | Вставляет дочерний узел, располагая его в текущем поддереве "левее" узла, указанного параметром refChild. |
| cloneNode (deep) | Создание копии текущего элемента. |
| getAttribute (name) getAttributeNode (name) setAttribute(name, value) setAttributeNode(XMLDOMAttribute) | Доступ к атрибутам (создание, чтение, запись) объекта. Name - имя аттрибута, value - его значение. Возращает значение объект XMLDOMAttribute. |
| replaceChild(newChild, oldChild) removeChild(oldChild) | Замена объекта oldChild текущего списка дочерних объектов на newChild. Удаление объекта oldChild |
| selectNodes(patternString) selectSingleNode(patternString) | Возвращает объект XMLDOMNodeList, выбранное по шаблону поиска или первый узел |
| transformNode(stylesheet) transformNodeToObject(stylesheet, outputObject) | Назначает стилевую таблицу для поддерева текущего узла и возвращает строку - результат обработки. В качестве параметра передается ссылка на объект DOMDocument, в котором находятся XSL инструкции. |
| Объект XMLDOMNodeList | |
| Представляет собой список узлов - поддеревья и содержит методы, при помощи которых можно организовать процедуру обхода дерева. | |
| length | число элементов списка узлов |
| item(i) | Выбор i-того элемента из списка. Возвращает объект XMLDOMNode |
| nextNode() | Выбор следующего элемента в списке. |
Для более ясной картины необходимо пояснение, а зачем все это нужно с тем, что бы понять, как это работает…
При построении B2B или корпоративной ERP системы, при организации информационного обмена XML-документами между предприятиями или филиалами пр-я, используются эффективно себя зарекомендовавшая система передачи информации на основе имеющихся WEB серверов по HTTP протоколам.
С одной стороны, приложение выступает в качестве клиента, которое в режиме POST выдает HTTP запрос, с другой стороны, находится WEB сервер, на стороне которого осуществляется обработка запроса и выдача ответа. В качестве обмена используются XML-документы.
Например, в простой корпоративной ERP системе бухгалтерской программе (АСУ Бухучет) необходимо сформировать некий запрос на выписку накладной и передать его в филиал, который имеет склад (АСУ Склад). АРМ Аналогичная постановка задачи при создании В2В системы, когда Предприятие А запрашивает наличие продукции (делает заказ на приобретение) у Поставщика В.
Предприятие А и его программа выступает в качестве клиента. Склад обслуживает Поставщик В, у которого находится складской комплекс БД на SQL сервере. Обмен осуществляется через корпоративный WEB сервер Поставщика В.
Ниже представлен следующий типовой алгоритм обмена:
Рис 3.
Предприятие А инициирует процесс А (заказ продукции), который выступает в качестве WEB-клиента. Процесс А формирует XML документ (например запрос- накладная) и передает его как POST запрос http на WEB сервер Поставщика В. В качестве URI - используется идентификатор ресурса обрабатывающего приложения. URI может быть одинаковым как для всех типов документов, либо индивидуальным для каждого типа. Все зависит от структуры B2B (WEB) сервера. WEB сервер анализирует запрос и порождает серверный Процесс В, передавая в качестве параметра тело XML документа.
Процессе В запускается WEB-сервером и обрабатывается либо как ASP страница,CGI (ISAPI) - приложение или JAVA севрлет (серверное приложение) Процесс В - формирует запрос на SQL-сервер базы данных. SQL-сервер производит необходимые операции в БД, формирует ответ и возвращает его Процессу В. По ответу от SQL-сервера Процесс В формирует XML документ (ответ) и возращает его как на ответ на http запрос клиентскому приложению. Далее, в зависимости от ситуации на стороне клиента формируется либо новый http запрос, либо заканчивается сеанс.
Несколько слов об организации документооборота.
Общим правилом разработки системы обмена XML документами является: во-первых - разработка схемы потоков электронных документов и их структуры; во-вторых - разработка таблиц функций процессов (подпроцессов) т.е. какую функцию по отношению к какому XML-документу будет реализовывать каждый процесс.
Каждый XML документ, подобно HTML документу, должен состоять из заголовка сообщения (информация заключенная тагами ) и тела сообщения (для запроса эта информация обрамленная тагами для ответа на запрос ). Для того, чтобы XML документ был правильно сформирован, необходимо его две составные части "Заголовок" и "Запрос" обрамить тегами, например <xmlDoc>. Вид типового документа представлен ниже:
Рис 4.
Рис 5.
Заголовок (Рис 4), в отличие HTML документа, должен содержать разного рода служебную информацию, в том числе информацию о типе передаваемого документа и процессе его обработки. В информационную обработку поступает тело документа, т.е. содержательная часть обрамленная тагами . Следует отметить, что структуру заголовков должна быть единой для всех типов документов.
Для запущенного сервером Процесса, алгоритм обработки предпочтительно (но не обязательно) строить следующим образом:
Рис 6.
Некоторые принципиальные моменты при создании клиентской части
Как уже пояснялось, при создание XML-документа используется его представление в виде DOM модели. Ниже приведен пример части текста Delphi программы создания заголовка xml сообщения.
procedure TThread1.HeaderCreate(Sender: Tobject);
var
coDoc : CoDomDocument ; // объявление сокласса, необходим для создания
Doc : DomDocument ; // объекта XMLDomDocument
r : IXMLDOMElement; // объявление объектов DOMElement
Node : IXMLDOMElement; //
txt : IXMLDOMText; // DOMText
attr : IXMLDOMAttribute; // DOMAttribute
begin
Doc:=coDoc.Create; // создание документа DOM
Doc.Set_async(false); // установка синхронного режима обрабработки
Doc.LoadXML('<Header/>'); // начальная инициация DOM документа
r:=Doc.Get_documentElement; // получение адреса корневого элемента
Node := Doc.createElement ( 'Sender'); // создание DOMElement (таг <Sender>)
txt := Doc.createTextNode( 'ООО "Тайфун"'); // создание техстового узла 'ООО "Тайфун"'
Node.appendChild(txt); // присвоение узлу <Sender> значение
// техстового узла 'ООО "Тайфун"'
r.appendChild(Node); // добавление элемента <Sender> в корень
// документа как дочернего
Node := Doc.createElement ( 'From'); // аналогичные операции для тага <From>
txt := Doc.createTextNode( 'http://tayfun.ru/xml/default.asp');
Node.appendChild(txt);
r.appendChild(Node);
Node := Doc.createElement ( 'To'); // аналогичные операции для тага <To>
txt := Doc.createTextNode( 'http://irbis.ru');
Node.appendChild(txt);
r.appendChild(Node);
Node := Doc.createElement ( 'TypeDocument'); // создание DOMElement (<TypeDocument Id>)
Att := Doc.createAttribute ( 'Id ', ' Order'); // создание узла XMLDOMAttribute
Node.appendChild(Att); // <TypeDocument Id="Order"/>
r.appendChild(Node);
end;
Следует отметить, что объявление переменной coDoc : CoDomDocument и Doc : DomDocument , а также ее создание методом Create ( Doc:=coDoc.Create;) осуществляется один раз. Объявление переменной находится в секции описания глобальных переменных, а не в локальной процедуре, как было продемонстрировано для наглядности в данном примере (т.е. одна глобальная переменная типа DomDocument на один программный модуль).
Результатом работы вышеприведенной программы будет созданный заголовок <Header>, приминительно к нашему примеру xml-документа: изображен на рис. 7.
Рис 7.
Рис 8.
Основное приемущество передачи информации в виде XML-документов в том, что существует возможно формировать сообщение, используя независимые структуры таблиц в СУБД как на принимаемой, так и на передаваемой стороне. Используя наш пример, пусть требуется передать информацию об инвойсах Предприятия А, из СУБД имеющий структуру, изображенную на рис. 6.
Для формирования xml-документа, содержащего инвойс первоначально строится SQL-запрос (запрос А) с информацией о самом инвойсе:
SELECT * FROM Invoice_General
WHERE InvoiceNum = :num // :num - параметр, который задает номер инвойса.
И далее строится SQL-запрос (запрос В) информация о товарах, описываемых в инвойсе (детальная спецификация):
SELECT Goods,Qulity,Price, HZ_cod
FROM Goods
WHERE InvoiceNum = :num // :num - параметр, который задает номер инвойса.
Ниже представлена часть программы, формирующей тело xml-документа:
procedure TThread1.DataBodyCreate(Sender: Tobject);
var
//coDoc : CoDomDocument ; // объявление сокласса и объекта XMLDomDocument
//Doc : DomDocument ; // должно быть глобальным, для всего модуля.
r : IXMLDOMElement; // объявление объектов DOMElement
Node, Node2 : IXMLDOMElement; // DOMElement;
Node3, Node4 : IXMLDOMElement; //
txt : IXMLDOMText; // DOMText
str : String;
// InvoiceNumber: integer; - глобальная переменная - имеет значение 987654
// queryA, queryB : String; - глобальная переменная, имеет значение, соответсвующее запросу
// queryA - запрос А генеральная информацией об инвойсе
// queryB - запрос B информация о товарах, описываемых в инвойсе (см. текст)
begin
Query.Close; // закрывает запрос для доступа
Query.Text := queryA; // см. по тексту "запрос А"
Query.Params[0].AsInteger := InvoiceNumber; // присваивание значения параметров
Query.ExecSQL; // выполнение запроса
Query.Open; // открытие доступа к данным запроса
r:=Doc.Get_documentElement; // получение адреса корневого элемента
Node2 := Doc.createElement ( ' Request '); // создание DOMElement (таг <Request>)
Node := Doc.createElement ( 'Invoice'); // создание DOMElement (таг <Invoice >)
r.appendChild(Node2); // добавление элемента <Request> в корень
Node2. appendChild(Node); // добавление элемента <Invoice> в <Request>
Node3 := Doc.createElement ( 'Depurture') ; // создание DOMElement (таг <Depurture>)
Node. appendChild(Node3); // добавление элемента <Depurture> в <Invoice>
str:= Query.FieldByName('Depurture').AsString; // обращение к полю 'Depurture' запроса
txt := Doc.createTextNode( str); // создание техстового узла = значению поля
Node.appendChild(txt); // присвоение узлу <Invoice> значение
// техстового узла, переменной str
// аналогичные операции для тага <Destination>, <DataSend>, <DataDepurture>, <Currency>
// <DestinationCompany> (поле DB "Consignee" )
Node := Doc.createElement ( 'Destination');
str:= Query.FieldByName('Consignee ').AsString; // имя поля БД может и не совпадать с именем
txt := Doc.createTextNode( str); // тага, в этом приемущество использования
Node.appendChild(txt); // DOM интерфейса перед СУБД, имеющим
// поддержку XML-интерфейса, типа ORACLE 8i
... // или Ms SQL 2000
// формирование запроса на спецификацию по товарам
Query.Close; // закрывает запрос для доступа
Query.Text := qиегуВ; // см. по тексту "запрос В", информац. О товарах
Query.Params[0].AsInteger := InvoiceNumber; // присваивание значения параметров
Query2.ExecSQL; // выполнение запроса
Query.Open; // открытие доступа к данным запроса
Node3 := Doc.createElement ( ' Imems') ; // создание DOMElement (таг <Imems>)
Node. appendChild(Node3); // добавление элемента <Imems> в <Invoice>
while not Eof.Query do begin // цикл по всем строкам запроса
Node4 := Doc.createElement ( 'Imem');
Node3.appendChild(Node4); // добавление элемента <Imem> в <Imems>
str:= Query.FieldByName('Price').AsString; // формирование данных для тага <Price>
txt := Doc.createTextNode( str);
Node.appendChild(txt);
...
// аналогичные операции для тагов <HZ_Cod>, <Quality>, <GoodName>
end;
end;
В результате выполнения данной процедуры формируется следующий текст XML-документа:
Для формирования запроса используется метод Open объекта IXMLHttpRequest:
procedure Open(const bstrMethod, -тип метода = "POST" bstrUrl, -Url адрес сервера varAsync, -режим связи асинхронный / синхронный = true bstrUser, -имя пользователя для аутентификации bstrPassword) - парольСоздании серверной части обработки документа
Как было отмечено ранее, обработка HTTP запроса может осуществляться либо CGI-приложениями, либо Java-сервлетами. Возможен и вариант написания ASP-страниц. Но в этом случае передача данных возможна только методом "GET" через строку запроса. Хотя, обработка HTTP запроса ASP-страниц работает более эффективнее, чем CGI-приложением. Однако, на мой взгляд, без разницы, как обрабатывать, а важнее решить вопрос - как построить программму обработки, а не какими средствами.
Если из предыдущей главы мы рассмотрели варианты формирования XML-документ, то задача серверного приложения обратная - разбор XML-документов. Ниже представлена часть программы, осуществляющей разбор xml-документа:
procedure Tthread1.DataParser(Sender: Tobject);
var
r,FNode : IXMLDOMElement; // объявление объектов DOMElement
Str,Filename : String;
parm : String;
CoDocXML, CoDocXSL, CoDocResult : CoDomDocument ; // объявление сокласса и
XMLDoc, XSLDoc, ResultDoc : DomDocument ; // объекта XMLDomDocument
// HttpStr : String; - глобальная переменная, содержащая строку HTTP запроса
Begin
XMLDoc:=coDocXML.Create; // создание документа XMLDoc
XMLDoc.Set_async(false); // установка синхронного режима обрабработки
XMLDoc.LoadXML(HttpStr); // загрузка DOM документа из строки HttpStr
r:=Doc.Get_documentElement; // получение адреса корневого элемента
FNode:= r.SelectSingleNode("//TypeDocument"); // получение значения элемента <TypeDocument>
FileName:= FNode.GetAttibute("id"); // получение значения аттрибута id="Order"
FileName:= FileName+".xsl"; // и формирование имени файла Order.xsl
XSLDoc:=coDocXSL.Create; // создание документа XSLDoc
XSLDoc.Set_async(false); // установка синхронного режима обрабработки
XSLDoc.LoadXML(FileName); // загрузка DOM документа из файла Order.xsl
ResultDoc:=coDocResult.Create; // создание документа XMLDoc
ResultDoc.Set_async(false); // установка синхронного режима обрабработки
ResultDoc.validateOnParse := true; // установка проверки разбора
XMLDoc.transformNodeToObject(XSLDoc, ResultDoc); // разбор XMLDoc по XSL-шаблону
Str:= ResultDoc.text; // переменной Str присваивается текстовое значение
// результирующего документа.
FNode:= r.SelectSingleNode("//InvoiceNumber"); // поиск элемента <InvoiceNumber>
parm:= FNode.text; // и получение значения элемента
Query.Close; // закрывает запрос для доступа
Query.Text := Str;
Query.Params[0].AsString := parm; // присваивание значения параметра
Query.ExecSQL; // выполнение запроса
end;
Вся изюминка разбора заключается в применении XSL-шаблона, который сформирован для каждого типа документа индивидуально. Результатом разбора является строка SQL-запроса. Впоследствие выполнение сформированной строки SQL-запроса осуществит необходимые изменения данных в СУБД.
Приимущество использования разбора через щаблон еще и в том, что получается некая гибкость данных, и получается полная независимость работы алгоритма от программного кода. Ниже приведен используемый для обработки документа типа ORDER текст XSL-шаблона:
<!-- файл Order.xsl -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<xsl:template match="/">
<!-- применяем шаблон ко всему документу, начиная с корневого элемента "/" -->
<HTML>
<xsl:for-each select="//header">
<!-- формирование запроса для записи данных -->
<!-- в таблицу регистрации приходящих сообщений TABREG -->
INSERT into TABREG ( FROM, TO, TYPEDOC,body) VALUES( '
<xsl:value-of select="from" />' ,'
<xsl:value-of select="to" />','<xsl:value-of select="TypeDocument/@id" />' )
</xsl:for-each>
<!-- формирование запроса для записи данных в таблицу GOODS -->
<!-- информация о товарах -->
<xsl:for-each select="//item">
INSERT into GOODS ( invoiceNumber, name, price, quality) VALUES( ' :num',
'<xsl:value-of select="name" />' , '<xsl:value-of select="price" />',
'<xsl:value-of select="quality" />
' )
</xsl:for-each>
</HTML>
</xsl:template>
</xsl:stylesheet>
Поясняя вышеприведенный пример, надо отметить, что использование пары тагов <HTML> и </HTML> носит формальный характер, т.к. после разбора в результирующем XML-документе формально должен присутствовать хотябы один узел. Метод ResultDoc.text присваивает текстовае значение полученного в ходе разбора XML-документа ResultDoc. В этом случае значением является все то, что обрамлено пары тегов <HTML> и </HTML>, т.е. сформированный нами SQL-запрос.
Другой особенностью написания программы надо отметить возможность использования SQL-параметра :num. Использование параметра позволяет упростить текст xsl-шаблона. Определение значение соответствующих элементов узлов XML-документа определякется первоночально выбора по имени соответствующего узла, например:
FNode := r.SelectSingleNode("
//InvoiceNumber"); // поиск элемента >InvoiceNumber> и далее использование свойства text:
parm := FNode.text; // и получение значения элемента >InvoiceNumber>
Коротко об XSL
Абревиатура XSL происходит от eXtensible Stylesheet Language - язык форматирования таблиц стилей (XML данных). Как понятно из заголовка eXtensible Stylesheet Language (XSL) используется для форматирования XML данных. По определению W3C XSL состоит из двух частей:
XSLT - XSL Transformation. Язык, используемый для преобразования или форматирования (трансформирования) XML документов. Таким образом, при помощи XSLT мы можем получить разные разрезы множества данных и формы представления данных. Элементы форматирования. К этим элементам относятся все элементы типографического оформления данных, после их обработки их при помощи XSL. Используется только для формирования HTML страниц.При помощи XSLT мы можем отобрать нужные нам данные из XML файла, и оформить их в виде для предоставления пользователю. Например, в нашем случае мы преобразовали XML данные в виде SQL запроса. Классическое применение XSL - это, как правило форматирование данных в виде HTML страниц или более редкое представление в виде RTF файлов.
XSL файл описывает шаблон (template), согласно которому будет совершаться преобразование XML данных. Возращаясь к xsl-шаблонам, в XSLT можно выделить следующие элементы (директивы):
XSL-директивы описание xsl:apply-templates Директива, указывающая на применение соответствующих шаблонов аттрибуту select="имя шаблона" xsl:attribute создает дерево аттрибутов и добавляет его в выходной элемент, пареметр name="имя аттрибута", namespace - URI на пространство имен (преффикс пространства имен) xsl:call-template вызывает шаблон, аттрибуту name=" URI на шаблон" xsl:choose xsl:when xsl:otherwise осуществление выбора по условию xsl:when expr="вычисление выражения на script ", language="language-name" test= "вычисляемое выражение" xsl:comment генерирует комментарий в выходной документ xsl:copy xsl:copy-of копирует текущей узел в выходной источник или вставляет фрагмент документа в узел, где аттрибут select="имя узла источника" xsl:element создает выходной элемент по имени, аттрибут name="имя элемента", namespace="uri сслылка на пространство имен" xsl:for-each повторно применяет шаблон ко всем узлам списка узлов, аттрибут select задает список узлов xsl:if проверка условия, задается аттрибутом test в виде выражения xsl:include включает внешний шаблон, аттрибут href = "URI reference" xsl:output специфицирует выходной результат, аттрибут method может иметь значения "xml", "html" или "text" xsl:param специфицирует значение параметров, аттрибут name="имя параметра", select = "значание" xsl:processing-instruction создает инструкцию обработки, аттрибут name="имя процесс инструкции" xsl:sort сортирует множество узлов, аттрибуты select = "имя узла", data-type = тип данных {"text" | "number" | Qname}, order = направление сортировки {"ascending" | "descending"} xsl:stylesheet определяет документ xsl-шаблонов, является корневым элементом для XSLT xsl:template определяет xsl-шаблон, аттрибут name= " URI преффикс на имя шиблона", match= "указание на узел, к которому применяется шаблон" xsl:text генерирует текст в выходной поток, аттрибут disable-output-escaping = "yes" или "no", указывает на возможность генерации символов ESC xsl:value-of вставляет значение выбранного узла как текст, аттрибут select= "указатель на узел" из которого берут значение xsl:variable специфицирует значение границ переменных, аттрибут name = "имя переменной", select = "вычисление значения переменной" xsl:with-param применяет параметр к шаблону, аттрибут name ="имя параметра", select = выражение для вычисления текущего контекста, значениие по умолчанию "."
ЗаключениеВ заключение, необходимо отметить, что использование стандартного XML-парсера msxml.dll является не единственным средством разбора и создания XML-документов. Например, для создание XML документов эффективно использовать компоненты TPageProduser и ТТаь1еРгоdиsег. Но, данная статья лишь подчеркивает широту и возможность применения на практике DOM модели .
Автор будет очень благодарен за отзывы об актуальности темы, общем содержании, стиле изложения, а также всем остальным комментариям, которые помогут в дальнейшем улучшить качество написания сборника статей и выпуску книги, освещающую тему практической стороны использования XML-документов в электронной коммерции. Более подробную информацию о практической стороне применения электронных документов можно почерпнуть на авторском сайте www.eDocs.al.ru Также на авторском сайте планируется разместить исходные тексты и примеры.
Крестики - нолики с коброй.

| - Какая разница межу Win95 и рос. рублем? - Никакой. Каждый день падает, и раз в три года выходит новая версия. |
Известно расшифровывается как Common Object Request Broker Architecture, и представляет собой объектно-ориентированную архитектуру связи между клиентом и сервером. Приложения на основе CORBA состоят из двух частей: CORBA-сервер и CORBA-клиент. И сервер и клиент могут быть реализованы на любом языке и запущены на любой платформе. CORBA представляет собой независимую от языка программирования и операционной системы технологию. Это возможно, так как все параметры и типы, возвращаемые методами транспортируются через сеть в специально универсальном формате. А вот для того чтобы сервер и клиент понимали друг друга необходимо определить интерфейс CORBA-сервера, при этом необходимо учитывать независимость от операционной системы и языка на котором происходит разработка приложения. Для этой цели и был разработан интерфейс общения клиента и сервера Interface Definition Language (IDL). Используя IDL, можно определять специфические объекты с присущими им методами и свойствами. Данные методы подобны функциям, которые могут быть вызваны клиентом, и которые могут быть реализованы сервером. В Delphi например для реализации подобного интерфейса прийдеться компилировать специализированный IDL-файл. Вообще же преобразование из стандартного внутреннего стандарта языка программирования в подобный переносимый формат обозначают как marshalling. Обратный процесс преобразования из универсального формата в стандарт понятный программе называется unmarshalling.
Особенности установки VisiBroker
В стандартный набор Delphi 6 Enterprise входит поддержка CORBA в двух вариантах. Во время инсталляции Delphi необходимо выбрать поддержку VisiBroker 3.3 или VisiBroker 4. Это связано с тем, что VisiBroker 3.3 и VisiBroker 4 не могут быть установлены одновременно. В противном случаи, возможны проблемы при работе с Delphi 6. В более ранней версии VisiBroker 3.3 существует полезная возможность динамического вызова интерфейса. В VisiBroker 4 это функциональная особенность не поддерживается. Несмотря на это VisiBroker 4 представляет собой более совершенную реализацию стандарта CORBA, поэтому вопросы, связанные с предыдущей версией VisiBroker 3.3 рассматриваться не будут.
TicTacToe
А теперь рассмотрим возможности технологии CORBA в Delphi, с использованием VisiBroker 4, на примере практического создания небольшой программы. Ниже представлена конструкция IDL известной всем игры в "крестики-нолики", которая имеет гордое английское название TicTacToe. Модуль TTT с интерфейсом TicTacToe реализуется CORBA сервером, и CORBA клиент может соединяться с сервером во время игры.
module TTT
{
interface TicTacToe
{
typedef long TGame;
typedef long TPlace; // 0,1..9
enum TPlayer
{
user,
computer,
none
};
exception PlaceTaken
{
TPlayer TakenBy;
};
TGame NewGame();
void MakeMove(in TGame Game, in TPlayer player, in TPlace Place)
raises(PlaceTaken);
TPlace NextMove(in TGame Game, in TPlayer player);
TPlayer IsWinner(in TGame Game);
TPlayer GetValue(in TGame Game, in TPlace Place);
};
};
|
Модуль TTT имеет интерфейс TicTacToe. Это интерфейс содержит определения ряда типов (видимы только внутри области интерфейса), определение исключения и определения ряда методов. Обратите внимание, что метод MakeMove может вызывать исключение PlaceTaken. Исключение PlaceTaken - фактически структура, которая также будет обработана.
IDL2Pas Wizard
Для использования IDL файла, его необходимо скомпилировать для Server Skeletons и Client Stubs. Для этого используется файл IDL2Pas, который является частью VisiBroker for Delphi. Но более простой путь, использовать мастера CORBA Server Application и CORBA Client Найти их можно в File | New | Other, закладка Corba.
При выборе мастера CORBA Server Application появится окно и вы можете добавить туда IDL.
Закладка Options содержит ряд специфических установок, который будут выполнены в командной строке IDL2Pas. Обратите внимание на опцию "Overwrite Implementation Units", она не установлена по умолчанию. Кстати, при повторной компиляции данную опцию необходима снять - иначе созданная до этого IDL-файл будет перекомпилировать.
Установки закладки Options мастера IDL2Pas хранятся в секции [idl2pas] файла defproj.dof, находящегося в директории Delphi6\bin, и все выбранные установки будут использованы при следующей загрузки мастера IDL2Pas.
CORBA Server Skeleton
После того как вы нажмете на кнопку ОК в CORBA Server Application Wizard, будут сгенерировано несколько файлов: TTT.IDL будет использован для генерации файла TTT_c.pas (client stubs и helpers), TTT_i.pas будет содержать определения интерфейса, TTT_impl.pas будет использован для реализации интерфейса и TTT_s.pas содержащий server skeletons. Далее можно будет только модифицировать файл TTT_impl.pas, тогда как другие могут быть сгенерированы заново с помощью IDL2Pas.
Interface Definitions (TTT_i.pas)
Файл интерфейса ТТТ TTT_i.pas содержит определение интерфейса TicTacToe. Причиной использования в определениях типов префикса TicTacToe_ является использование этих типов внутри интерфейса. Если мы определяем их вне интерфейса TicTacToe, то транслироваться они буду без префикса TicTacToe_.
unit TTT_i;
interface
uses CORBA;
type
TicTacToe_TPlayer = (user, computer, none);
type
TicTacToe = interface;
TicTacToe_TGame = Integer;
TicTacToe_TPlace = Integer;
TicTacToe = interface ['{50B30FC5-4B18-94AB-1D5F-4148BB7467B4}']
function NewGame: TTT_i.TicTacToe_TGame;
procedure MakeMove (const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer;
const Place: TTT_i.TicTacToe_TPlace);
function NextMove (const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer):
TTT_i.TicTacToe_TPlace;
function IsWinner (const Game: TTT_i.TicTacToe_TGame):
TTT_i.TicTacToe_TPlayer;
function GetValue (const Game: TTT_i.TicTacToe_TGame;
const Place: TTT_i.TicTacToe_TPlace):
TTT_i.TicTacToe_TPlayer;
end;
|
Можно заметить, что здесь не видны определения исключения. Оно появится в файле Client Stub TTT_c.pas.
Client Stubs and Helpers (TTT_c.pas)
Файл TTT_s.pas содержит не только Client Stubs, но и классы helper. Конечно, лучше было бы если Client Stubs был включен в TTT_c.pas, а классы helper в TTT_h.pas. Но раз все обстоит не так, придется включить файл TTT_c.pas в предложение uses нашего файла Server Skeleton TTT_s.pas.
unit TTT_c;
interface
uses CORBA, TTT_i;
type
TTicTacToeHelper = class;
TTicTacToeStub = class;
TTicTacToe_TGameHelper = class;
TTicTacToe_TPlaceHelper = class;
TTicTacToe_TPlayerHelper = class;
ETicTacToe_PlaceTaken = class;
TTicTacToeHelper = class
class procedure Insert (var _A: CORBA.Any; const _Value: TTT_i.TicTacToe);
class function Extract(var _A: CORBA.Any): TTT_i.TicTacToe;
class function TypeCode: CORBA.TypeCode;
class function RepositoryId: string;
class function read (const _Input: CORBA.InputStream): TTT_i.TicTacToe;
class procedure write(const _Output: CORBA.OutputStream; const _Value:
TTT_i.TicTacToe);
class function Narrow(const _Obj: CORBA.CORBAObject; _IsA: Boolean = False):
TTT_i.TicTacToe;
class function Bind(const _InstanceName: string = ''; _HostName: string = ''):
TTT_i.TicTacToe; overload;
class function Bind(_Options: BindOptions; const _InstanceName: string = '';
_HostName: string = ''): TTT_i.TicTacToe; overload;
end;
TTicTacToeStub = class(CORBA.TCORBAObject, TTT_i.TicTacToe)
public
function NewGame: TTT_i.TicTacToe_TGame; virtual;
procedure MakeMove(const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer;
const Place: TTT_i.TicTacToe_TPlace); virtual;
function NextMove(const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer):
TTT_i.TicTacToe_TPlace; virtual;
function IsWinner(const Game: TTT_i.TicTacToe_TGame):
TTT_i.TicTacToe_TPlayer; virtual;
function GetValue(const Game: TTT_i.TicTacToe_TGame;
const Place: TTT_i.TicTacToe_TPlace):
TTT_i.TicTacToe_TPlayer; virtual;
end;
TTicTacToe_TGameHelper = class
class procedure Insert (var _A: CORBA.Any; const _Value: TTT_i.TicTacToe_TGame);
class function Extract(const _A: CORBA.Any): TTT_i.TicTacToe_TGame;
class function TypeCode: CORBA.TypeCode;
class function RepositoryId: string;
class function read (const _Input: CORBA.InputStream): TTT_i.TicTacToe_TGame;
class procedure write(const _Output: CORBA.OutputStream; const _Value:
TTT_i.TicTacToe_TGame);
end;
TTicTacToe_TPlaceHelper = class
class procedure Insert (var _A: CORBA.Any; const _Value: TTT_i.TicTacToe_TPlace);
class function Extract(const _A: CORBA.Any): TTT_i.TicTacToe_TPlace;
class function TypeCode: CORBA.TypeCode;
class function RepositoryId: string;
class function read (const _Input: CORBA.InputStream): TTT_i.TicTacToe_TPlace;
class procedure write(const _Output: CORBA.OutputStream; const _Value:
TTT_i.TicTacToe_TPlace);
end;
TTicTacToe_TPlayerHelper = class
class procedure Insert (var _A: CORBA.Any; const _Value: TTT_i.TicTacToe_TPlayer);
class function Extract(const _A: CORBA.Any): TTT_i.TicTacToe_TPlayer;
class function TypeCode: CORBA.TypeCode;
class function RepositoryId: string;
class function read (const _Input: CORBA.InputStream): TTT_i.TicTacToe_TPlayer;
class procedure write(const _Output: CORBA.OutputStream; const _Value:
TTT_i.TicTacToe_TPlayer);
end;
ETicTacToe_PlaceTaken = class(UserException)
private
FTakenBy: TTT_i.TicTacToe_TPlayer;
protected
function _get_TakenBy: TTT_i.TicTacToe_TPlayer; virtual;
public
property TakenBy: TTT_i.TicTacToe_TPlayer read _get_TakenBy;
constructor Create; overload;
constructor Create(const TakenBy: TTT_i.TicTacToe_TPlayer); overload;
procedure Copy(const _Input: InputStream); override;
procedure WriteExceptionInfo(var _Output: OutputStream); override;
end;
|
На что следует обратить внимание, так это на декларацию исключения ETicTacToe_PlaceTaken, которое имеет два конструктора: по умолчанию без аргументов и с одним аргументом TakenBy, который автоматически инициализируя исключение.
Server Skeletons (TTT_s.pas)
Класс TticTacToeSkeleton единственный класс, который мы используем для создания экземпляра CORBA Server TicTacToe, принимающего в качестве аргументов имя InstanceName и экземпляр интерфейса TicTacToe .
unit TTT_s;
interface
uses CORBA, TTT_i, TTT_c;
type
TTicTacToeSkeleton = class;
TTicTacToeSkeleton = class(CORBA.TCorbaObject, TTT_i.TicTacToe)
private
FImplementation: TicTacToe;
public
constructor Create(const InstanceName: string; const Impl: TicTacToe);
destructor Destroy; override;
function GetImplementation: TicTacToe;
function NewGame: TTT_i.TicTacToe_TGame;
procedure MakeMove(const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer;
const Place: TTT_i.TicTacToe_TPlace);
function NextMove(const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer):
TTT_i.TicTacToe_TPlace;
function IsWinner(const Game: TTT_i.TicTacToe_TGame):
TTT_i.TicTacToe_TPlayer;
function GetValue(const Game: TTT_i.TicTacToe_TGame;
const Place: TTT_i.TicTacToe_TPlace):
TTT_i.TicTacToe_TPlayer;
published
procedure _NewGame(const _Input: CORBA.InputStream; _Cookie: Pointer);
procedure _MakeMove(const _Input: CORBA.InputStream; _Cookie: Pointer);
procedure _NextMove(const _Input: CORBA.InputStream; _Cookie: Pointer);
procedure _IsWinner(const _Input: CORBA.InputStream; _Cookie: Pointer);
procedure _GetValue(const _Input: CORBA.InputStream; _Cookie: Pointer);
end;
|
Implementation (TTT_impl.pas)
Файл TTT_impl.pas, единственный файл который редактируется и в который вставляется код реализации CORBA сервера. Тут использован модуль Magic, который использовался для ITicTacToe web service в Delphi 6.
unit TTT_impl;
interface
uses
SysUtils, CORBA, TTT_i, TTT_c,
Magic; // implementation of Magic.TTicTacToe
type
TTicTacToe = class(TInterfacedObject, TTT_i.TicTacToe)
protected
TTT: Magic.TTicTacToe;
public
constructor Create;
function NewGame:TTT_i.TicTacToe_TGame;
procedure MakeMove(const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer;
const Place: TTT_i.TicTacToe_TPlace);
function NextMove(const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer):
TTT_i.TicTacToe_TPlace;
function IsWinner(const Game: TTT_i.TicTacToe_TGame):
TTT_i.TicTacToe_TPlayer;
function GetValue(const Game: TTT_i.TicTacToe_TGame;
const Place: TTT_i.TicTacToe_TPlace):
TTT_i.TicTacToe_TPlayer;
end;
implementation
constructor TTicTacToe.Create;
begin
inherited;
{ *************************** }
{ *** User code goes here *** }
{ *************************** }
TTT := Magic.TTicTacToe.Create;
end;
function TTicTacToe.NewGame: TTT_i.TicTacToe_TGame;
begin
{ *************************** }
{ *** User code goes here *** }
{ *************************** }
Result := TTT.NewGame
end;
procedure TTicTacToe.MakeMove(const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer;
const Place: TTT_i.TicTacToe_TPlace);
begin
{ *************************** }
{ *** User code goes here *** }
{ *************************** }
TTT.MakeMove(Game, Ord(Player), Place);
end;
function TTicTacToe.NextMove(const Game: TTT_i.TicTacToe_TGame;
const player: TTT_i.TicTacToe_TPlayer):
TTT_i.TicTacToe_TPlace;
begin
{ *************************** }
{ *** User code goes here *** }
{ *************************** }
Result := TTT.NextMove(Game, Ord(Player))
end;
function TTicTacToe.IsWinner(const Game: TTT_i.TicTacToe_TGame):
TTT_i.TicTacToe_TPlayer;
begin
{ *************************** }
{ *** User code goes here *** }
{ *************************** }
Result := TTT_i.TicTacToe_TPlayer(TTT.IsWinner(Game))
end;
function TTicTacToe.GetValue(const Game: TTT_i.TicTacToe_TGame;
const Place: TTT_i.TicTacToe_TPlace):
TTT_i.TicTacToe_TPlayer;
begin
{ *************************** }
{ *** User code goes here *** }
{ *************************** }
Result := TTT_i.TicTacToe_TPlayer(TTT.GetValue(Game, Place))
end;
initialization
end.
|
Теперь мы имеем на руках практически все части для создания приложения с использованием технологии CORBA . Пусть даже это и игрушка.
CORBA Server Application
Помимо сгенерированных файлов должен же быть и сам проект с главным модулем формы. Сохранив проект как TTTServer.dpr а модуль главной формы как GameUnit. Если заменить фактический ТТТ на объект skeleton типа TicTacToe, код модуля будет выглядеть следующим образом. Тут следует обратить внимание на использование четырех модулей в предложении uses секции interface:
unit GameUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, Corba, TTT_i, TTT_c, TTT_s, TTT_impl;
type
TForm1 = class(TForm)
private
{ private declarations }
protected
{ protected declarations }
TTT: TicTacToe; // skeleton object
procedure InitCorba;
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.InitCorba;
begin
CorbaInitialize;
TTT := TTicTacToeSkeleton.Create('TTT', TTicTacToe.Create);
BOA.ObjIsReady(TTT as _Object)
end;
end.
|
Вызов InitCorba будем производить из обработчика события OnCreate формы:
procedure TForm1.FormCreate(Sender: TObject); begin InitCorba; end; procedure TForm1.FormDestroy(Sender: TObject); begin TTT := nil; end; |
Можно сделать вывод, что сервер лучше иметь в виде консольного приложения. Ниже оно представлено. Там используется старомодный оператор writeln, с помощью которого и сообщается пользователю о запуске новой игры. Консольное приложение использует те же самые элементы, что и визуальная версия, но в конце добавлен вызов BOA.ImplIsReady.
program TTTCServer;
{$APPTYPE CONSOLE}
uses
SysUtils, CORBA, TTT_c, TTT_i, TTT_s, TTT_impl;
var
TTT: TicTacToe; // skeleton object
begin
writeln('CorbaInitialize');
CorbaInitialize;
writeln('TTicTacToe.Create');
TTT := TTicTacToeSkeleton.Create('TTT', TTicTacToe.Create);
writeln('BOA.ObjIsReady');
BOA.ObjIsReady(TTT as _Object);
writeln('BOA.ImplIsReady');
BOA.ImplIsReady
end.
|
Теперь можно приступать к созданию CORBA-клиента.
CORBA Client Application
Для создания CORBA-клента так же можно использовать CORBA Wizard. Проделываем тоже самое что мы делали для формирования сервера CORBA. Только не следует создавать снова TTT_impl.pas. Кроме уже описанных выше файлов, в наличие есть и файл главной формы и файл проекта. Сохраним их как MainForm.pas и TTTClient.dpr. Модуль MainForm.pas содержит подсказки, чтобы показать вам как создать экземпляр CORBA сервера:
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, Corba;
type
TForm1 = class(TForm)
private
{ private declarations }
protected
{ protected declarations }
// declare your Corba interface variables like this
// Acct : Account;
procedure InitCorba;
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.InitCorba;
begin
CorbaInitialize;
// Bind to the Corba server like this
// Acct := TAccountHelper.bind;
end;
end.
|
Здесь нужно вызвать метод InitCorba из обработчика OnCreate формы. Надо включить в предложение uses модуля MainForm модули TTT_c, TTT_i и TTT_impl, без которых не будут доступны классы helpers. Непосредственно же объявление переменной типа интерфейса CORBA, может выглядеть следующим образом:
private TicTacToe: TicTacToe; |
Фактическое связывание интерфейса TicTacToe с CORBA сервером реализуется следующим образом:
TicTacToe := TTicTacToeHelper.bind; |
Теперь можно использовать TicTacToe как обыкновенный класс, включающий поддержку Code Insight.
Action!
Внизу представлен небольшой компонент, основанный на оригинальном компоненте игры TicTacToe. Результирующий код, реализован в MagicTTT.pas - содержит в предложении uses модули TTT_i, TTT_c and TTT_impl и создает экземпляр интерфейса TicTacToe:
unit MagicTTT;
interface
uses
SysUtils, Classes, Controls, StdCtrls, Dialogs, TTT_c, TTT_i, TTT_impl;
const
NoneID = 0;
UserID = 1;
CompID = 2;
const
chrUser = 'X';
chrComp = '@';
const
FirstPlace = 1;
LastPlace = 9;
type
TPlace = FirstPlace..LastPlace;
type
TTTTControl = class(TWinControl)
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
private
TicTacToe: TicTacToe;
private { 9 game buttons }
Game: Integer;
Button: array[TPlace] of TButton;
procedure ButtonClick(Sender: TObject);
procedure ComputerMove;
procedure UserMove(Move: TPlace);
private { start button }
TheStartButton: TButton;
procedure StartButtonClick(Sender: TObject);
private { game properties }
FStartButton: Boolean;
FUserStarts: Boolean;
FUserChar: Char;
FCompChar: Char;
protected { design interface }
procedure SetStartButton(Value: Boolean);
procedure SetUserStarts(Value: Boolean);
procedure SetUserChar(Value: Char);
procedure SetCompChar(Value: Char);
function GetCaption: string;
procedure SetCaption(Value: string);
published { user interface }
property StartButton: Boolean
read FStartButton write FStartButton default False;
property Caption: string
read GetCaption write SetCaption;
property UserStarts: Boolean
read FUserStarts write SetUserStarts default False;
property UserChar: Char
read FUserChar write SetUserChar default chrUser;
property CompChar: Char
read FCompChar write SetCompChar default chrComp;
end {TTTTControl};
procedure register;
implementation
uses Forms;
constructor TTTTControl.Create(AOwner: TComponent);
var
ButtonIndex: TPlace;
begin
inherited Create(AOwner);
Game := 0;
UserStarts := False;
FUserChar := chrUser;
FCompChar := chrComp;
TheStartButton := TButton.Create(Self);
TheStartButton.Parent := Self;
TheStartButton.Visible := True;
TheStartButton.Caption := 'Humor me...';
TheStartButton.OnClick := StartButtonClick;
CorbaInitialize;
TicTacToe := TTicTacToeHelper.bind;
for ButtonIndex := Low(ButtonIndex) to High(ButtonIndex) do
begin
Button[ButtonIndex] := TButton.Create(Self);
Button[ButtonIndex].Parent := Self;
Button[ButtonIndex].Caption := '';
Button[ButtonIndex].Visible := False;
Button[ButtonIndex].OnClick := ButtonClick;
end;
SetBounds(Left,Top,132,132)
end {Create};
destructor TTTTControl.Destroy;
var
ButtonIndex: TPlace;
begin
TheStartButton.Destroy;
for ButtonIndex := Low(ButtonIndex) to High(ButtonIndex) do
Button[ButtonIndex].Destroy;
TicTacToe := nil; { explicit! }
inherited Destroy;
end; {Destroy};
procedure TTTTControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
const
Grid = 3;
GridX = 2;
GridY = 2;
var
X,DX,W,Y,DY,H: Word;
begin
inherited SetBounds(ALeft,ATop,AWidth,AHeight);
TheStartButton.SetBounds(0,0,Width,Height);
X := GridX;
DX := (Width div (Grid * (GridX+GridX))) * (GridX+GridX);
W := DX - GridX;
Y := GridY;
DY := (Height div (Grid * (GridY+GridY))) * (GridY+GridY);
H := DY - GridY;
Button[8].SetBounds(X, Y, W,H);
Button[1].SetBounds(X, Y+DY, W,H);
Button[6].SetBounds(X, Y+DY+DY, W,H);
Inc(X,DX);
Button[3].SetBounds(X, Y, W,H);
Button[5].SetBounds(X, Y+DY, W,H);
Button[7].SetBounds(X, Y+DY+DY, W,H);
Inc(X,DX);
Button[4].SetBounds(X, Y, W,H);
Button[9].SetBounds(X, Y+DY, W,H);
Button[2].SetBounds(X, Y+DY+DY, W,H)
end {SetBounds};
procedure TTTTControl.StartButtonClick(Sender: TObject);
var
ButtonIndex: TPlace;
begin
try
Game := TicTacToe.NewGame;
if Parent is TForm then
(Parent as TForm).Caption := IntToStr(Game);
TheStartButton.Visible := False;
for ButtonIndex := Low(ButtonIndex) to High(ButtonIndex) do
Button[ButtonIndex].Visible := True;
if UserStarts then
begin
MessageDlg('You may start...', mtInformation, [mbOk], 0);
Button[5].SetFocus; { hint... }
end
else
ComputerMove
except
on E: Exception do
MessageDlg('Sorry: '+E.message, mtError, [mbOk], 0)
end
end {StartButtonClick};
procedure TTTTControl.ButtonClick(Sender: TObject);
var
ButtonIndex: TPlace;
begin
Enabled := False;
for ButtonIndex := Low(ButtonIndex) to High(ButtonIndex) do
if Button[ButtonIndex] = Sender as TButton then
UserMove(ButtonIndex)
end {ButtonClick};
procedure TTTTControl.ComputerMove;
var
Move: Integer;
begin
Move := TicTacToe.NextMove(Game,TicTacToe_TPlayer(CompID));
if Move = 0 then
MessageDlg('Neither has won, the game is a draw!', mtInformation, [mbOk], 0)
else
begin
TicTacToe.MakeMove(Game,TicTacToe_TPlayer(CompID),Move);
Button[Move].Caption := CompChar;
Button[Move].Update;
if TicTacToe.IsWinner(Game) = TicTacToe_TPlayer(CompID) then
MessageDlg('I have won!', mtInformation, [mbOk], 0)
else
begin
Move := TicTacToe.NextMove(Game,TicTacToe_TPlayer(UserID));
if Move = 0 then
MessageDlg('Neither has won, the game is a draw!', mtInformation, [mbOk], 0)
else
if Move in [FirstPlace..LastPlace] then
begin
Enabled := True;
Button[Move].SetFocus { hint... }
end
else
if Parent is TForm then
(Parent as TForm).Caption := IntToStr(Move)
end
end
end {ComputerMove};
procedure TTTTControl.UserMove(Move: TPlace);
begin
if Button[Move].Caption <> '' then
MessageDlg('This place is occupied!', mtWarning, [mbOk], 0)
else
begin
Button[Move].Caption := UserChar;
Button[Move].Update;
TicTacToe.MakeMove(Game,TicTacToe_TPlayer(UserID),Move);
if TicTacToe.IsWinner(Game) = TicTacToe_TPlayer(UserID) then
MessageDlg('Congratulations, you have won!', mtInformation, [mbOk], 0)
else
ComputerMove
end
end {UserMove};
procedure TTTTControl.SetUserChar(Value: Char);
begin
if Value = FCompChar then
MessageDlg('Character '+Value+' already in use by CompChar!', mtError, [mbOk], 0)
else
FUserChar := Value
end {SetUserChar};
procedure TTTTControl.SetCompChar(Value: Char);
begin
if Value = FUserChar then
MessageDlg('Character '+Value+' already in use by UserChar!', mtError, [mbOk], 0)
else
FCompChar := Value
end {SetCompChar};
procedure TTTTControl.SetUserStarts(Value: Boolean);
begin
FUserStarts := Value;
end {SetUserStarts};
procedure TTTTControl.SetStartButton(Value: Boolean);
begin
FStartButton := Value
end {SetStartButton};
function TTTTControl.GetCaption: string;
begin
GetCaption := TheStartButton.Caption
end {GetCaption};
procedure TTTTControl.SetCaption(Value: string);
begin
TheStartButton.Caption := Value
end {SetCaption};
procedure register;
begin
RegisterComponents('DrBob42', [TTTTControl])
end {Register};
end.
|
Обратите внимание, что конструктор TTTControl также вызывает CorbaInitialize для того чтобы Smart Agent был запущен до того как вы фактически создаете этот компонент.
Пример компонента типа XPButton.
| Раньше казалось, что невозможно было преодолеть нестабильность Windows-98. Но теперь мы видим, что Windows XP ее в этом преодолела. |
unit GsXPButton;
{
written by Jeff Caler,
Please use this responsibly and
freely in your applications, modify, etc,
but dont take credit for my work, i wouldnt yours,
I reserve all rights given by copyright
in the US and other countries.
No right to resell this component or it's code is given nor implied.
Enjoy it responsibly. As in, Dont take my name out and put in yours.
Thank you.
Jeff Caler
madmacs@netzero.net
GenieOS Software
http://www.genieos.co.uk
3-20-02
}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Buttons;
type
TGsXPButton = class(TSpeedButton)
private
{ Private declarations }
eColor, eSelBackColor, eSelFontColor, eSelBorderColor: TColor;
FActive: boolean;
FButtonDown: Boolean;
FForm: TScrollingWinControl;
FStyle: integer;
FDefault: boolean;
Procedure CMMouseEnter( Var Message: TMessage ); Message CM_MOUSEENTER;
Procedure CMMouseLeave( Var Message: TMessage ); Message CM_MOUSELEAVE;
procedure SetForm(const Value: TScrollingWinControl);
protected
{ Protected declarations }
public
{ Public declarations }
Selected: Boolean;
constructor Create( AOwner : TComponent ); override;
property Form: TScrollingWinControl read FForm write SetForm;
Procedure Paint; Override;
procedure SetActive(const Value: boolean);
procedure SetStyle (const Value: integer);
procedure SetDefault(const Value: boolean);
Procedure Style0(AOwner : TComponent);
Procedure Style1(AOwner : TComponent);
published
{ Published declarations }
property Active: boolean read FActive write SetActive;
Property Color: TColor read eColor write eColor;
property Default: Boolean read FDefault write SetDefault;
property Flat default True;
Property SelBackColor: TColor read eSelBackColor write eSelBackColor;
Property SelBorderColor: TColor read eSelBorderColor write eSelBorderColor;
Property SelFontColor: TColor read eSelFontColor write eSelFontColor;
Property Style: integer read FStyle write SetStyle;
end;
type
TGsXPOkButton = class(TGsXPButton)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create( AOwner : TComponent ); override;
published
{ Published declarations }
end;
type
TGsXPCancelButton = class(TGsXPButton)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create( AOwner : TComponent ); override;
published
{ Published declarations }
end;
procedure Register;
implementation
constructor TGsXPButton.Create( AOwner : TComponent );
begin
inherited Create( AOwner );
ControlStyle:= ControlStyle + [csAcceptsControls];
Font.Color := clWindowText;
Color := clBtnFace;
FButtonDown := FALSE;
Flat := True;
FForm := Owner as TScrollingWinControl;
Selected:= False;
Height:= 22;
SelFontColor := clHighLight;
SelBackColor := clHighLightText;
SelBorderColor := $0000A9E1;
//Top:= 4;
Transparent:= False;
Width:= 65;
Active := True;
Style := 0;
end;
procedure TGsXPButton.SetStyle(const Value: integer);
begin
if FStyle <> Value then
begin
FStyle := Value;
end;
end;
Procedure TGsXPButton.CMMouseEnter( Var Message: TMessage );
Begin
//hmmm, no selected property either ehhh.
//Flicker GONE!
If Selected = False then
begin
Selected := True;
RePaint;
end
else
Selected := True;
exit;
End;
Procedure TGsXPButton.CMMouseLeave( Var Message: TMessage );
Begin
Selected:= False;
FButtonDown := FALSE;
RePaint;
IF Selected = False then exit;
End;
procedure TGsXPButton.SetForm(const Value: TScrollingWinControl);
var
Hold: boolean;
begin
if Value <> FForm then
begin
Hold := Active;
Active := false;
FForm := Value;
if Hold then
Active := True;
end;
end;
procedure TGsXPButton.SetActive(const Value: boolean);
begin
FActive := Value;
end;
procedure TGsXPButton.SetDefault(const Value: boolean);
begin
FDefault := Value;
end;
function NewColor(Canvas: TCanvas; clr: TColor; Value: integer): TColor;
var
r, g, b: integer;
begin
if Value > 100 then Value := 100;
clr := ColorToRGB(clr);
r := Clr and $000000FF;
g := (Clr and $0000FF00) shr 8;
b := (Clr and $00FF0000) shr 16;
r := r + Round((255 - r) * (value / 100));
g := g + Round((255 - g) * (value / 100));
b := b + Round((255 - b) * (value / 100));
Result := Windows.GetNearestColor(Canvas.Handle, RGB(r, g, b));
end;
Procedure TGsXPButton.style0(AOwner : TComponent);
var
Gbtn: TGsXPButton;
Right, Bottom: integer;
Selector, txtRect, ARect, iconRect, FRect: TRect;
FColor, FSelBackColor, FSelFontColor: TColor;
FSelBorderColor: TColor;
SelText: String;
TextFormat: integer;
x1, y1 : integer;
i,v : integer;
CWidth, CHeight: INTEGER;
begin
inherited;
Gbtn:= TGsXPButton(Self);
FColor := eColor;
FSelBackColor := eSelBackColor;
FSelFontColor := eSelFontColor;
Bottom := (Gbtn.Canvas.ClipRect.Bottom);
Right := (Gbtn.Canvas.ClipRect.Right);
ARect.Top := (Gbtn.Canvas.ClipRect.Top);
ARect.Right := Right;
ARect.Bottom := Bottom;
Arect.Left := (Gbtn.Canvas.ClipRect.Left);
TextFormat := DT_Left;
SelText :=( ' ' + (Gbtn.Caption));
If Gbtn.Active then
Gbtn.Style := 0;
begin
Flat:= True;
font.Color := font.Color;
Color := FColor;
Selector := Rect( ARect.Left , ARect.Top ,
ARect.right , ARect.bottom );
FRect:= Rect(Arect.Left+3,Arect.Top +3,
Arect.Right -3, Arect.Bottom -3);
//capture the image rect and layout image and text
//Heavy on the math...
if not Glyph.Empty then begin
if Layout = blGlyphLeft then begin
SelText:= SelText;
iconRect:=(Glyph.Canvas.ClipRect);
CWidth:=(Glyph.Width + Canvas.TextWidth(SelText));
iconRect.Left := (ARect.Right - CWidth) div 2;
iconRect.Top := (ARect.Bottom - Glyph.Height) div 2 ;
iconRect.Right := (iconRect.Left + Glyph.Width);
iconRect.Bottom := (iconRect.Top + Glyph.Height);
x1:= (iconRect.Right);
y1:= (Arect.Bottom - Canvas.TextHeight(SelText)) div 2;
TxtRect:= Rect (X1,Y1, Selector.Right, Selector.Bottom );
end
else
if Layout = blGlyphRight then begin
Seltext := SelText + ' ';
iconRect:=(Glyph.Canvas.ClipRect);
CWidth:=(Glyph.Width + Canvas.TextWidth(SelText));
TxtRect:= (Gbtn.Canvas.ClipRect);
TxtRect.Left := (ARect.Right - CWidth) div 2;
TxtRect.Top := (Arect.Bottom - Canvas.TextHeight(SelText)) div 2;
TxtRect.Right := (TxtRect.Left + Canvas.TextWidth(SelText));
TxtRect.Bottom := (TxtRect.Top + Canvas.TextHeight(SelText));
IconRect.Left := (TxtRect.Right);
iconRect.Top := (ARect.Bottom - Glyph.Height) div 2 ;
iconRect.Right := (iconRect.Left + Glyph.Width);
iconRect.Bottom := (iconRect.Top + Glyph.Height);
end
else
if Layout = blGlyphTop then begin
SelText := SelText;
iconRect:=(Glyph.Canvas.ClipRect);
CHeight:= (Canvas.TextHeight(SelText)+ Glyph.Height);
TxtRect:= (Gbtn.Canvas.ClipRect);
IconRect.Left := (Canvas.ClipRect.Right div 2) - (glyph.Width div 2);
iconRect.Top := (ARect.Bottom div 2) - ((glyph.Height + Canvas.TextHeight(Seltext)) div 2) ;
iconRect.Right := (iconRect.Left + Glyph.Width);
iconRect.Bottom := (iconRect.Top + Glyph.Height);
TxtRect.Left := (ARect.Right div 2) - (Canvas.TextWidth(Seltext) div 2);
TxtRect.Top := (iconRect.Bottom + 2);
TxtRect.Right := (TxtRect.Left + Canvas.TextWidth(SelText));
TxtRect.Bottom := (TxtRect.Top + Canvas.TextHeight(SelText));
end
else
if Layout = blGlyphBottom then begin
SelText := SelText;
iconRect:=(Glyph.Canvas.ClipRect);
CHeight:= (Canvas.TextHeight(SelText)+ Glyph.Height);
TxtRect:= (Gbtn.Canvas.ClipRect);
TxtRect.Left := (ARect.Right div 2) - (Canvas.TextWidth(Seltext) div 2);
TxtRect.Top := (ARect.Bottom div 2) - ((glyph.Height + Canvas.TextHeight(Seltext)) div 2) ;
TxtRect.Right := (TxtRect.Left + Canvas.TextWidth(SelText));
TxtRect.Bottom := (TxtRect.Top + Canvas.TextHeight(SelText));
IconRect.Left := (Canvas.ClipRect.Right div 2) - (glyph.Width div 2);
iconRect.Top := (txtRect.Bottom + 2) ;
iconRect.Right := (iconRect.Left + Glyph.Width);
iconRect.Bottom := (iconRect.Top + Glyph.Height);
end;
end
else
if Glyph.Empty Then
begin
SelText := SelText;
x1:= (Arect.Right - Canvas.TextWidth(SelText))div 2;
y1:= (Arect.Bottom - Canvas.TextHeight(SelText)) div 2;
TxtRect:= Rect (X1,Y1, Selector.Right, Selector.Bottom );
end;
Canvas.Brush.Style:= bsSolid;
Canvas.Brush.Color := FColor;
Canvas.FillRect(ARect);
IF GBtn.Default THEN
IF not Selected then
If not Gbtn.MouseCapture then
begin
v:=0;
Selector.top := Selector.bottom - 1;
for i := ARect.bottom downto ARect.top do
begin
if (Selector.top < ARect.bottom)
and (Selector.top > Arect.bottom + 5) then
inc(v, 1)
else
inc(v, 2);
if v > 96 then v := 96;
Canvas.Brush.Color := NewColor(Canvas, $00FF8080, v);
Canvas.FillRect(Selector);
Selector.top := Selector.top -1;
Selector.bottom := Selector.top + 1;
Canvas.Brush.Style := bsClear;
Canvas.Pen.color := $00984E00;
Canvas.RoundRect(ARect.Left ,
Arect.top , Arect.right ,
Arect.bottom , 5, 5);
Canvas.Brush.Style:= bsSolid;
Canvas.Brush.Color := FSelBackColor;
Canvas.FillRect(FRect);
//draw the text
DrawtextEx(Gbtn.Canvas.Handle,
PChar(SelText),
Length(SelText),
txtRect,
TextFormat, nil);
if not gbtn.Glyph.Empty THEN Canvas.Rectangle (iconRect);
begin
Canvas.CopyRect(IconRect,Canvas,(GBtn.Glyph.Canvas.ClipRect));
Canvas.BrushCopy (IconRect, Glyph,(GBtn.Glyph.Canvas.ClipRect),(clBlue));
Gbtn.Glyph.FreeImage;
end;
end;
end;
If Gbtn.Selected then
if not gbtn.MouseCapture then
begin
//gradient begin
v:=0;
Selector.top := Selector.bottom - 1;
for i := ARect.bottom downto ARect.top do
begin
if (Selector.top < ARect.bottom)
and (Selector.top > Arect.bottom + 5) then
inc(v, 1)
else
inc(v, 3);
if v > 96 then v := 96;
Canvas.Brush.Color := NewColor(Canvas, $0000A9E1, v);
Canvas.FillRect(Selector);
Selector.top := Selector.top -1;
Selector.bottom := Selector.top + 1;
Canvas.Brush.Style := bsClear;
Canvas.Pen.color := $00984E00;
Canvas.RoundRect(ARect.Left ,
Arect.top , Arect.right ,
Arect.bottom , 5, 5);
Canvas.Brush.Style:= bsSolid;
Canvas.Brush.Color := FSelBackColor;
Canvas.FillRect(FRect);
Canvas.Font.color := FSelFontColor;
DrawtextEx(Gbtn.Canvas.Handle,
PChar(SelText),
Length(SelText),
txtRect,
TextFormat, nil);
if not gbtn.Glyph.Empty THEN Canvas.Rectangle (iconRect);
begin
Canvas.CopyRect(IconRect,Canvas,(GBtn.Glyph.Canvas.ClipRect));
Canvas.BrushCopy (IconRect, Glyph,(GBtn.Glyph.Canvas.ClipRect),(clBlue));
Gbtn.Glyph.FreeImage;
end;
end;
end;
//draw the outline
Canvas.Brush.Style := bsClear;
Canvas.Pen.color := $00984E00;
Canvas.RoundRect(ARect.Left,
ARect.top, ARect.right,
ARect.bottom, 5, 5);
//override the button canvas
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color:= FColor;
Canvas.FillRect(Frect);
//over ride the text
DrawtextEx(Gbtn.Canvas.Handle,
PChar(SelText),
Length(SelText),
txtRect,
TextFormat, nil);
if not gbtn.Glyph.Empty THEN Canvas.Rectangle (iconRect);
begin
Canvas.CopyRect(IconRect,Canvas,(GBtn.Glyph.Canvas.ClipRect));
Canvas.BrushCopy (IconRect, Glyph,(GBtn.Glyph.Canvas.ClipRect),(clBlue));
gbtn.Glyph.FreeImage;
end;
end;
end;
Procedure TGsXPButton.Style1(AOwner : TComponent);
var
Gbtn: TGsXPButton;
Right, Bottom: integer;
Selector, txtRect, ARect, iconRect, FRect: TRect;
FColor, FSelBackColor, FSelFontColor: TColor;
FSelBorderColor: TColor;
SelText: String;
TextFormat: integer;
x1, y1 : integer;
i,v : integer;
CWidth, CHeight: INTEGER;
begin
inherited;
//let the games begin
Gbtn:= TGsXPButton(Self);
FColor := eColor;
FSelBackColor := eSelBackColor;
FSelFontColor := eSelFontColor;
Bottom := (Gbtn.Canvas.ClipRect.Bottom);
Right := (Gbtn.Canvas.ClipRect.Right);
ARect.Top := (Gbtn.Canvas.ClipRect.Top);
ARect.Right := Right;
ARect.Bottom := Bottom;
Arect.Left := (Gbtn.Canvas.ClipRect.Left);
TextFormat := DT_Left;
SelText :=( ' ' + (Gbtn.Caption));
If Gbtn.Active then
Gbtn.Style := 1;
begin
Flat:= True;
font.Color := font.Color;
Color := FColor;
Selector := Rect( ARect.Left , ARect.Top ,
ARect.right , ARect.bottom );
FRect:= Rect(Arect.Left+3,Arect.Top +3,
Arect.Right -3, Arect.Bottom -3);
//capture the image rect and layout image and text
//Heavy on the math...
if not Glyph.Empty then begin
if Layout = blGlyphLeft then begin
SelText:= SelText;
iconRect:=(Glyph.Canvas.ClipRect);
CWidth:=(Glyph.Width + Canvas.TextWidth(SelText));
iconRect.Left := (ARect.Right - CWidth) div 2;
iconRect.Top := (ARect.Bottom - Glyph.Height) div 2 ;
iconRect.Right := (iconRect.Left + Glyph.Width);
iconRect.Bottom := (iconRect.Top + Glyph.Height);
x1:= (iconRect.Right);
y1:= (Arect.Bottom - Canvas.TextHeight(SelText)) div 2;
TxtRect:= Rect (X1,Y1, Selector.Right, Selector.Bottom );
end
else
if Layout = blGlyphRight then begin
Seltext := SelText + ' ';
iconRect:=(Glyph.Canvas.ClipRect);
CWidth:=(Glyph.Width + Canvas.TextWidth(SelText));
TxtRect:= (Gbtn.Canvas.ClipRect);
TxtRect.Left := (ARect.Right - CWidth) div 2;
TxtRect.Top := (Arect.Bottom - Canvas.TextHeight(SelText)) div 2;
TxtRect.Right := (TxtRect.Left + Canvas.TextWidth(SelText));
TxtRect.Bottom := (TxtRect.Top + Canvas.TextHeight(SelText));
IconRect.Left := (TxtRect.Right);
iconRect.Top := (ARect.Bottom - Glyph.Height) div 2 ;
iconRect.Right := (iconRect.Left + Glyph.Width);
iconRect.Bottom := (iconRect.Top + Glyph.Height);
end
else
if Layout = blGlyphTop then begin
SelText := SelText;
iconRect:=(Glyph.Canvas.ClipRect);
CHeight:= (Canvas.TextHeight(SelText)+ Glyph.Height) div 2;
TxtRect:= (Gbtn.Canvas.ClipRect);
IconRect.Left := (Canvas.ClipRect.Right div 2) - (glyph.Width div 2);
iconRect.Top := (ARect.Bottom div 2) - (CHeight) ;
iconRect.Right := (iconRect.Left + Glyph.Width);
iconRect.Bottom := (iconRect.Top + Glyph.Height);
TxtRect.Left := (ARect.Right div 2) - (Canvas.TextWidth(Seltext) div 2);
TxtRect.Top := (iconRect.Bottom + 2);
TxtRect.Right := (TxtRect.Left + Canvas.TextWidth(SelText));
TxtRect.Bottom := (TxtRect.Top + Canvas.TextHeight(SelText));
end
else
if Layout = blGlyphBottom then begin
SelText := SelText;
iconRect:=(Glyph.Canvas.ClipRect);
TxtRect:= (Gbtn.Canvas.ClipRect);
TxtRect.Left := (ARect.Right div 2) - (Canvas.TextWidth(Seltext) div 2);
TxtRect.Top := (ARect.Bottom div 2) - ((glyph.Height + Canvas.TextHeight(Seltext)) div 2) ;
TxtRect.Right := (TxtRect.Left + Canvas.TextWidth(SelText));
TxtRect.Bottom := (TxtRect.Top + Canvas.TextHeight(SelText));
IconRect.Left := (Canvas.ClipRect.Right div 2) - (glyph.Width div 2);
iconRect.Top := (txtRect.Bottom + 2) ;
iconRect.Right := (iconRect.Left + Glyph.Width);
iconRect.Bottom := (iconRect.Top + Glyph.Height);
end;
end
else
if Glyph.Empty Then
begin
SelText := SelText;
x1:= (Arect.Right - Canvas.TextWidth(SelText))div 2;
y1:= (Arect.Bottom - Canvas.TextHeight(SelText)) div 2;
TxtRect:= Rect (X1,Y1, Selector.Right, Selector.Bottom );
end;
Canvas.Brush.Style:= bsSolid;
Canvas.Brush.Color := FColor;
Canvas.FillRect(ARect);
IF GBtn.Default THEN
IF not Selected then
If not Gbtn.MouseCapture then
begin
v:=0;
Selector.top := Selector.bottom - 1;
for i := ARect.bottom downto ARect.top do
begin
if (Selector.top < ARect.bottom)
and (Selector.top > Arect.bottom + 5) then
inc(v, 1)
else
inc(v, 2);
if v > 96 then v := 96;
Canvas.Brush.Color := NewColor(Canvas, $00FF8080, v);
Canvas.FillRect(Selector);
Selector.top := Selector.top -1;
Selector.bottom := Selector.top + 1;
Canvas.Brush.Style := bsClear;
Canvas.Pen.color := $00984E00;
Canvas.Rectangle(ARect.Left ,
Arect.top , Arect.right ,
Arect.bottom );
Canvas.Brush.Style:= bsSolid;
Canvas.Brush.Color := FSelBackColor;
Canvas.FillRect(FRect);
//draw the text
DrawtextEx(Gbtn.Canvas.Handle,
PChar(SelText),
Length(SelText),
txtRect,
TextFormat, nil);
end;
end;
If Gbtn.Selected then
if not gbtn.MouseCapture then
begin
//gradient begin
v:=0;
Selector.top := Selector.bottom - 1;
for i := ARect.bottom downto ARect.top do
begin
if (Selector.top < ARect.bottom)
and (Selector.top > Arect.bottom + 5) then
inc(v, 1)
else
inc(v, 3);
if v > 96 then v := 96;
Canvas.Brush.Color := NewColor(Canvas, $0000A9E1, v);
Canvas.FillRect(Selector);
Selector.top := Selector.top -1;
Selector.bottom := Selector.top + 1;
Canvas.Brush.Style := bsClear;
Canvas.Pen.color := $00984E00;
Canvas.Rectangle(ARect.Left ,
Arect.top , Arect.right ,
Arect.bottom );
Canvas.Brush.Style:= bsSolid;
Canvas.Brush.Color := FSelBackColor;
Canvas.FillRect(FRect);
Canvas.Font.color := FSelFontColor;
DrawtextEx(Gbtn.Canvas.Handle,
PChar(SelText),
Length(SelText),
txtRect,
TextFormat, nil);
end;
end;
//draw the outline
Canvas.Brush.Style := bsClear;
Canvas.Pen.color := $00984E00;
Canvas.Rectangle(ARect.Left,
ARect.top, ARect.right,
ARect.bottom);
//override the button canvas
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color:= FColor;
Canvas.FillRect(Frect);
//over ride the text
DrawtextEx(Gbtn.Canvas.Handle,
PChar(SelText),
Length(SelText),
txtRect,
TextFormat, nil);
if not gbtn.Glyph.Empty THEN Canvas.Rectangle (iconRect);
begin
Canvas.CopyRect(IconRect,Canvas,(GBtn.Glyph.Canvas.ClipRect));
Canvas.BrushCopy (IconRect, Glyph,(GBtn.Glyph.Canvas.ClipRect),(clBlue));
gbtn.Glyph.FreeImage;
end;
end;
end;
Procedure TGsXPButton.Paint;
var
Gbtn: TGsXPButton;
begin
//this grabs all the speedbuttons properties
inherited;
//protect our design environment
if not (csDesigning in ComponentState) then
begin
Gbtn:= TGsXPButton(Self);
If Gbtn.Active then
If Gbtn.Style = 0 then
begin
Style0(Self);
end
else
If Gbtn.Style = 1 then
begin
Style1(Self);
end;
end;
end;
//Begin Ok Button
//inherits from the GSXPButton. only caption changed
//same for cancel button.
//make mods to main button, changes are inherited.
constructor TGsXPOkButton.Create( AOwner : TComponent );
begin
// Don't forget to call the ancestor's constructor
inherited Create( AOwner );
Caption := '&OK';
end;
//Begin Cancel Button
constructor TGsXPCancelButton.Create( AOwner : TComponent );
begin
inherited Create( AOwner );
Caption := '&Cancel';
end;
procedure Register;
begin
RegisterComponents('GenieOSTools', [TGsXPButton, TGsXPOKButton, TGsXPCancelButton]);
end;
end.
|
Включить Drop Shadow Effect окна в XP.
| - Какая разница между вирусом и Windows XP? - Вирус стабильнее работает. |
type
TForm1 = class(TForm)
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.CreateParams(var Params: TCreateParams);
const
CS_DROPSHADOW = $00020000;
begin
inherited;
Params.WindowClass.Style := Params.WindowClass.Style or CS_DROPSHADOW;
end;
|
Использовать визуальный стиль XP для своего приложения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
To benefit from the new look and feel (Visual Styles) for the Windows XP
environment, you must include a Manifest in your application.
(Either as resource or as file in the same directory where your application
resides)
The manifest is a XML document. It will allow Windows XP to decide which
version of the comctl32.dll to use when binding.
The XML document contains information about the application you are
writing as well as information concerning the version of the comctl32.dll to use.
The following instruction shows how to
* create the manifest document
* create the XP resource file
* include the file in your application
The steps 1-4 show how to create the files.
You can also download the manifest and resource file from the
Demo-download.
}
{
Damit eine Anwendung das neue Look-and-Feel (Visual Styles) von Windows XP
annimmt, muss eine Manifest Datei in der Applikation enthalten sein.
(Entweder als Ressource oder im gleichen Verzeichnis, wo sich die Applikation
befindet)
Das Manifest ist ein XML Dokument.
Wenn Windows dieses in einer EXE Datei vorfindet, wird automatisch die Version 6
von comctl32.dll geladen und die Controls erscheinen im XP Design.
Die Folgende Anleitung zeigt, wie man
* das manifest Dokument erstellt
* die Ressourcen Datei erstellt
* die Ressource in die Anwendung einbindet.
Das Manifest und die Ressourcen Datei konnen auch uber das "Demo-Download" heruntergeladen
werden. Dann konnen die Schritte 1-4 ausgelassen werden.
}
{1)
Copy this sample manifest and paste it into notepad or any text editor.
Kopiere das Beispiel Manifest und fuge es in einen Texteditor ein (z.B Notepad}
<?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="X86"
name="CompanyName.ProductName.MyProgram"
type="win32"
/>
<description>Your Application Description</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
{2)
To customize to fit your application, replace "name" from assemblyIdentity and the
"description" string with your own data. Then save the file as WinXP.manifest
Andere im Manifest den "name" von assemblyIdentity und die
"description" mit eigenen Angaben. Speichere das Manifest als WinXP.manifest}
{3)
Create another file which contains instructions to include the
WinXP.manifest (XML) document.
The contents of the WinXP.rc looks like this:
Erstelle eine weitere Datei, welche die Instruktionen zur Erstellung
der Ressourcen Datei enthalt.
Der Inhalt von WinXP.rc schaut so aus:}
1 24 "WinXP.manifest"
{4)
Now we need to use Delphi''s resource compiler (brcc32.exe) to compile the WinXP.rc file.
Doing so will result in a WinXP.res
From the command line, type the following:
Compilire nun mit Borland''s Resource Compiler (brcc32.exe),
die Datei WinXP.rc. Es wird dann eine WinXP.res Datei erstellt.
Gibt im MS-DOS Prompt, im Verzeichnis wo sich WinXP.rc befindet, nun folgendes ein:}
brcc32 WinXP.rc
(*5)
Now include the resource in your application.
Include the following compiler directive:
immediately after {$R *.DFM}:
Nun muss noch eine Compiler Directive dem Sourcecode hinzugefugt werden.
Der Eintrag sollte unmittelbar nach der Form Directive {$R *.DFM} folgen,
so wie hier:*)
{$R WinXP.res}
{6)
Compile your application and run it!
Compiliere die Anwendung und starte sie!}
{7)
Test it if it runs correctly.
Note that some controls don''t adapt the new XP design such
as TGroupBox, TSpeedButton and some others.
If you use the TListView component with the view style of vsReport, have a look at this tip:
http://www.swissdelphicenter.ch/de/showcode.php?id=1117
Uberpruf nun, ob die Anwendung korrekt lauft. Einige Controls nehmen das neue XP Design
nicht an (TGroupBox, TSpeedButton und andere)
Bei der ListView mit vsReport Style muss dies beachtet werden:
http://www.swissdelphicenter.ch/de/showcode.php?id=1117
}
Показать символы для пароля в XP.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Set the font to Wingdings and then type on the
PasswordChar := l;
or ...
}
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Font.Name := 'Wingdings';
Edit1.PasswordChar := 'l';
end;
Создать Системную Точку Восстановления (XP).
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
If you haven't installed the Microsoft Scripting Control yet
(TScriptControl component),
get it from http://www.msdn.microsoft.com/scripting/
Once you've downloaded and run the installation, start Delphi and go to the
Component | Import ActiveX Control... menu.
Select "Microsoft Script Control 1.0" from the Listbox amd click "Install"
to install the component into Delphi's palette.
What you should end up with now is a TScriptControl component on your ActiveX tab.
Start a new application, and drop a TButton, and a TScriptControl onto the main form.
In the OnClick event of Button1, put the following code:
}
{
Wenn du das Microsoft Scripting Control noch nicht installiert hast
(Komponente TScriptControl), dann kannst du hier runterladen:
http://www.msdn.microsoft.com/scripting/
Nachdem Herunterladen und der Installation wahle in der Delphi IDE unter dem
Menu "Komponente" den Menupunkt "ActiveX importieren".
Dann in der Listbox "Microsoft Script Control 1.0" auswahlen und "Installieren..."
anklicken.
Nun sollte die TScriptControl Komponente unter dem ActiveX Register zu finden sein.
Plaziere nun eine TScriptControl Komponente und einen TButton auf einer Form und
schreibe im OnClick Ereignis des Buttons den folgenden Code:
}
procedure TForm1.Button1Click(Sender: TObject);
var
sr: OLEVAriant;
begin
ScriptControl1.Language := 'VBScript';
sr := ScriptControl1.Eval('getobject("winmgmts:\\.\root\default:Systemrestore")');
if sr.CreateRestorePoint('Automatic Restore Point', 0, 100) = 0 then
ShowMessage('New Restore Point successfully created.')
// Wiederherstellungspunkt erfolgreich erstellt
else
ShowMessage('Restore Point creation Failed!');
// Wiederherstellungspunkt Erstellung fehlgeschlagen.
end;
{*****************************************************}
{2. Using the SRSetRestorePoint() API from SrClient.dll}
// Translation from SRRestorePtAPI.h
const
// Type of Event
BEGIN_SYSTEM_CHANGE = 100;
END_SYSTEM_CHANGE = 101;
// Type of Restore Points
APPLICATION_INSTALL = 0;
CANCELLED_OPERATION = 13;
MAX_DESC = 64;
MIN_EVENT = 100;
// Restore point information
type
PRESTOREPTINFOA = ^_RESTOREPTINFOA;
_RESTOREPTINFOA = packed record
dwEventType: DWORD; // Type of Event - Begin or End
dwRestorePtType: DWORD; // Type of Restore Point - App install/uninstall
llSequenceNumber: INT64; // Sequence Number - 0 for begin
szDescription: array [0..MAX_DESC] of CHAR; // Description - Name of Application / Operation
end;
RESTOREPOINTINFO = _RESTOREPTINFOA;
PRESTOREPOINTINFOA = ^_RESTOREPTINFOA;
// Status returned by System Restore
PSMGRSTATUS = ^_SMGRSTATUS;
_SMGRSTATUS = packed record
nStatus: DWORD; // Status returned by State Manager Process
llSequenceNumber: INT64; // Sequence Number for the restore point
end;
STATEMGRSTATUS = _SMGRSTATUS;
PSTATEMGRSTATUS = ^_SMGRSTATUS;
function SRSetRestorePointA(pRestorePtSpec: PRESTOREPOINTINFOA; pSMgrStatus: PSTATEMGRSTATUS): Bool;
stdcall; external 'SrClient.dll' Name 'SRSetRestorePointA';
// Example how to create and cancel a previous restore point.
// Ref: http://tinyurl.com/78pv
procedure TForm1.Button1Click(Sender: TObject);
const
CR = #13#10;
var
RestorePtSpec: RESTOREPOINTINFO;
SMgrStatus: STATEMGRSTATUS;
begin
// Initialize the RESTOREPOINTINFO structure
RestorePtSpec.dwEventType := BEGIN_SYSTEM_CHANGE;
RestorePtSpec.dwRestorePtType := APPLICATION_INSTALL;
RestorePtSpec.llSequenceNumber := 0;
RestorePtSpec.szDescription := 'SAMPLE RESTORE POINT';
if (SRSetRestorePointA(@RestorePtSpec, @SMgrStatus)) then
begin
ShowMessage('Restore point set. Restore point data:' + CR+
'Sequence Number: ' + Format('%d', [SMgrStatus.llSequenceNumber]) + CR+
'Status: ' + Format('%u', [SMgrStatus.nStatus]));
// Restore Point Spec to cancel the previous restore point.
RestorePtSpec.dwEventType := END_SYSTEM_CHANGE;
RestorePtSpec.dwRestorePtType := CANCELLED_OPERATION;
RestorePtSpec.llSequenceNumber := SMgrStatus.llSequenceNumber;
// This is the sequence number returned by the previous call.
// Canceling the previous restore point
if (SRSetRestorePointA(@RestorePtSpec, @SMgrStatus)) then
ShowMessage('Restore point canceled. Restore point data:' + CR+
'Sequence Number: ' + Format('%d', [SMgrStatus.llSequenceNumber]) + CR+
'Status: ' + Format('%u', [SMgrStatus.nStatus]))
else
ShowMessage('Couldn''t cancel restore point.');
end
else
ShowMessage('Couldn''t set restore point.');
end;
end;
Преобразование даты и времени в строковый вид YYYYMMDDHHNNSS и обратно.
Автор: Mfender WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Преобразование даты-времени в строковый вид и обратно (yyyymmddhhnnss)
Иногда становится нужно хранить дату и время в виде yyyymmddhhnnss.
Так, по некоторым причинам, с ними порой легче общаться и сортировать.
Зависимости: Windows, StdCtrls, SysUtils
Автор: mfender, mfender@fromru.com, Майкоп
Copyright: mfender
Дата: 10 августа 2003 г.
***************************************************** }
function mfStringToDateTime(const mfDTStr: string): TDateTime;
//Возвращает значение TDateTime из входящей строки mfDTStr
//в формате YYYYMMDDHHMMSS
var
Safe: string;
begin
Safe := ShortDateFormat; //сохраняем формат даты
ShortDateFormat := 'dd.mm.yyyy hh:nn:ss'; //придаем произвольный вид
//формату даты-времени
mfStringToDateTime := StrToDateTime(Copy(mfDTStr, 7, 2) + '.' +
Copy(mfDTStr, 5, 2) + '.' +
Copy(mfDTStr, 1, 4) + ' ' +
Copy(mfDTStr, 9, 2) + ':' +
Copy(mfDTStr, 11, 2) + ':' +
Copy(mfDTStr, 13, 2));
//Преобразуем, собственно, части строки в соответствующие
//детали даты и времени
ShortDateFormat := Safe; //возвращаем дате первоначальный вид
end;
function mfDateTimeToString(const Date: TDateTime): string;
//Возвращает строку в формате YYYYMMDDHHNNSS из входящей DateTime
begin
mfDateTimeToString := FormatDateTime('yyyymmddhhnnss', Date); //No comments
end;
Технология dbExpress (статья).
Одной из проблем различных технологий доступа к данным, используемым в приложениях Delphi, является трудность распространения готовых приложений. Для BDE требуется отдельная установка, которая занимает порядка 15 Мбайт дискового пространства, а также специальная настройка псевдонимов (см. гл. 16). ADO предустановлена в операционной системе, но нуждается в настраиваемых провайдерах данных (см. гл. 19). При необходимости обновить версию ADO, дистрибутив вашего приложения "потяжелеет" более чем на 2 Мбайт.
Новая технология доступа к данным dbExpress обеспечивает взаимодействие приложения с серверами баз данных. Драйверы dbExpress используют для получения данных исключительно запросы SQL. При этом на клиентской стороне отсутствует кэширование данных, вследствие этого здесь применяются исключительно однонаправленные курсоры и отсутствует возможность прямого редактирования наборов данных.
Примечание.
Проблема редактирования данных в dbExpress может быть решена несколькими путями (см. ниже). Однако любые предлагаемые способы повышают затраты на программирование и снижают эффективность полученного кода.
Взамен этих (весьма существенных для построения полноценных приложений) неудобств разработчики получили легкий и быстрый механизм доступа к данным.
Для функционирования компонентов dbExpress необходим только один драйвер, который взаимодействует напрямую с клиентским программным обеспечением для выбранного сервера БД. В поставку входят драйверы для первых четырех из списка серверов баз данных:
DB2;
InterBase;
MySQL;
Oracle;
Microsoft SQL Server 2000.
Драйверы реализованы в виде динамических библиотек, а при необходимости могут быть прикомпилированы непосредственно к исполняемому файлу приложения. Поэтому проблема распространения совместно с приложением средств доступа к данным в случае с dbExpress снимается полностью. Естественно, на компьютере должно быть установлено клиентское ПО соответствующего SQL сервера.
Кроме того, технология dbExpress обеспечивает доступ к данным в кроссплатформенных приложениях для Windows и Linux, т. к. применяется и в Delphi и Kylix (см. гл. 4), а способы ее применения идентичны.
Таким образом, технология dbExpress является наилучшим решением для приложений, в которых необходим быстрый и необременительный просмотр данных серверов SQL. И вряд ли он подойдет для сложных клиент-серверных или многоуровневых приложений, обеспечивающих серьезную работу с данными.
Технология dbExpress представляет собой совокупность драйверов, компонентов, инкапсулирующих соединения, транзакции, запросы и наборы данных, а также интерфейсов, обеспечивающих универсальный доступ к функциям dbExpress.
Компоненты dbExpress располагаются в Палитре компонентов на одноименной странице.
В этой главе рассматриваются следующие вопросы:
Настройка соединений с различными серверами баз данных, подключение драйверов и установка их параметров;
Способы использования компонентов dbExpress для просмотра данных и создание пользовательского интерфейса приложений;
Программная реализация редактирования данных;
Работа с данными в режиме кэширования изменений и использование компонента TSimpleDataSet;
Использование интерфейсов;
Распространение приложений с интегрированной технологией dbExpress.
©Drkb::02685.
| }{0TT@БЬIЧ |
Автор: Александр
Code: Unit Stilizator; Interface. Uses. Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, XPMan; Type. TForm1 = class(TForm). MemoSource: TMemo; MemoDest: TMemo; Button: TButton; XPManifest1: TXPManifest; Procedure ButtonClick(Sender: TObject); Procedure FormCreate(Sender: TObject); Private. { Private declarations }. Public. Procedure ConvertText(Letter: String); End; Var. Form1: TForm1; RS : Array [0..65] of String; PRS : Array [0..65] of String; ES : Array [0..51] of String; PPS : Array [0..51] of String; Implementation. Uses AboutUnit; {$R *.DFM}. Procedure TForm1.FormCreate(Sender: TObject); Begin. RS[0]:='а'; PRS[0]:='@'; RS[1]:='б'; PRS[1]:='6'; RS[2]:='в'; PRS[2]:='B'; RS[3]:='г'; PRS[3]:='r'; RS[4]:='д'; PRS[4]:='g'; RS[5]:='е'; PRS[5]:='e'; RS[6]:='ё'; PRS[6]:='e'; RS[7]:='ж'; PRS[7]:='}|{'; RS[8]:='з'; PRS[8]:='3'; RS[9]:='и'; PRS[9]:='u'; RS[10]:='й'; PRS[10]:='u'; RS[11]:='к'; PRS[11]:='k'; RS[12]:='л'; PRS[12]:='JI'; RS[13]:='м'; PRS[13]:='M'; RS[14]:='н'; PRS[14]:='H'; RS[15]:='о'; PRS[15]:='o'; RS[16]:='п'; PRS[16]:='n'; RS[17]:='р'; PRS[17]:='p'; RS[18]:='с'; PRS[18]:='c'; RS[19]:='т'; PRS[19]:='T'; RS[20]:='у'; PRS[20]:='y'; RS[21]:='ф'; PRS[21]:='%'; RS[22]:='х'; PRS[22]:='x'; RS[23]:='ц'; PRS[23]:='?'; RS[24]:='ч'; PRS[24]:='4'; RS[25]:='ш'; PRS[25]:='III'; RS[26]:='щ'; PRS[26]:='III,'; RS[27]:='ъ'; PRS[27]:='b'; RS[28]:='ы'; PRS[28]:='bI'; RS[29]:='ь'; PRS[29]:='b'; RS[30]:='э'; PRS[30]:='&'; RS[31]:='ю'; PRS[31]:='I-o'; RS[32]:='я'; PRS[32]:='9I'; RS[33]:='А'; PRS[33]:='A'; RS[34]:='Б'; PRS[34]:='6'; RS[35]:='В'; PRS[35]:='B'; RS[36]:='Г'; PRS[36]:='r'; RS[37]:='Д'; PRS[37]:='g'; RS[38]:='Е'; PRS[38]:='E'; RS[39]:='Ё'; PRS[39]:='E'; RS[40]:='Ж'; PRS[40]:='}|{'; RS[41]:='З'; PRS[41]:='3'; RS[42]:='И'; PRS[42]:='U'; RS[43]:='Й'; PRS[43]:='U'; RS[44]:='К'; PRS[44]:='K'; RS[45]:='Л'; PRS[45]:='JL'; RS[46]:='М'; PRS[46]:='M'; RS[47]:='Н'; PRS[47]:='H'; RS[48]:='О'; PRS[48]:='O'; RS[49]:='П'; PRS[49]:='n'; RS[50]:='Р'; PRS[50]:='p'; RS[51]:='С'; PRS[51]:='c'; RS[52]:='Т'; PRS[52]:='T'; RS[53]:='У'; PRS[53]:='Y'; RS[54]:='Ф'; PRS[54]:='%'; RS[55]:='Х'; PRS[55]:='X'; RS[56]:='Ц'; PRS[56]:='?'; RS[57]:='Ч'; PRS[57]:='4'; RS[58]:='Ш'; PRS[58]:='III'; RS[59]:='Щ'; PRS[59]:='III,'; RS[60]:='Ъ'; PRS[60]:='b'; RS[61]:='Ы'; PRS[61]:='bI'; RS[62]:='Ь'; PRS[62]:='b'; RS[63]:='Э'; PRS[63]:='&'; RS[64]:='Ю'; PRS[64]:='I-O'; RS[65]:='Я'; PRS[65]:='9I'; ES[0]:='a'; PPS[0]:='@'; ES[1]:='b'; PPS[1]:='6'; ES[2]:='c'; PPS[2]:='|_|,'; ES[3]:='d'; PPS[3]:=',^,'; ES[4]:='e'; PPS[4]:='e'; ES[5]:='f'; PPS[5]:='%'; ES[6]:='g'; PPS[6]:='|"'; ES[7]:='h'; PPS[7]:=']['; ES[8]:='i'; PPS[8]:='|/|'; ES[9]:='j'; PPS[9]:='>|<'; ES[10]:='k'; PPS[10]:='K'; ES[11]:='l'; PPS[11]:='/\'; ES[12]:='m'; PPS[12]:='M'; ES[13]:='n'; PPS[13]:='|-|'; ES[14]:='o'; PPS[14]:='0'; ES[15]:='p'; PPS[15]:='n'; ES[16]:='q'; PPS[16]:='k|3'; ES[17]:='r'; PPS[17]:='P'; ES[18]:='s'; PPS[18]:='$'; ES[19]:='t'; PPS[19]:='T'; ES[20]:='u'; PPS[20]:='Y'; ES[21]:='v'; PPS[21]:='\/'; ES[22]:='w'; PPS[22]:='|3'; ES[23]:='x'; PPS[23]:='kc'; ES[24]:='y'; PPS[24]:='9'; ES[25]:='z'; PPS[25]:='3'; ES[26]:='A'; PPS[26]:='@'; ES[27]:='B'; PPS[27]:='6'; ES[28]:='C'; PPS[28]:='|_|,'; ES[29]:='D'; PPS[29]:=',^,'; ES[30]:='E'; PPS[30]:='E'; ES[31]:='F'; PPS[31]:='%'; ES[32]:='G'; PPS[32]:='|"'; ES[33]:='H'; PPS[33]:='}{'; ES[34]:='I'; PPS[34]:='|/|'; ES[35]:='J'; PPS[35]:='>|<'; ES[36]:='K'; PPS[36]:='K'; ES[37]:='L'; PPS[37]:='/\'; ES[38]:='M'; PPS[38]:='M'; ES[39]:='N'; PPS[39]:='|-|'; ES[40]:='O'; PPS[40]:='0'; ES[41]:='P'; PPS[41]:='n'; ES[42]:='Q'; PPS[42]:='K|3'; ES[43]:='R'; PPS[43]:='P'; ES[44]:='S'; PPS[44]:='$'; ES[45]:='T'; PPS[45]:='T'; ES[46]:='U'; PPS[46]:='Y'; ES[47]:='V'; PPS[47]:='\/'; ES[48]:='W'; PPS[48]:='|3'; ES[49]:='X'; PPS[49]:=']['; ES[50]:='Y'; PPS[50]:='9'; ES[51]:='Z'; PPS[51]:='3'; End; Procedure TForm1.ButtonClick(Sender: TObject); Var. I : Integer; Letter : String; Begin. MemoDest.Clear; For i := 0 to MemoSource.GetTextLen - 1 do begin. MemoSource.SelStart := i; MemoSource.SelLength := 1; Letter := MemoSource.SelText; ConvertText(Letter); Sleep(5). End; MemoSource.SelLength := 0; End; Procedure TForm1.ConvertText(Letter: String); Var. I : Integer; Begin. For i := 0 to 65 do begin. If Letter = RS[i] then. Letter := PRS[i]; End; For i := 0 to 51 do begin. If Letter = ES[i] then. Letter := PPS[i]; End; MemoDest.Text := MemoDest.Text + Letter; End; End.
©Drkb::04654.
DelphiWorld 6.0.
Драйверы доступа к данным.
Технология dbExpress обеспечивает доступ к серверу баз данных при помощи драйвера, реализованного как динамическая библиотека. Для каждого сервера имеется своя динамическая библиотека.
Драйверы dbExpress.
Сервер БД Драйвер Клиентское ПО.
DB2 Dbexpdb2.dll Db2cli.dll.
InterBase Dbexpint.dll GDS32.DLL.
Informix Dbexpinf.dll Isqlb09a.dll.
MS SQL Server Dbexpmss.dll OLE DB.
MySQL Dbexpmys.dll LIBMYSQL.DLL.
Oracle Dbexpora.dll OCI.DLL.
Перечисленные в табл. файлы находятся в папке \Delphi7\Bin.
Для доступа к данным сервера драйвер должен быть установлен на компьютере клиента. Для доступа к данным драйвер взаимодействует с клиентским ПО сервера, которое также должно быть инсталлировано на клиентской стороне.
Стандартные настройки для каждого драйвера хранятся в файле \Borland Shared\DBExpress\dbxdrivers.ini.
Отладка приложений с технологией dbExpress.
Наряду с обычными методами отладки исходного кода, в dbExpress существует возможность контроля запросов, проходящих на сервер через соединение. Для этого используется компонент TSQLMonitor.
Через свойство.
Property SQLConnection: TSQLConnection;
Компонент связывается с отлаживаемым соединением. Затем компонент включается установкой Active = True.
Теперь во время выполнения приложения сразу после открытия соединения свойство.
Property TraceList: TStrings;
Будет заполняться информацией обо всех проходящих командах. Содержимое этого списка можно сохранить в файле при помощи метода.
Procedure SaveToFile(AFileName: string);
Эту же информацию можно автоматически добавлять в текстовый файл, определяемый свойством.
Property FileName: string;
Но только тогда, когда свойство.
Property AutoSave: Boolean;
Будет иметь значение True. Свойство.
Property MaxTraceCount: Integer;
Определяет максимальное число контролируемых команд, а также управляет процессом контроля. При значении -1 ограничения снимаются, а при значении 0 контроль останавливается. Текущее число проверенных команд содержится в свойстве.
Property TraceCount: Integer;
Перед записью команды в список вызывается метод-обработчик.
TTraceEvent = procedure(Sender: TObject; CBInfo: pSQLTRACEDesc; var LogTrace: Boolean) of object;
Property OnTrace: TTraceEvent;
А сразу после записи в список вызывается.
TTraceLogEvent = procedure (Sender: TObject; CBInfo: pSQLTRACEDesc) of object;
Property OnLogTrace: TTraceLogEvent;
Таким образом, разработчик получает компактный и симпатичный компонент, позволяющий без усилий получать информацию о прохождении команд в соединении.
Если же компонент TSQLMonitor не подходит, можно воспользоваться методом.
Procedure SetTraceCallbackEvent(Event: TSQLCallbackEvent; IClientlnfo: Integer);
Компонента TSQLConnection. Параметр процедурного типа Event определяет функцию, которая будет вызываться при выполнении каждой команды. Параметр iclientinfo должен содержать любое число.
Он позволяет разработчику самостоятельно определить функцию типа.
TSQLCallbackEvent:
TRACECat = TypedEnum;
TSQLCallbackEvent = function(CallType: TRACECat; CBInfo: Pointer): CBRType; stdcall;
Эта функция будет вызываться каждый раз при прохождении команды. Текст команды будет передаваться в буфер CBInfo. Разработчику необходимо лишь выполнить запланированные действия с буфером внутри функции.
Рассмотрим в качестве примера следующий исходный код.
Function GеtТгасе1nfо(CallType: TRACECat; CBInfo: Pointer): CBRType;stdcall;
Begin.
If Assigned(Forml.TraceList) then Forml.TraceList.Add(pChar(CBinfo));
End;
Procedure TForml.MyConnectionBeforeConnect(Sender: TObject);
Begin.
TraceList := TStringList.Create;
End;
Procedure TForml.MyConnectionAfterDisconnect(Sender: TObject);
Begin.
If Assigned(TraceList) then.
Begin.
TraceList.SaveToFile('с:\Temp\TraceInfo.txt');
TraceList.Free;
End;
End;
Procedure TForml.StartBtnClick(Sender: TObject);
Begin.
MyConnection.SetTraceCallbackEvent(GetTracelnfo, 8);
MyConnection.Open;
{...}
MyConnection.Close;
End;
Перед открытием соединения в методе-обработчике BeforeConnection создается объект типа TStringList. После закрытия соединения этот объект сохраняется в файле и уничтожается.
Перед открытием соединения (метод-обработчик нажатия кнопки Start) при помощи метода SetTraceCallbackEvent с соединением связывается функция GetTracelnfo.
Таким образом, по мере прохождения команд информация о них будет накапливаться в списке. После закрытия соединения список сохраняется в текстовом файле.
Примечание.
В своей работе компонент TSQLMonitor также использует вызовы метода SetTraceCallbackEvent. Поэтому одновременно применять компонент и собственные функции нельзя.
| Программирование серверов на основе сокетов в Дельфи. |
Данная статья посвящена созданию приложений архитектуры клиент/сервер в Borland Delphi на основе сокетов ("sockets" - гнезда). В отличие от предыдущей статьи на тему сокетов, здесь мы разберем создание серверных приложений.
Следует сразу заметить, что для сосуществования отдельных приложений клиента и сервера не обязательно иметь несколько компьютеров. Достаточно иметь лишь один, на котором Вы одновременно запустите и сервер, и клиент. При этом нужно в качестве имени компьютера, к которому надо подключиться, использовать хост-имя localhost или IP-адрес - 127.0.0.1.
Итак, начнем с теории. Если Вы убежденный практик (и в глаза не можете видеть всяких алгоритмов), то Вам следует пропустить этот раздел.
Алгоритм работы сокетного сервера.
Что же позволяет делать сокетный сервер?.. По какому принципу он работает?.. Сервер, основанный на сокетном протоколе, позволяет обслуживать сразу множество клиентов. Причем, ограничение на их количество Вы можете указать сами (или вообще убрать это ограничение, как это сделано по умолчанию). Для каждого подключенного клиента сервер открывает отдельный сокет, по которому Вы можете обмениваться данными с клиентом. Также отличным решением является создание для каждого подключения отдельного процесса (Thread).
Ниже следует примерная схема работы сокетного сервера в Дельфи-приложениях:









Разберем схему подробнее:
· Определение св-в Port и ServerType - чтобы к серверу могли нормально подключаться клиенты, нужно, чтобы порт, используемый сервером точно совпадал с портом, используемым клиентом (и наоборот). Свойство ServerType определяет тип подключения (подробнее см.ниже);
· Открытие сокета - открытие сокета и указанного порта. Здесь выполняется автоматическое начало ожидания подсоединения клиентов (Listen);
· Подключение клиента и обмен данными с ним - здесь подключается клиент и идет обмен данными с ним. Подробней об этом этапе можно узнать ниже в этой статье и в статье про сокеты (клиентская часть);
· Отключение клиента - Здесь клиент отключается и закрывается его сокетное соединение с сервером;
· Закрытие сервера и сокета - По команде администратора сервер завершает свою работу, закрывая все открытые сокетные каналы и прекращая ожидание подключений клиентов.
Следует заметить, что пункты 3-4 повторяются многократно, т.е. эти пункты выполняются для каждого нового подключения клиента.
Примечание: Документации по сокетам в Дельфи на данный момент очень мало, так что, если Вы хотите максимально глубоко изучить эту тему, то советую просмотреть литературу и электронную документацию по Unix/Linux-системам - там очень хорошо описана теория работы с сокетами. Кроме того, для этих ОС есть множество примеров сокетных приложений (правда, в основном на C/C++ и Perl).
Краткое описание компонента TServerSocket.
Здесь мы познакомимся с основными свойствами, методами и событиями компонента.
Свойства.
Socket - класс TServerWinSocket, через который Вы имеете доступ к открытым сокетным каналам. Далее мы рассмотрим это свойство более подробно, т.к. оно, собственно и есть одно из главных. Тип: TServerWinSocket;
ServerType - тип сервера. Может принимать одно из двух значений: stNonBlocking - синхронная работа с клиентскими сокетами. При таком типе сервера Вы можете работать с клиентами через события OnClientRead и OnClientWrite. stThreadBlocking - асинхронный тип. Для каждого клиентского сокетного канала создается отдельный процесс (Thread). Тип: TServerType;
ThreadCacheSize - количество клиентских процессов (Thread), которые будут кэшироваться сервером. Здесь необходимо подбирать среднее значение в зависимости от загруженности Вашего сервера. Кэширование происходит для того, чтобы не создавать каждый раз отдельный процесс и не убивать закрытый сокет, а оставить их для дальнейшего использования. Тип: Integer;
Active - показатель того, активен в данных момент сервер, или нет. Т.е., фактически, значение True указывает на то, что сервер работает и готов к приему клиентов, а False - сервер выключен. Чтобы запустить сервер, нужно просто присвоить этому свойству значение True. Тип: Boolean;
Port - номер порта для установления соединений с клиентами. Порт у сервера и у клиентов должны быть одинаковыми. Рекомендуются значения от 1025 до 65535, т.к. от 1 до 1024 - могут быть заняты системой. Тип: Integer;
Service - строка, определяющая службу (ftp, http, pop, и т.д.), порт которой будет использован. Это своеобразный справочник соответствия номеров портов различным стандартным протоколам. Тип: string;
Методы.
Open - Запускает сервер. По сути, эта команда идентична присвоению значения True свойству Active;
Close - Останавливает сервер. По сути, эта команда идентична присвоению значения False свойству Active.
События.
OnClientConnect - возникает, когда клиент установил сокетное соединение и ждет ответа сервера (OnAccept);
OnClientDisconnect - возникает, когда клиент отсоединился от сокетного канала;
OnClientError - возникает, когда текущая операция завершилась неудачно, т.е. произошла ошибка;
OnClientRead - возникает, когда клиент передал берверу какие-либо данные. Доступ к этим данным можно получить через пеаедаваемый параметр Socket: TCustomWinSocket;
OnClientWrite - возникает, когда сервер может отправлять данные клиенту по сокету;
OnGetSocket - в обработчике этого события Вы можете отредактировать параметр ClientSocket;
OnGetThread - в обработчике этого события Вы можете определить уникальный процесс (Thread) для каждого отдельного клиентского канала, присвоив параметру SocketThread нужную подзадачу TServerClientThread;
OnThreadStart, OnThreadEnd - возникает, когда подзадача (процесс, Thread) запускается или останавливается, соответственно;
OnAccept - возникает, когда сервер принимает клиента или отказывает ему в соединении;
OnListen - возникает, когда сервер переходит в режим ожидания подсоединения клиентов.
TServerSocket.Socket (TServerWinSocket).
Итак, как же сервер может отсылать данные клиенту? А принимать данные? В основном, если Вы работаете через события OnClientRead и OnClientWrite, то общаться с клиентом можно через параметр ClientSocket (TCustomWinSocket). Про работу с этим классом можно прочитать в статье про клиентские сокеты, т.к. отправка/посылка данных через этот класс аналогична - методы (Send/Receive)(Text,Buffer,Stream). Также и при работе с TServerSocket.Socket. Однако, т.к. здесь мы рассматриваем сервер, то следует выделить некоторые полезные свойства и методы:
· ActiveConnections (Integer) - количество подключенных клиентов;
· ActiveThreads (Intеgег) - количество работающих процессов;
· Connections (array) - массив, состоящий из отдельных классов TClientWinSocket для каждого подключенного клиента. Например, такая команда:
· ServerSocket1.Socket.Connections[0].SendText('Hello!');
· отсылает первому подключенному клиенту сообщение 'Hello!'. Команды для работы с элементами этого массива - также (Send/Receive)(Text,Buffer, Stream);
· IdleThreads (Integer) - количество свободных процессов. Такие процессы кэшируются сервером (см. ThreadCacheSize);
· LocalAddress, LocalHost, LocalPort - соответственно - локальный IP-адрес, хост-имя, порт;
· RemoteAddress, RemoteHost, RemotePort - соответственно - удаленный IP-адрес, хост-имя, порт;
· Методы Lock и UnLock - соответственно, блокировка и разблокировка сокета.
Практика и примеры.
А теперь рассмотрим вышеприведенное на конкретном примере. Скачать уже готовые исходники можно, щелкнув здесь.
Итак, рассмотрим очень неплохой пример работы с TServerSocket (этот пример - наиболее наглядное пособие для изучения этого компонента). В приведенных ниже исходниках демонстрируется протоколирование всех важных событий сервера, плюс возможность принимать и отсылать текстовые сообщения:
Пример 1. Протоколирование и изучение работы сервера, посылка/прием сообщений через сокеты.
Code: {... Здесь идет заголовок файла и определение формы TForm1 и ее экземпляра Form1}. {Полный исходник смотри здесь}. Procedure TForm1.Button1Click(Sender: TObject); Begin. {Определяем порт и запускаем сервер}. ServerSocket1.Port := 1025; {Метод Insert вставляет строку в массив в указанную позицию}. Memo2.Lines.Insert(0,'Server starting'); ServerSocket1.Open; End; Procedure TForm1.Button2Click(Sender: TObject); Begin. {Останавливаем сервер}. ServerSocket1.Active := False; Memo2.Lines.Insert(0,'Server stopped'); End; Procedure TForm1.ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket); Begin. {Здесь сервер "прослушивает" сокет на наличие клиентов}. Memo2.Lines.Insert(0,'Listening on port '+IntToStr(ServerSocket1.Port)); End; Procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket); Begin. {Здесь сервер принимает клиента}. Memo2.Lines.Insert(0,'Client connection accepted'); End; Procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); Begin. {Здесь клиент подсоединяется}. Memo2.Lines.Insert(0,'Client connected'); End; Procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); Begin. {Здесь клиент отсоединяется}. Memo2.Lines.Insert(0,'Client disconnected'); End; Procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; Var ErrorCode: Integer); Begin. {Произошла ошибка - выводим ее код}. Memo2.Lines.Insert(0,'Client error. Code = '+IntToStr(ErrorCode)); End; Procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); Begin. {От клиента получено сообщение - выводим его в Memo1}. Memo2.Lines.Insert(0,'Message received from client'); Memo1.Lines.Insert(0,'> '+Socket.ReceiveText); End; Procedure TForm1.ServerSocket1ClientWrite(Sender: TObject; Socket: TCustomWinSocket); Begin. {Теперь можно слать данные в сокет}. Memo2.Lines.Insert(0,'Now can write to socket'); End; Procedure TForm1.ServerSocket1GetSocket(Sender: TObject; Socket: Integer; Var ClientSocket: TServerClientWinSocket); Begin. Memo2.Lines.Insert(0,'Get socket'); End; Procedure TForm1.ServerSocket1GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket; Var SocketThread: TServerClientThread); Begin. Memo2.Lines.Insert(0,'Get Thread'); End; Procedure TForm1.ServerSocket1ThreadEnd(Sender: TObject; Thread: TServerClientThread); Begin. Memo2.Lines.Insert(0,'Thread end'); End; Procedure TForm1.ServerSocket1ThreadStart(Sender: TObject; Thread: TServerClientThread); Begin. Memo2.Lines.Insert(0,'Thread start'); End; Procedure TForm1.Button3Click(Sender: TObject); Var i: Integer; Begin. {Посылаем ВСЕМ клиентам сообщение из Edit1}. For i := 0 to ServerSocket1.Socket.ActiveConnections-1 do begin. ServerSocket1.Socket.Connections[i].SendText(Edit1.Text); End; Memo1.Lines.Insert(0,'< '+Edit1.Text); End;
| Примеры использования Drag and Drop для различных визуальных компонентов. |
Перетаскивание информации с помощью мыши стало стандартом для программ, работающих в Windows. Часто это бывает удобно и позволяет добиться более быстрой работы. В данной статье я постарался показать максимальное количество примеров использования данной технологии при разработке приложений в среде Delphi. Конечно, результат может быть достигнут различными путями, продемонстрированные приемы не являются единственными и, возможно, не всегда самые оптимальные, но вполне работоспособны, и указывают направление поиска. Надеюсь, что они побудят начинающих программистов к более широкому использованию Drag'n'Drop в своих программах, тем более что пользователи, особенно неопытные, быстро привыкают к перетаскивание и часто его применяют.
Проще всего делать Drag из тех компонентов, для которых однозначно ясно, что именно перетаскивать. Для этого устанавливаем у источника DragMode = dmAutomatic, а у приемника пишем обработчики событий OnDragOver - разрешение на прием, и OnDragDrop - действия, производимые при окончании перетаскивания.
Code: Procedure TForm1.StringGrid2DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); Begin. Accept := Source = Edit1; // разрешено перетаскивание только из Edit1, // при работе программы меняется курсор. End; Procedure TForm1.StringGrid2DragDrop(Sender, Source: TObject; X, Y: Integer); Var. ACol, ARow: Integer; Begin. StringGrid2.MouseToCell( X, Y, ACol, ARow); // находим, над какой ячейкой произвели Drop. StringGrid2.Cells[ Acol, Arow] := Edit1.Text; // записываем в нее содержимое Edit1. End;
| Delphi и Flash. |
Delphi и Flash. Совмещение несовместимого!
Разве возможно совместить Флэш-ролики и Дельфи-приложения. Раньше я думал что НЕТ. Но теперь я знаю не только, что это возможно, но и знаю как это делается!!! И сейчас я вам расскажу об этом. Во-первых хочется отметить преимущества использования флэш-роликов в ваших программах. Если вы сумеете гармонично вписать небольшой флэш-ролик в вашу программу, то несомненно внешний вид программы будет намного привлекательнее (главное не переборщить, увлекаясь дизайном, не надо забывать о том что программа должна быть удобна и проста в использовании! ).
Итак, как же совместить Флэш и Дельфи? (Надеюсь, что у вас Флэш установлен:)).
Запустите Дельфи и выберите пункт меню Component->Import ActiveX Control... Перед вами откроется диалоговое окно с заголовком Import ActiveX Control. В разделе Registered Controls выберите Shockwave Flash. В разделе Pallete Page... Выберите страницу в палитре компонентов, на которой будет располагаться установленный компонент (по умолчанию это ActiveX). В разделе Unit Dir Name... путь к папке куда будет установлен компонент.
Нажмите на кнопку Install. Перед вами появится окно, в котором вам нужно будет выбрать в какой пакет будет установлен компонент (вы можете установить как в уже существующий, так и в новый пакет). Затем перед вами появится окно редактирования выбранного пакета и Дельфи вас спросит: "...Package will be rebuilt. Continue?". Ответьте Yes. Все готово теперь можно использовать флэш в ваших приложениях!!!
Теперь, чтобы показать вам как пользоваться этим компонентом, попробуем вместе сделать программу для просмотра *.SWF файлов. Для этого нам понадобятся следующие компоненты: TShockwaveFlash (для удобства назовите его просто Flash1), TTrackBar, TTimer, TOpendialog и три кнопки TButton ("открыть", "старт" и "стоп").
Для начала установим необходимые свойства OpenDialog'a.
Свойство Filter может быть таким: Флэш-ролики|*.swf.
Свойство DefaultExt должно быть: *.swf.
Для Timer'a нужно установить свойство Interval равным 1.
Для TShockwaveFlash:
Name сделайте равным Flash1.
Свойство Playing установите в false.
Свойство BGColor, установите как вам хочется (цвет фона).
Теперь напишем обработчик события OnClick для кнопки, которая вызывать OpenDialog:
If open1.Execute then begin.
Flash1.Movie:=open1.FileName;
Trackbar1.Max:=flash1.TotalFrames; {это делается для того, чтобы потом можно было перемещаю ползунок посмотреть каждый кадр ролика}.
В обработчик события OnClick для второй кнопки ("Старт") напишем:
Flash1.Play;
Ну тут вообще все просто! Почти таким же образом это будет выглядеть для третьей кнопки ("Стоп"):
Flash1.Stop;
Теперь сделаем, чтобы при перемещении ползунка Trackbar'a мы могли посмотреть каждый кадр (событие OnChange):
If Flash1.IsPlaying=true then Flash1.Stop; {если ролик проигрывается, то надо его остановить}.
Flash1.GotoFrame(trackbar1.position); {открываем кадр номер которого соответствует позиции ползунка}.
Ну и наконец осталось сделать чтобы при проигрывании ролика ползунок перемещался, указывая сколько осталось и сколько прошло. Для этого то мы и используем Timer. В обработчик события OnTimer,напишем:
Trackbar1.Position:=flash1.CurrentFrame;
Приведу полный код приложения:
Code: Unit flash; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, OleCtrls, ShockwaveFlashObjects_TLB, ExtCtrls; Type. TForm1 = class(TForm). Flash1: TShockwaveFlash; Button1: TButton; TrackBar1: TTrackBar; Open1: TOpenDialog; Button2: TButton; Button3: TButton; Timer1: TTimer; Procedure Button1Click(Sender: TObject); Procedure Button2Click(Sender: TObject); Procedure Button3Click(Sender: TObject); Procedure TrackBar1Change(Sender: TObject); Procedure Timer1Timer(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.Button1Click(Sender: TObject); Begin. If open1.Execute then. Begin. Flash1.Movie := open1.FileName; Trackbar1.Max := flash1.TotalFrames; End; End; Procedure TForm1.Button2Click(Sender: TObject); Begin. Flash1.Play; End; Procedure TForm1.TrackBar1Change(Sender: TObject); Begin. If Flash1.IsPlaying = true then Flash1.Stop; Flash1.GotoFrame(trackbar1.position); End; Procedure TForm1.Button3Click(Sender: TObject); Begin. Flash1.Stop; End; Procedure TForm1.Timer1Timer(Sender: TObject); Begin. Trackbar1.Position := flash1.CurrentFrame; End; End.
| Принцип создания плугинов в Delphi. |
Иногда нужные мысли приходят после того, как программа сдана заказчику. Для этого придумали плугины. Плугины - это простая dll библиотека, в которой обязательно присутствует ряд процедур и функций, которые выполняют определенные разработчиком действия, например (из моей практики) : function PluginType : PChar; - функция, определяющая назначение плугина. function PluginName : PChar; - функция, которая возвращает название плугина. Это название будет отоброжаться в меню. function PluginExec(AObject:ТТип) : boolean; - главный обработчик, выполняет определённые действия и возвращает TRUE; и ещё, я делал res файл с небольшим битмапом и компилировал его вместе с плугином, который отображался в меню соответствующего плугина. Откомпилировать res файл можно так: 1. создайте файл с расширением *.rc 2. напишите в нём : bitmap RCDATA LOADONCALL 1.bmp где bitmap - это идентификатор ресурса RCDATA LOADONCALL - тип и параметр 1.bmp - имя локального файла для кампиляций 3. откомпилируйте этот файл программой brcc32.exe, лежащей в папке ...\Delphi5\BIN\ .
Загрузка плугина.
Перейдём к теоретической части.
Раз плугин это dll значит её можно подгрузить следующими способами:
1. Прищипыванием её к программе!
Function PluginType : PChar; external 'myplg.dll';
//в таком случае dll должна обязательно лежать возле exe и мы не можем передать.
Туда конкретное имя! не делать же все плугины одного имени! это нам не подходит.
Программа просто не загрузится без этого файла! Выдаст сообщение об ошибке. Этот способ.
Может подойти для поддержки обновления вашей программы!
2. Динамический.
Это оэночает, что мы грузим её так, как нам надо! Вот пример:
Code: Var. PluginType: function: PChar; // объявляем процедурный тип функции из плугина. PlugHandle: THandle; //объявляем переменную типа хендл в которую мы занесём хендл плугина. Procedure Button1Click(Sender: TObject); Begin. PlugHandle := LoadLibrary('MYplg.DLL'); //грузим плугин. If PlugHandle <> 0 then //Получилось или нет?? Begin. @PluginType := GetProcAddress(plugHandle,'Plugintype'); // ищем функцию в dll. If @PluginType <> nil then ShowMessage(PluginType); //вызываем функцию. End; FreeLibrary(LibHandle);//освобождаем библиотеку. End;
Библиотека KOL.
KOL - кодоэкономичная объектная библиотека для Delphi.
Кладов В.Л.
Цель данной статьи - убедить читателя (я надеюсь, этот текст попадет в руки программиста), привыкшего к большим размерам современных программ (о, нет, приложений, программы-то как раз были еще не очень большими) в том, что его бессовестно надувают. Когда утверждают, что программа для среды Windows, если она что-то полезное умеет делать, никак не может быть меньше... ну, скажем, трехсот килобайт. А если это очень "умная" программа, содержащая очень много полезных возможностей, хороший интерфейс, отлично взаимодействующая с пользователем, поддерживает различные форматы данных, современные клиент-серверные технологии, то без полсотни мегабайт ну никак не обойтись. Чушь несусветная. Нас обманывают!
На самом деле, объектное программирование позволяет создавать очень экономичный по размеру код. Причем, достаточно эффективный. Примеры? Пожалуйства. Ява - объектно-ориентированное программирование. Ява-апплеты очень невелики по своим размерам, а как много полезного они умеют делать! Впрочем, речь пойдет не о Яве. Предметом данного разговора будет среда Delphi.
Как ни странно, именно Delphi оказался тем инструментом, с помощью которого оказалось возможным изготовить библиотеку KOL - Key Objects Library (Ключевую Объектную Библиотеку). Странно потому, может быть, что программы, изготовленные средствами Delphi, обычно маленькими не бывают. Минимальный стартовый размер приложения, представляющего из себя одно пустое окно, которое можно подвигать по экрану и закрыть, и которое, собственно, ничего больше делать не умеет, составляет около трехсот килобайт. Причем, с выпуском каждой очередной версии Delphi этот стартовый размер вырастает еще на несколько десятков ни в чем неповинных килобайт.
Библиотека KOL позволяет изготавливать не менее мощные приложения, чем стандартная библиотека Delphi - VCL (Visual Component Library, Визуальная Библиотека Компонентов). И при этом добиваться уменьшения размеров программ в 5-15 раз! Например, приложение DirComp, доступное для загрузки на сайте KOL, занимает без сжатия упаковывающими программами около 65 килобайт. Аналогичное приложение, написанное за два года до этого с использованием стандартной библиотеки Delphi, занимало 750 килобайт. Разница впечатляет, не правда ли?
KOL - не только объектно-ориентированная, но и визуальная библиотека. Программы и их графический интерфейс возможно проектировать практически так же, как и в визуальной среде VCL. В дополнение к KOL идет библиотека MCK (Mirror Classes Kit, Библиотека Зеркальных Классов), которая содержит VCL-компоненты, устанавливающиеся на палитру обычным образом. Единственное отличие в том, что зеркальные компоненты библиотеки MCK существуют только на стадии разработки (design time), участвуя в генерации "настоящего" кода, совместимого с требованиями библиотеки KOL. Во время работы (run time) выполняется этот код, и тот, который был добавлен самим разработчиком. В коде времени исполнения нет ссылок на компоненты VCL, есть только объекты KOL, компактные и эффективные.
В чем же заключается секрет компактности кода? Ответ не один, но выделить главные составляющие все же представляется возможным. В первую очередь следует отметить способность компилятора Delphi не включать в код конечного приложения невостребованный код. Процедуры и переменные, на которые нет ссылок из того кода, который уже внесен в список участков кода, подлежащих включению в конечный продукт, отбрасываются и в дальнейшей сборке не учавствуют. К сожалению, данная способность компилятора Delphi, называемая самими разработчиками компилятора "smart linking" (умное связывание), несколько ограничена. В частности, виртуальные методы используемых классов и объектов не могут быть изъяты из процесса компиляции и сборки приложения. Соответственно, и те переменные и процедуры (методы), на которые имеются ссылки из таких виртуальных методов, также не могут быть отброшены.
При разработке библиотеки KOL это обстоятельство было учтено. Автору пришлось отказаться от жесткого следования канонам объектно-ориентированного программирования. В частности, в KOL один и тот же объектный тип может использоваться для инкапсуляции нескольких подобных друг другу объектов. Например, тип TControl не является базовым для описания визуальных объектов подобно тому, как это сделано в VCL. Представители объектного типа TControl в библиотеке KOL уже без какого-либо наследования могут выполнять роль различных визуальных объектов (кнопок, меток, панелек, и т.п.) - в зависимости от того, какая глобальная функция использовалась для конструирования каждого конкретного объекта (например, NewPanel, NewButton и т.д.).
Такое совмещение нескольких объектов в одном объектном типе, вообще говоря, может приводить к некоторой путанице, поскольку наряду с методами и свойствами, общими для всех объектов, инкапсулированных этим объектным типом, могут иметься методы и свойства сугубо индивидуальные, характерные только для некоторой конкретной разновидности объектов. Поскольку тип (класс) тот же самый, существует вероятность ошибочного применения метода, не свойственного для данной разновидности объекта. Единственная причина, заставившая автора поступать так, это необходимость избежать большого числи виртуальных методов.
Разумеется, если бы виртуальные методы благополучно пропускались компилятором в тех случаях, когда они не нужны (а потенциально такая возможность существует), структуру объектов можно было бы сделать более ясной. Тем не менее, даже и в этом случае VCL не позволил бы программам стать намного компактнее. И проблема здесь уже в том, что разработчики VCL спроектировали свою библиотеку так, что многие объекты создаются и многие действия производятся еще до того, как будет известно, понадобятся ли они вообще, или так и останутся лежать в коде программы мертвым грузом. Например, если создается визуальный объект, то для него инициализируется шрифт, полотно для рисования, менеджеры перетаскивания, множество других объектов - на всякий случай: а вдруг понадобятся! Конечно, программе может понадобиться что-нибудь нарисовать, или изменить какой-нибудь шрифт. Программа может быть спроектирована для использования популярного интерфейса расположения плавающих панелей drag-and-dock. Может, но ведь не обязана, так?
В противоположность VCL, библиотека KOL поступает с необязательными действиями и объектами значительно более аккуратно. Они (действия) выполняются и (объекты) инициализируются только тогда, когда они впервые потребуются. Очистка ресурсов и памяти по завершении использования при этом проблем как раз не представляет. Один и тот же (виртуальный) метод Free прекрасно справляется с освобождением отработавших подчиненных объектов, независимо от их типа. Собственно, это и есть главная причина того, почему программы, изготовленные с использованием библиотеки KOL, настолько кодоэкономичны.
В описываемой библиотеке используется несколько различных способов реализации такого отложенного принятия решения, в зависимости от природы необязательного к включению в программу кода. Важнее не столько перечислить эти способы, которые, вообще говоря, являются просто удачным применением общепринятых программистских приемов, сколько объяснить и понять суть их действия.
Как известно, исходный текст программы превращается в исполняемый машинный код в результате работы иногда нескольких программ: прекомпилятора, компилятора, сборщика. Отметим, что когда речь идет о среде Delphi, разделять эти шаги особого смысла не имеет, так как все они выполняются одной и той же программой. Поэтому, говоря "компилятор", будем подразумевать все эти этапы вместе. Поскольку именно компилятор Delphi принимает решение, подключать или не подключать код той или иной процедуры к программе, откладывание решения о необходимости ее использования путем первого обращения к ней в коде только тогда, когда ее функциональность оказалась востребована разработчиком проекта, требует пояснения.
На самом деле, все очень просто разъясняется слеующим небольшим примером. Пусть наш визуальный объект (кнопка, к примеру) содержит свойство Font (шрифт). В отличие от VCL, не будем создавать подчиненный объект, соответствующий этому свойству, в конструкторе нашего объекта (кнопки, хозяина шрифта). Создадим его в том методе, который выполняет чтение свойства Font (в методе GetFont), в случае если он еще не создан. В итоге, если в приложении к свойству Font нигде нет обращений (т.е. разработчику не было нужды изменять шрифт своих визуальных объектов, и его устраивают стандартные шрифты, настроенные пользователем), компилятор не обнаружит и ни одного вызова метода GetFont, и соответственно, не включит в программу код этого метода. Следовательно, ни одной ссылки не будет обнаружено ни на конструктор объекта шрифта, ни на другие процедуры, которые иначе бы оказались задействованы и попали бы в исполнимый модуль.
Понятно, что приведенный пример поясняет только один из многих использованных приемов. Но принцип всех таких приемов один и тот же, а именно, как уже сказано выше: отложить принятие решения о подключении дополнительного кода до тех пор, пока он не потребуется разработчику программногопродукта. По мнению автора, данный принцип в корне расходится со сложившейся практикой программирования. Пожалуй, на примере KOL, в частности доказана нелепость общепринятого подхода, который приводит к тому, что 90% кода в современных приложениях - это шлак, мусор, который если и работает, то вхолостую, и лишь попусту затрачивает ресурсы процессора, оперативной памяти, занимает место на жестком диске, отнимает время при передаче лишних сотен килобайт по сети и через интернет, и залезает при этом в ваш карман. И недаром в ответ на вопрос, как уменьшить размер программы, иногда можно получить такой ответ, что, дескать, зачем уменьшать? - чем больше объем, тем больше заплатят. (Варианты: "солидней", заказчик больше уважает). Не бессмыслица ли?
Если кто-то из Delphi-программистов, прочитавших эту статью, заинтересуется, то милости прошу на интернет-страницу KOL/MCK, берите себе эти библиотеки (все совершенно бесплатно, в исходных кодах), и обязательно попробуйте. Уверяю: не пожалеете!
Интернет-страница KOL/MCK: http://xcl.cjb.net.
Почта: bonanzas@xcl.cjb.net.
Взято с сайта www.emanual.ru.
©Drkb::04526.
| Создание COM-объектов средствами Delphi. |
Часть 1.
Как преодолеть отсутствие множественного наследования в Delphi.
Все сообщество программистов разделяется по приверженности к той или иной платформе и языку программирования. Один предпочитает Delphi для Windows, другому нравится ассемблер для DOS, третий программирует на Си++ для OS/2. Навыки работы для одной платформы совсем не обязательно станут полезными при переходе на другую, а знание отдельного языка программирования может даже затруднить изучение другого. Все эти преграды можно было бы преодолеть, используя межпроцессное взаимодействие между программами, однако здесь возникает новая трудность - разные формы внутреннего представления данных в этих программах.
Однако есть способ решения этих проблем: применение единого стандарта для организации связи между объектами, который не зависит от используемой платформы и языка. Именно такова разработанная Microsoft компонентная модель объекта COM (Component Object Model). Данная технология уже получила широкое внедрение: ведь на ее базе работают механизмы OLE и ActiveX.
К сожалению, в изданной на текущий момент литературе недостаточно четко отражен тот факт, что программировать для COM-модели можно на самых разных языках. В большинстве примеров, за очень редким исключением, используется Си++. Некоторые примеры ориентированы только на Си++ и средства этого языка для множественного наследования. Другие примеры строятся на основе библиотеки MFC, причем в этом случае настолько интенсивно используются ее специфические макроконструкции для COM, что создается впечатление, будто это вообще не Си. Вывод следующий: если у вас нет опыта работы в Си++, то вам будет трудно разобраться, как программировать для COM.
В этой и следующей за ней статьях мы рассмотрим процесс формирования COM-объектов в среде разработки Borland Delphi. В первой части мы коснемся проблем организации COM-объектов в Delphi и покажем несколько вариантов их решения. Во второй части будут приведены примеры пяти типовых объектов для стандартных надстроек оболочки Windows 95. В отдельных случаях COM-объекты целесообразно хранить как EXE-файлы. Однако в этой статье с целью простоты изложения материала будут рассматриваться лишь COM-объекты, записанные в наиболее часто используемой для них форме DLL-модулей.
Основные понятия о COM-объектах.
Что же кроется внутри COM-объекта? Нам совершенно не нужно вникать в это! Весь обмен информацией между COM-объектом и внешним миром осуществляется через конкретные интерфейсы. Каждый из них реализует доступ к одной или нескольким функциям, обратиться к которым может любой объект или программа. Все COM-объекты должны иметь интерфейс IUnknown с тремя его функциями - AddRef, Release и QueryInterface. Функции AddRef и Release отвечают за обычную задачу сопровождения жизненного цикла объекта. При каждом обращении к Addref содержимое счетчика ссылок данного объекта увеличивается на единицу, а при каждом обращении к Release - уменьшается. Когда значение счетчика достигает нуля, объект уничтожается. Практический интерес представляет третья функция интерфейса IUnknown - QueryInterface. Получив доступ к обязательно присутствующему интерфейсу IUnknown, программа или любой другой объект сразу может обратиться к функции QueryInterface и узнать обо всех остальных имеющихся у этого объекта интерфейсах. IUnknown находится на вершине иерархического дерева всех COM-интерфейсов. Любой другой интерфейс фактически наследуется от IUnknown и поэтому также должен обеспечивать доступ ко всем трем IUnknown-функциям.
Понятие объекта как в терминологии COM-модели, так и в Delphi или Си++ имеет практически одинаковый смысл. А вот COM-интерфейс больше напоминает Delphi- или Си++-объект, у которого отсутствуют public-переменные и имеются лишь виртуальные методы. Список функций интерфейса соответствует виртуальной таблице методов Object Pascal или объекта Си++. Создать COM-интерфейс можно средствами практически любого языка: достаточно лишь объявить объект с требуемым списком виртуальных методов. Само собой разумеется, что задаваемые определения методов должны в точности соответствовать определениям функций в самих интерфейсах. Однако, кроме того, необходимо соблюдать правильный порядок их размещения в виртуальной таблице. Сказанное означает, что эти определения следуют в заданном порядке, а перед ними нет никаких других виртуальных методов.
В файле OLE2.PAS, входящем в комплект Delphi 2.0, показано, как давать определение типу интерфейсного объекта для IUnknown и для нескольких десятков других, производных от IUnknown интерфейсов, например IClassFactory, IMarshal и IMalloc. Каждому методу, входящему в состав этих интерфейсных объектов, дается такое определение, как virtual, stdcall или abstract. Пояснение, зачем указывается virtual, уже было дано. Ключевое слово stdcall сообщает компилятору, что вызов данного метода следует производить по стандартным правилам. Слово abstract указывает, что функциональная часть данного метода в текущем объекте отсутствует, но она должна присутствовать у некоторого дочернего объекта, для которого будет создаваться его экземпляр. В файле OLE2.PAS дается определение для более чем 50 интерфейсов, непосредственно наследуемых от IUnknown, причем каждый из них предоставляет как собственный интерфейс, так и IUnknown.
Однако из-за необходимости иметь для COM-объекта два или более интерфейса, не считая IUnknown, возникает одна проблема. В Си++ достаточно дать определение COM-объекту как многократно наследуемому от тех объектов, где требуемые интерфейсы содержатся. Однако для объектов Delphi возможность множественного наследования не допускается. Поэтому приходится искать иное решение. (К сведению программистов на Си++: при создании COM-объектов на базе MFC применяется технология, аналогичная описываемой здесь для Delphi. Эта особенность остается незамеченной на фоне великого множества макроконструкций, которые используются при определении COM-объекта средствами MFC.).
Сателлиты и контейнеры.
Ключевой фактор создания в Delphi COM-объекта с несколькими интерфейсами состоит в том, что объект рассматривается как передающий контейнер этих интерфейсов. Совсем не обязательно иметь их внутри данного COM-объекта. Необходимо лишь при запросе, когда вызывается метод QueryInterface его интерфейса IUnknown предоставлять доступ к нужному интерфейсу. Такой COM-объект, созданный в Delphi, может лишь непосредственно обслуживать три свои функции IUnknown, а при запросе через QueryInterface интерфейса IUnknown, передавать указатель на самого себя. Он действует как передаточный механизм и распорядитель других объектов, имеющих свои интерфейсы. Такие интерфейсные объекты-сателлиты отображают свои три IUnknown-метода на общий объект-контейнер. Если приходит запрос на один из сателлитных интерфейсов (как правило, через метод QueryInterface), контейнер передает указатель на соответствующий объект-сателлит. На листинге показан пример, как средствами Delphi можно создать такие интерфейсные объекты с типами сателлит и контейнер, а также как подготовить соответствующий интерфейс IClassFactory.
Листинг. С помощью этих обобщенных объектов с описанием интерфейсов можно создавать в среде Delphi COM-объекты с несколькими интерфейсами.
Code: Unit DelphCom; // "Обобщенные" объекты. Предназначены для создания COM-объектов. // в Delphi. ISatelliteUnknown - интерфейсный объект, который. // будет обслуживаться через IContainerUnknown. Любой реальный. // COM-объект с несколькими интерфейсами. // будет наследоваться из IContainerUnknown и содержать. // функцию QueryInterface. Interface. Uses Windows, Ole2, Classes, SysUtils, ShellApi, ShlObj; Var DllRefCount : Integer; Type. IContainerUnknown = class; ISattelliteUnknown = class(IUnknown). // Этот интерфейс будет обслуживаться через IContainerUnknown. // Отображает три IUnknown-функции на свой объект-контейнер. Protected. FContainer : IContainerUnknown; Public. Constructor Create(vContainer : IContainerUnknown); Function QueryInterface(const WantIID: TIID; Var ReturnedObject): HResult; override; Function AddRef: Longint; override; Function Release: Longint; override; End; IContainerUnknown = class (IUnknown). Protected. FRefCount : Integer; Public. Соnstгисtог Create; Destructor Destroy; override; (IUnknown-функции). Function QueryInterface(const WantIID: TIID; Var ReturnedObject): HResult; override; Function AddRef: LongInt; override; Function Release: LongInt; override; End; IMyClassFactory = с1аss(IClassFactory). Private. FRefcount : Integer; Public. Constructor Create; Destructor Destroy; override; Function QueryInterface(const WantIID: TIID; Var ReturnedObject): HResult; override; Function AddRef: LongInt; override; Function Release: LongInt; override; // В дочернем объекте должно быть дано определение. // для функции CreateInstance. Function LockServer(fLock: BOOL): HResult; override; End; Function DLLCanUnloadNow : HResult; StdCall; Export; Implementation. (****** ISatelliteUnknown *****). Constructor ISatelliteUnknown.Create(vContainer: IContainerUnknown); Begin fContainer := vContainer; end; Function ISatelliteUnknown.QueryInterface(const WantIID: TIID; Var ReturnedObject): HResult; Begin. Result := fContainer.QueryInterface(WantIid, ReturnedObject); End; Function ISatelliteUnknown.AddRef: LongInt; Begin Result := fContainer.AddRef; end; Function ISatelliteUnknown.Release: LongInt; Begin Result := fContainer.Release; end; (****** IContainerUnknown ******). Constructor IContainerUnknown.Create; Begin. Inherited Create; FRefCount := 0; Inc(DllRefCount); End; Destructor IContainerUnknown.Destroy; Begin. Dec(DllRefCount); Inherited Destroy; End; Function IContainerUnknown.QueryInterface(const WantIID: TIID; Var ReturnedObject): HResult; Var P : IUnknown; Begin. If IsEqualIID(WantIID, IID_IUnknown) then P := Self. Else P:= nil; Pointer(ReturnedObject) := P; If P = nil then Result := E_NOINTERFACE. Else begin. P.AddRef; Result := S_OK; End; End; Function IContainerUnknown.AddRef: LongInt; Begin Inc(FRefCount); Result := FRefCount; end; Function IContainerUnknown.Release: LongInt; Begin. Dec(FRefCount); Result := FRefCount; If FRefCount = 0 then Free; End; (****** IMyClassFactory ******). Constructor IMyClassFactory.Create; Begin. Inherited Create; Inc(DllRefCount); FRefCount := 0; End; Destructor IMyClassFactory.Destroy; Begin. Dec(DllRefCount); Inherited Destroy; End; Function IMyClassFactory.QueryInterface(const WantIID: TIID; Var ReturnedObject): HResult; Begin. If IsEqualIID(WantIiD, IID_IUnknown) or. IsEqualIID(WantIiD, IID_IClassFactory) then. Begin. Pointer(ReturnedObject) := Self; AddRef; Result := S_OK; End. Else begin. Pointer(ReturnedObject) := NIL; Result := E_NOINTERFACE; End. End; Function IMyClassFactory.AddRef: LongInt; Begin. Inc(FRefCount); Result := FRefCount; End; Function IMyClassFactory.Release: LongInt; Begin. Dec(FRefCount); Result := FRefCount; If FRefCount = 0 then Free; End; Function IMyClassFactory.LockServer(fLock: Bool):HResult; Begin Result := E_NOTIMPL; end; (****** экспортируемая функция ******). Function DLLCanUnloadNow: hResult; StdCall; Export; Begin. If DllRefCount = 0 then Result := S_OK. Else Result := S_FALSE; End; Initialization. DllRefCount := 0; End.
| Как определить установлен ли Excel? |
Функция возвращает True если найден OLE-объект.
Пример использования.
Code: If not IsOLEObjectInstalled('Excel.Application') then. ShowMessage('Класс не зарегистрирован'). Else. ShowMessage('Класс найден'); Function IsOLEObjectInstalled(Name: String): boolean; Var. ClassID: TCLSID; Rez : HRESULT; Begin. // Ищем CLSID OLE-объекта. Rez := CLSIDFromProgID(PWideChar(WideString(Name)), ClassID); If Rez = S_OK then // Объект найден. Result := true. Else. Result := false; End;
| Как определить запущен ли Excel? |
Данный пример ищет активный экземпляр Excel и делает его видимым.
Code: Var. ExcelApp : Variant; Begin. Try. // Ищем запущеный экземплят Excel, если он не найден, вызывается исключение. ExcelApp := GetActiveOleObject('Excel.Application'); // Делаем его видимым. ExcelApp.Visible := true; Except. End;
| Как вывести данные в Excel? |
Можно выводить данные последовательно в каждую ячейку, но это очинь сильно замедляет работу. Лучше сформировать вариантный массив, и выполнить присвоение области (Range) этого массива.
Code: Var. ExcelApp, Workbook, Range, Cell1, Cell2, ArrayData : Variant; TemplateFile : String; BeginCol, BeginRow, i, j : integer; RowCount, ColCount : integer; Begin. // Координаты левого верхнего угла области, в которую будем выводить данные. BeginCol := 1; BeginRow := 5; // Размеры выводимого массива данных. RowCount := 100; ColCount := 50; // Создание Excel. ExcelApp := CreateOleObject('Excel.Application'); // Отключаем реакцию Excel на события, чтобы ускорить вывод информации. ExcelApp.Application.EnableEvents := false; // Создаем Книгу (Workbook). // Если заполняем шаблон, то Workbook := ExcelApp.WorkBooks.Add('C:\MyTemplate.xls'); Workbook := ExcelApp.WorkBooks.Add; // Создаем Вариантный Массив, который заполним выходными данными. ArrayData := VarArrayCreate([1, RowCount, 1, ColCount], varVariant); // Заполняем массив. For I := 1 to RowCount do. For J := 1 to ColCount do. ArrayData[I, J] := J * 10 + I; // Левая верхняя ячейка области, в которую будем выводить данные. Cell1 := WorkBook.WorkSheets[1].Cells[BeginRow, BeginCol]; // Правая нижняя ячейка области, в которую будем выводить данные. Cell2 := WorkBook.WorkSheets[1].Cells[BeginRow + RowCount - 1, BeginCol + ColCount - 1]; // Область, в которую будем выводить данные. Range := WorkBook.WorkSheets[1].Range[Cell1, Cell2]; // А вот и сам вывод данных. // Намного быстрее поячеечного присвоения. Range.Value := ArrayData; // Делаем Excel видимым. ExcelApp.Visible := true;
| Создание или открытие книги. |
Повторюсь, не смотря на то, что я уже писал об этом в предыдущей статье. В главной форме проекта-примера я объявил свойство IWorkbook. Оно будет содержать интерфейс книги, которую мы будем создавать и использовать. Естественно, в обработчике FormDestroy я его освобождаю.
Property IWorkbook: Excel8TLB._Workbook read FIWorkbook;
Книгу можно создать разными способами и с разными намерениями. Если необходимо создать абсолютно чистую книгу, достаточно выполнить следующий код:
If Assigned(IXLSApp) and (not Assigned(IWorkbook) ) then.
FIWorkbook := IXLSApp.Workbooks.Add(EmptyParam, 0);
Вопрос в том, зачем нам может понадобиться новая книга, с количеством пустых листов, выставленным по умолчанию. Сегодня, я не могу уже ответить на этот вопрос, ибо не создаю новых пустых книг.
Коллекция Workbooks содержит все открытые книги и предоставляет возможность кое-как управлять всем этим.
Боже, как убоги коллекции от Microsoft, и особенно поиск в них! Я отклонюсь, но это надо видеть. Вот пример поиска книги с заданным именем, приведенный как совет в MSDN Office Developer.
Code: Public Function SheetExists(strSearchFor As String) As Boolean. SheetExists = False. For Each sht In ThisWorkbook.Worksheets. If sht.Name = strSearchFor Then. SheetExists = True. End If. Next sht. End Function.
| Работа с листами книги. |
Есть в VBA одна вещь, которая меня раздражает. Это ActiveSheet и ActiveWorkbook, а также возможность работы с Cells и Range без указания, к какому листу или книге они принадлежат. Одно время я боролся сам с собой, то применяя, то совсем отказываясь от подобных конструкций. Окончательно я отказался от этого лишь после обнаружения многочисленных ошибок в анализе «лога» моего Web-сервера, который я сделал на VBA. Благо, при работе в Delphi нет возможности написать Cells(x, y) = NewValue, подразумевая при этом какой-то неуловимый ActiveSheet. Поэтому прежде, чем работать с отдельными ячейками, я всегда получаю интерфейс на конкретный и вполне осязаемый лист книги. И делю это так:
Code: Var ISheet: Excel8TLB._Worksheet; … ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
| Как получить картинку с видео источника. |
Для использования следующиего примера необходимо иметь "Microsoft Video for Windows SDK". Пример показывает, как открыть видео устройство для захвата видео, как сграбить фрейм с устройства, как сохранить этот фрейм на диск в виде файла .BMP, как записать .AVI файл (со звуком, но без предварительного просмотра), и как закрыть устройство.
Замечание: Для работы примера необходимо иметь установленное устройство захвата видео (video capture device).
Пример:
Code: Unit Unit1; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls; Type. TForm1 = class(TForm). Panel1: TPanel; OpenVideo: TButton; CloseVideo: TButton; GrabFrame: TButton; SaveBMP: TButton; StartAVI: TButton; StopAVI: TButton; SaveDialog1: TSaveDialog; Procedure FormCreate(Sender: TObject); Procedure OpenVideoClick(Sender: TObject); Procedure CloseVideoClick(Sender: TObject); Procedure GrabFrameClick(Sender: TObject); Procedure SaveBMPClick(Sender: TObject); Procedure StartAVIClick(Sender: TObject); Procedure StopAVIClick(Sender: TObject); Private. { Private declarations }. HWndC : THandle; CapturingAVI : bool; Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Const WM_CAP_START = WM_USER; Const WM_CAP_STOP = WM_CAP_START + 68; Const WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10; Const WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11; Const WM_CAP_SAVEDIB = WM_CAP_START + 25; Const WM_CAP_GRAB_FRAME = WM_CAP_START + 60; Const WM_CAP_SEQUENCE = WM_CAP_START + 62; Const WM_CAP_FILE_SET_CAPTURE_FILEA = WM_CAP_START + 20; Function capCreateCaptureWindowA(lpszWindowName : PCHAR; DwStyle : longint; X : integer; Y : integer; NWidth : integer; NHeight : integer; ParentWin : HWND; NId : integer): HWND; STDCALL EXTERNAL 'AVICAP32.DLL'; Procedure TForm1.FormCreate(Sender: TObject); Begin. CapturingAVI := false; HWndC := 0; SaveDialog1.Options := [ofHideReadOnly, ofNoChangeDir, ofPathMustExist]. End; Procedure TForm1.OpenVideoClick(Sender: TObject); Begin. HWndC := capCreateCaptureWindowA('My Own Capture Window', WS_CHILD or WS_VISIBLE , Panel1.Left, Panel1.Top, Panel1.Width, Panel1.Height, Form1.Handle, 0); If hWndC <> 0 then. SendMessage(hWndC, WM_CAP_DRIVER_CONNECT, 0, 0); End; Procedure TForm1.CloseVideoClick(Sender: TObject); Begin. If hWndC <> 0 then begin. SendMessage(hWndC, WM_CAP_DRIVER_DISCONNECT, 0, 0); HWndC := 0; End; End; Procedure TForm1.GrabFrameClick(Sender: TObject); Begin. If hWndC <> 0 then. SendMessage(hWndC, WM_CAP_GRAB_FRAME, 0, 0); End; Procedure TForm1.SaveBMPClick(Sender: TObject); Begin. If hWndC <> 0 then begin. SaveDialog1.DefaultExt := 'bmp'; SaveDialog1.Filter := 'Bitmap files (*.bmp)|*.bmp'; If SaveDialog1.Execute then. SendMessage(hWndC, WM_CAP_SAVEDIB, 0, Longint(pchar(SaveDialog1.FileName))); End; End; Procedure TForm1.StartAVIClick(Sender: TObject); Begin. If hWndC <> 0 then begin. SaveDialog1.DefaultExt := 'avi'; SaveDialog1.Filter := 'AVI files (*.avi)|*.avi'; If SaveDialog1.Execute then begin. CapturingAVI := true; SendMessage(hWndC, WM_CAP_FILE_SET_CAPTURE_FILEA, 0, Longint(pchar(SaveDialog1.FileName))); SendMessage(hWndC, WM_CAP_SEQUENCE, 0, 0); End; End; End; Procedure TForm1.StopAVIClick(Sender: TObject); Begin. If hWndC <> 0 then begin. SendMessage(hWndC, WM_CAP_STOP, 0, 0); CapturingAVI := false; End; End; End.
| Библиотека для работы с LAN. |
Code: Unit NetProcs; Interface. Uses Classes, Windows; Type. TAdapterStatus = record. Adapter_address: array [0..5] of Char; Filler: array [1..4*SizeOf(Char)+19*SizeOf(Word). +3*SizeOf(DWORD)] of Byte; End; THostInfo = record. Username: PWideChar; Logon_domain: PWideChar; Oth_domains: PWideChar; Logon_server: PWideChar; End;{record}. Function IsNetConnect : Boolean; {Возвращает TRUE если компьютер подключен к сети, иначе - FALSE}. Function AdapterToString(Adapter: TAdapterStatus): string; {Преобразует MAC адес в привычный xx-xx-xx-xx}. Function GetMacAddresses(const Machine: string; Const Addresses: TStrings): Integer; {Заполняет Addresses MAC-адресами компьютера с сетевым именем Machine. Возвращает число МАС адресов на компьютере}. Function GetNetUser(HostName: WideString): THostInfo; {Возвращает LOGIN текущего пользователя на HOSTNAME компьютере}. Implementation. Uses NB30, SysUtils; Function IsNetConnect : Boolean; Begin. If GetSystemMetrics(SM_NETWORK) AND $01 = $01. Then Result:= True. Else Result:= False; End;{function}. Function AdapterToString(Adapter: TAdapterStatus): string; Begin. With Adapter do Result := Format('%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x', [ Integer(adapter_address[0]), Integer(adapter_address[1]), Integer(adapter_address[2]), Integer(adapter_address[3]), Integer(adapter_address[4]), Integer(adapter_address[5])]); End;{function}. Function GetMacAddresses(const Machine: string; Const Addresses: TStrings): Integer; Const NCBNAMSZ = 16; // absolute length of a net name. MAX_LANA = 254; // lana's in range 0 to MAX_LANA inclusive. NRC_GOODRET = $00; // good return. NCBASTAT = $33; // NCB ADAPTER STATUS. NCBRESET = $32; // NCB RESET. NCBENUM = $37; // NCB ENUMERATE LANA NUMBERS. Type. PNCB = ^TNCB; TNCBPostProc = procedure (P: PNCB); stdcall; TNCB = record. Ncb_command: Byte; Ncb_retcode: Byte; Ncb_lsn: Byte; Ncb_num: Byte; Ncb_buffer: PChar; Ncb_length: Word; Ncb_callname: array [0..NCBNAMSZ - 1] of Char; Ncb_name: array [0..NCBNAMSZ - 1] of Char; Ncb_rto: Byte; Ncb_sto: Byte; Ncb_post: TNCBPostProc; Ncb_lana_num: Byte; Ncb_cmd_cplt: Byte; Ncb_reserve: array [0..9] of Char; Ncb_event: THandle; End; PLanaEnum = ^TLanaEnum; TLanaEnum = record. Length: Byte; Lana: array [0..MAX_LANA] of Byte; End; ASTAT = record. Adapt: TAdapterStatus; Namebuf: array [0..29] of TNameBuffer; End; Var. NCB: TNCB; Enum: TLanaEnum; I: Integer; Adapter: ASTAT; MachineName: string; Begin. Result := -1; Addresses.Clear; MachineName := UpperCase(Machine); If MachineName = '' Then MachineName := '*'; FillChar(NCB, SizeOf(NCB), #0); NCB.ncb_command := NCBENUM; NCB.ncb_buffer := Pointer(@Enum); NCB.ncb_length := SizeOf(Enum); If Word(NetBios(@NCB)) = NRC_GOODRET. Then begin. Result := Enum.Length; For I := 0 to Ord(Enum.Length) - 1. Do begin. FillChar(NCB, SizeOf(TNCB), #0); NCB.ncb_command := NCBRESET; NCB.ncb_lana_num := Enum.lana[I]; If Word(NetBios(@NCB)) = NRC_GOODRET. Then begin. FillChar(NCB, SizeOf(TNCB), #0); NCB.ncb_command := NCBASTAT; NCB.ncb_lana_num := Enum.lana[i]; StrLCopy(NCB.ncb_callname, PChar(MachineName),NCBNAMSZ); StrPCopy(@NCB.ncb_callname[Length(MachineName)], StringOfChar(' ', NCBNAMSZ - Length(MachineName))); NCB.ncb_buffer := PChar(@Adapter); NCB.ncb_length := SizeOf(Adapter); If Word(NetBios(@NCB)) = NRC_GOODRET. Then Addresses.Add(AdapterToString(Adapter.adapt)); End; End; End; End;{function}. Function. NetWkstaUserEnum(servername: PWideChar; Level : DWord; Var bufptr: Pointer; Prefmaxlen: DWord; Var entriesread: PDWord; Var totalentries: PDWord; Var resumehandle: PDWord ): LongInt ; Stdcall; external 'netapi32.dll' name 'NetWkstaUserEnum'; Function GetNetUser(HostName: WideString): THostInfo; Var. Info: Pointer; ElTotal: PDWord; ElCount: PDWord; Resume: PDWord; Error: LongInt; Begin. Resume:=0; NetWkstaUserEnum(PWideChar(HostName),1, Info,0, ElCount,ElTotal,Resume); Error:=NetWkstaUserEnum(PWideChar(HostName),1,Info,256*Integer(ElTotal), ElCount,ElTotal,Resume); Case Error of. ERROR_ACCESS_DENIED: Result.UserName:= 'ERROR - ACCESS DENIED'; ERROR_MORE_DATA: Result.UserName:= 'ERROR - MORE DATA'; ERROR_INVALID_LEVEL: Result.UserName:= 'ERROR - INVALID LEVEL'; Else if Info <> nil. Then Result:=THostInfo(info^). Else begin. Result.username:= '???'; Result.logon_domain:= '???'; Result.oth_domains:= '???'; Result.logon_server:= '???'; End;{if}. End;{case}. End; {function}. End.
| Определение установленных версий .NET Framework в системе. |
Code: /// <summary> /// Enumerates all installed Common Language Runtime Engines. /// </summary> /// <param name="Index">Zero-based index of looked runtime. Record.</param> /// <returns>True if runtime with specified index found.</returns> Function EnumInstalledRuntimes(Index: Integer; out VersionName: String): Boolean; Var. Hkey: Windows.HKEY; Hsubkey: Windows.HKEY; I: Cardinal; J: Cardinal; NameBuf: array[0..MAX_PATH] of Char; CNameBuf: Cardinal; Lwt: TFileTime; Vt: DWORD; AnyFound: Boolean; Begin. Result := False; VersionName := ''; If ERROR_SUCCESS = RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar('SOFTWARE\Microsoft\.NETFramework\policy'), 0, KEY_ENUMERATE_SUB_KEYS, hkey) then. Try. I := 0; While True do. Begin. AnyFound := False; CNameBuf := MAX_PATH + 1; If ERROR_SUCCESS <> RegEnumKeyEx(hkey, I, @NameBuf[0], CNameBuf,nil, nil, nil, @lwt) then. Begin. Break; End; If (NameBuf[0] = 'v') and (NameBuf[1] in ['1'..'9']) then. Begin. VersionName := String(NameBuf); If ERROR_SUCCESS = RegOpenKeyEx(hkey, @NameBuf[0], 0,KEY_QUERY_VALUE, hsubkey) then. Try. J := 0; While true do. Begin. CNameBuf := MAX_PATH + 1; If ERROR_SUCCESS <> RegEnumValue(hsubkey, J, @NameBuf[0],CNameBuf, nil, @vt, nil, nil) then. Begin. Break; End; If (vt = REG_SZ) and (NameBuf[0] <> #0) then. Begin. VersionName := VersionName + '.' + String(NameBuf); AnyFound := True; Break; End; Inc(J); End; Finally. RegCloseKey(hsubkey); End; End; Inc(I); If AnyFound then. Begin. If Index = 0 then. Begin. Result := True; Break; End; Dec(Index); End; End; Finally. RegCloseKey(hkey); End; End;
Список зарезервированных слов Local SQL BDE.
ACTIVE, ADD, ALL, AFTER, ALTER, AND, ANY, AS, ASC, ASCENDING,
AT, AUTO, AUTOINC, AVG.
BASE_NAME, BEFORE, BEGIN, BETWEEN, BLOB, BOOLEAN, BOTH, BY,
BYTES.
CACHE, CAST, CHAR, CHARACTER, CHECK, CHECK_POINT_LENGTH,
COLLATE, COLUMN, COMMIT, COMMITTED, COMPUTED, CONDITIONAL,
CONSTRAINT, CONTAINING, COUNT, CREATE, CSTRING, CURRENT,
CURSOR.
DATABASE, DATE, DAY, DEBUG, DEC, DECIMAL, DECLARE, DEFAULT,
DELETE, DESC, DESCENDING, DISTINCT, DO, DOMAIN, DOUBLE, DROP.
ELSE, END, ENTRY_POINT, ESCAPE, EXCEPTION, EXECUTE, EXISTS,
EXIT, EXTERNAL, EXTRACT.
FILE, FILTER, FLOAT, FOR, FOREIGN, FROM, FULL, FUNCTION.
GDSCODE, GENERATOR, GEN_ID, GRANT, GROUP,
GROUP_COMMIT_WAIT_TIME.
HAVING, HOUR.
IF, IN, INT, INACTIVE, INDEX, INNER, INPUT_TYPE, INSERT,
INTEGER, INTO, IS, ISOLATION.
JOIN.
KEY.
LONG, LENGTH, LOGFILE, LOWER, LEADING, LEFT, LEVEL, LIKE,
LOG_BUFFER_SIZE.
MANUAL, MAX, MAXIMUM_SEGMENT, MERGE, MESSAGE, MIN, MINUTE,
MODULE_NAME, MONEY, MONTH.
NAMES, NATIONAL, NATURAL, NCHAR, NO, NOT, NULL,
NUM_LOG_BUFFERS, NUMERIC.
OF, ON, ONLY, OPTION, OR, ORDER, OUTER, OUTPUT_TYPE, OVERFLOW.
PAGE_SIZE, PAGE, PAGES, PARAMETER, PASSWORD, PLAN, POSITION,
POST_EVENT, PRECISION, PROCEDURE, PROTECTED, PRIMARY,
PRIVILEGES.
RAW_PARTITIONS, RDB$DB_KEY, READ, REAL, RECORD_VERSION,
REFERENCES, RESERV, RESERVING, RETAIN, RETURNING_VALUES,
RETURNS, REVOKE, RIGHT, ROLLBACK.
SECOND, SEGMENT, SELECT, SET, SHARED, SHADOW, SCHEMA,
SINGULAR, SIZE, SMALLINT, SNAPSHOT, SOME, SORT, SQLCODE,
STABILITY, STARTING, STARTS, STATISTICS, SUB_TYPE, SUBSTRING,
SUM, SUSPEND.
TABLE, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE,
TO, TRAILING, TRANSACTION, TRIGGER, TRIM.
UNCOMMITTED, UNION, UNIQUE, UPDATE, UPPER, USER.
VALUE, VALUES, VARCHAR, VARIABLE, VARYING, VIEW.
WAIT, WHEN, WHERE, WHILE, WITH, WORK, WRITE.
YEAR.
||, -, *, /, <>, <, >, ,(comma), =, <=, >=, ~=, !=, ^=, (, ).
Взято с сайта http://www.delphikingdom.ru/
©Drkb::02501.
| Как получить строковое значение перечисляемого типа? |
Code: Procedure GetEnumNameList(Pti: PTypeInfo; AList: TStrings; X: Integer); {(********************************************************** Will return in AList string version of an. Enumerated type less the first X characters . Eg X = 4. And. Type. EXORBuySell = ( XOR_BUY, XOR_SELL. ); GetEnumNameList(TypeInfo(eXORBuySell), ComboBox1.Items, 4); Now ComboBox1.Items[0] = 'BUY' And ComboBox1.Items[1] = 'SELL' ************************************************************)} Var. I: Integer; Begin. AList.Clear; With GetTypeData(pti)^ do. For I := MinValue to MaxValue do. AList.Add(Copy(GetEnumName(pti, I), X + 1, 255)); End;
Каковы текущие ограничения BDE?
Основные ограничения BDE:
48 клиентов в системе;
32 сессии на одного клиента (для версии 3.5 и ниже, 16 Bit, 32 Bit).
256 сессий на одного клиента (для версии 4.0 и выше, 32 Bit).
32 открытых баз данных на сессию (для версии 3.5 и ниже, 16 Bit, 32 Bit).
2048 открытых баз данных на сессию (для версии 4.0 и выше, 32 Bit).
32 загруженных драйвера.
64 сессии в системе (для версии 3.5 и ниже, 16 Bit, 32 Bit).
12288 сессии в системе (для версии 4.0 и выше, 32 Bit).
4000 курсоров на сессию.
16 вхождений в стеке ошибок.
8 типов таблиц на один драйвер.
16 типов полей на один драйвер.
8 типов индексов на один драйвер.
48K Размер конфигурационного файла (IDAPI.CFG).
64K Максимальный размер оператора SQL при RequestLive=False.
4K Максимальный размер оператора SQL при RequestLive=True (для версии 4.0 и ниже, 16/32 Bit).
6K Максимальный размер оператора SQL при RequestLive=True (для версии 4.01 и выше, 32 Bit).
16K Размер буфера записи (SQL и ODBC).
31 Размер имени таблицы и имени поля в символах.
64 Размер имени хранимой процедуры в символах.
16 Полей в ключе.
3 Размер расширения имени файла в символах.
260 Длина имени таблицы в символах (некоторые сервера могут иметь другие ограничения).
260 Длина полного имени файла и пути файловой системы в символах.
Ограничения Paradox:
127 открытых таблиц в системе (для версии 4.0 и ниже, 16/32 Bit).
254 открытых таблиц в системе (для версии 4.01 и выше, 32 Bit).
64 блокировки на запись на одну таблицу (16Bit) на одну сессию.
255 блокировок на запись на одну таблицу (32Bit) на одну сессию.
255 записей, учавствующих в транзакции на таблицу (32 Bit).
512 открытых физически файлов (DB, PX, MB, X??, Y??, VAL, TV) (для версии 4.0 и ниже, 16/32 Bit).
1024 открытых физически файлов (DB, PX, MB, X??, Y??, VAL, TV) (для версии 4.01 и выше, 32 Bit).
300 пользователей в одном файле PDOXUSRS.NET.
255 полей в таблице.
255 размер символьных полей.
2 миллиарда записей в таблице.
2 миллиарда байт в .DB (таблица) файле.
10800 байт на запись для индексированных таблиц.
32750 байт на запись для неиндексированных таблиц.
127 вторичных индексов на таблицу.
16 полей на индекс.
255 одновременно работающих пользователей на таблицу.
256 Мегабайт данных на одно BLOb поле.
100 паролей на сессию.
15 длина пароля.
63 паролей на таблицу.
159 полей с проверками корректности (validity check) (32 Bit).
63 поля с проверками корректности (validity check) (16 Bit).
Ограничения dBase:
256 открытых таблиц dBASE на систему (16 Bit).
350 открытых таблиц dBASE на систему (BDE 3.0 - 4.0, 32 Bit).
512 открытых таблиц dBASE на систему (BDE 4.01 и выше, 32 Bit).
100 блокировок на запись на одной таблице dBASE (16 and 32 Bit).
100 записей, учавствующих в транзакции на таблицу (32 Bit).
1 миллиард записей в таблице.
2 миллиарда байт в файле .DBF (таблица).
4000 Размер записи в байтах (dBASE 4).
32767 Размер записи в байтах (dBASE for Windows).
255 Количество полей в таблице (dBASE 4).
1024 Количество полей в таблице (dBASE for Windows).
47 Количество тэгов индексов на один .MDX-файл.
254 Размер символьных полей.
10 открытых основных индексов (.MDX) на таблицу.
220 Длина ключевого выражения в символах.
Взято из Akzhan's Database Delphi.
©Drkb::02500.
| Работа с MS Word из Delphi. |
1. Управление Word-ом из Delphi.
(опубликовано - 13 SoftWare).
Здесь мы рассмотрим пример того, как управлять объектами Word-а (Excel - аналогично) из программ на Delphi. Исходный код примера можно скачать на страничке 'DownLoad'
А). Для чего это нужно ?
Задачи могут быть самые разные, в общем случае это использование возможностей Word-а в своей программе, н-р: проверка текста на орфографию; печать текста, графики; экспорт отчетов в Word или изначальное создание их там и т.д.
Б). Подготовительные работы. На самом деле существует несколько способов сделать это, мы рассмотрим только один (пример кроме Delphi 5, в Delphi5 для этого есть компоненты на закладке Servers переименуете в программе типы на соответствующие компоненты, дальше так же).
Для начала начнем новый проект File, New Application; File, Save All. Создадим отдельную папку для проекта и сохраним Unit1 как Main, а Project1 как WordWriter.
Далее для работы с Word-ом нам потребуется библиотека типов Word-а, это делается так:
Project, Import Type Library, Add, далее переходим в папку, где стоит Word ( у меня это - "c:\program files\microsoft office) , заходим в папку Office и выбираем файл - msword8.olb (цифра -? версии Word-а - у Вас может отличаться ) или excel8.olb (для Excel).Нажимаем Ок. Delphi создаст 2 файла - Word_tlb.pas и Office_tlb.pas, их надо включить в раздел uses модуля Main нашего проекта:
Code: Uses ... ,Office_Tlb, word_tlb;
| Как вызывать функцию 16-битной DLL из 32-битного приложения? |
Надо использовать Thunks.
Кусок работающего только под Windows 95 кода -
Code: Const. Gfsr_SystemResources = 0; Gfsr_GdiResources = 1; Gfsr_UserResources = 2; Var. HInst16: THandle; GFSR: Pointer; { Undocumented Kernel32 calls. }. Function LoadLibrary16(LibraryName: PChar): THandle; stdcall; external kernel32 index 35; Procedure FreeLibrary16(HInstance: THandle); stdcall; external kernel32 index 36; Function GetProcAddress16(Hinstance: THandle; ProcName: PChar): Pointer; stdcall; external kernel32 index 37; Procedure QT_Thunk; cdecl; external kernel32 name 'QT_Thunk'; { QT_Thunk needs a stack frame. }. {$StackFrames On}. { Thunking call to 16-bit USER.EXE. The ThunkTrash argument. Allocates space on the stack for QT_Thunk. }. Function NewGetFreeSystemResources(SysResource: Word): Word; Var. ThunkTrash: array[0..$20] of Word; Begin. { Prevent the optimizer from getting rid of ThunkTrash. }. ThunkTrash[0] := hInst16; HInst16 := LoadLibrary16('user.exe'); If hInst16 < 32 then. Raise Exception.Create('Cannot load USER.EXE!'); { Decrement the usage count. This doesn't really free the. Library, since USER.EXE is always loaded. }. FreeLibrary16(hInst16); { Get the function pointer for the 16-bit function in USER.EXE. }. GFSR := GetProcAddress16(hInst16, 'GetFreeSystemResources'); If GFSR = nil then. Raise Exception.Create('Cannot get address of GetFreeSystemResources!'); { Thunk down to USER.EXE. }. Asm. Push SysResource { push arguments }. Mov edx, GFSR { load 16-bit procedure pointer }. Call QT_Thunk { call thunk }. Mov Result, ax { save the result }. End; End;
| Отладка экспертов. |
Delphi Run-Time Error Codes.
Delphi's online help documentation seems to miss the run-time error codes. I usually looked them up in my old Borland Pascal for Windows help file - now they are here for fast access:
1 Invalid function number.
2 File not found.
3 Path not found.
4 Too many open files.
5 File access denied.
6 Invalid file handle.
12 Invalid file access code.
15 Invalid drive number.
16 Cannot remove current directory.
17 Cannot rename across drives.
100 Disk read error.
101 Disk write error.
102 File not assigned.
103 File not open.
104 File not open for input.
105 File not open for output.
106 Invalid numeric format.
200 Division by zero.
201 Range check error.
202 Stack overflow error.
203 Heap overflow error.
204 Invalid pointer operation.
205 Floating point overflow.
206 Floating point underflow.
207 Invalid floating point operation.
210 Object not initialized.
211 Call to abstract method.
212 Stream registration error.
213 Collection index out of range.
214 Collection overflow error.
215 Arithmetic overflow error.
216 General protection fault.
©Drkb::00013.
Взято с сайта http://www.delphifaq.com.
Типы файлов в среде Дельфи.
DPR - Delphi Project File. This is actually a Pascal source file; it just happens to be the main program for the application.
PAS - In Delphi, PAS files are always the source code to either a unit or a form. The main program of an application is in the DPR file.
DFM - These files are always paired with PAS files. The DFM file is the binary data used to set up initial data for components (IE, the properties you set in design mode rather than in code). You can't edit a DFM file with a text editor, but if you open it in Delphi, you will see a textual version of the contents.
DCU - A compiled unit, similar in concept to an OBJ file.
OPT - Project Options; i.e. compiler and linker settings, which form is the main form, what icon to use for the application, etc. Generally, the stuff you edit under Options/Project.
RES - A Windows resource file; generated automatically by Delphi and required by the compilation process. You don't need to worry about this file, but don't delete it either.
EXE - All of the above linked together into runnable format.
~DP - A backup file of the DPR file before the last save operation.
~PA - A backup of a .PAS file.
~DF - A backup of a .DFM file.
©Drkb::00047.
Взято с сайта http://www.delphifaq.com.
Формат .CAB-файлов.
Это формат файлов, который Delphi предлагает теперь своим пользователям для размещения в Интернете. Cabinet-формат является эффективным средством для упаковки нескольких файлов. Cabinet-формат имеет две ключевых характеристики: в отдельном кабинете (.cab-файл) могут храниться несколько файлов, и сжатие данных выполняется в зависимости от типа файлов, что значительно увеличивает коэффициент сжатия. Создание Cabinet-файла зависит также от количества упаковываемых файлов и ожидаемого к ним типа доступа (последовательный, произвольный, одновременный ко всем файлам или доступ к нескольким файлам в одно и тоже время). Delphi не пользуется преимуществами сжатия файлов в зависимости от их типа.
Формат .LIC-файлов.
В действительности, как такового, формата .lic-файла не существует. Обычно это такие же текстовые файлы, содержащие одну или две ключевых строки.
Формат .INF-файлов.
Все inf-файлы состоят из секций и пунктов. Каждая именованная секция содержит соответствующие пункты. Все inf-файлы начинаются с заголовочной секции. После заголовка включенные секции могут располагаться в любом порядке. Каждый заголовок представляет собой строку с [Именем Заголовка]. Далее следуют пункты: ItemA = ItemDetail. Для получения дополнительной информации обратитесь к документу "Device Information File Reference".
Формат .dpr-файлов.
.dpr-файл является центральным файлом Delphi-проекта. Для программы он является первой точкой входа. dpr содержит ссылки на другие файлы проекта и связывает формы с соответствующими модулями. Данный файл нужно редактировать с предельной осторожностью, так как неумелые действия могут привести к тому, что вы не сможете загрузить ваш проект. Этот файл является критическим при загрузке и перемещении (копировании) проекта.
Формат .pas-файлов.
Это стандартный текстовый файл, который можно редактировать в текстовом редакторе. Данный файл нужно редактировать с некоторой долей осторожности, поскольку это может закончиться потерей некоторых преимуществ двух других инструментов. К примеру, добавление кода для кнопки с декларацией типа никак не отразится на соответствующем .dfm-файле формы. Все pas-файлы являются критическими при пересборке проекта.
Формат .dfm-файлов.
Данный файл содержит описание объектов, расположенных на форме. Содержимое файла можно увидеть в виде текста, вызвав правой кнопкой мыши контекстное меню и выбрав пункт "view as text", или же с помощью конвертера convert.exe (расположенного в каталоге bin), также позволяющего перевести файл в текстовый вид и обратно. Данный файл нужно редактировать очень осторожно, поскольку это может закончиться тем, что IDE не сможет загрузить форму. Этот файл является критическим при перемещении и пересборке проекта.
Формат .DOF-файлов.
Данный текстовый файл содержит текущие установки для опций проекта, как например, настройки компилятора и компоновщика, каталоги, условные директивы и параметры командной строки. Данные установки могут быть изменены пользователем путем изменений настроек проекта.
Формат .DSK-файлов.
Данный текстовый файл хранит информацию относительно состояния вашего проекта, как например, открытое окно и его координаты. Подобно .DOF-файлу, данный файл создается на основе текущей обстановки проекта.
Формат .DPK-файлов.
Данный файл содержит исходный код пакета (аналогично .DPR-файлу стандартного проекта Delphi). Подобно файлу .DPR, .DPK-файл также является простым текстовым файлом, который можно редактировать (см. предупреждение выше) в стандартном редакторе. Одной из причин, по которой вы можете это сделать - использование компилятора командной строки.
Формат .DCP-файлов.
Данный бинарный image-файл состоит фактически из реально скомпилированного пакета. Информация о символах и дополнительных заголовках, требуемых IDE, полностью содержится в .DCP-файле. Чтобы собрать (build) проект, IDE должен иметь доступ к этому файлу.
Формат .DPL-файла.
В действительности это выполняемый runtime-пакет. Данный файл является Windows DLL с интегрированными Delphi-специфическими характеристиками. Данный файл необходим в случае развертывания приложения, использующего пакеты.
Формат .DCI-файла.
Данный файл содержит как стандартные, так и определенные пользователем шаблоны кода, используемых в IDE. Файл может редактироваться стандартным текстовым редактором, или в самой IDE. Как и любой текстовый файл данных, используемый Delphi, редактировать его самостоятельно не рекомендуется.
Формат .DCT-файла.
Это "частный" бинарный файл, содержащий информацию об определенных пользователями шаблонах компонентов. Данный файл не может быть отредактирован никакими способами через IDE. Поскольку данный файл является "личным" файлом IDE, то совместимость с последующими версиями Delphi не гарантируется.
Формат .TLB-файла.
.TLB-файл является "частным" двоичным файлом библиотеки типов. Обеспечивает информацией для идентификации типов объектов и интерфейсов, доступных в ActiveX сервере. Подобно модулю или заголовочному файлу, .TLB служит в качестве хранилища для необходимой символьной информации приложения. Поскольку данный файл является "личным", то совместимость с последующими версиями Delphi не гарантируется.
Формат .DRO-файла.
Данный текстовый файл содержит информацию об объектном хранилище. Каждый пункт данного файла содержит специфическую информацию о каждом доступном элементе в хранилище объектов. Хотя этот файл и является простым текстовым файлом, мы настоятельно не рекомендуем править его вручную. Хранилище может редактироваться только с помощью меню Tools|Repository в самом IDE.
Формат .RES-файла.
Это стандартный двоичный windows-формата файл ресурсов, включающий в себя информацию о приложении. По умолчанию, Delphi создает новый .RES-файл при каждой компиляции проекта в исполняемое приложение.
Формат .DB-файла.
Файлы с таким расширением - стандартные файлы Paradox.
Формат .DBF-файла.
Файлы с таким расширением - стандартные dBASE-файлы.
Формат .GDB-файла.
Файлы с таким расширением - стандартные Interbase-файлы.
Формат .DMT-файла.
Этот "частный" бинарный файл содержит встроенные и определенные пользователем шаблоны меню. Данный файл не может быть отредактирован никакими способами через IDE. Поскольку данный файл является "личным", то совместимость с последующими версиями Delphi не гарантируется.
Формат .DBI-файла.
Данный текстовый файл содержит информацию, необходимую для инициализации Database Explorer. Данный файл не может быть отредактирован никакими способами через Database Explorer.
Формат .DEM-файла.
Данный текстовый файл содержит некоторые стандартные, привязанные к стране, форматы компонента TMaskEdit. Как и любой текстовый файл данных, используемый Delphi, редактировать его самостоятельно не рекомендуется.
Формат .OCX-файла.
.OCX-файл является специализированной DLL, которая содержит все или несколько функций, связанных с элементом управления ActiveX. Файл OCX задумывался как "обертка", которая содержала бы сам объект, и средства для связи с другими объектами и серверами.
©Drkb::00048.
Взято с http://delphiworld.narod.ru.
Как заставить стартовать дельфи без заставки?
| Как заставить стартовать Дельфи без заставки? |
Командной строкой:
Delphi32.EXE -ns.
©Drkb::00049.
Взято с сайта http://www.delphifaq.com.
| Delphi 7 ToolsAPI: Компоненты. |
Создание pop-up меню своего компонента и кое-что еще о классе TComponentExpert.
Давайте рассмотрим создание простейшего одно уровневого контекстного меню на своем компоненте, которое будет открываться при щелчке правой кнопкой по нему в самом верху контекстного меню Delphi.
Как мне избавиться от выскакивающего окна CPU при ошибках?
HKEY_CURRENT_USER\Software\Borland\Delphi\4.0\Debugging.
ViewCPUOnException=0.
©Drkb::00014.
Взято с сайта http://www.delphifaq.com.
Как заставить стартовать дельфи без проекта?
| Как заставить стартовать Дельфи без проекта? |
Командной строкой:
Delphi32.exe -np.
©Drkb::00050.
Взято с сайта http://www.delphifaq.com.
Список префиксов Internal Error.
Иногда выскакивают ошибки типа "Internal Error SY424", вот расшифровка префиксов:
B : debugger.
BC : debugger.
BR : browser.
C : codegen.
CM : command line version of the compiler.
D : parser.
DB : debugger.
DBG: debug info output.
DM : IDE version of the compiler.
E : parser.
EO : debugger/evaluator.
FN : filename / pathname parsing.
GH : HPP generator.
I : code generator.
IN : inspectors.
L : linker.
LI : BPI file writing.
LO : object file loading.
M : memory allocation.
MA : name mangling.
MB : multi-byte (MBCS) support.
O : object (OMF) file handling.
P : package managment.
R : resource writing.
S : scanner.
ST : standard procedure handling.
SY : symbol table.
T : code generator.
TI : RTTI generator.
U : parser.
UD : IDE version of the compiler.
UI : error handling.
URW: DCU reading/writing.
W : Object file (OMF) writing.
X : code generator.
©Drkb::00019.
Взято с сайта http://www.delphifaq.com.
| Delphi и Corel Draw. |
Надеюсь, многие сталкивались с Corel Draw? А у многих слюнки текли, что это мощнейший графический редактор и хотелось бы под него свои программы писать, к примеру, чертежи выводить? Я один из вас :).
Формат файлов *.cdr конечно, не представлю, т.к. сам его не знаю :), но как с этим зверем работать расскажу. Вычитал, что с Corel Draw можно работать только через скрипт, причем изначально я готовил файлы скриптов *.csc, а затем их запускал в самом редакторе. Рабочий инструмент для освоения - Corel Script Editor. Если Вы хотите действительно что-то написать, то он вам просто необходим, хотя бы ради того, что смотреть как Corel Draw их сам создает, ну и самое главное - дока по языку и функциям. Все замечательно, только вот скрипты медленно работают т.к. они эмитируют работу человека - т.е. кнопочки сами нажимаются, панельки меняются и т.д.
А чертеж, к примеру на котором около 3000 объектов мог загружаться и исполнятся до часу! Нет, когда это утомляет, то можно и самому посидеть - глядишь за неделю сделаешь :).
И тут я "чисто случайно" наткнулся на статейку http://www.djpate.freeserve.co.uk/AutoCDrw.htm. Оказывается можно и через OLE этот Corel Draw дергать, и как оказывается, не так уж оно и сложно. Да, совершенно верно, нужно использовать CorelDraw.Automation.xx. Я возился с 8-й версией. Забегая на перед, скажу, что тот же чертеж выводился в течении 5-10 минут.
Ну что, начнем?
Code: Var. CorelDraw: Variant; … CorelDraw := CreateOleObject('CorelDraw.Automation.8'); // цифирку можете свою поставить. CorelDraw.FileNew; // или CorelDraw.FileOpen(FileName); CorelDraw.SetDocVisible(True); // можно и не показывать, что он там делает, но ведь интересно! :). // кстати, можно нарисовать, а потом показать - будет на 30% быстрее. ... // ну и в конце. CorelDraw.FileSave('NewName', 0, False, 0, False); CorelDraw.FileExit(False); // можно не писать, если не надо закрывать. CorelDraw := Unassigned;
Проблема использования компонентов в D6/7 от предыдущих версий.
Решение в случае ошибок компиляции пакетов типа: Missing unit 'Proxies.pas' и Missing DsgnIntf.pas.
1) Сначала заменить.
Uses DsgnIntf;
На.
Uses DesignIntf, DesignEditors;
2) Затем.
Добавить DesignIde.dcp в лист требуемых модулей.
©Drkb::00057.
Взято с сайта http://www.delphifaq.com.
Переименование модулей не решит проблемы 'Proxies.pas' Эта проблема возкает из-за того, что сами компоненты и редакторы свойств помещают в один пакет. Это нехорошо, но в D5 и ниже проходило, а начиная с D6 уже не проходит. Пакет разбиваете на два: run-time и design-time. RT-пакет содержит ТОЛЬКО код самих компонентов и больше НИЧЕГО. Ни один его модуль не должен ссылаться в uses ни на один модуль DT-пакета. В его секции Requires могут содержаться ТОЛЬКО run-time модули VCL. Компилируется RT-пакет в любой каталог, доступный через пути поиска IDE, либо через переменную системного окружения Path. Инсталлировать в IDE его не нужно. DT-пакет содержит процедуру Register, иконки компонентов и все design-time редакторы (свойств и компонентов), если такие есть. Любой его модуль может ссылаться в uses на любой модуль из RT-пакета. В его секцию Requires и подключается DesignIDE.dcp. Компилируется он куда угодно и должен инсталлироваться в IDE. Таким образом, в конечные приложения будут подключаться только модули RT-пакета, в которых нет ссылок на DesignEditors - а тогда не потребуется и Proxies. Кроме того, в конечный проект не будут подтягиваться ненужные в run-time код и ресурсы из DT-пакета. Отвечал Юрий Зотов
©Drkb::00058.
| Как определить - находится ли Ваше приложение в режиме отладки ? |
Автор: Simon Carter.
Обычно господа взломщики, для того, чтобы взломать защиту приложения, запускают его в режиме отладки и анализируют машинный код для определения точки перехвата ввода пароля с клавиатуры.
Обычно таким способом ломаются игрушки :).
Конечно данный способ не сможет полностью защитить Ваш программный продукт от взлома, но прекратить выполнение секретного кода - запросто. Для этого мы будем использовать API функцию IsDebuggerPresent. Единственный недостаток этой функции, заключается в том, что она не работет под Windows 95.
Теперь посмотрим как эту функцию реализовать в Delphi:
Code: Function DebuggerPresent: boolean; Type. TDebugProc = function: boolean; stdcall; Var. Kernel32: HMODULE; DebugProc: TDebugProc; Begin. Result := False; Kernel32 := GetModuleHandle('kernel32.dll'); If Kernel32 <> 0 then. Begin. @DebugProc := GetProcAddress(Kernel32, 'IsDebuggerPresent'); If Assigned(DebugProc) then. Result := DebugProc; End; End;
| Как разместить прозрачную надпись на TBitmap? |
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. OldBkMode : integer; Begin. Image1.Picture.Bitmap.Canvas.Font.Color := clBlue; OldBkMode := SetBkMode(Image1.Picture.Bitmap.Canvas.Handle,TRANSPARENT); Image1.Picture.Bitmap.Canvas.TextOut(10, 10, 'Hello'); SetBkMode(Image1.Picture.Bitmap.Canvas.Handle,OldBkMode); End;
Как использовать клавишу-акселератор в TTabsheets?
Как использовать клавишу-акселератор в TTabsheets? Я добавляю клавишу-акселератор в заголовок каждого Tabsheet моего PageControl, но при попытке переключать страницы этой клавишей программа пикает и ничего не происходит.
Можно перехватить сообщение CM_DIALOGCHAR.
Code: Type. TForm1 = class(TForm). PageControl1: TPageControl; TabSheet1: TTabSheet; TabSheet2: TTabSheet; TabSheet3: TTabSheet; Private. {Private declarations}. Procedure CMDialogChar(var Msg: TCMDialogChar); Message CM_DIALOGCHAR; Public. {Public declarations}. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.CMDialogChar(var Msg: TCMDialogChar); Var. I: integer; Begin. With PageControl1 do. Begin. If Enabled then. For i := 0 to PageControl1.PageCount - 1 do. If ((IsAccel(Msg.CharCode, Pages[i].Caption)) and. (Pages[i].TabVisible)) then. Begin. Msg.Result := 1; ActivePage := Pages[i]; Exit; End; End; Inherited; End;
©Drkb::01160.
Взято из.
DELPHI VCL FAQ Перевод с английского.
Подборку, перевод и адаптацию материала подготовил Aziz(JINX).
Специально для Королевства Дельфи.
Можно ли динамически изменять свойство owner компонента во время выполнения программы?
Вы можете менять свойство "owner" и после создания компонента с помощью методов InsertComponent() и RemoveComponent().
©Drkb::00105.
Взято из.
DELPHI VCL FAQ Перевод с английского.
Подборку, перевод и адаптацию материала подготовил Aziz(JINX).
Специально для Королевства Дельфи.
Как определить наличие сопроцессора?
В отличие от общепринятого мнения не всее клоны 486/586/686/ и Pentium имеют сопроцессор для вычислений с плавающей запятой. В примере определяется наличие сопроцессора и под Win16 и под Win32.
Пример:
Code: {$IFDEF WIN32}. Uses Registry; {$ENDIF}. Function HasCoProcesser : bool; {$IFDEF WIN32}. Var. TheKey : hKey; {$ENDIF}. Begin. Result := true; {$IFNDEF WIN32}. If GetWinFlags and Wf_80x87 = 0 then. Result := false; {$ELSE}. If RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System\FloatingPointProcessor',0, KEY_EXECUTE, TheKey) <> ERROR_SUCCESS then result := false; RegCloseKey(TheKey); {$ENDIF}. End; Procedure TForm1.Button1Click(Sender: TObject); Begin. If HasCoProcesser then. ShowMessage('Has CoProcessor'). Else. ShowMessage('No CoProcessor - Windows Emulation Mode'); End;
©Drkb::01432.
Взято из.
DELPHI VCL FAQ Перевод с английского.
Подборку, перевод и адаптацию материала подготовил Aziz(JINX).
Специально для Королевства Дельфи.
Как узнать серийный номер аудио CD?
CD может иметь или не иметь серийный номер и/или универсальный код продукта (Universal Product Code). MCI-расширение Windows предоставляет эту информации с помощью комманды MCI_INFO_MEDIA_IDENTITY command. Эта команда возвращает уникальную ID-строку.
Пример:
Code: Uses MMSystem, MPlayer; Procedure TForm1.Button1Click(Sender: TObject); Var. Mp : TMediaPlayer; Msp : TMCI_INFO_PARMS; MediaString : array[0..255] of char; Ret : longint; Begin. Mp := TMediaPlayer.Create(nil); Mp.Visible := false; Mp.Parent := Application.MainForm; Mp.Shareable := true; Mp.DeviceType := dtCDAudio; Mp.FileName := 'D:'; Mp.Open; Application.ProcessMessages; FillChar(MediaString, sizeof(MediaString), #0); FillChar(msp, sizeof(msp), #0); Msp.lpstrReturn := @MediaString; Msp.dwRetSize := 255; Ret := mciSendCommand(Mp.DeviceId, MCI_INFO, MCI_INFO_MEDIA_IDENTITY, Longint(@msp)); If Ret <> 0 then. Begin. MciGetErrorString(ret, @MediaString, sizeof(MediaString)); Memo1.Lines.Add(StrPas(MediaString)); End. Else. Memo1.Lines.Add(StrPas(MediaString)); Mp.Close; Application.ProcessMessages; Mp.free; End; End.
©Drkb::01401.
Взято из.
DELPHI VCL FAQ Перевод с английского.
Подборку, перевод и адаптацию материала подготовил Aziz(JINX).
Специально для Королевства Дельфи.
| Как узнать есть ли в заданном CD-RОМ'е Audio CD? |
Можно использовать функцию Windows API GetDriveType() чтобы определить является ли дисковод CD-RОМ'мом. И функцию API GetVolumeInformation() чтобы проверить VolumeName на равенство 'Audio CD'.
Пример:
Code: Function IsAudioCD(Drive : char) : bool; Var. DrivePath : string; MaximumComponentLength : DWORD; FileSystemFlags : DWORD; VolumeName : string; Begin. Sult := false; DrivePath := Drive + ':\'; If GetDriveType(PChar(DrivePath)) <> DRIVE_CDROM then. Exit; SetLength(VolumeName, 64); GetVolumeInformation(PChar(DrivePath),PChar(VolumeName), Length(VolumeName),nil,MaximumComponentLength,FileSystemFlags,nil,0); If lStrCmp(PChar(VolumeName),'Audio CD') = 0 then. Result := true; End; Function PlayAudioCD(Drive : char) : bool; Var. Mp : TMediaPlayer; Begin. Result := false; Application.ProcessMessages; If not IsAudioCD(Drive) then. Exit; Mp := TMediaPlayer.Create(nil); Mp.Visible := false; Mp.Parent := Application.MainForm; Mp.Shareable := true; Mp.DeviceType := dtCDAudio; Mp.FileName := Drive + ':'; Mp.Shareable := true; Mp.Open; Application.ProcessMessages; Mp.Play; Application.ProcessMessages; Mp.Close; Application.ProcessMessages; Mp.free; Result := true; End; Procedure TForm1.Button1Click(Sender: TObject); Begin. If not PlayAudioCD('D') then. ShowMessage('Not an Audio CD'); End;
| Как узнать откуда была установленна Windows? |
Code: Uses Registry; Procedure TForm1.Button1Click(Sender: TObject); Var. Reg: TRegistry; Begin. Reg := TRegistry.Create; Reg.RootKey := HKEY_LOCAL_MACHINE; Reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\SETUP',false); ShowMessage(reg.ReadString('SourcePath')); Reg.CloseKey; Reg.free; End;
Декомпиляция в Delphi.
(перевод одноимённой статьи с delphi.about.com ).
Читая форумы по программированию, иногда натыкаешься на вопрос типа: "У меня есть откомпилированная программа на Delphi. Как мне получить её исходный код?". Обычно такой вопрос возникает, когда программист потерял файлы проекта и у него остался только .exe. Как правило полностью восстановить исходный код на языке высокого уровня невозможно. Значит ли это, что другие тоже не смогут восстановить исходный код Вашей программы ? Хм ... и да и нет ...
Для начала сразу скажу, что восстановить исходный код в точности каким он был однозначно невозможно, так как не существует в мире такого декомпилятора, который бы смог сотворить такое.
После компиляции и линковки проекта и получения исполняемого файла все имена, используемые в программе конвертируются в адреса. Потеря имён означет, что декомпилятор создаст уникальное имя для каждой константы, переменной, функции и процедуры. Даже если мы и достигнем какого-то успеха в декомпиляции исполняемого файла, то получим уже другой синтаксис программы. Данная проблема связана с тем, что при компиляции практически идентичные куски кода могут быть скомпилированы в разные последовательности машинных команд (ASM), которые присутствуют в .exe файле. Естевственно декомпилятор не обладает такой степенью интеллектуальности, чтобы решить - какова же была последовательность инструкций языка высокого уровня в исходном проекте.
Когда же применяется декомпиляция ? Для этого существует довольно много причин. Вот некторые из них:
- Восстановление исходного кода;
- Перенос приложения на другую платформу;
- Определение наличия вирусов в коде программы или вредоносного кода;
- Исправление ошибок в программе, в случае, если создатель приложения не собирается этого делать :).
Легально ли всё это ? Хотя декомпиляция и не является взломом, но утвердительно ответить на этот вопрос довольно сложно. Обычно программы защищены законом об авторских правах, однако в большинстве стран на декомпиляцию делается исключение. В часности, когда необходимо изменить интерфейс программы для конкретной страны, а сервис приложения не позволяет этого сделать.
На данный момент Borland не предоставляет никаких программных продуктов, способных декомпилировать исполняемые файлы (.exe) либо откомпилированные Delphi-модули (.dcu) в исходный код (.pas).
Если же Вы всё-таки решились попробовать декомпилировать исполняемый файл, то необходимо знать следующие вещи. Исходные коды на Delphi обычно хранятся в файлах двух типов: сам исходник в ASCII кодировке (.pas, .dpr) и файлы ресурсов (.res, .rc, .dfm, .dcr). Dfm файлы хранят в себе свойства объектов, содержащихся в форме. При создании конечного .exe, Delphi копирует в него информацию из .dfm файлов. Каждый раз, когда мы изменяем координаты формы, описания кнопок или связанные с ними события, то Delphi записывает эти изменения в .dfm (за исключением кода процедур. Он сохраняется в файлах pas/dcu ). И наконец, чтобы получить при декомпиляции файл .dfm, нужно знать - какие типы ресурсов хранятся внутри Win32 исполняемого модуля.
Все программы, скомпилированные в Delphi имеют следующие секции: CODE, DATA, BSS, .idata, tls, .rdata, .rsrc. Самые важные для декомпиляции секции CODE и .rsrc. В статье "Adding functionality to a Delphi program" приведены некоторые интересные факты о исполняемых форматах Delphi, а так же информация о классах и DFM ресурсах. В этой статье есть один интересный момент под заголовком: "Как добавить свой обработчик события в уже откомпилированный файл, например, чтобы изменять тект на кнопке".
Среди многих типов ресурсов, которые сохранены в .exe файле, интерес представляет RT_RCDATA, который хранит информацию, которая были в DFM файле перед трансляцией. Чтобы извлеч DFM данные из .exe файла, мы можем вызываться API функцией EnumResourceNames.
Исскуство декомпилирования традиционно было уделом мастеров, знакомых с ассемблером и отладчиками. Некоторые Delphi декомпиляторы создают впечатление, что любой, даже с ограниченными техническими знаниями, может изменить по своему желанию большинство исполняемых файлов Delphi.
И в заключение, если Вы заинтересовались декомпилованием, то предлагаю Вам несколько Delphi декомпиляторов:
DeDe.
DeDe довольно шустрая программка, позволяющая анализировать экзешники, скомпилированные в Delphi. После декомпиляции DeDe даёт Вам следующее:
- Все dfm файлы. Вы сможете открывать их и редактировать в Delphi.
- Все объявленные методы с хорошо комментированным кодом на ассемблере с ссылками на строки, импортированных функций, методов и компонент в юните, блоки Try-Except и Try-Finally.
- Большое количество дополнительной информации.
- Вы можете создать папку Delphi проекта со всеми файлами dfm, pas, dpr. Не забудьте, что pas файлы содержат ассемблерный код.
Revendepro.
Revendepro находит почти все структуры (классы, типы, процедуры, и т.д.) в программе, и генерирует их паскальное представление, процедуры естевственно будут представлены на языке ассемблера. К сожалению, полученный ассемблерный код не может быть заново откомпилирован. Так же доступен исходник этого декомпилятора. К сожалению, этот декомпилятор не совсем рабочий - генерирует ошибку при декомпиляции.
MRIP.
Позволяет извлекать из Delphi приложения любые ресурсы: курсоры, иконки, dfm файлы, pas файлы и т.д. Но главная его особенность - это способность извлекать файлы, хранящиеся в других файлах. Поддерживается более 100 форматов файлов. MRip работает под DOS.
Exe2Dpr.
Эта программа может восстановить частично потерянные исходники проекта. Не имеет интерфейса и работает с командной строки, например: 'exe2dpr [-o] exeFile' ( исходники проекта будут созданы в текущей директории).
©Drkb::04511.
Взято из http://forum.sources.ru.
Как сделать меню как в Дельфи?
1. Разместите на форме TControlBar. (закладка Additional) Установите Align = Client.
2. Разместите TToolBar (закладка Win32) внутри TControlBar.
3. Установите в True свойства Flat и ShowCaptions этого TToolBar.
4. Создайте на TToolBar столько TToolButtons сколько Вам нужно. (щелкнув по TToolBar.
Правой кнопкой и выбрав NewButton).
5. Установите свойство Grouped = True для всех TToolButtons. Это позволит меню выпадать.
При перемещении курсора между главными пунктами меню (если меню уже показано).
6. Разместите на фоме TMainMenu и убедитесь, что оно *НЕ присоденено* как меню главной.
Формы. (посмотрите свойство Menu формы).
7. Создайте все пункты меню (щелкнув по TMainMenu кнопкой и выбрав Menu Designer).
8. Для каждой TToolButton установите ее MenuItem равным соответсвующему пункту TMainMenu.
©Drkb::01303.
Взято из.
DELPHI VCL FAQ Перевод с английского.
Подборку, перевод и адаптацию материала подготовил Aziz(JINX).
Специально для Королевства Дельфи.
| Exception при попытке создать обьект класса TPrinter. |
В создании обьекта класса TPrinter с использованием TPrinter.Create нет необходимости,
Так как обьект класса TPrinter (называемый Printer) автоматически создается при.
Использовании модуля Printers.
Пример:
Code: Uses Printers; Procedure TForm1.Button1Click(Sender: TObject); Begin. Printer.BeginDoc; Printer.Canvas.TextOut(100, 100, 'Hello World!'); Printer.EndDoc; End;
| Как перехватить события в неклиентской области формы? |
Создайте обработчик одного из сообщений WM_NC (non client - не клиентских) (посмотрите.
WM_NC в Windows API help). Пример показывает как перехватить вижение мыши во всей.
Неклиенстской области окна (рамка и заголовок).
Пример:
Code: Unit Unit1; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Type. TForm1 = class(TForm). Private. {Private declarations}. Procedure WMNCMOUSEMOVE(var Message: TMessage); Message WM_NCMOUSEMOVE; Public. {Public declarations}. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.WMNCMOUSEMOVE(var Message: TMessage); Var. S : string; Begin. Case Message.wParam of. HTERROR: S:= 'HTERROR'; HTTRANSPARENT: S:= 'HTTRANSPARENT'; HTNOWHERE: S:= 'HTNOWHERE'; HTCLIENT: S:= 'HTCLIENT'; HTCAPTION: S:= 'HTCAPTION'; HTSYSMENU: S:= 'HTSYSMENU'; HTSIZE: S:= 'HTSIZE'; HTMENU: S:= 'HTMENU'; HTHSCROLL: S:= 'HTHSCROLL'; HTVSCROLL: S:= 'HTVSCROLL'; HTMINBUTTON: S:= 'HTMINBUTTON'; HTMAXBUTTON: S:= 'HTMAXBUTTON'; HTLEFT: S:= 'HTLEFT'; HTRIGHT: S:= 'HTRIGHT'; HTTOP: S := 'HTTOP'; HTTOPLEFT: S:= 'HTTOPLEFT'; HTTOPRIGHT: S:= 'HTTOPRIGHT'; HTBOTTOM: S:= 'HTBOTTOM'; HTBOTTOMLEFT: S:= 'HTBOTTOMLEFT'; HTBOTTOMRIGHT: S:= 'HTBOTTOMRIGHT'; HTBORDER: S:= 'HTBORDER'; HTOBJECT: S:= 'HTOBJECT'; HTCLOSE: S:= 'HTCLOSE'; HTHELP: S:= 'HTHELP'; Else s:= ''; End; Form1.Caption := s; Message.Result := 0; End; End.
Kylix Tutorial. Часть 1. Установка.
Итак, свершилось, проходя мимо ларька с CD я увидел компакт с этикеткой синего цвета и магической надписью Kylix. Вернее диска было два и оба были немедленно куплены.
Беглое изучение их показало, что один содержит непосредственно Kylix, а второй дополнительные компоненты и утилиты третьих фирм и Interbase 6. По хорошей традиции пиратами была слита самая "продвинутая" версия Kylix Server Developer.
Ставить Kylix я пробовал под RedHat Linux 7.0.
Для установки нам потребуется.
А) Компьютер с установленной ОС Linux.
Б) В составе Linux должны быть установлены графические оболочки GNOME и / или KDE (возможно Kylix будет работать и с другими, но эти две наиболее распространенны и Borland гарантирует работу под ними). Лично я пользуюсь KDE, посему про нее и буду рассказывать.
В) Права пользователя root.
Первым делом необходимо установить патч на библиотеку glibc, по каким-то причинам не устроившую фирму Borland. Для этого и необходимо иметь права root. Заплатки (patches) сложены в директории /patches CD-ROM. Для Redhat 7.0 необходимо перейти в директорию <путь к CD>/patches/glibc_redhat/7.0. Установка патчей выполняется командами.
Rpm -Uvh glibc-[2c]*
Rpm -Fvh glibc-[dp]* nscd-*
Возможно также Вам придется установить пакет libjpeg либо из дистрибутива RedHat либо из папки <путь к CD>/patches/jpeg6.2.0.
Далее установка может проводиться как под аккаунтом root, так и под простым пользователем. Для инсталляции необходимо запустить на выполнение скрипт setup.sh. из корня CD.Графическая оболочка установки сделана удобно и интуитивно понятна. По умолчанию установка предлагается в домашнюю директорию пользователя, однако, чтобы при наличии нескольких пользователей лучше устанавливать в /usr/kylix, чтобы Kylix был доступен всем.
После установки запуск IDE Kylix можно выполнить.
1) из командной строки: <путь инсталляции>/bin/stаrtкуliх.
2) из выпадающего меню KDE (аналог кнопки Пуск в Windows). Меню Borland Kylix/Kylix.
3) из выпадающего меню KDE (аналог кнопки Пуск в Windows). Меню /RedHat/ Borland Kylix/Kylix.
Далее обнаруживается следующая неприятная вещь: в среде разработки программы компилируются и работают, однако при запуске вне IDE возникают ошибки. Лечится это так.
1. Прописать пути к библиотекам. В файле .bash_profile в строку PATH добавить путь к директории bin Kylix-а. Например, если он установлен в /usr/kylix - то добавить надо путь /usr/kylix/bin.
2. В файл /etc/ld.so.conf добавить строчку с путем к libqtintf.so (в нашем примере /usr/kylix/bin).
3. запустить ldconfig.
Автор: Mike Goblin.
Взято с сайта http://www.delphimaster.ru/
С разрешения автора.
| Как определить, запущена ли Delphi. |
Иногда, особенно при создании компонент, бывает необходимо получить доступ к компоненту только когда запущена Delphi IDE.
Code: If FindWindow('TAppBuilder',nil) <= 0 then. ShowMessage('Delphi is not running !'). Else. ShowWindow('Delphi is running !');
Как включить/выключить спикер?
Это выключит спикер:
SyStemParametersInfo(SPI_SETBEEP,0,nil,SPIF_UPDATEINIFILE);
Это включит:
SyStemParametersInfo(SPI_SETBEEP,1,nil,SPIF_UPDATEINIFILE);
Alexey Lesovik.
(2:5020/898.15).
Взято из FAQ:
Delphi and Windows API Tips'n'Tricks.
Olmal@mail.ru.
Http://www.chat.ru/~olmal.
©Drkb::02259.
| Алгоритм переноса русского текста по слогам? |
Code: Interface. Uses. Windows,Classes,SysUtils; Function SetHyph(pc:PChar;MaxSize:Integer):PChar; Function SetHyphString(s : String):String; Function MayBeHyph(p:PChar;pos:Integer):Boolean; Implementation. Type. TSymbol=(st_Empty,st_NoDefined,st_Glas,st_Sogl,st_Spec); TSymbAR=array [0..1000] of TSymbol; PSymbAr=^TSymbAr; Const. HypSymb=#$1F; Spaces=[' ', ',',';', ':','.','?','!','/', #10, #13 ]; GlasCHAR=['?', 'L', 'х', '+', 'v', '-','р', '-', 'ю', '+', ' ', '-', 'ш', 'L', '¦', '¦', '?', '¦', { english }. 'e', 'E', 'u', 'U','i', 'I', 'o', 'O', 'a', 'A', 'j', 'J' ]; SoglChar=['?', 'г' , 'ъ', '¦' ,'э', '=' , 'у', '+' , '°', '+' , '-' , 'ч', '¦' , '?', '-' ,'?', 'L' , 'т', 'T' , 'я', '¦' , 'Ё', '¦' , 'ы', 'T' , 'ф', '-' ,'ц', '¦' , '?', '+' , 'ё', 'T' , 'ь', '¦' , '?', 'T' , 'с', '+' , { english }. 'q', 'Q','w', 'W', 'r', 'R','t', 'T','y', 'Y','p', 'P','s', 'S', 'd', 'D','f', 'F', 'g', 'G','h', 'H','k', 'K','l', 'L','z', 'Z', 'x', 'X','c', 'C', 'v', 'V', 'b', 'B', 'n', 'N','m', 'M' ]; SpecSign= [ '·', '-','c', '-', 'щ', 'г']; Function isSogl(c:Char):Boolean; Begin. Result:=c in SoglChar; End; Function isGlas(c:Char):Boolean; Begin. Result:=c in GlasChar; End; Function isSpecSign(c:Char):Boolean; Begin. Result:=c in SpecSign; End; Function GetSymbType(c:Char):TSymbol; Begin. If isSogl(c) then begin Result:=st_Sogl;exit;end; If isGlas(c) then begin Result:=st_Glas;exit;end; If isSpecSign(c) then begin Result:=st_Spec;exit;end; Result:=st_NoDefined; End; Function isSlogMore(c:pSymbAr;start,len:Integer):Boolean; Var i:Integer; GlFlag:Boolean; Begin. GlFlag:=false; For i:=Start to Len-1 do. Begin. If c^[i]=st_NoDefined then begin Result:=false;exit;end; If (c^[i]=st_Glas)and((c^[i+1]<>st_Nodefined)or(i<>Start)). Then. Begin. Result:=True; Exit; End; End; Result:=false; End; { Ёрёё?рты ыър яхЁхэюёют }. Function SetHyph(pc:PChar;MaxSize:Integer):PChar; Var. HypBuff : Pointer; H : PSymbAr; I : Integer; Len : Integer; Cur : Integer; { }. Cw : Integer; { =юьхЁ с?ътv т ёыютх }. Lock: Integer; { ё?х??шъ сыюъшЁютюъ }. Begin. Cur:=0; Len := StrLen(pc); If (MaxSize=0)OR(Len=0) then. Begin. Result:=nil; Exit; End; GetMem(HypBuff,MaxSize); GetMem(h,Len+1); For i:=0 to len-1 do h^[i]:=GetSymbType(pc[i]); Cw:=0; Lock:=0; For i:=0 to Len-1 do. Begin. PChar(HypBuff)[cur]:=PChar(pc)[i];Inc(Cur); If i>=Len-2 then Continue; If h^[i]=st_NoDefined then begin cw:=0;Continue;end else Inc(cw); If Lock<>0 then begin Dec(Lock);Continue;end; If cw<=1 then Continue; If not(isSlogMore(h,i+1,len)) then Continue; If. (h^[i]=st_Sogl)and(h^[i-1]=st_Glas)and(h^[i+1]=st_Sogl)and(h^[i+2]<>st_Spec). Then begin PChar(HypBuff)[cur]:=HypSymb;Inc(Cur);Lock:=1;end; If. (h^[i]=st_Glas)and(h^[i-1]=st_Sogl)and(h^[i+1]=st_Sogl)and(h^[i+2]=st_Glas). Then begin PChar(HypBuff)[cur]:=HypSymb;Inc(Cur);Lock:=1;end; If. (h^[i]=st_Glas)and(h^[i-1]=st_Sogl)and(h^[i+1]=st_Glas)and(h^[i+2]=st_Sogl). Then begin PChar(HypBuff)[cur]:=HypSymb;Inc(Cur);Lock:=1;end; If (h^[i]=st_Spec) then begin. PChar(HypBuff)[cur]:=HypSymb;Inc(Cur);Lock:=1; end; End; {} FreeMem(h,Len+1); PChar(HypBuff)[cur]:=#0; Result:=HypBuff; End; Function Red_GlasMore(p:Pchar;pos:Integer):Boolean; Begin. While p[pos]<>#0 do. Begin. If p[pos] in Spaces then begin Result:=False; Exit; end; If isGlas(p[pos]) then begin Result:=True; Exit; end; Inc(pos); End; Result:=False; End; Function Red_SlogMore(p:Pchar;pos:Integer):Boolean; Var BeSogl,BeGlas:Boolean; Begin. BeSogl:=False; BeGlas:=False; While p[pos]<>#0 do. Begin. If p[pos] in Spaces then Break; If Not BeGlas then BeGlas:=isGlas(p[pos]); If Not BeSogl then BeSogl:=isSogl(p[pos]); Inc(pos); End; Result:=BeGlas and BeSogl; End; Function MayBeHyph(p:PChar;pos:Integer):Boolean; Var i:Integer; Len:Integer; Begin. I:=pos; Len:=StrLen(p); Result:= (Len>3). AND. (i>2). AND. (i<Len-2). AND. (not (p[i] in Spaces)). AND. (not (p[i+1] in Spaces)). AND. (not (p[i-1] in Spaces)). AND. ( (isSogl(p[i])and isGlas(p[i-1])and isSogl(p[i+1])and. Red_SlogMore(p,i+1)). OR. ((isGlas(p[i]))and(isSogl(p[i-1]))and(isSogl(p[i+1]))and(isGlas(p[i+2]))). OR. ((isGlas(p[i]))and(isSogl(p[i-1]))and(isGlas(p[i+1])) and. Red_SlogMore(p,i+1) ). OR. ((isSpecSign(p[i]))). ); End; Function SetHyphString(s : String):String; Var Res:PChar; Begin. Res:=SetHyph(PChar(S),Length(S)*2). Result:=Res; FreeMem(Res,Length(S)*2); End; End.
| Как удобнее работать с буфером обмена как последовательностью байт? |
Code: Unit ClipStrm; { This unit is Copyright (c) Alexey Mahotkin 1997-1998. And may be used freely for any purpose. Please mail. Your comments to. E-Mail: alexm@hsys.msk.ru. FidoNet: Alexey Mahotkin, 2:5020/433. This unit was developed during incorporating of TP Lex/Yacc. Into my project. Please visit ftp://ftp.nf.ru/pub/alexm. Or FREQ FILES from 2:5020/433 or mail me to get hacked. Version of TP Lex/Yacc which works under Delphi 2.0+. } Interface uses Classes, Windows; Type. TClipboardStream = class(TStream). Private. FMemory : pointer; FSize : longint; FPosition : longint; FFormat : word; Public. Constructor Create(fmt : word); Destructor Destroy; override; Function Read(var Buffer; Count : Longint) : Longint; override; Function Write(const Buffer; Count : Longint) : Longint; override; Function Seek(Offset : Longint; Origin : Word) : Longint; override; End; Implementation uses SysUtils; Constructor TClipboardStream.Create(fmt : word); Var. Tmp : pointer; FHandle : THandle; Begin. FFormat := fmt; OpenClipboard(0); FHandle := GetClipboardData(FFormat); FSize := GlobalSize(FHandle); FMemory := AllocMem(FSize); Tmp := GlobalLock(FHandle); MoveMemory(FMemory, tmp, FSize); GlobalUnlock(FHandle); FPosition := 0; CloseClipboard; End; Destructor TClipboardStream.Destroy; Begin. FreeMem(FMemory); End; Function TClipboardStream.Read(var Buffer; Count : longint) : longint; Begin. If FPosition + Count > FSize then. Result := FSize - FPosition. Else. Result := Count; MoveMemory(@Buffer, PChar(FMemory) + FPosition, Result); Inc(FPosition, Result); End; Function TClipboardStream.Write(const Buffer; Count : longint) : longint; Var. FHandle : HGlobal; Tmp : pointer; Begin. ReallocMem(FMemory, FPosition + Count); MoveMemory(PChar(FMemory) + FPosition, @Buffer, Count); FPosition := FPosition + Count; FSize := FPosition; FHandle := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE or GMEM_ZEROINIT, FSize); Try. Tmp := GlobalLock(FHandle); Try. MoveMemory(tmp, FMemory, FSize); OpenClipboard(0); SetClipboardData(FFormat, FHandle); Finally. GlobalUnlock(FHandle); End; CloseClipboard; Except. GlobalFree(FHandle); End; Result := Count; End; Function TClipboardStream.Seek(Offset : Longint; Origin : Word) : Longint; Begin. Case Origin of. 0 : FPosition := Offset; 1 : Inc(FPosition, Offset); 2 : FPosition := FSize + Offset; End; Result := FPosition; End; End.
| Как сэкономить память в Ваших программах. |
Автор: Diego Jones.
Совместимость: Delphi 4.x (или выше).
Обычно, когда класс располагается в памяти, то между полями остаются небольшие пространства, несодержащие никакой информации. Оказывается можно избавиться от таких участков памяти и соответственно Ваше приложение будет меньше расходовать оперативной памяти.
Но сначала обратимся к основам типов данных, используемых в Delphi, и детально рассмотрим - сколько байт памяти занимает каждый тип данных:
· boolean, char and byte = 1 байт
· smallInt, word, wordbool = 2 байт
· string, pointers, longint, integer = 4 байт
Теперь давайте посмотрим на объявление класса в нашем исходном коде:
Code: TMyClass = class. Private. Field1: char; Field2: longint; Field3: boolean; Field4: string; Field5: byte; Field6: integer; Public. Procedure proc1; End;
Свойства.
| Kylix Tutorial. Часть 2. Работа с базами данных через dbExpress. |
Фирма Borland в Kylix и Delphi 6 реализовала новый движок для работы с базой данных dbExpress. Данный движок предназначен для работы с серверными БД. На сегодняшний день поддерживаются MySQL, Oracle, Interbase, DB2. К сожалению, на момент выпуска Kylix он работал не со всеми версиями MySQL. В частности, он не заработал с MySQL 3.23.22-beta. За неимением Oracle и DB2 я пользовался Interbase 6, находившемся на втором диске дистрибутива.
Архитектура доступа к данным.
В самом виде приложение для работы с базами данных может быть представлено в следующем виде:

Ничего принципиально нового здесь нет, по сравнению с Delphi. Но это только на первый взгляд. В dbExpress датасеты делятся на два вида:
1. Клиентский датасет (client dataset).
2. Однонаправленные датасеты (unidirectional dataset).
Клиентский датасет хранит выбранные записи в памяти. Это позволяет осуществлять навигацию в любом направлении, фильтровать записи, кешировать изменения итд. Именно данный вид используется для отображения данных пользователю.
Однонаправленные запросы не кешируют данные. Передвигаться по ним можно только в направлении указанном в конструкции ORDER BY SQL запроса, данные не могут быть изменены. Однако они предоставляют быстрый доступ к большим массивам записей.
Компоненты закладки dbExpress.
Посмотрим, что приготовила нам фирма Borland по части компонентов на закладке dbExpress.
| Описание | |
| SQLConnection | |
| Компонент для организации связи с сервером базы данных. Аналог Database в BDE. Позволяет управлять параметрами соединения с сервером БД, такие как путь к базе данных, имя и пароль пользователя итд. | |
| Connected:boolean | Признак установления соединения с БД. True - соединение активно. |
| ConnectionName: string | Имя конфигурации, содержащей параметры соединения. Аналог AliasName в TDatabase для BDE |
| DriverName: string | Имя драйвера для соединения. (DB2, Interbase,Oracle, MySQL). Устанавливается автоматически при установке св-ва ConnectionName |
| KeepConnection: boolean | Поддерживать соединение с сервером БД, если в приложении нет активизированных датасетов. |
| LibraryName: string | Имя библиотеки, содержащей драйвер для связи с сервером БД |
| LoadParamsOnConnect: boolean | Загружать ли параметры соединения, ассоциированные с именем соединения, перед установкой соединения в run time. Полезно в случае когда параметры соединения могут быть изменены вне приложения или меняются в design time |
| LoginPrompt: Boolean | Запрашивать логин и пароль при соединении |
| Name: TComponentName | Имя компонента |
| Params: TStrings | Параметры соединения |
| TableScope: TTableScopes | Параметры видимости таблиц TsSynonym - видеть синонимы TsSysTable - видеть системные таблицы TsTable - видеть таблицы TsView - видеть просмотры |
| VendorLib: string | Имя библиотеки с клиентской частью БД |
| SQLDataSet | |
| Однонаправленный датасет общего назначения. | |
| Active: boolean | Активность датасета |
| CommandText: string | Текст команды (запроса) на получение или манипуляции с данными |
| CommandType: TSQLCommandType | Тип датасета CtQuery - SQL запрос CtTable - таблица, автоматически генерируется запрос на выборку всех записей по всем полям CtStoredProc - хранимая процедура |
| DataSource: TDataSource | Источник данных для мастер датасета |
| MaxBlobSize: integer | Максимальный размер BLOB полей |
| ObjectView: Boolean | Включить иерархическое представление для вложенных полей |
| ParamCheck:Boolean | Обновлять список параметров при изменении текста команды |
| Params:Tparams | Список параметров команды |
| SortFieldNames: string | Список полей для сортировки датасета, поля разделяются точкой с запятой. Действует для CommandType ctTable |
| SQLConnection: TSQLConnection | Имя компонента SQLConnection через который будет происходить работа с БД |
| Tag: integer | Тэг |
| SQLQuery | |
| Запрос к БД (однонаправленный) | |
| Active: boolean | Активность запроса |
| DataSource: TDataSource | Источник данных для мастер датасета |
| MaxBlobSize: integer | Максимальный размер BLOB полей |
| ObjectView: Boolean | Включить иерархическое представление для вложенных полей |
| ParamCheck:Boolean | Обновлять список параметров при изменении текста запроса |
| Params:Tparams | Список параметров запроса |
| SQL:TStrings | Текст запроса |
| SQLConnection: TSQLConnection | Имя компонента SQLConnection через который будет происходить работа с БД |
| Tag: integer | Тэг |
| SQLStoredProc | |
| Хранимая процедура (в случае получения данных однонаправленная) | |
| Active: boolean | Активность хранимой процедуры |
| MaxBlobSize: integer | Максимальный размер BLOB полей |
| ObjectView: Boolean | Включить иерархическое представление для вложенных полей |
| ParamCheck:Boolean | Обновлять список параметров при изменении процедуры |
| Params:Tparams | Список параметров процедуры |
| SQLConnection: TSQLConnection | Имя компонента SQLConnection через который будет происходить работа с БД |
| Tag: integer | Тэг |
| SQLTable | |
| Таблица базы данных (однонаправленный датасет) | |
| Active: boolean | Активность таблицы |
| IndexFieldNames: string | Список полей сортировки (через точку с запятой) |
| IndexName: string | Имя индекса сортировки. Возможно использование либо IndexName или IndexFieldNames |
| MasterSource: TdataSource | Мастер источник данных для организации отношений главный-подчиненный (master-detail) |
| MasterFields:string | Поля связи главный-подчиненный |
| MaxBlobSize: integer | Максимальный размер BLOB полей |
| ObjectView: Boolean | Включить иерархическое представление для вложенных полей |
| SQLConnection: TSQLConnection | Имя компонента SQLConnection через который будет происходить работа с БД |
| TableName: string | Имя таблицы БД из которой будут выбраны данные |
| Tag: integer | Тэг |
| SQLMonitor | |
| Организация наблюдения за работой компонентов доступа к данным | |
| Active: boolean | Активность монитора |
| AutoSave: Boolean | Автоматическое сохранения журнала событий в файл, указанный в FileName |
| FileName: string | Имя файла для хранения журнала событий |
| SQLConnection: TSQLConnection | Имя компонента SQLConnection через который будет происходить работа с БД |
| Tag: integer | Тэг |
| TraceList:Tstrings | Журнал событий |
| SQLClientDataSet | |
| Клиентский датасет общего назначения | |
| Active: boolean | Активность датасета |
| Aggregates: Taggregates | Список доступных агрегатов |
| AggregatesActive: boolean | Вычисление агрегатов |
| AutoCalcFields: boolean | Генерировать событие OnCalcFields и обновлять Lookup поля True - · при открытии датасета · при переходе датасета в состояни dsEdit · Передача фокуса ввода другому компоненту или другому столбцу (для сетки) при наличии изменений в текущей ячейке False · при открытии датасета · при переходе датасета в состояни dsEdit · Запись извлекается из БД |
| CommandText: string | Текст команды для выполнения (SQL запрос). При установке св-ва FileName данное св-во игнорируется При сбросе флага poAllowCommandText в св-ве Options также текст команды игнорируется |
| CommandType: TSQLCommandType | Тип датасета CtQuery - SQL запрос CtTable - таблица, автоматически генерируется запрос на выборку всех записей по всем полям CtStoredProc - хранимая процедура |
| ConnectionName: string | Имя конфигурации, содержащей параметры соединения. Аналог AliasName в TDatabase для BDE |
| Constraints: TConstraints | Ограничения на значения на уровне одной записи |
| DBConnection: TSQLConnection | Имя компонента SQLConnection через который будет происходить работа с БД |
| DisableStringTrim: boolean | Удалять конечные пробелы в строковых полях при их вставке БД |
| FetchOnDemand: boolean | Получать данные по мере необходимости |
| FieldDefs: TFieldDefs | Определения полей |
| FileName: string | Имя файла для сохранения кеша данных |
| Filter: string | Фильтр |
| Filtered: Boolean | Включение фильтрации |
| FilterOptions: TFilterOptions | Параметры фильтрации |
| IndexDefs: TindexDefs | Определения индексов |
| IndexFieldNames: string | Список полей сортировки (через точку с запятой) |
| IndexName: string | Имя индекса сортировки. Возможно использование либо IndexName либо IndexFieldNames |
| MasterSource: TdataSource | Мастер источник данных для организации отношений главный-подчиненный (master-detail) |
| MasterFields:string | Поля связи главный-подчиненный |
| ObjectView: Boolean | Включить иерархическое представление для вложенных полей |
| Options: TProviderOptions | Параметры работы с данными |
| PacketRecord: integer | Количество записей в одном пакете данных -1 - все >0 - количество 0 - включать в пакет только метаданные |
| Params: Tparams | Значение параметров для выборки данных |
| ReadOnly: Boolean | Доступ только для чтения |
| Tag: integer | Тэг |
| UpdateMode: TUpdateMode | Способ поиска записи при записи изменений UpWhereAll - использовать все поля UpWhereChanged - ключевые поля+старые значения измененных полей UpWhereKeyOnly - только ключевые поля |
Попробуем написать простейшее приложение для просмотра данных из базы в /usr/ibdb (будем считать что папка уже создана). Для этого выполним следующие шаги:
1. Создадим базу данных в Interbase 6. У меня он проинсталировался в /opt/interbase .
1.1 Запустим сервер /opt/interbase/bin/ibguard &
1.2 Войдем в оболочку isql - /opt/interbase/bin/isql.
1.3 Введем SQL запросы на создание БД и таблицы users:
Code: Create database '/usr/ibdb/test.gdb'; Create table users( ID integer not null primary key, NAME varchar(20)); Commit; Quit;
| Kylix Tutorial. Часть 3. Работа с базами данных через dbExpress. Коннект - есть коннект. |
Как говорилось раньше, SQLConnection предназначен для.
1. Подключения к базе данных с заданными параметрами.
2. Управления параметрами соединения и драйвера БД.
3. Получения списка установленных драйверов баз данных и доступных соединений.
4. Создания, удаления, редактирования соединений.
5. Управление транзакциями.
6. Выполнение SQL операторов.
7. Получение метаданных.
Подключения к базе данных.
Подключение к базе данных осуществляется установкой св-ва Connected в True, либо вызвом метода Open. Отключение - установка Connected в False или вызов метода Close. Перед подключением необходимо установить имя соединения (св-во ConnectionName).
Пример установления соединения в run-time:
SQLConnection1.Connected:=false;
SQLConnection1.ConnectionName:='test_connect';
SQLConnection1.Connected:=true;
Управления параметрами соединения и драйвера БД.
Значения параметров по умолчанию для соединений хранятся в файле <домашняя директория пользователя>/.borland/dbxconnections или при отсутствии файлов по указанному пути в /usr/local/etc/dbxconnections.conf. Он представляет собой текстовый файл формата ini файлов. То есть содержит набор секций, заключенных в квадратные скобки и параметров в формате Имя параметра=значение параметра. Вот кускок этого файла:
[DB2Connection].
DriverName=DB2.
BlobSize=-1.
Database=DBNAME.
Для драйверов настройки по умолчанию хранятся по указанным выше путям в файле dbxdrivers.
Для получения настроек, в принципе можно анализировать содержимое этих файлов, написав собственный код, однако гораздо удобнее пользоваться стандартными решениями. Свойство Params типа TStrings предоставляет нам доступ к параметрам соединения.
Пример - установка имени и пароля пользователя:
SQLConection1.Params.Values['User_Name']:='SYSDBA';
SQLConection1.Params.Values['Password']:='masterkey';
Свойство LoadParamsOnConnect типа Boolean позволяет управлять автоматической установкой св-в DriverName и Params в значения по умолчанию для данного соединения перед установкой соединения. Данное св-во полезно лишь во время выполнения приложения при смене имени соединения. Во время разработки загрузка установка св-в по умолчанию происходит автоматически.
Получения списка установленных драйверов баз данных и доступных соединений.
Способ №1: анализировать содержимое файлов dbxconnections и dbxdrivers. Неудобно надо писать много собственного кода (который уже написан программистами Borland).
Способ №2. Использование функций из модуля SqlExpr.
GetDriverNames(List: TStrings; DesignMode:Boolean = True) - получение списка доступных драйверов.
List - список для заполнения именами драйверов.
DesignMode - в каком режиме разработки (true) или выполнения программы (false) вызывается функция.
Пример получение списка драйверов dbExpress:
Code: Procedure TForm1.Button2Click(Sender:TObject); Begin. // cb_drivers - это комбобокс для отображения списка драйверов. GetDriverNames(cb_drivers.Items, false); End; Procedure GetConnectionNames(List: TStrings; Driver:string = ''; DesignMode:Boolean = True);
| Kylix Tutorial. Часть 4. Использование однонаправленных наборов данных. |
Однонаправленные наборы данных предоставляют механизм доступа для чтения данных из таблиц сервера БД с помощью SQL команд. Они созданы для быстрого и "легковесного" доступа к информации сервера БД с минимальной загрузкой последнего. Однонаправленные наборы данных посылают команды серверу БД и в случае наличия набора записей, получают однонаправленный курсор для доступа к этим записям. Однонаправленные наборы данных не буферизируют полученные данные в памяти, что делает их более быстрыми и менее ресурсоемкими по сравнению с другими типами наборов данных. Однако, из-за отсутствия буфера записей, однонаправленные наборы данных менее гибки. Многие из возможностей, объявленных в TDataSet либо не реализованы, либо вызывают генерацию исключительных ситуаций. Например:
· Из методов навигации поддерживаются только First и Next. Большинство других методов вызывают исключения. Некоторые, такие как методы работы с закладками (Bookmarks), представляют собой просто заглушки.
· Нет встроенной поддержки для редактирования записей, поскольку для этого требуется буферизация.
· Свойство CanModify всегда равно False, и попытки перевода датасета в режим редактирования всегда приводят к неудаче. Редактирование данных, однако, может осуществляться с помощью SQL команды UPDATE или при помощи подключения клиентского набора данных.
· Отсутствует поддержка фильтров, поскольку это требует буферизации. При попытке фильтрации вызывается исключение. Все ограничения на диапазон выбираемых записей должны содержаться в SQL команде.
· Отсутствует поддержка lookup полей, т.к при этом необходимо буферизировать возможные значения такого поля.
Однако отсутствие буферизации делает однонаправленные наборы данных быстрыми, простыми в реализации и распространении. На закладке dbExpress палитры компонентов представлены четыре типа однонаправленных наборов данных: TSQLDataSet, TSQLQuery, TSQLTable и TSQLStoredProc.
TSQLDataSet наиболее общий из всех четырех. Может использоваться для выборки любых данных или выполнения SQL команд. Данный компонент рекомендуется использовать для работы через dbExpress.
TSQLQuery можно использовать так же как и TSQLDataSet. Главное отсутсвии поддержки хранимых процедур (хотя многие сервера позволяют вызывать хранимые процедуры из запросов, но каждый делает это по-своему, стандартный синтаксис отсутствует). Данный компонент присутствует, в основном, для обеспечения совместимости при переносе приложений из Windows.
TSQLTable компонент, дающий доступ ко всем записям и полям таблицы. Данный компонент присутствует, в основном, для обеспечения совместимости при переносе приложений из Windows.
TStoredProc компонент доступа к хранимым процедурам сервера. Данный компонент присутствует, в основном, для обеспечения совместимости при переносе приложений из Windows.
Подключение к базе и открытие. Управление транзакциями.
Так как TSQLDataSet рекомендован в качестве универсального датасета доступа к данным далее мы его и рассмотрим, хотя многие моменты, как Вы заметите, будут верны для всех однонаправленных датасетов. Для получения данных с SQL сервера необходимо подключить однонаправленный датасет к БД, задать комаду и активизировать (открыть) его.
Для подключения к БД необходимо установить свойство SQLConnection равным имени компонента TSQLConection, который обеспечивает подключение к базе. В инспекторе объектов на этапе разработки автоматически отображается выпадающий список доступных подключений.
Текст команды необходимо указать в свойстве CommandText, интерпретация текста команды зависит от типа команды, указанного в свойстве CommandType. TSQLDataSet поддерживает 3 типа команд:
· ctQuery - в свойстве CommandText должен присутсвовать SQL запрос;
· ctStoredProc - в свойстве CommandText указывается имя хранимой процедуры;
· ctTable - в свойстве CommandText указывается имя таблицы из которой будут выбраны записи.
Активизация датасета в случае выполнения команды, возвращающей набор данных, (запросы SELECT, выборка таблицы итд) производится установкой свойства Active в True, либо вызовом метода Open. В обратном случае, если набор данных при выполнении команды не формируется (не путайте с пустым набором данных), нужно использовать метод ExecSQL.
Управление транзакцией, в которой осуществляется работа с сервером БД, однонаправленный датасет реализует через public свойство TransactionLevel. При одновременном открытии нескольких транзакций в компоненте TSQLConnection, можно установить TransactionLevel в значение идентификатора желаемой транзакции (поле TransactionID в структуре описания транзакции TTransactionDesc) при этом выполнение команд будет происходить в заданной транзакции.
Примеры:
Code: // Выполнение запроса возвращающего набора данных. Begin. SQLDataSet1.CommandType:=ctQuery; SQLDataSet1.CommandText:='select * from MyTable'; SQLDataSet1.ExecSQL; End; // Выполнение запроса не возвращающего набора данных. Begin. SQLDataSet1.CommandType:=ctQuery; SQLDataSet1.CommandText:='update MyTable set StrField1 = '''+Edit1.Text+''' where id = 5'; SQLDataSet1.ExecSQL; End; // Выполнение запроса возвращающего таблицу с SQL сервера. Begin. SQLDataSet1.CommandType:=ctTable; SQLDataSet1.CommandText:='MyTable'; SQLDataSet1.ExecSQL; End;
Обзор Kylix.
Недавно выпущенный фирмой Borland продукт - Kylix (Delphi for Linux) вызвал большой интерес в среде разработчиков. На предрелизном этапе разработки мнения о Kylix порой были диаметрально противоположны, от экзальтированно-восторженных до агрессивно-негативных.
Автор попытался в данной статье изложить свое мнение о данном продукте, основанное на недельном опыте работы его эксплуатации. Претендовать на 100% объективность, поработав столь недолгое время, трудно, но надеюсь, что мои мысли будут интересны и полезны читателям.
Итак, то, что нам так долго обещала компания Borland, свершилось, Kylix выпущен. О начале разработки компания Borland (тогда еще Inprise) объявила приблизительно год назад (или даже ранее). Это вызвало огромную заинтересованность в мире разработчиков ПО, т.к на сегодня Linux является одной из наиболее быстро растущих операционных систем. В последнее время рост Linux намечается не только как ОС для серверов, но и как операционной системы настольных ПК. (в подтверждение этого, я пишу эти строки в Linux) Развитие Linux, как ОС для настольных систем вызвало необходимость создания средств разработки приложений с графическим интерфейсом. Конечно, разработчики графических систем GNOME и KDE предлагают свои средства разработки, такие как GLADE и KDeveloper (которые к тому же бесплатны), но ни одно из этих средств разработки не может полностью удовлетворить возрастающие требования разработчиков. Таким образом, незаполненная ниша средств разработки приложений под Linux и огромная популярность Delphi дает Borland отличные шансы закрепиться на данном рынке.
Установка.
Для тестирования была выбрана версия Kylix Server Developer Version 1.0 Build 5.62. Комплект поставки включал 2 CD. Один собственно с Kylix, а второй содержит дополнительные инструменты и компоненты. Откуда все взялось ? Вопрос риторический (ларек за углом). Сразу хочу принести искренние извинения компании Borland, ну нет меня нужной суммы (~2000$) чтобы оценить качество продукта (спонсоры где вы :-) ). Ну хватит о грустном .
В качестве ПК использовался P-III 500 , RAM 64 Mb, HD 10 Gb. Опрерационные системы - Red Hat Linux 7.0, Red Hat Linux 6.2. В качестве графической оболочки использовалась KDE KDE 2, GNOME. Установка проводилась как под аккаунтом root, так и под простым пользователем. Сразу отмечу, что инсталляция проходила гладко и без проблем (если не считать установку патчей glibc, что было запрошено программой инсталляции и также не вызвало затруднений). Графическая оболочка сделана удобно и интуитивно понятна.
После установки запуск графической оболочки также проходил без проблем. Не скажу, что скорость работы IDE меня поразила, но была вполне приемлема и не вызывала неприятных ощущений от заторможенности.
Резюме: инсталляция и запуск IDE не должны вызывать проблем.
IDE и Help.
IDE Kylix практически дублирует IDE Delphi 5, чего, вообще говоря, и следовало ожидать. Первое впечатление, что ты и не покидал Delphi. Однако, по прошествии некоторого времени, обнаруживаешь, что отсутствует меню Database (где любимый SQL Explorer?). Средства создания приложения с поддержкой нескольких языков также не обнаружились . Наблюдаются различия и в палитре компонентов: отсутствует закладка Win32 (ну еще бы), на закладку Additional перенесены таймер и PaintBox (закладка System, в которой они находятся в Delphi упразднена, по всей видимости за ненадобностью), введены новые компоненты LCDNumber (ЖК индикатор), TextBrowser и TextViewer.
Чего действительно не будет хватать, так элементов для ввода даты, времени. Поработав еще немного, обнаруживаешь и приятные нововведения. Так, например, TButton обзавелся свойством Color, ТМаinМеnи имеет свойство Bitmap, позволяющее накладывать под меню картинку.
Object Pascal тоже не огорчил меня, все тот же простой и удобный язык программирования.
Поддержка "великого и могучего" определяется в первую очередь настройкой самой ОС.
Система встроенной подсказки аналогична привычной системе в Windows. В Kylix программа вывода подсказки называется HyperHelp и, судя по About Box, выпущена Bristol Technology Inc.Файлы подсказки имеют расширение hlp, но не открываются в Windows :(.
Формате pdf (Acrobat Reader). Один из них - Developer Guide использовался мной очень часто и продуктивно. Документация достаточно полна, детализирована и удобна для изучения.
Обнаружение в каталоге установки папки wineserver подтвердило слухи о том, что в своей работе IDE использует эмуляцию Windows. Какие конкретно функции используются, выяснить не удалось, но наличие данного факта радости мне не прибавило.
Резюме: IDE практически аналогичен Delphi 5. Переход от IDE Delphi к Kylix проходит безболезненно.
Функциональные возможности Kylix.
Компоненты создания пользовательского интерфейса.
Естественно, что так как сама Borland позиционирует Kylix как Delphi под Linux я часто буду сравнить эти два средства разработки.
Начнем с набора визуальных компонентов в палитре. Большинство визуальных компонентов для создания пользовательского интерфейса присутствуют в обеих средах разработки. Отметим, что в Kylix стандартные компоненты получили свое дальнейшее развитие, т.е были добавлены новые свойства и методы, расширяющие функциональность компонентов и делающие их использование более гибким. В то же время в компонентах Kylix не были замечены какие-либо средства для поддержки восточных языков (Bidi Modes), так же отсутствуют свойства, связанные с плавающими окнами (docking).
Каких-либо кардинальных нововведений в процессе визуального программирования в Kylix мной замечено не было, да и, наверное они не нужны, т.к Delphi предоставляет одну из лучших (если не лучшую) среду визуальной разработки пользовательского интерфейса. Минут за 20-30 без всяких проблем мне удалось написать тестовое приложение типа редактора Notepad.
Резюме: Таким образом, можно сказать, что Kylix является одним из самых удобных средств быстрой разработки графических интерфейсов под Linux.
Работа с базами данных.
Еще одним критически важным для разработчика сегментом является создание приложений работы с БД. Данную задачу можно разделить на три части:
1. Отображение данных пользователю.
2. Механизм организации передачи данных приложение - БД.
3. Генерация отчетов и диаграмм.
Первая задача решается компонентами с закладки DataControls. Почему-то на данной закладке не было компонентов DBRichEdit, DBCtrlGrid, DBChart. Особенно хочется выразить сожаление по поводу отсутствия последнего из перечисленных компонентов, т.к на сегодня практически невозможно писать приложения без деловой графики. Остальные компоненты были аналогичны Delphi, что позволяет без проблем отображать данные пользователю.
Механизм организации передачи данных приложение - БД. Реализация данной задачи фирмой Borland вызвала у меня самые неоднозначные реакции.
Во первых наблюдаются некоторые зачатки (а может и атавизмы) технологии MIDAS. Они представлены компонентами Provider и ClientDataSet.
Во вторых, как и анонсировалось ранее, в Kylix в качестве движка работы с серверами БД используется новая технология dbExperss (которую обещают сделать много платформенной). Компоненты размещены на закладке с соответствующим именем. Набор компонентов наиболее близок к закладке ADO в Delphi 5 и включает:
SQLConnection - соединение с БД. Данный компонент по функции аналогичен ADOConnetion, т.е позволяет настроить параметры связи с сервером БД.
SQLQuery, SQLTable, SQLDataSet, SQLStoredProc - компоненты работы с объектами БД. В документации эти компоненты названы unidirectional datasets (о значении этого термина позднее).
SQLMonitor - компонент регистрации событий работы с БД.
SQLClientDataSet - компонент доступа к данным (client dataset).
Решив разработать небольшое приложение для просмотра и редактирования БД, я разместил на форме SQLConnection и попытался связаться с сервером MySQL (ver 10.8 Distrib 3.23.22-beta, входит в поставку Red Hat Linux 7.0). Здесь я наступил на первые грабли в виде сообщения Unable to load libmysql.so.6.0.0. После недолгих поисков обнаружилась /usr/lib/myslq/libmysql.so.9.0.0, которая и была прописана в свойстве VendorLib, но и это не помогло мне, т.к на все последующие попытки ввода login/password я с неизменным успехом видел Invalid User/Password. Обозлившись на это дело и перепробовав все возможные комбинации host/database name/login/password, с соседней машины Windows подключился к базе без всяких проблем (через ODBC), из командной строки подключение к MySQL проблем тоже не вызвало. Через несколько дней на сайте Borland обнаружилось подтверждение, того что я не одинок. Как было указано, с версией MySQL 3.23 dbExpress не работает. К Interbase 6 на локальной машине подключение не вызвало проблем. Протестировать подключение к Oracle и DB2 не представилось возможным за отсутствием оных. Драйверов для других типов баз данных в поставке не обнаружилось.
Далее по старой памяти кинув на форму DataSource и SQLTable, настроив их нужные свойства (по аналогии с Delphi) и активизировав SQLTable (Active:=true), я было попытался установить свойство DataSource в сетке данных (DBGrid), и тут меня ждали вторые грабли - Operation not allowed on unidirectional dataset. После тщетных попыток найти ошибку в своих действиях, когда уже ничего не помогало, я прибегнул к самому отвратительному :) - чтению документации.
Как оказалось, все компоненты типа DataSet делятся на два типа.
Unidirectional.
Client dataset.
Unidirectional - данный вид набора данных предназначен для извлечения данных через SQL запросы. Он позволяет двигаться по данным только вперед (согласно сортировке заданной order by) и не кэширует изменения, что делает его более быстрым и менее требовательным к ресурсам. Однако, он не позволяет пользователю редактировать данные, накладывать фильтры и Lookup. По вышеуказанным причинам я и не смог подключить unidirectional dataset для отображения в DBGrid.
Второй тип наборов данных - Client dataset. Данный тип наборов данных сохраняет записи из БД в буфер, позволяя перемещаться по записям в любом направлении. Именно данный тип набора данных может быть отображен в DBGrid и других элементах пользовательского интерфейса.
На основе всего вышесказанного, в документации предложено несколько архитектур взаимодействия с БД.
1. Работа на локальной машине с набором данных формируемым TClientDataSet. В Delphi 5 данная модель взаимодействия называлась briefcase. В Kylix (по крайней мере сегодня) нет возможности передавать изменения на сервер, т.е они остаются в файлах *.cds (или xml). В данном случае файлы *.cds представляют собой аналог локальных БД. Загружать/выгружать содержимое на диск можно соответственно функциями LoadFromFile/SaveToFile. Что неудобно в данном методе - определять структуру таблиц.
2. Работа с серверами БД через unidirectional dataset.
Данная архитектура позволяет просматривать только одну запись из базы в каждый момент времени. Редактирование недоступно, двигаться по набору записей можно только вперед. Данный вид архитектуры полезен для статистического анализа записей БД, но на его основе практически нельзя построить приложение для ввода и редактирования данных пользователями.
3. Комбинация client и unidirectional dataset. Unidirectional dataset используется для извлечения данных с сервера БД, client dataset для буферизации, отображения, редактирования данных. Здорово завернуто, правда, особенно после Delphi, где до такого еще не додумались. Практически здесь выделены все компоненты трехзвенной архитектуры, но сервер приложений находится в одном приложении с презентационным слоем.
4. Понятное дело, что для создания классического двухуровневого клиент-сервера вышеописанная архитектура (3) слишком сложна и не дает никаких преимуществ. Посему для облегчения нам жизни существует еще одна архитектура доступа к серверам БД.
В качестве SQL client dataset используется компонент SQLClientDataSet, один за всех (Table, Query, StoredProc). Он может отображать данные в элементах управления, вставлять и редактировать их.
5. И наконец последнее, что предлагает Borland. Многоуровневая архитектура баз данных. Данная модель является логическим развитием модели №3.
Но не спешите радоваться, т.к внизу как всегда есть приписочка, что компоненты типа Connection component придется приобретать отдельно, когда это станет возможно не сказано. Фактически, в той поставке, что тестировалась, создать трехзвенное приложение было невозможно. Причины этого на мой взгляд, в том что в Delphi было по большому счету две технологии взаимодействия приложений DCOM и CORBA. При тщательном рассмотрении оказывалось, что CORBA в Delphi являлась надстройкой над DCOM, а последний является на сегодня жестко привязанным к платформе Windows. Из данной ситуации есть два выхода: реализовывать DCOM для Linux (кстати частично это все же присутствует в Kylix), либо переходить на платформно незвависимую технологию CORBA. Оба эти пути достаточно сложны и имеют свои подводные камни, так что не будем судить Borland слишком строго.
Генерация отчетов и диаграмм. Решения данной задачи (т.е каких-либо генераторов отчетов) я не обнаружил вовсе. Что, мягко говоря, не очень понятно прикладным программистам.
Подведем итоги. Итак, базы данных и приложения работы с ними, по их устройству можно классифицировать на.
1. Локальные БД (Dbase, Paradox). Решения для работы с локальными базами данных, даже формата dbf я не обнаружил, что для меня не понятно. Остается лишь вариант ClientDataSet+*.cds (архитектура 1), что не вызывает у меня большого восторга, т.к на сегодняшний день существует большое количество унаследованных систем хранящих данные именно в формате DBase или Paradox.
2. Двухуровневые БД (клиент - сервер). Приемлемо, достаточно удобно, хотя и непривычно.
3. Многоуровневые. Сейчас невозможно. Хотя в документации описание присутствует. Надеюсь, что в ближайшее время разработка многоуровневых БД станет доступной.
Резюме: работа с базами данных в Kylix еще далека от совершенства. Реально, на сегодня можно разрабатывать приложения клиент-сервер для работы с DB2, Oracle, MySQL( надеюсь, что в ближайшее время ошибки будут устранены) и Interbase.
Разработка приложений для Internet.
Что действительно порадовало меня, так это включение в Kylix набора компонентов для работы с Internet - Indy. Данная библиотека содержит большое количество компонентов, обеспечивающих работу с многими сервисами интернета (сокеты TCP и UDP, DNS, FTP итд). Причем для многих протоколов реализованы не только клиенты, но и сервера. Включение Indy явилось безусловным плюсом в развитии Kylix (и Delphi, т.к Indy должен войти в Delphi 6). Большое количество примеров работы с данной библиотекой находится в архиве /demos/Internetdirect/IndyDemos.tar.gz.
Другой стороной работы с Internet является расширение возможностей Web серверов, помимо CGI сценариев, Kylix позволяет разрабатывать и модули популярного Web сервера Apache. Принцип построения модулей Apache аналогичен написанию CGI, т.е используется WebModule итд. По ставнению c Delphi технология разработки не претерпела изменений, поэтому останавливаться подробно на ней мы не будем. Для запуска примеров, по крайней мере под RedHat, пришлось перекомпилировать сервер Apache с включением поддержки DSO. Данная процедура подробно была описана в документации Kylix и не вызывала проблем. А вот инсталляция примера заставила меня немного потрудиться. Вызвано это было тем, что в документации и примере содержались ошибки, связанные с тем, что параметры конфигурации были указаны неверно, вернее был неправильно указан регистр букв( замечу, что для Apache в Unix имена параметров HelloWorld и Helloword две большие разницы). В конце концов тестовый пример был благополучно запущен, но впредь было бы хорошо, чтобы Borland более внимательно относилась к тестированию примеров.
Резюме: разработка приложений для взаимодействия с сервисами Internet удобна и достаточно развита. Создание CGI приложений было дополнено средствами создания модулей для популярного Web сервера Apache. Однако отсутствие поддержки регулярных выражений (а HTTP текстоориентированный протокол ), на мой взгляд, является заметным недостатком в данной области.
Объектная библиотека (CLX).
Библиотека CLX (Borland Component Library for Cross Platform - произносится как кликс) , на основе которой построена иерархия объектов в Kylix, является дальнейшим развитием объектной библиотеки Delphi - VCL. Главной целью разработки CLX стало стремление создать независимую от операционной системы библиотеку классов и компонентов. Наверное, с выходом Delphi 6 эта цель будет окончательно достигнута.
Все основные классы VCL обнаружились и в CLX, т.е родитель - это TObject итд. Миграция от VCL к CLX, как мне кажется, не вызовет серьезных проблем у большинства разработчиков.
Как и в Delphi кроме классов в иерархии присутствуют интерфейсы. Вот тут и кроется один интересный момент. В иерархии интерфейсов явно виднеется IUnknown, в интерфейсах есть GUID, wine для оболочки, чую Windows. Думаю, далее следует вопрос, а сможет ли когда-либо Kylix окончательно стать независимым от Windows COM или Borland будет последовательно переносить COM на Linux?
Конечно, COM является одним из лидеров технологий распределенных приложений, но ведь в последнее время все более заметной становится другая технология CORBA, объективно обладающая рядом достоинств, по сравнению с COM. CORBA активно и довольно успешно поддерживалась на платформе Windows, был разработана версия VisiBroker для Linux.
Если касаться внутреннего устройства CLX, то надо отметить, что она базируется на мультиплатформенной библиотеке Qt фирмы TrollTech http://www.troll.no/. Поскольку Qt написана на C++, необходимо было реализовать промежуточный слой, транслирующий модель компонентов Kylix в вызовы функций Qt (напомню, что в С++ не поддерживаются свойства). Данный в слой был назван CLXDisplay. Отношения между Qt и CLX по части визуальных компонентов сродни отношениям VCL и Win32 API (в части элементов управления). В CLX "общение" c Qt происходит по следующим правилам.
Компоненты CLX имеют указатель(handle), связывающий их с объектом соответствующего widget (графический элемент управления - в терминологии Qt). Имя типа handle формируется как имя класса Qt+H. Имена классов в Qt начинаются с Q. Например, для Label имя класса Qt - Qt. Handle при этом будет типа QLabelH. Прямой доступ к методам и членам widget может быть осуществлен через Handle. Так конструктор создания Label на C++
QLabel(QWidget *parent=0, const char *nome=0, WFlags f=0).
Транслируется в Object Pascal.
Function QLabel_create(parent: QWidgetH; name: PAnsiChar; f: WFlags): QLabelH; overload; cdecl;
При использовании метода класса первым параметром является Handle. Так если, в Qt метод установки Caption.
Virtual void setText(const QString &).
В Object Pascal будет выглядеть как.
TxtLbl := 'this is a test';
QLabel_setText(LabelHandle,@Txtlbl);
События (в терминологии Qt - signals) и обработчики события (в терминологии Qt - slots). В Qt связывание события с обработчиком выглядит так:
// Декларация.
Bool connect (const QObject *sender, const char *signal, const char *member).
// Привязка.
Connect(Button,SIGNAL(clicked()),App,SLOT(quit()).
В Object Pascal.
Function QObject_connect(sender: QObjectH; signal: PAnsiChar; Receiver: QObjectH; member: PAnsiChar): Boolean; overload;
Так например если связать сигнал нажатия кнопки со слотом quit приложения, то при нажатии на кнопку приложение будет закрываться. Таким образом осуществляется делегирование обработки событий. Однако данная техника не позволяет в Kylix (в отличии от С++) писать собственные обработчики событий (слоты). Так, в нашем примере мы не сможем выдать диалог подтверждения закрытия.
Для создания возможности написания собственных обработчиков событий применяются перехватчики (hooks). Для подключения собственного обработчика необходимо: создать hook (ButtonHook := QButton_hook_create(Button);), получить ссылку на обработчик (QButton_clicked_Event(BtnClicked) := DummyClass.ButtonClicked;), связать их (QButton_hook_hook_clicked(ButtonHook, BtnClicked);).
Все вышеописанное говорит о большой работе, проделанной программистами Borland при разработке CLX. За подробностями взаимодействия CLX с Qt рекомендую обратиться к статье Bruno Sonnino "Programming Kylix with the CLXDisplay API", размещенную на сайте Borland.
В связи со всем сказанным возникает вопрос - как будет взаимодействовать с Qt анонсируемый Borland аналог C++ Builder. С одной стороны он должен поддерживать CLX, с другой вышеописанные проблемы с созданием собственных слотов перед ним стоять, по идее, не должны.
Совместимость с Delphi 5.
Одной из задач разработки Kylix являлось обеспечение максимальной совместимости с Delphi. Данную задачу можно разбить на несколько подзадач.
Обеспечение идентичности языка программирования. Достигнуто - в обоих средствах разработки приложений используется Object Pascal.
Близкое сходство IDE. Достигнуто. Переход из IDE Delphi в IDE Kylix не вызывает проблем.
Общность библиотеки классов. CLX разрабатывался максимально совместимым с VCL. Анонсировано, что в Delphi 6 VCL будет замещен CLX. Так что, в ближайшее время задача будет решена.
Одинаковость файлов, составляющих проект. Почти достигнута, в Kylix, по умолчанию, файлы *.dfm (описание свойств компонентов формы) имеют расширение *.xfm.
Документация Kylix содержит довольно большой раздел, посвященный переносу приложений и совместимости с Delphi.
В частности, о чем придется забыть в Kylix.
1. Вызовы Windows АРIРеестр Windows.
2. COM & ActiveX.
3. BDE&ADO.
4. CORBA.
5. Трехзвенные БД.
Таким образом, на сегодняшний день можно говорить о частичной совместимости Delphi и Kylix, и то на уровне графического интерфейса пользователя.
Моя личная попытка перенести простейший проект с одной формой из Delphi в Kylix и обратно увенчались успехом, однако, по моему мнению, ни один из серьезных проектов автоматически или даже полуавтоматически из Delphi5 в Kylix портирован не будет.
Ранее для Delphi неоднократно отмечалось, что размер даже минимального проекта с одной формой составлял более 200 кбайт. Kylix, к сожалению, поддержал и развил эту традицию. Размер вышеуказанного проекта составил 385 к байт.
Резюме: Разработчики приложили максимум усилий для совместимости Kylix и Delphi. По анонсам Delphi 6 должна быть максимально совместима с Kylix, однако глубокие различия Linux и Windows приводят к тому, что на практике 100% совместимость будет вряд ли достигнута.
Заключение.
Итак, подведем итоги. Безусловно, что выход Kylix является событием в мире разработчиков программного обеспечения. Необходимость в продукте данного класса назрела и нужно выразить большую благодарность Borland за усилия, затраченные на разработку. Сильной стороной продукта являются:
Удобный IDE.
Документированность продукта.
Хорошо продуманная среда разработки графического интерфейса пользователя.
Средства разработки приложений для Internet.
Максимальная приближенность к Delphi.
Слабые стороны.
Отсутствие технологий создания распределенных объектов (в первую очередь CORBA).
Некоторые неисправленные ошибки.
Небольшой набор драйверов для серверов баз данных.
Невозможность работы с локальными БД.
Отсутствие отчетов в БД.
Как мне кажется, как среда разработки Kylix дозрел до уровня Delphi 2-3. Можно рекомендовать его как средство разработки графического интерфейса, работы с серверами базами данных клиент-сервер, разработки приложений для работы с сервисами интернета. В остальных областях программирования ( мультимедиа, распределенные объекты, трехзвенные БД) налицо наличие нереализованного потенциала развития. Данному продукту можно поставить твердую четверку.
Автор: Mike Goblin.
Взято с сайта http://www.delphimaster.ru/
С разрешения автора.
©Drkb::04578.
| Работа с клиентскими наборами данных (DBExpress). |
Введение.
В данной части будет рассмотрено применение клиентских наборов данных в dbExpress. Согласно иерархии классов в Kylix к клиентским наборам данных относятся классы TSQLClientDataSet и TClientDataSet. Последний из них является частью технологии MIDAS. Так как на сегодняшний день поддержка данной технологии в Kylix до конца не реализована, то основное внимание мы уделим рассмотрению TSQLClientDataSet.
Компоненты класса TSQLClientDataSet предназначены для создания двухзвенных приложений клиент сервер. Так же как и однонаправленные наборы данных, они используются для работы с сервером БД через TSQLConnection. С другой стороны многие из методов и событий класса TSQLClientDataSet характерны для клиентского датасета в технологии MIDAS. На самом деле TSQLClientDataSet - это гибрид, содержащий в себе объекты однонаправленного набора данных, клиентский набор данных и объект провайдера для применения внесенных изменений на сервере БД. "Запихивание под капот" этих объектов позволило существенно упростить разработку двухзвенных приложений баз данных в dbExpress.
Простейший проект.
Работа с TSQLClientDataSet будет проиллюстрирована на примере простой базы данных служащих организации. В качестве сервера БД выбран Interbase 6, т.к он входит в поставку Kylix. Предварительно необходимо создать базу данных с таблицей EMPLOYEERS, описанной следующим образом:
Code: /* Table: EMPLOYEERS, Owner: SYSDBA */ CREATE TABLE "EMPLOYEERS". ( "ID" INTEGER NOT NULL, "NAME" VARCHAR(200) NOT NULL, PRIMARY KEY ("ID"). ); СRЕАТЕ GENERATOR "EMP_GEN"; SET TERM ^ ; /* Triggers only will work for SQL triggers */ CREATE TRIGGER "EMPLOYEERS_BEFORE_INS" FOR "EMPLOYEERS". ACTIVE BEFORE INSERT POSITION 0. AS. BEGIN. NEW.ID = GEN_ID(EMP_GEN,1); END. ^ COMMIT WORK ^ SET TERM ;^
Использование SQLMonitor (DBExpress).
Следи за собой - будь осторожен или использование SQLMonitor.
Итак, остался всего один компонент на закладке dbExpress, не обследованный нами - это SQLMonitor. Как вы уже догадались - его задача - это протоколирование событий, происходящих в программе при обращении к базе данных. Протокол может выводиться как в StringList, так и файл.
Создадим новый проект (File/ New Application). На главную форму положим SQLConection, SQLClientDataSet. Настроим SQLConnection на соединение с БД, а SQLClientDataSet на получение данных через SQLConnection. Свойства Connected SQLConnection и Active SQLClientDataSet установим в True. Все стандартно.
Теперь положим на форму компонент - SQL Monitor. Обглядим, какими свойствами И событиями он обладает:
Active Boolean Активность монитора- вкл/выкл.
AutoSave Boolean Авто сохранение протокола в файл, имя файла должно быть указано в св-ве FileName.
FileName String Имя файла для сохранения протокола. Если св-во AutoSave установлено в true, то в указанный файл протокол записывается автоматически, если AutoSave :=false, то FileName используется как файл по умолчанию, для методов LoadFromFile, SaveToFile.
SQLConnection TSQLConnection Соединение с БД, работа с которым будет протколироваться.
TraceList TStrings Содержимое протокола.
Методы.
LoadFromFile (AFileName:string) Загрузить протокол из файла.
SaveToFile (AFileName:string) Сохранить протокол в файл.
События.
OnLogTrace TTraceLogEvent Генерируется сразу после вставки нового сообщения в протокол.
OnTrace TTraceEvent Генерируется при наличии сообщения для протокола, перед его вставкой.
Что нам остается сделать - в компоненте SQLMonitor1 установить свойство SQLConnection равным SQLConnection1, а затем свойство Active SQLMonitor1 установим в True. Для вывода сообщений расположим на форме компонент Memo. Для его заполнения создадим обработчик события OnLogTrace компонента SQLMonitor1 со следующим кодом:
Memo1.Clear;
Memo1.LinesAddString(SQLMonitor1.TraceList);
Все запускаем полученное приложение - читаем протокол.
У меня возник по поводу использования монитора только один вопрос (Кто идет за "Клинским"? :) А как отслеживать не все события, а только какие-то определенные? Ответ нашелся довольно быстро.
Структура CBInfo:pSQLTraceDesc имеет поле eTraceCat, содержащее категорию произошедшего события. Однако у меня значение этого поля всегда было равно 256. Таким образом, нам остается только анализировать текст сообщения, находящийся в поле pszTrace вышеуказанной структуры.
Ну вот и все. Ваши замечания и предложения я рад буду увидеть в почтовом ящике mgoblin@mail.ru.
Автор: Mike Goblin.
Взято с сайта http://www.delphimaster.ru/
С разрешения автора.
©Drkb::04581.
Как разделить обработку OnClick и OnDblClick? Ведь OnClick будет вызываться всегда, и перед DblClick.
Именно так и происходит в Windows - посылаются оба сообщения. Для того чтобы обработать только какое-то одно событие необходимо чуть "задержать" выполнение OnClick. Сделать это можно следующим способом:
Code: Procedure TForm1.ListBox1Click(Sender: TObject); Var. Msg: TMsg; TargetTime: Longint; Begin. { get the maximum time to wait for a double-click message }. TargetTime := GetTickCount + GetDoubleClickTime; { cycle until DblClick received or wait time run out }. While GetTickCount < TargetTime do. If PeekMessage(Msg, ListBox1.Handle, WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK, WM_NOREMOVE). Then Exit; { Double click }. MessageDlg('Single clicked', mtInformation, [mbOK], 0); End;
©Drkb::00343.
Какой формат данных предпочесть в Delphi? DBase или Paradox?
Если вам действительно все равно, то вот несколько пунктов "за" формат Paradox:
1. Широкий выбор типов полей, включая автоинкремент, BLOBs, и т.п.
2. Соблюдение целостности данных, контроля данных, обновления индексов на уровне ядра BDE.
3. Первичный индекс таблицы автоматически соблюдает уникальность записей, вторичные индексы обеспечивают отсортированный "вид" на записи таблицы.
©Drkb::02636.
Copyright © 1996 Epsylon Technologies.
Взято из FAQ Epsylon Technologies (095)-913-5608; (095)-913-2934; (095)-535-5349.
Как восстановить индекс Paradox?
BDE включает функцию для этого - DbiRegenIndexes.
©Drkb::02629.
Copyright © 1996 Epsylon Technologies.
Взято из FAQ Epsylon Technologies (095)-913-5608; (095)-913-2934; (095)-535-5349.
| Как определить реальный размер поля типа BLOB, которое сохранено в таблице? |
Ниже приведена функция GetBlobSize, которая возвращает размер данного BLOB или MEMO поля.
Пример вызова:
Code: Function GetBlobSize(Field: TBlobField): Longint; Begin. With TBlobStream.Create(Field, bmRead) do. Try. Result := Seek(0, 2); Finally. Free; End; End; Procedure TForm1.Button1Click(Sender: TObject); Begin. { This sets the Edit1 edit box to display the size of }. { a memo field named Notes. }. Edit1.Text := IntToStr(GetBlobSize(Notes)); End;
| Как заставить приложение Delphi отвечать на сообщения Windows? |
Используем WM_WININICHANGED в качестве примера :
Объявление метода в TForm позволит вам обрабатывать сообщение WM_WININICHANGED:
Procedure WMWinIniChange(var Message: TMessage); message WM_WININICHANGE;
Код в implementation может выглядеть так:
Code: Procedure TForm1.WMWinIniChange(var Message: TMessage); Begin. Inherited; { ... ваша реакция на событие ... }. End;
| В каком порядке происходят события при создании и показе окна? |
При создании окна обработчики событий выполняются в следующем порядке:
OnCreate.
OnShow.
OnPaint.
OnActivate.
OnResize.
OnPaint (снова).
| Как экспортировать таблицу базы данных в ASCII-файл? |
Code: Procedure TMyTable.ExportToASCII; Var. I: Integer; Dlg: TSaveDialog; ASCIIFile: TextFile; Res: Boolean; Begin. If Active then. If (FieldCount > 0) and (RecordCount > 0) then. Begin. Dlg := TSaveDialog.Create(Application); Dlg.FileName := FASCIIFileName; Dlg.Filter := 'ASCII-Fiels (*.asc)|*.asc'; Dlg.Options := Dlg.Options+[ofPathMustExist, OfOverwritePrompt, ofHideReadOnly]; Dlg.Title := 'Экспоритровать данные в ASCII-файл'; Try. Res := Dlg.Execute; If Res then. FASCIIFileName := Dlg.FileName; Finally. Dlg.Free; End; If Res then. Begin. AssignFile(ASCIIFile, FASCIIFileName); Rewrite(ASCIIFile); First; If FASCIIFieldNames then. Begin. For I := 0 to FieldCount-1 do. Begin. Write(ASCIIFile, Fields[I].FieldName); If I <> FieldCount-1 then. Write(ASCIIFile, FASCIISeparator); End; Write(ASCIIFile, #13#10); End; While not EOF do. Begin. For I := 0 to FieldCount-1 do. Begin. Write(ASCIIFile, Fields[I].Text); If I <> FieldCount-1 then. Write(ASCIIFile, FASCIISeparator); End; Next; If not EOF then. Write(ASCIIFile, #13#10); End; CloseFile(ASCIIFile); If IOResult <> 0 then. MessageDlg('Ошибка при создании или переписывании '+ 'в ASCII-файл', mtError, [mbOK], 0); End; End. Else. MessageDlg('Нет данных для экспортирования.', MtInformation, [mbOK], 0). Else. MessageDlg('Таблица должна быть открытой, чтобы данные '+ 'можно было экспортировать в ASCII-формат.', mtError, [mbOK], 0); End;
| Как явно вызвать виртуальный метод дедушки? |
Проблема в следующем. Допустим, есть иерархия классов, у которых перекрывается один и тот же виртуальный (или динамический - не важно) метод и в одной из реализаций этого метода вы хотите вызвать виртуальный метод предка своего предка. Новая объектная модель Delphi допускает только вызов методов предка (с помощью ключевого слова inherited) либо вызов методов класса с префиксом - типом класса (например, TLevel1.ClassName).
Эта проблема стандартными средствами не решается. Но сделать требуемый вызов можно. Причем способом, показанным ниже, можно вызвать любой метод для любого класса, однако, в этом случае вся ответственность за правильность работы с методами и полями ложится на программиста. Ниже в методе VirtualFunction класса TLevel3 вызывается метод класса TLevel1, а в функции Level1Always всегда вызывается метод класса TLevel1 для любого его наследника.
Code: TLevel1 = class(TComponent). Public. Function VirtualFunction: string; virtual; End; TLevel2 = class(TLevel1). Public. Function VirtualFunction: string; override; End; TLevel3 = class(TLevel2). Public. Function VirtualFunction: string; override; End; Function Level1Always(MyLevel: TLevel1): string; Implementation. Type. PClass = ^TClass; Function TLevel1.VirtualFunction: string; Begin. Result := 'Level1'; End; Function TLevel2.VirtualFunction: string; Begin. Result := inherited VirtualFunction+' Level2'; End; Function TLevel3.VirtualFunction: string; Var. ClassOld: TClass; Begin. ClassOld := PClass(Self)^; PClass(Self)^ := TLevel1; Result := VirtualFunction + ' Level3'; PClass(Self)^ := ClassOld; End; Function Level1Always(MyObject: TObject): string; Var. ClassOld: TClass; Begin. ClassOld := PClass(MyObject)^; PClass(MyObject)^ := TLevel1; Result := (MyObject as TLevel1).VirtualFunction; PClass(MyObject)^ := ClassOld; End;
Как же это работает? Стандартные так называемые объектные типы (object types - class of ...) на самом деле представляют из себя указатель на VMT (Virtual Method Table) - таблицу виртуальных методов, который (указатель) лежит по смещению 0 в экземпляре класса. Воспользовавшись этим, мы сначала сохраняем 'старый тип класса' - указатель на VMT, присваиваем ему указатель на VMT нужного класса, делаем вызов и восстанавливаем все как было. Причем нигде не требуется, чтобы один из этих классов был бы порожден от другого, т.е. функция Level1Always вызовет требуемый метод вообще для любого экземпляра любого класса.
Если в функции Level1Always сделать попробовать вызов.
Code: Result := MyObject.VirtualFunction;
То будет ошибка на стадии компиляции, так как у класса TObject нет метода VirtualFunction. Другой вызов.
Code: Result := (MyObject as TLevel3).VirtualFunction;
| Таскаем форму за её поверхность. |
Code: Unit DragMain; Interface. Uses. SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCrtls; Type. TForm1 = class(TForm). Button1: TButton; Procedure ButtonClick(Sender: TObject); Private. Procedure WMNCHitTest(var M: TWMNCHitTest); Message wm_NCCHitTest; End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1. WMNCHitTest(var M: TWMNCHitTest); Begin. Inherited; If M.Result = htClient then. M.Result := htCaption; End; Procedure TForm1.Button1Click(Sender: TObject); Begin. Close; End; End.
Получение имени конфигурации HardWare profile.
Code: Function GettingHWProfileName: string; //Win95OSR2 or later and NT4.0 or later. Var. PInfo: tagHW_PROFILE_INFOA; Begin. GetCurrentHwProfile(pInfo); Result:=pInfo.szHwProfileName; End;
©Drkb::01330.
| Получение типа клавиатуры. |
Code: Function GettingKeybType: string; //Win95 or later and NT3.1 or later. Var. Flag: integer; Begin. Flag:=0; Case GetKeyboardType(Flag) of. 1: Result:='IBM PC/XT or compatible (83-key) keyboard'; 2: Result:='Olivetti "ICO" (102-key) keyboard'; 3: Result:='IBM PC/AT (84-key) or similar keyboard'; 4: Result:='IBM enhanced (101- or 102-key) keyboard'; 5: Result:='Nokia 1050 and similar keyboards'; 6: Result:='Nokia 9140 and similar keyboards'; 7: Result:='Japanese keyboard'; End; End;
©Drkb::01520.
Code: // The GetKeyboardType function retrieves information about the current keyboard. Function KeyBoardType: string; Begin. Case GetKeyboardType(0) of. 1: Result := 'IBM PC/XT or compatible (83-key) keyboard'; 2: Result := 'Olivetti "ICO" (102-key) keyboard'; 3: Result := 'IBM PC/AT (84-key) or similar keyboard'; 4: Result := 'IBM enhanced or compatible'; 5: Result := 'Nokia 1050 and similar keyboards'; 6: Result := 'Nokia 9140 and similar keyboards'; 7: Result := 'Japanese keyboard' Else. Result := 'Unrecognized keyboard type'; End; End; Function KeyBoardSubype: Integer; Begin. Result := GetKeyboardType(1); End; Function NumberOfFunctionKeys: string; Begin. Case GetKeyboardType(3) of. 1: Result := '20'; 2: Result := '12/18'; 3: Result := '10'; 4: Result := '12'; 5: Result := '10'; 6: Result := '24'; 7: Result := 'Hardware dependent' Else. Result := 'N/A'; End; End; Procedure TForm1.Button1Click(Sender: TObject); Begin. Label1.Caption := Format('Keyboard Type: %s', [KeyBoardType]); Label2.Caption := Format('Keyboard Subtype: %d', [KeyBoardSubype]); Label3.Caption := Format('Keyboard Subtype: %s', [NumberOfFunctionKeys]); End;
Получение уровня процессора.
Code: Function GettingProcLevel: string; //Win95 or later and NT3.1 or later. Var. Struc: _SYSTEM_INFO; Begin. GetSystemInfo(Struc); Case Struc.wProcessorLevel of. 3: Result:='Intel 80386'; 4: Result:='Intel 80486'; 5: Result:='Intel Pentium'; 6: Result:='Intel Pentium II or better'; End; End;
©Drkb::01434.
Получение количества установленных процессоров.
Code: Function GettingProcNum: string; //Win95 or later and NT3.1 or later. Var. Struc: _SYSTEM_INFO; Begin. GetSystemInfo(Struc); Result:=IntToStr(Struc.dwNumberOfProcessors); End;
©Drkb::01433.
| Как написать свой хранитель экрана? |
1.В файл проекта (*.DPR) добавить строку {$D SCRNSAVE <название хранителя>} после строки подключения модулей (Uses...). 2.У окна формы убрать системное меню, кнопки и придать свойству WindowState значение wsMaximize. 3.Предусмотреть выход из хранителя при нажатии на клавиши клавиатуры, мыши и при перемещении курсора мыши. 4.Проверить параметры с которым был вызван хранитель и если это /c - показать окно настройки хранителя, а иначе (можно проверять на /s, а можно и не проверять) сам хранитель. /p - для отображения в окне установок хранителя экрана. 5.Скомпилировать хранитель экрана. 6.Переименовать *.EXE файл в файл *.SCR и скопировать его в каталог WINDOWS\SYSTEM\. 7.Установить новый хранитель в настройках системы! Название хранителя может состоять из нескольких слов с пробелами, на любом языке. При работе хранителя необходимо прятать курсор мыши, только не забывайте восстанавливать его после выхода. Все параметры и настройки храните в файле .INI, так как хранитель и окно настройки не связаны друг с другом напрямую. Старайтесь сделать свой хранитель как можно меньше и быстрее. Иначе ваши долго работающие (в фоновом режиме) приложения будут работать еше дольше!
{в файле *.DPR}.
{$D SCRNSAVE Пример хранителя экрана}.
{проверить переданные параметры}.
Code: IF (ParamStr(1) = '/c') OR (ParamStr(1) = '/C') THEN. {скрыть курсор мыши}. ShowCursor(False); {восстановить курсор мыши}. ShowCursor(True);
Работа с базами данных.
Each function listed below returns information about a specific database, available databases, or performs a database-related task, such as opening or closing a database.
DbiCloseDatabase:
Closes a database and all tables associated with this database handle.
DbiGetDatabaseDesc:
Retrieves the description of the specified database from the configuration file.
DbiGetDirectory:
Retrieves the current working directory or the default directory.
DbiOpenDatabase:
Opens a database in the current session and returns a database handle.
DbiOpenDatabaseList:
Creates an in-memory table containing a list of accessible databases and their descriptions.
DbiOpenFileList:
Opens a cursor on the virtual table containing all the tables accessible by the client application.
And their descriptions.
DbiOpenIndexList:
Opens a cursor on an in-memory table listing the indexes on a specified table, along with.
Their descriptions.
DbiOpenTableList:
Creates an in-memory table with information about all the tables accessible to the client application.
©Drkb::02505.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Конфигурация и настройка.
Each function listed below returns information about the client application environment, such as the supported table, field and index types for the driver type, or the available driver types. Functions in this category can also perform tasks that affect the client application environment, such as loading a driver.
DbiAddAlias:
Adds an alias to the BDE configuration file (IDAPI.CFG).
DbiAddDriver:
Adds a driver to the BDE configuration file (IDAPI.CFG). NEW FUNCTION BDE 4.0.
DbiAnsiToNative:
Multipurpose translate function.
DbiDebugLayerOptions:
Activates, deactivates, or sets options for the BDE debug layer. OBSOLETE FUNCTION BDE 4.0.
DbiDeleteAlias:
Deletes an alias from the BDE configuration file (IDAPI.CFG).
DbiDeleteDriver:
Deletes a driver from the BDE configuration file (IDAPI.CFG). NEW FUNCTION BDE 4.0.
DbiDllExit:
Prepares the BDE to be disconnected within a DLL. NEW FUNCTION BDE 4.0.
DbiExit:
Disconnects the client application from BDE.
DbiGetClientInfo:
Retrieves system-level information about the client application environment.
DbiGetDriverDesc:
Retrieves a description of a driver.
DbiGetLdName:
Retrieves the name of the language driver associated with the specified object name (table name).
DbiGetLdObj:
Retrieves the language driver object associated with the given cursor.
DbiGetNetUserName:
Retrieves the user's network login name. User names should be available for all networks.
Supported by Microsoft Windows.
DbiGetProp:
Returns a property of an object.
DbiGetSysConfig:
Retrieves BDE system configuration information.
DbiGetSysInfo:
Retrieves system status and information.
DbiGetSysVersion:
Retrieves the system version information, including the BDE version number, date, and time,
And the client interface version number.
DbiInit:
Initializes the BDE environment.
DbiLoadDriver:
Load a given driver.
DbiNativeToAnsi:
Translates a string in the native language driver to an ANSI string.
DbiOpenCfgInfoList:
Returns a handle to an in-memory table listing all the nodes in the configuration file.
Accessible by the specified path.
DbiOpenDriverList:
Creates an in-memory table containing a list of driver names available to the client application.
DbiOpenFieldTypesList:
Creates an in-memory table containing a list of field types supported by the table type for.
The driver type.
DbiOpenFunctionArgList:
Returns a list of arguments to a data source function.
DbiOpenFunctionList:
Returns a description of a data source function.
DbiOpenIndexTypesList:
Creates an in-memory table containing a list of all supported index types for the driver type.
DbiOpenLdList:
Creates an in-memory table containing a list of available language drivers.
DbiOpenTableList:
Creates an in-memory table with information about all the tables accessible to the client application.
DbiOpenTableTypesList:
Creates an in-memory table listing table type names for the given driver.
DbiOpenUserList:
Creates an in-memory table containing a list of users sharing the same network file.
DbiSetProp:
Sets the specified property of an object to a given value.
DbiUseIdleTime:
Allows BDE to accomplish background tasks during times when the client application is idle.
OBSOLETE FUNCTION BDE 4.0.
©Drkb::02506.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
10 вещей которые убедят вас перейти на сервер 2005.
| 10 вещей которые убедят вас перейти на сервер 2005. |
Arthur Fuller и Stephen Giles (оригинал: 10 things that will convince you to upgrade to SQL Server 2005) Перевод Моисеенко С.И.
Узнайте 10 самых важных причин того, почему Вы должны модернизировать свой сервер до SQL Server 2005. Нововведения последней версии SQL Server включают Студию Управления (Management Studio), SSIS и встроенные бизнес-решения (BI - business intelligence). Большинство ИТ профессионалов работает с SQL Server 2000 и все еще поддерживают базы данных на SQL Server 7 (некоторые до сих пор поддерживают даже базы данных на SQL Server 6.5). С выходом SQL Server 2005 нас часто спрашивают: нужно ли переходить? В этой статье мы даем 10 самых веских причин для перехода на SQL Server 2005.
1. Все, что работает сейчас, будет продолжать работать. Студия Управления SQL Server 2005 позволит Вам управлять базами данных SQL Server 2000 и SQL Server 2005. Студия Управления не будет работать для SQL Server 6.5 и 7.0, однако, такие базы данных достаточно просто перевести на приемлемые версии. Из-за проблем совместимости, некоторые вещи не будут легко переносимы в Студию Управления. Например, если ваша база данных SQL Server 2000 содержит диаграммы, то Вы не сможете добраться до них из SQL Server 2005 без модернизации базы данных.
2. SQL Server 2005 включает большее количество компонент. Более ранние версии SQL Server комплектовались различными компонентами несколькими разными способами. Например, полный Enterprise edition включал все, но Вы, возможно, не были настолько счастливы, чтобы приобрести эту редакцию. Компонента Analysis Services не была включена в SQL Server 2000 Standard, и Вы должны были покупать ее отдельно. Microsoft изменила свою маркетинговую стратегию и включила все компоненты в единый пакет. SQL Server 2005 действительно стоит дороже, чем предыдущие версии, но есть одна невероятная вещь. Так, если Вы посетите презентацию выпуска SQL Server 2005, то Microsoft предоставит Вам бесплатную версию без всяких проволочек.
3. В корне отличные пользовательские интерфейсы закатали в один интерфейс пользователя (UI). В старые добрые времена Enterprise Manager'а и Query Analyzer'а Query Profiler, Reporting Services и Data Transformation Services (DTS) были изолированными приложениями, интерфейсы которых были совсем не согласованы. Студия Управления SQL Server 2005 дает Вам один прозрачный UI, который дает доступ ко всем компонентам, включая даже онлайновую аналитическую обработку (OLAP) и Службы Интеграции SQL Server (SSIS) безотносительно сервера, на котором находятся эти компоненты. Это выливается в большую производительность и удешевление затрат на обучение. Даже если Вы предпочтете сохранить ваши базы данных в формате SQL Server 2000, Вы сможете использовать замечательный новый интерфейс для решения своих задач.
4. Подъем языков .NET на новую высоту. T-SQL по-прежнему является инструментом, на котором делается 90% вашей работы. Однако некоторые задачи требуют особой точности; в этих ситуациях использование T-SQL является, по крайней мере, неуклюжим. Примерами являются построчная обработка, особенно когда Вы должны сравнить текущую строку с предыдущей, и запись во множество таблиц в пределах одной процедуры. Вы можете сделать это в рамках T-SQL. Однако для вас может оказаться более простым использовать наборы строк .NET (rowset) для реализации очень сложной логики. Мы не предлагаем, чтобы Вы переписали все ваши хранимые процедуры в .NET; напротив, думайте об этом как о дополнительной прекрасной возможности, а не как о замене T-SQL.
5. Вы можете получить выигрыш в использовании Reporting Services. Мы придерживаемся следующего общего принципа: все, что может быть сделано на стороне сервера, должно делаться на стороне сервера. Например, мы полагаем, что построение динамических запросов к SQL Server в приложении чаще всего глупо. Это приходится делать время от времени, но лучшее и более безопасное решение состоит в том, чтобы собрать в приложении параметры, а затем передать их в хранимую процедуру. Reporting Services в SQL Server 2005 переносит эту концепцию на более высокий уровень. В SQL Server 2000 и более ранних версиях отчеты готовились средствами приложений (C ++, VB, Delphi, Access, Crystal Reports и т.д.). Вы можете получить огромные преимущества, встроив их в Reporting Services. Сначала Вы убираете всю логику из данного приложения. Затем Вы можете использовать Reporting Services фактически из любого приложения. Это означает, что ваши разработчики приложений смогут удалить большие объемы кода из своих приложений. В результате получаем один отчет для всех возможных UI, поэтому, если обнаруживается ошибка в отчете, Вы исправляете ее в одном месте, и все UI наследуют это исправление.
6. Встроенные бизнес-решения (BI). Бизнес-решения (OLAP) не были встроены в предыдущих версиях SQL Server, если Вы не покупали редакцию Enterprise edition для SQL Server 2000. Даже если Вы могли позволить себе это, то Вам приходилось осваивать новый интерфейс. Используя SQL Server 2005, Вы можете делать все, что и прежде, но с помощью прозрачного интегрированного UI.
7. Прощай DTS и привет SSIS. SQL Server 2005 полностью заменяет DTS новой технологией по имени SSIS, которая является квантовым скачком вперед. Мы думаем, что самой крутой частью SSIS является то, что преобразование данных теперь - это объект SSIS. Наконец, Вы можете действительно сделать управление потоком и обработку ошибок, строя задачи вне операций извлечения, преобразования, и загрузки (ETL).
8. Мудрая модернизация с помощью Upgrade Advisor. SQL Server 2005 ввел новые функциональные возможности и изменил существующие, чтобы повысить производительность, безопасность, и управляемость. Эти изменения могут затронуть ваши уже существующие приложения. Вот почему команда Microsoft SQL Server разработала Upgrade Advisor, который разумно проведет Вас через процесс модернизации и укажет на любые проблемы совместимости, которые могут при этом возникнуть.
9. Вы теперь имеете детализированную безопасность под руками. При дополнении надлежащей схемой базы данных и способностью назначать некоторые административные задачи, не наделяя каждого разработчика и младшего администратора базы данных всеми правами Старшего Архитектора (Senior Architect), Вы можете в значительной степени увеличить безопасность SQL Server 2005, давая пользователям только те права, которые им необходимы для выполнения своей работы. (По общему признанию, некоторые разработчики не могли не видеть в этом серьезное основание для перехода).
10. Выгода от масштабируемости предприятия. SQL Server 2000 имеет проблемы с масштабируемостью предприятия, но все это теперь в прошлом после выхода в свет SQL Server 2005. Похоже на то, что SQL Server 2005 готов соревноваться с решениями Oracle и DB2 масштаба предприятия. Причем он обойдется значительно дешевле, чем аналогичные продукты от Oracle или DB2, независимо от того, как Вы измеряете стоимость (на процессор или же на место).
Резюме Даже если у Вас нет неотложной потребности для миграции от SQL Server 7 или 2000, Вам следует установить SQL Server 2005, поскольку преимущества огромны. Вы можете продолжить администрирование баз данных на SQL Server 2000, не преобразовывая их, наслаждаясь при этом новыми крутыми расширениями в SQL Server 2005. Час, проведенный в новой Студии Управления сервера, заставит Вас забыть все о Enterprise Manager и Query Analyzer. Они будут казаться вам столь же древними, как CP/М.
©Drkb::02865.
| 10 способов взлома пароля |
1) Попытайся залогиниться без пароля. Просто нажми . Если это не работает, залогинься с паролем <пробел> <пробел>.
Распространение приложений с технологией dbExpress.
Готовое приложение, использующее технологию dbExpress, можно поставлять заказчикам двумя способами.
Вместе с приложением поставляется динамическая библиотека для выбранного сервера. Она находится в папке \Delphi7\Bin.
Дополнительно, если в приложении используется компонент TSimpleDataSet, необходимо включить в поставку динамическую библиотеку Midas.dll.
Приложение компилируется вместе со следующими DCU-файлами: dbExpInt.dcu, dbExpOra.dcu, dbExpDb2.dcu, dbExpMy.dcu (в зависимости от выбранного сервера). Если в приложении используется компонент TSimpieDataSet, следует добавить файлы Crtl.dcu и MidasLib.dcu. В результате необходимо поставлять только исполняемый файл приложения.
Если дополнительная настройка соединений не требуется, файл dbxconnections.ini не нужен.
Работа с сессиями.
Each function listed below returns information about a session or performs a task that affects the session, such as starting a session or adding a password.
DbiAddPassword:
Adds a password to the current session.
DbiCheckRefresh:
Checks for remote updates to tables for all cursors in the current session, and refreshes the cursors.
If changed.
DbiCloseSession:
Closes the session associated with the given session handle.
DbiDropPassword:
Removes a password from the current session.
DbiGetCallBack:
Returns a pointer to the function previously registered by the client for the given callback type.
DbiGetCurrSession:
Returns the handle associated with the current session.
DbiGetDateFormat:
Gets the date format for the current session.
DbiGetNumberFormat:
Gets the number format for the current session.
DbiGetSesInfo:
Retrieves the environment settings for the current session.
DbiGetTimeFormat:
Gets the time format for the current session.
DbiRegisterCallBack:
Registers a callback function for the client application.
DbiSetCurrSession:
Sets the current session of the client application to the session associated with hSes.
DbiSetDateFormat:
Sets the date format for the current session.
DbiSetNumberFormat:
Sets the number format for the current session.
DbiSetPrivateDir:
Sets the private directory for the current session.
DbiSetTimeFormat:
Sets the time format for the current session.
DbiStartSession:
Starts a new session for the client application.
©Drkb::02507.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Обработка исключений.
Each function listed below returns error handling information or performs a task that relates to error handling.
DbiGetErrorContext:
After receiving an error code back from a call, enables the client to probe BDE for more specific.
Error information.
DbiGetErrorEntry:
Returns the error description of a specified error stack entry.
DbiGetErrorInfo:
Provides descriptive error information about the last error that occurred.
DbiGetErrorString:
Returns the message associated with a given error code.
©Drkb::02508.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Поддержка блокировок.
Each function listed below returns information about lock status or acquires or releases a lock at the table or record level.
DbiAcqPersistTableLock:
Acquires an exclusive persistent lock on the table preventing other users from using the table.
Or creating a table of the same name.
DbiAcqTableLock:
Acquires a table-level lock on the table associated with the given cursor.
DbiGetRecord:
Record positioning functions have a lock parameter.
DbiIsRecordLocked:
Checks the lock status of the current record.
DbiIsTableLocked:
Returns the number of locks of a specified type acquired on the table associated with the.
Given session.
DbiIsTableShared:
Determines whether the table is physically shared or not.
DbiOpenLockList:
Creates an in-memory table containing a list of locks acquired on the table.
DbiOpenUserList:
Creates an in-memory table containing a list of users sharing the same network file.
DbiRelPersistTableLock:
Releases the persistent table lock on the specified table.
DbiRelRecordLock:
Releases the record lock on either the current record of the cursor or only the locks acquired.
In the current session.
DbiRelTableLock:
Releases table locks of the specified type associated with the current session (the session in.
Which the cursor was created).
DbiSetLockRetry:
Sets the table and record lock retry time for the current session.
©Drkb::02509.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Поддержка курсоров.
Each function listed below returns information about a cursor, or performs a task that performs a cursor-related task such as positioning of a cursor, linking of cursors, creating and closing cursors, counting of records associated with a cursor, filtering, setting and comparing bookmarks, and refreshing all buffers associated with a cursor.
DbiActivateFilter:
Activates a filter.
DbiAddFilter:
Adds a filter to a table, but does not activate the filter (the record set is not yet altered).
DbiApplyDelayedUpdates:
When cached updates cursor layer is active, writes all modifications made to cached data to the.
Underlying database.
DbiBeginDelayedUpdates:
Creates a cached updates cursor layer so that users can make extended changes to temporarily.
Cached table data without writing to the actual table, thereby minimizing resource locking.
DbiBeginLinkMode:
Converts a cursor to a link cursor. Given an open cursor, prepares for linked access. Returns a.
New cursor.
DbiCloneCursor:
Creates a new cursor (clone cursor) which has the same result set as the given cursor.
(source cursor).
DbiCloseCursor:
Closes a previously opened cursor.
DbiCompareBookMarks:
Compares the relative positions of two bookmarks in the result set associated with the cursor.
DbiDeactivateFilter:
Temporarily stops the specified filter from affecting the record set by turning the filter off.
DbiDropFilter:
Deactivates and removes a filter from memory, and frees all resources.
DbiEndDelayedUpdates:
Closes a cached updates cursor layer ending the cached updates mode.
DbiEndLinkMode:
Ends linked cursor mode, and returns the original cursor.
DbiExtractKey:
Retrieves the key value for the current record of the given cursor or from the supplied record buffer.
DbiForceRecordReread:
Rereads a single record from the server on demand, refreshing one row only, rather than clearing.
The cache.
DbiForceReread:
Refreshes all buffers associated with the cursor, if necessary.
DbiFormFullName:
Returns the fully qualified table name.
DbiGetBookMark:
Saves the current position of a cursor to the client-supplied buffer called a bookmark.
DbiGetCursorForTable:
Finds the cursor for the given table.
DbiGetCursorProps:
Returns the properties of the cursor.
DbiGetExactRecordCount:
Retrieves the current exact number of records associated with the cursor. NEW FUNCTION BDE 4.0.
DbiGetFieldDescs:
Retrieves a list of descriptors for all the fields in the table associated with the cursor.
DbiGetLinkStatus:
Returns the link status of the cursor.
DbiGetNextRecord:
Retrieves the next record in the table associated with the cursor.
DbiGetPriorRecord:
Retrieves the previous record in the table associated with the given cursor.
DbiGetProp:
Returns a property of an object.
DbiGetRecord:
Retrieves the current record, if any, in the table associated with the cursor.
DbiGetRecordCount:
Retrieves the current number of records associated with the cursor.
DbiGetRecordForKey:
Finds and retrieves a record matching a key and positions the cursor on that record.
DbiGetRelativeRecord:
Positions the cursor on a record in the table relative to the current position of the cursor.
DbiGetSeqNo:
Retrieves the sequence number of the current record in the table associated with the cursor.
DbiLinkDetail:
Establishes a link between two tables such that the detail table has its record set limited to the.
Set of records matching the linking key values of the master table cursor.
DbiLinkDetailToExp:
Links the detail cursor to the master cursor using an expression.
DbiMakePermanent:
Changes a temporary table created by DbiCreateTempTable into a permanent table.
DbiOpenTable:
Opens the given table for access and associates a cursor handle with the opened table.
DbiResetRange:
Removes the specified table's limited range previously established by the function DbiSetRange.
DbiSaveChanges:
Forces all updated records associated with the cursor to disk.
DbiSetFieldMap:
Sets a field map of the table associated with the given cursor.
DbiSetProp:
Sets the specified property of an object to a given value.
DbiSetRange:
Sets a range on the result set associated with the cursor.
DbiSetToBegin:
Positions the cursor to BOF (just before the first record).
DbiSetToBookMark:
Positions the cursor to the location saved in the specified bookmark.
DbiSetToCursor:
Sets the position of one cursor (the destination cursor) to that of another (the source cursor).
DbiSetToEnd:
Positions the cursor to EOF (just after the last record).
DbiSetToKey:
Positions an index-based cursor on a key value.
DbiSetToRecordNo:
Positions the cursor of a dBASE table to the given physical record number.
DbiSetToSeqNo:
Positions the cursor to the specified sequence number of a Paradox table.
DbiUnlinkDetail:
Removes a link between two cursors.
©Drkb::02510.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Работа с индексами.
Each function listed below returns information about an index or indexes, or performs a task that affects an index, such as dropping it, deleting it, or adding it.
DbiAddIndex:
Creates an index on an existing table.
DbiCloseIndex:
Closes the specified index on a cursor.
DbiCompareKeys:
Compares two key values based on the current index of the cursor.
DbiDeleteIndex:
Drops an index on a table.
DbiExtractKey:
Retrieves the key value for the current record of the given cursor or from the supplied record buffer.
DbiGetIndexDesc:
Retrieves the properties of the given index associated with the cursor.
DbiGetIndexDescs:
Retrieves index properties.
DbiGetIndexForField:
Returns the description of any useful index on the specified field.
DbiGetIndexSeqNo:
Retrieves the ordinal number of the index in the index list of the specified cursor.
DbiGetIndexTypeDesc:
Retrieves a description of the index type.
DbiOpenIndex:
Opens the index for the table associated with the cursor.
DbiRegenIndex:
Regenerates an index to make sure that it is up-to-date (all records currently in the table.
Are included in the index and are in the index order).
DbiRegenIndexes:
Regenerates all out-of-date indexes on a given table.
DbiSwitchToIndex:
Allows the user to change the active index order of the given cursor.
©Drkb::02511.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Работа с запросами.
Each function listed below performs a query task, such as preparing and executing a SQL or QBE query.
DbiGetProp:
Returns a property of an object.
DbiQAlloc:
Allocates a new statement handle for a prepared query.
DbiQExec:
Executes the previously prepared query identified by the supplied statement handle and.
Returns a cursor to the result set, if one is generated.
DbiQExecDirect:
Executes a SQL or QBE query and returns a cursor to the result set, if one is generated.
DbiQExecProcDirect:
Executes a stored procedure and returns a cursor to the result set, if one is generated.
DbiQFree:
Frees the resources associated with a previously prepared query identified by the supplied.
Statement handle.
DbiQGetBaseDescs:
Returns the original database, table, and field names of the fields that make up the result.
Set of a query.
DbiQInstantiateAnswer:
Creates a permanent table from the cursor to the result set.
DbiQPrepare:
Prepares a SQL or QBE query for execution, and returns a handle to a statement containing.
The prepared query.
DbiQPrepareProc:
Prepares and optionally binds parameters for a stored procedure.
DbiQSetParams:
Associates data with parameter markers embedded within a prepared query.
DbiQSetProcParams:
Binds parameters for a stored procedure prepared with DbiQPrepareProc.
DbiSetProp:
Sets the specified property of an object to a given value.
DbiValidateProp:
Validates a property.
©Drkb::02512.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Работа с таблицами.
Each function listed below returns information about a specific table, such as all the locks acquired on the table, all the referential integrity links on the table, the indexes open on the table, or whether or not the table is shared. Functions in this category can also perform a table-wide operation, such as copying and deleting.
DbiBatchMove:
Appends, updates, subtracts, and copies records or fields from a source table to a destination table.
DbiCopyTable:
Duplicates the specified source table to a destination table.
DbiCreateInMemTable:
Creates a temporary, in-memory table.
DbiCreateTable:
Creates a table.
DbiCreateTempTable:
Creates a temporary table that is deleted when the cursor is closed, unless the call is followed.
By a call to DbiMakePermanent.
DbiDeleteTable:
Deletes a table.
DbiDoRestructure:
Changes the properties of a table.
DbiEmptyTable:
Deletes all records from the table associated with the specified table cursor handle or table name.
DbiGetTableOpenCount:
Returns the total number of cursors that are open on the specified table.
DbiGetTableTypeDesc:
Returns a description of the capabilities of the table type for the driver type.
DbiIsTableLocked:
Returns the number of locks of a specified type acquired on the table associated with the.
Given session.
DbiIsTableShared:
Determines whether the table is physically shared or not.
DbiMakePermanent:
Changes a temporary table created by DbiCreateTempTable into a permanent table.
DbiOpenFamilyList:
Creates an in-memory table listing the family members associated with a specified table.
DbiOpenFieldList:
Creates an in-memory table listing the fields in a specified table and their descriptions.
DbiOpenIndexList:
Opens a cursor on an in-memory table listing the indexes on a specified table, along with.
Their descriptions.
DbiOpenLockList:
Creates an in-memory table containing a list of locks acquired on the table associated with the cursor.
DbiOpenRintList:
Creates an in-memory table listing the referential integrity links for a specified table, along with.
Their descriptions.
DbiOpenSecurityList:
Creates an in-memory table listing record-level security information about a specified table.
DbiOpenTable:
Opens the given table for access and associates a cursor handle with the opened table.
DbiPackTable:
Optimizes table space by rebuilding the table associated with the cursor and releasing any free space.
DbiQInstantiateAnswer:
Creates a permanent table from a cursor handle.
DbiRegenIndexes:
Regenerates all out-of-date indexes on a given table.
DbiRenameTable:
Renames the table and all of its resources to the new name specified.
DbiSaveChanges:
Forces all updated records associated with the table to disk.
DbiSortTable:
Sorts an opened or closed table, either into itself or into a destination table. There are options to.
Remove duplicates, to enable case-insensitive sorts and special sort functions, and to control the.
Number of records sorted.
©Drkb::02513.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Доступ к данным.
Each function listed below accesses data in a table, such as retrieving data from a specified BLOB field or from the record buffer.
DbiAppendRecord:
Appends a record to the end of the table associated with the given cursor.
DbiDeleteRecord:
Deletes the current record of the given cursor.
DbiFreeBlob:
Closes the BLOB handle located within the specified record buffer.
DbiGetBlob:
Retrieves data from the specified BLOB field.
DbiGetBlobHeading:
Retrieves information about a BLOB field from the BLOB heading in the record buffer.
DbiGetBlobSize:
Retrieves the size of the specified BLOB field in bytes.
DbiGetField:
Retrieves the data contents of the requested field from the record buffer.
DbiGetFieldDescs:
Retrieves a list of descriptors for all the fields in the table associated with the cursor.
DbiGetFieldTypeDesc:
Retrieves a description of the specified field type.
DbiInitRecord:
Initializes the record buffer to a blank record according to the data types of the fields.
DbiInsertRecord:
Inserts a new record into the table associated with the given cursor.
DbiModifyRecord:
Modifies the current record of table associated with the cursor with the data supplied.
DbiOpenBlob:
Prepares the cursor's record buffer to access a BLOB field.
DbiPutBlob:
Writes data into an open BLOB field.
DbiPutField:
Writes the field value to the correct location in the supplied record buffer.
DbiReadBlock:
Reads a specified number of records (starting from the next position of the cursor) into a buffer.
DbiSaveChanges:
Forces all updated records associated with the cursor to disk.
DbiSetFieldMap:
Sets a field map of the table associated with the given cursor.
DbiTruncateBlob:
Shortens the size of the contents of a BLOB field, or deletes the contents of a BLOB field.
From the record, by shortening it to zero.
DbiUndeleteRecord:
Undeletes a dBASE record that has been marked for deletion (a "soft" delete).
DbiVerifyField:
Verifies that the data specified is a valid data type for the field specified, and that all validity.
Checks in place for the field are satisfied. It can also be used to check if a field is blank.
DbiWriteBlock:
Writes a block of records to the table associated with the cursor.
©Drkb::02514.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Управление транзакциями.
Each function listed below begins, ends, or inquires about the status of a transaction.
DbiBeginTran:
Begins a transaction.
DbiEndTran:
Ends a transaction.
DbiGetTranInfo:
Retrieves the transaction state.
©Drkb::02515.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Схемы и возможности.
Each function listed below returns information about capabilities or the schema.
DbiOpenCfgInfoList:
Returns a handle to an in-memory table listing all the nodes in the configuration file accessible by.
The specified path.
DbiOpenDatabaseList:
Creates an in-memory table containing a list of accessible databases and their descriptions.
DbiOpenDriverList:
Creates an in-memory table containing a list of driver names available to the client application.
DbiOpenFamilyList:
Creates an in-memory table listing the family members associated with a specified table.
DbiOpenFieldList:
Creates an in-memory table listing the fields in a specified table and their descriptions.
DbiOpenFieldTypesList:
Creates an in-memory table containing a list of field types supported by the table type for the driver type.
DbiOpenFunctionArgList:
Returns a list of arguments to a data source function.
DbiOpenFunctionList:
Returns a description of a data source function.
DbiOpenIndexList:
Opens a cursor on an in-memory table listing the indexes on a specified table, along with.
Their descriptions.
DbiOpenIndexTypesList:
Creates an in-memory table containing a list of all supported index types for the driver type.
DbiOpenLockList:
Creates an in-memory table containing a list of locks acquired on the table.
DbiOpenRintList :
Creates an in-memory table listing the referential integrity links for a specified table, along with.
Their descriptions.
DbiOpenSecurityList:
Creates an in-memory table listing record-level security information about a specified table.
DbiOpenTableList:
Creates an in-memory table with information about all the tables accessible to the client application.
DbiOpenTableTypesList:
Creates an in-memory table listing table type names for the given driver.
DbiOpenVchkList:
Creates an in-memory table containing records with information about validity checks for fields.
Within the specified table.
©Drkb::02516.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Работа с форматами данных.
Each function listed below sets or retrieves date or time, or decodes/encodes date and time into or from a timestamp.
DbiBcdFromFloat:
Converts FLOAT data to binary coded decimal (BCD) format.
DbiBcdToFloat:
Converts binary coded decimal (BCD) data to FLOAT format.
DbiDateDecode:
Decodes DBIDATE into separate month, day and year components.
DbiDateEncode:
Encodes separate date components into date for use by DbiPutField and other functions.
DbiGetDateFormat:
Gets the date format for the current session.
DbiGetNumberFormat:
Gets the number format for the current session.
DbiGetTimeFormat:
Gets the time format for the current session.
DbiSetDateFormat:
Sets the date format for the current session.
DbiSetNumberFormat:
Sets the number format for the current session.
DbiSetTimeFormat:
Sets the time format for the current session.
DbiTimeDecode:
Decodes time into separate components (hours, minutes, milliseconds).
DbiTimeEncode:
Encodes separate time components into time for use by DbiPutField and other functions.
DbiTimeStampDecode:
Extracts separate encoded date and time components from the timestamp.
DbiTimeStampEncode:
Encodes the encoded date and encoded time into a timestamp.
©Drkb::02517.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как получить параметры Alias? |
The following function uses the GetAliasParams method of TSession to get the directory mapping for an alias:
Code: Uses DbiProcs, DBiTypes; Function GetDataBaseDir(const Alias: string): string; {* Will return the directory of the database given the alias. (without trailing backslash) *}. Var. Sp: PChar; Res: pDBDesc; Begin. Try. New(Res); Sp := StrAlloc(length(Alias) + 1); StrPCopy(sp, Alias); If DbiGetDatabaseDesc(sp, Res) = 0 then. Result := StrPas(Res^.szPhyName). Else. Result := ''; Finally. StrDispose(sp); Dispose(Res); End; End;
| Как копировать и удалять таблицы? |
Here is an example of a routine that I use for copying and deleting tables. It uses DB, DBTables, DbiProcs,DbiErrs, and DbiTypes.
You simply provide the directory to copy from, the source table name, the directory to copy to, and the destination table name, and the BDE will copy the entire table, indexes and all to the new file.
The delete function takes the path to delete from and the name of the table to delete, the BDE takes care of deleting all associated files (indexes, etc.).
These procedures have been pulled off a form of mine, and I've edited them to remove some dependencies that existed with that form. They should now be completely stand-alone.
Code: Procedure TConvertForm.CopyTable(FromDir, SrcTblName, ToDir, DestTblName: string); Var. DBHandle: HDBIDB; ResultCode: DBIResult; Src, Dest, Err: array[0..255] of Char; SrcTbl, DestTbl: TTable; Begin. SrcTbl := TTable.Create(Application); DestTbl := TTable.Create(Application); Try. SrcTbl.DatabaseName := FromDir; SrcTbl.TableName := SrcTblName; SrcTbl.Open; DBHandle := SrcTbl.DBHandle; SrcTbl.Close; ResultCode := DbiCopyTable(DBHandle, false, StrPCopy(Src, FromDir + '\' + SrcTblName), nil, StrPCopy(Dest, ToDir + '\' + DestTblName)); If ResultCode <> DBIERR_NONE then. Begin. DbiGetErrorString(ResultCode, Err); Raise EDatabaseError.Create('While copying ' + FromDir + '\' + SrcTblName + ' to ' + ToDir + '\' + DestTblName + ', the ' + ' database engine generated the error ''' + StrPas(Err) + ''''); End; Finally. SrcTbl.Free; DestTbl.Free; End; End; Procedure TConvertForm.DeleteTable(Dir, TblName: string); Var. DBHandle: HDBIDB; ResultCode: DBIResult; Tbl, Err: array[0..255] of Char; SrcTbl, DestTbl: TTable; Begin. SrcTbl := TTable.Create(Application); Try. SrcTbl.DatabaseName := Dir; SrcTbl.TableName := TblName; SrcTbl.Open; DBHandle := SrcTbl.DBHandle; SrcTbl.Close; ResultCode := DbiDeleteTable(DBHandle, StrPCopy(Tbl, Dir + '\' + TblName), nil); If ResultCode <> DBIERR_NONE then. Begin. DbiGetErrorString(ResultCode, Err); Raise EDatabaseError.Create('While deleting ' + Dir + '\' + TblName + ', the database ' + 'engine generated the error ''' + StrPas(Err) + ''''); End; Finally. SrcTbl.Free; End; End;
| Как создать постоянный Alias? |
There has been a number of occasions where I needed to create persistent BDE aliases. The point is that the DB API isn't very discussed and is unkown to most Delphi developers. Despite that fact, the Dbi calls are very powerful and useful functions.
The function below, CreateAlias, encapsulates the DbiAddAlias call, with some error checking and BDE initialization and finalization procedures.
Code: Uses Windows, SysUtils, DbiProcs, DbiErrs, DBTables; Const. CRLF = #13 + #10; ERR_ALIASDRIVERNOTFOUND = 'Specified driver does not exist.'; ERR_ALIASALREADYEXISTS = 'The Alias (%s) already exists.' + CRLF + 'Would you like to reconfigure it?'; ERR_ALIASINVALIDPARAM = 'Invalid Alias name.'; ERR_ALIASCLOSEBDE = 'Error closing the BDE.' + CRLF + 'Please close all applications and restart Windows'; ERR_ALIASOPENBDE = 'Error initializing BDE. Cannot create Alias.'; Procedure CreateAlias(sAlias, sPath, sDriver: string; PersistentAlias: Boolean); Var. DbEnv: DbiEnv; DbRes: DbiResult; Resp: word; Begin. { Sets the BDE environment }. With dbEnv do. Begin. StrPCopy(szWorkDir, sPath); StrPCopy(szIniFile, ''); BForceLocalInit := True; StrPCopy(szLang, ''); StrPCopy(szClientName, 'dbClientName'); End; { Initalizes BDE with the environment dbEnv }. If DbiInit(@dbEnv) <> DbiERR_NONE then. Raise Exception.Create(ERR_ALIASOPENBDE); { Adds the specified Alias }. If sDriver = 'STANDARD' then. DbRes := DbiAddAlias(nil, pchar(sAlias), nil, Pchar('PATH:' + sPath), PersistentAlias). Else. DbRes := DbiAddAlias(nil, pchar(sAlias), pchar(sDriver), Pchar('PATH:' + sPath), PersistentAlias); Case dbRes of. DbiERR_INVALIDPARAM: Raise Exception.Create(ERR_ALIASINVALIDPARAM); DbiERR_NAMENOTUNIQUE: Begin. Resp := MessageBox(0, pchar(Format(ERR_ALIASALREADYEXISTS, [sAlias])), 'CreateAlias', MB_ICONSTOP + MB_YESNO); If Resp = ID_YES then. Begin. Check(DbiDeleteAlias(nil, pchar(sAlias))); CreateAlias(sAlias, sPath, sDriver, PersistentAlias); End; End; DbiERR_UNKNOWNDRIVER: Raise Exception.Create(ERR_ALIASDRIVERNOTFOUND); End; If DbiExit <> DbiERR_NONE then. Raise Exception.Create(ERR_ALIASCLOSEBDE); End; {CreateAlias}.
Database index out of date error.
This is a BDE/Paradox error message. For newbies, BDE error messages are daunting, cryptic messages. Actually, even for seasoned veterans, they can sometimes be real "stumpers." Unfortunately, there's no real good reference available that I know of, so all I can offer with respect to this error message is my experience.
The "Index out of date" message can mean a couple of things:
1. 1. One of the more common causes of this error is one in which you have a couple of copies of a table existing on your network or machine. For instance, when I develop applications, I have my application tables residing in my development system, then have copies of them on my network. When I need to update my tables, I usually do the updates in my development system, then copy them over to my deployment system on the network. I've run into this exact error when I've copied only the table (.DB) file and not its accompanying index file(s) (.PX, .X01, .Y01, etc) as well. You see, when you update a table by changing it in any way, its index files are also resynched to reflect the changes. So if you copy just the table to a new place on your system and don't include its family members, you'll index files that aren't in synch with your table. Okay that's one cause.
2. 2. The next cause could be just this: One of your indexes is corrupt. This could be due to sector errors on your hard disk, or the rare, but possible, direct corruption of an index. This usually happens if your program abended while performing an update to a table with an index of some sort. In that case, the index doesn't get updated.
But in any case, the only way I know of to correct the problem is to do the following:
1. Open up your table in Database Desktop.
2. Restructure it.
3. Define/Rebuild all your indexes.
4. Save the file.
©Drkb::02573.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Автор: Tom Jensen.
Некоторое время назад у меня также была масса ошибок типа 'index out of date' и даже искажение данных. После продолжительного исследования я выяснил причину, она оказалось в различных установках Paradox Language в BDE (v1 и V3) на странице Driver и System в утилите конфигурирования BDE. Я не обратил внимание на установки на странице System одной из рабочих станций, и получил искажение данных.
©Drkb::02574.
Взято с http://delphiworld.narod.ru.
| Как по Alias узнать физический путь к базе данных? |
Code: Function GetAliasDir(alias: PChar): PChar; Var. S: TStringList; I: integer; T: string; Res: array[0..255] of char; Begin. Res := ''; If Session.IsAlias(alias) then. Begin {Check if alias exists}. S := TStringList.Create; Try. Session.GetAliasParams(Alias, s); T := ''; If s.count > 0 then. Begin. I := 0; While (i < s.count) and (Copy(s.Strings[i], 1, 5) <> 'PATH=') do. Inc(i); If (i < s.count) and (Copy(s.Strings[i], 1, 5) = 'PATH =') then. Begin. T := Copy(s.Strings[i], 6, Length(s.Strings[i]) - 4); If t[length(t)] <> '\' then. T := t + '\'; End; End; StrPCopy(res, t); Except. StrPCopy(res, ''); End; S.Free; End; Result := res; End;
BDE Error 2B04 Too many open files.
Open the tool 'BDE Administrator' and choose the right tab 'Configuration'. There open the node 'System' and click on subnode 'INIT'. Then update 'MaxFileHandles' on the right side (it is 48 by default).
©Drkb::02575.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как сделать список всех пользователей BDE? |
With Paradox:
Code: Procedure BDEGetPDXUserList(AList: TStrings); Var. HCur: hDBICur; UDesc: USERDesc; Begin. AList.Clear; Check(DBIOpenUserList(hCur)); Try. While DBIGetNextRecord(hCur, dbiNOLOCK, @UDesc, nil) <> DBIERR_EOF do. Begin. AList.Add(StrPas(UDesc.szUserName)); End; Finally. DBICloseCursor(hCur); End; End;
| Как инициализировать BDE, если она установлена в нестандартном месте? |
I need to use a BDE that is placed in another directory than default. How can I do it? DbiInit(pDbiEnv) doesn't work when pDbiEnv < > nil (not default).
Answer:
Code: PDbiEnv := nil; Check(DbiInit(pDbiEnv));
| Как результат Query сделать в виде постоянной таблицы? |
Traditionally, to write the results of a query to disk, you use a TBatchMove and a TTable in addition to your query. But you can short-circuit this process by making a couple of simple, direct calls to the BDE.
Make sure you have BDE declared in your uses section.
Code: Procedure MakePermTable(Qry: TQuery; PermTableName: string); Var. H: HDBICur; Ph: PHDBICur; Begin. Qry.Prepare; Check(dbiQExec(Qry.StmtHandle, ph)); H := ph^; Check(DbiMakePermanent(h, PChar(PermTableName), True)); End;
| Модификация настроек BDE. |
Is there a way to change the IDAPI.CFG file from Delphi coding using the BDE API, since I wish to avoid having my users utilize the BDECFG.EXE utility?
Answer:
Here is a unit that is supposed to allow changing the config file:
Code: Unit CFGTOOL; Interface. Uses. SysUtils, Classes, DB, DbiProcs, DbiTypes, DbiErrs; Type. TBDEConfig = class(TComponent). Private. FLocalShare: Boolean; FMinBufSize: Integer; FMaxBufSize: Integer; FSystemLangDriver: string; FParadoxLangDriver: string; FMaxFileHandles: Integer; FNetFileDir: string; FTableLevel: string; FBlockSize: Integer; FDefaultDriver: string; FStrictIntegrity: Boolean; FAutoODBC: Boolean; Procedure Init; Procedure SetLocalShare(Value: Boolean); Procedure SetMinBufSize(Value: Integer); Procedure SetMaxBufSize(Value: Integer); Procedure SetSystemLangDriver(Value: string); Procedure SetParadoxLangDriver(Value: string); Procedure SetMaxFileHandles(Value: Integer); Procedure SetNetFileDir(Value: string); Procedure SetTableLevel(Value: string); Procedure SetBlockSize(Value: Integer); Procedure SetDefaultDriver(Value: string); Procedure SetAutoODBC(Value: Boolean); Procedure SetStrictIntegrity(Value: Boolean); Procedure UpdateCFGFile(path, item, value: string); Protected. Public. Constructor Create(AOwner: TComponent); override; Destructor Destroy; override; Published. Property LocalShare: Boolean read FLocalShare write SetLocalShare; Property MinBufSize: Integer read FMinBufSize write SetMinBufSize; Property MaxBufSize: Integer read FMaxBufSize write SetMaxBufSize; Property SystemLangDriver: string read FSystemLangDriver write. SetSystemLangDriver; Property ParadoxLangDriver: string read FParadoxLangDriver write. SetParadoxLangDriver; Property MaxFileHandles: Integer read FMaxFileHandles write SetMaxFileHandles; Property NetFileDir: string read FNetFileDir write SetNetFileDir; Property TableLevel: string read FTableLevel write SetTableLevel; Property BlockSize: Integer read FBlockSize write SetBlockSize; Property DefaultDriver: string read FDefaultDriver write SetDefaultDriver; Property AutoODBC: Boolean read FAutoODBC write SetAutoODBC; Property StrictIntegrity: Boolean read FStrictIntegrity write SetStrictIntegrity; End; Procedure Register; Implementation. Function StrToBoolean(Value: string): Boolean; Begin. If (UpperCase(Value) = 'TRUE') or (UpperCase(Value) = 'ON') or. (UpperCase(Value) = 'YES') or (UpperCase(Value) = '.T.') then. Result := True. Else. Result := False; End; Function BooleanToStr(Value: Boolean): string; Begin. If Value then. Result := 'TRUE' Else. Result := 'FALSE'; End; Procedure Register; Begin. RegisterComponents('Data Access', [TBDEConfig]); End; Procedure TBDEConfig.Init; Var. H: hDBICur; PCfgDes: pCFGDesc; N, v: string; Begin. Check(DbiOpenCfgInfoList(nil, dbiREADWRITE, cfgPersistent, '\SYSTEM\INIT', h)); GetMem(pCfgDes, sizeof(CFGDesc)); Try. FillChar(pCfgDes^, sizeof(CFGDesc), #0); While (DbiGetNextRecord(h, dbiWRITELOCK, pCfgDes, nil) = DBIERR_NONE) do. Begin. N := StrPas(pCfgDes^.szNodeName); V := StrPas(pCfgDes^.szValue); If n = 'LOCAL SHARE' then. FLocalShare := StrToBoolean(v). Else if n = 'MINBUFSIZE' then. FMinBufSize := StrToInt(v). Else if n = 'MAXBUFSIZE' then. FMaxBufSize := StrToInt(v). Else if n = 'MAXFILEHANDLES' then. FMaxFileHandles := StrToInt(v). Else if n = 'LANGDRIVER' then. FSystemLangDriver := v. Else if n = 'AUTO ODBC' then. FAutoODBC := StrToBoolean(v). Else if n = 'DEFAULT DRIVER' then. FDefaultDriver := v; End; If (h <> nil) then. DbiCloseCursor(h); Check(DbiOpenCfgInfoList(nil, dbiREADWRITE, cfgPersistent, '\DRIVERS\PARADOX\INIT', h)); FillChar(pCfgDes^, sizeof(CFGDesc), #0); While (DbiGetNextRecord(h, dbiWRITELOCK, pCfgDes, nil) = DBIERR_NONE) do. Begin. N := StrPas(pCfgDes^.szNodeName); V := StrPas(pCfgDes^.szValue); If n = 'NET DIR' then. FNetFileDir := v. Else if n = 'LANGDRIVER' then. FParadoxLangDriver := v; End; If (h <> nil) then. DbiCloseCursor(h); Check(DbiOpenCfgInfoList(nil, dbiREADWRITE, cfgPersistent, '\DRIVERS\PARADOX\TABLE CREATE', h)); FillChar(pCfgDes^, sizeof(CFGDesc), #0); While (DbiGetNextRecord(h, dbiWRITELOCK, pCfgDes, nil) = DBIERR_NONE) do. Begin. N := StrPas(pCfgDes^.szNodeName); V := StrPas(pCfgDes^.szValue); If n = 'LEVEL' then. FTableLevel := v. Else if n = 'BLOCK SIZE' then. FBlockSize := StrToInt(v). Else if n = 'STRICTINTEGRITY' then. FStrictIntegrity := StrToBoolean(v); End; Finally. FreeMem(pCfgDes, sizeof(CFGDesc)); If (h <> nil) then. DbiCloseCursor(h); End; End; Procedure TBDEConfig.SetLocalShare(Value: Boolean); Begin. UpdateCfgFile('\SYSTEM\INIT', 'LOCAL SHARE', BooleanToStr(Value)); FLocalShare := Value; End; Procedure TBDEConfig.SetMinBufSize(Value: Integer); Begin. UpdateCfgFile('\SYSTEM\INIT', 'MINBUFSIZE', IntToStr(Value)); FMinBufSize := Value; End; Procedure TBDEConfig.SetMaxBufSize(Value: Integer); Begin. UpdateCfgFile('\SYSTEM\INIT', 'MAXBUFSIZE', IntToStr(Value)); FMaxBufSize := Value; End; Procedure TBDEConfig.SetSystemLangDriver(Value: string); Begin. UpdateCfgFile('\SYSTEM\INIT', 'LANGDRIVER', Value); FSystemLangDriver := Value; End; Procedure TBDEConfig.SetParadoxLangDriver(Value: string); Begin. UpdateCfgFile('\DRIVERS\PARADOX\INIT', 'LANGDRIVER', Value); FParadoxLangDriver := Value; End; Procedure TBDEConfig.SetMaxFileHandles(Value: Integer); Begin. UpdateCfgFile('\SYSTEM\INIT', 'MAXFILEHANDLES', IntToStr(Value)); FMaxFileHandles := Value; End; Procedure TBDEConfig.SetNetFileDir(Value: string); Begin. UpdateCfgFile('\DRIVERS\PARADOX\INIT', 'NET DIR', Value); FNetFileDir := Value; End; Procedure TBDEConfig.SetTableLevel(Value: string); Begin. UpdateCfgFile('\DRIVERS\PARADOX\TABLE CREATE', 'LEVEL', Value); FTableLevel := Value; End; Procedure TBDEConfig.SetBlockSize(Value: Integer); Begin. UpdateCfgFile('\DRIVERS\PARADOX\TABLE CREATE', 'BLOCK SIZE', IntToStr(Value)); FBlockSize := Value; End; Procedure TBDEConfig.SetStrictIntegrity(Value: Boolean); Begin. UpdateCfgFile('\DRIVERS\PARADOX\TABLE CREATE', 'STRICTINTEGRITY', BooleanToStr(Value)); FStrictIntegrity := Value; End; Procedure TBDEConfig.SetDefaultDriver(Value: string); Begin. UpdateCfgFile('\SYSTEM\INIT', 'DEFAULT DRIVER', Value); FDefaultDriver := Value; End; Procedure TBDEConfig.SetAutoODBC(Value: Boolean); Begin. UpdateCfgFile('\SYSTEM\INIT', 'AUTO ODBC', BooleanToStr(Value)); FAutoODBC := Value; End; Procedure TBDEConfig.UpdateCFGFile; Var. H: hDbiCur; PCfgDes: pCFGDesc; PPath: array[0..127] of char; Begin. StrPCopy(pPath, Path); Check(DbiOpenCfgInfoList(nil, dbiREADWRITE, cfgPersistent, pPath, h)); GetMem(pCfgDes, sizeof(CFGDesc)); Try. FillChar(pCfgDes^, sizeof(CFGDesc), #0); While (DbiGetNextRecord(h, dbiWRITELOCK, pCfgDes, nil) = DBIERR_NONE) do. Begin. If StrPas(pCfgDes^.szNodeName) = item then. Begin. StrPCopy(pCfgDes^.szValue, value); Check(DbiModifyRecord(h, pCfgDes, True)); End; End; Finally. FreeMem(pCfgDes, sizeof(CFGDesc)); If (h <> nil) then. DbiCloseCursor(h); End; End; Constructor TBDEConfig.Create(AOwner: TComponent); Begin. Inherited Create(AOwner); Init; End; Destructor TBDEConfig.Destroy; Begin. Inherited Destroy; End; End.
©Drkb::02498.
Problem/Question/Abstract:
How can my program access the idapi.cfg file and probably change its INIT (Local Share etc.) section?
Answer:
For 32bit only. You can of course use the registry to determine the default CFG File instead of passing it as a parameter here:
Code: Procedure ModifyCFG(const ACFGFile, AValue, AEntry, ACFGPath: string; SaveAsWin31: Bool); Var. HCfg: hDBICfg; PRecBuf, pTmpRec: pByte; PFields: pFLDDesc; Count: word; I: integer; Save: boolean; Reg: TRegistry; Const. RegSaveWIN31: array[bool] of string = ('WIN32', 'WIN31'); Begin. HCfg := nil; PFields := nil; PRecBuf := nil; Save := False; Check(DbiOpenConfigFile(PChar(ACFGFile), False, hCfg)); Try. Check(DbiCfgPosition(hCfg, PChar(ACfgPath))); {neccessary...?}. Check(DbiCfgGetRecord(hCfg, PChar(ACfgPath), Count, nil, nil)); PRecBuf := AllocMem(succ(Count) * 128); {128 additional safety...}. PFields := AllocMem(Count * sizeof(FLDDesc)); Check(DbiCfgGetRecord(hCfg, PChar(ACfgPath), Count, pFields, pRecBuf)); For i := 1 to Count do. Begin. If StrPas(pFields^.szName) = AEntry then. Begin. PTmpRec := pRecBuf; Inc(pTmpRec, 128 * (i - 1)); StrPCopy(PChar(pTmpRec), AValue); End; Inc(pFields); End; Dec(pFields, Count); Check(DbiCfgModifyRecord(hCfg, PChar(ACfgPath), Count, pFields, pRecBuf)); Save := True; Finally. If hCfg <> nil then. Check(DbiCloseConfigFile(hCfg, Save, True, SaveAsWin31)); If pRecBuf <> nil then. FreeMem(pRecBuf, succ(Count) * 128); If pFields <> nil then. FreeMem(pFields, Count * sizeof(FLDDesc)); End; {update registry SAVECONFIG value}. Reg := TRegistry.Create; Try. Reg.RootKey := HKEY_LOCAL_MACHINE; If not Reg.OpenKey('SOFTWARE\Borland\Database Engine', False) then. ShowMessage('Configuration Path not found'). Else. Begin. Reg.LazyWrite := False; Reg.WriteString('SAVECONFIG', RegSaveWIN31[SaveAsWin31]); Reg.CloseKey; End; Finally. Reg.Free; End; {DbiExit/Init to re-read cfg... make absolutely sure there are no active. DB components when doing this (it's is best done by a loader app)}. Session.Close; Session.Open; End;
Как создать DBase базу данных?
Code: Procedure MakeDataBase; Begin. With TTable.Create(nil) do. Begin. DatabaseName := 'c:\temp'; (* alias *). TableName := 'test.dbf'; TableType := ttDBase; With FieldDefs do. Begin. Add('F_NAME', ftString, 20, false); Add('L_NAME', ftString, 30, false); End; CreateTable; { create a calculated index }. With IndexDefs do. Begin. Clear; { don't forget ixExpression in calculated indexes! }. AddIndex('name', 'Upper(L_NAME)+Upper(F_NAME)', [ixExpression]); End; End; End;
©Drkb::02693.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как паковать таблицу? |
Code: Function dgPackParadoxTable(Tbl: TTable; Db: TDatabase): DBIResult; {Packs a Paradox table by calling the BDE DbiDoRestructure function. The TTable passed as the first parameter must be closed. The TDatabase passed as the second parameter must be connected.}. Var. TblDesc: CRTblDesc; Begin. Result := DBIERR_NA; FillChar(TblDesc, SizeOf(CRTblDesc), 0); StrPCopy(TblDesc.szTblName, Tbl.TableName); TblDesc.bPack := True; Result := DbiDoRestructure(Db.Handle, 1, @TblDesc, nil, nil, nil, False); End;
| Как предотвратить появление login dialog? |
To bypass the login dialog when connecting to a server database, use the property LoginPrompt.You will have to provide the username & password at runtime, but you also can set that up at design time in the object inspector, property Params.
This short source code shows how to do it:
Code: Database1.LoginPrompt := false; With Database1.Params do. Begin. Clear; // the parameters SYSDBA & masterkey should be. // retrieved somewhat different :-). Add('USER NAME=SYSDBA'); Add('PASSWORD=masterkey'); End; Database1.Connected := tr.
Как получить текущее время?
InterBase supports four DATE literals. They are: 'today', 'yesterday', 'tomorrow' and 'now'
Use it with a cast as shown in the example below.
Insert into mytable values(cast('now' as DATE), 'Test').
©Drkb::02905.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как получить список таблиц? |
A list of user tables can be retrieved by querying system table rdb$relations.
The example below shows how to do this - it inserts the table names sorted alphabetically into a ListBox (lbSourceTables).
Code: Begin. IbcSourceList.SQL.Clear; IbcSourceList.SQL.Add('select rdb$relation_name from rdb$relations'); IbcSourceList.SQL.Add('where rdb$system_flag = 0'); IbcSourceList.SQL.Add('order by rdb$relation_name'); IbcSourceList.Open; While not ibcSourceList.Eof do. Begin. LbSourceTables.Items.Add(ibcSourceList.Fields[0].AsString); IbcSourceList.Next; End; IbcSourceList.Close; End;
Как восстановить целостность автоинкрементного поля?
Problem/Question/Abstract:
Recently I got unique key violations during insert attempts on a piece of code that used to work (what can go bad, will go bad). I found that the offending field - was actually created by a generator. For some reason the generator returned values that where already in the database.
· how can I display the current value of the generator?
· how can I adjust the value of the generator?
Answer:
See the example (table name is SD_LOAD, generator name is GEN_SD_LOAD).
Note:
You cannot modify the value of the generator inside of a trigger or stored procedure. You only can call the gen_id() function to increment the value in a generator. The SET GENERATOR command will only work outside of a stored procedure or trigger.
SELECT DISTINCT(GEN_ID(gen_sd_load, 0))FROM sd_load.
Set GENERATOR gen_sd_load to 2021819.
©Drkb::02907.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Как остановить или запустить IB сервис?
Do you need to shutdown the Interbase db service e.g. for an installation program and afterwards restart it?
You could do this with a lot of Delphi code involving unit WinSvc and function calls to.
OpenSCManager().
EnumServicesStatus().
OpenService().
StartService() or ControlService().
But luckily there is a much easier solution that uses the NET.EXE program which has been part of Windows since Windows for Workgroups (Wfw 3.11). Just create the two batch files.
IBSTOP.BAT.
IBSTART.BAT.
And call them from your code. You may want to call them and wait for their termination.
IBSTOP.BAT.
=============
@echo off.
Net stop "InterBase Guardian" >NULL.
Net stop "InterBase Server" >NULL.
IBSTART.BAT.
=============
@echo off.
Net start "Interbase Guardian" >NULL.
©Drkb::02890.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Ошибка: lock manager out of room.
Go to the interbase/bin directory (Windows) or /usr/interbase (Unix) and locate the configuration file isc_config. By default your configuration file will look like this:
#V4_LOCK_MEM_SIZE 98304.
#ANY_LOCK_MEM_SIZE 98304.
#V4_LOCK_SEM_COUNT 32.
#ANY_LOCK_SEM_COUNT 32.
#V4_LOCK_SIGNAL 16.
#ANY_LOCK_SIGNAL 16.
#V4_EVENT_MEM_SIZE 32768.
#ANY_EVENT_MEM_SIZE 32768.
I increased the V4_LOCK_MEM_SIZE entry from 98304 to 198304 and things were fine then.
!!! Important !!!
By default all lines in the config file are commented out with the leading # sign. Make sure to remove the # sign in any line that you change - the default config file just shows the default parameters.
©Drkb::02908.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Ошибка о файле SAA.AAA.
The 'SAA.AAA' file is a temporary file used in processing a query.
A failure involving this file generally means that InterBase has run out of disk space processing the query.
(Remember that this will be a temporary file on the server, so the server is out of disk space, not your local machine!).
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Этот файл является временным, и создается когда при выполнении запроса возникает необходимость в сортировке результата. Ошибка может возникать при нехватке дискового пространства на томе, куда указывает TEMP. Для NT при работе IB в режиме сервиса необходимо изменить переменную TEMP для System (см. My Computer/Properties/Environment). Для IB 5.x и 6.0 (в архитектуре SuperServer только!) можно указать диски и размер временных файлов в файле конфигурации IBCONFIG, или в переменной окружения INTERBASE_TMP (см. Operations Guide, стр92). Например:
TMP_DIRECTORY "c:\" 10000000.
TMP_DIRECTORY "e:\temp\" 100000000.
Может быть указано несколько дисков или каталогов, которые будут использоваться последовательно. Размер должен быть указан в байтах. Кавычки для имени диска и каталога обязательны.
©Drkb::02909.
Borland Interbase / Firebird FAQ.
Borland Interbase / Firebird Q&A, версия 2.02 от 31 мая 1999.
Последняя редакция от 17 ноября 1999 года.
Часто задаваемые вопросы и ответы по Borland Interbase / Firebird.
Материал подготовлен в Демо-центре клиент-серверных технологий. (Epsylon Technologies).
Материал не является официальной информацией компании Borland.
E-mail mailto:delphi@demo.ru.
Www: http://www.ibase.ru/
Телефоны: 953-13-34.
Источники: Borland International, Борланд АО, релиз Interbase 4.0, 4.1, 4.2, 5.0, 5.1, 5.5, 5.6, различные источники на WWW-серверах, текущая переписка, московский семинар по Delphi и конференции, листсервер ESUNIX1, листсервер mers.com.
Составитель: Дмитрий Кузьменко.
| Как паковать базу данных? |
Using D6 Pro, Access XP and Jet 4.0 Sp6 - how can I compact Access files?
Answer:
This does it:
Code: Procedure TMainForm.ActionCompactAccessDBExecute(Sender: TObject); Var. JetEngine: Variant; TempName: string; AAccess: string; StAccessDB: string; SaveCursor: TCursor; Begin. StAccessDB := 'Provider = Microsoft.Jet.OLEDB.4.0;' + 'Data Source = %s;Jet OLEDB: Engine type = '; StAccessDB := stAccessDB + '5'; {5 for Access 2000 and 4 for Access 97}. OpenDialog1.InitialDir := oSoftConfig.ApplicationPath + 'Data\'; OpenDialog1.Filter := 'MS Access (r) (*.mdb)|*.mdb'; If OpenDialog1.execute and (uppercase(ExtractFileExt. (OpenDialog1.FileName)) = '.MDB') then. Begin. If MessageDlg('This process can take several minutes. Please wait till the end ' + #13 + #10 + 'of it. Do you want to proceed? Press No to exit.', mtInformation, [mbYes, mbNo], 0) = mrNo then. Exit; SaveCursor := screen.cursor; Screen.cursor := crHourGlass; AAccess := OpenDialog1.FileName; TempName := ChangeFileExt(aAccess, '.$$$'); DeleteFile(PChar(TempName)); JetEngine := CreateOleObject('JRO.JetEngine'); Try. JetEngine.CompactDatabase(Format(stAccessDB, [aAccess]), Format(stAccessDB, [TempName])); DeleteFile(PChar(aAccess)); RenameFile(TempName, aAccess); Finally. JetEngine := Unassigned; Screen.cursor := SaveCursor; End; End; End;
Как скопировать таблицу из одной базы данных в другую?
F I am not wrong you have an Access db with multiple tables and you want to copy one of these tables into another Access db. For this case i would do the next:
1. Create database TrasportDB.mdb - use ADOX.
2. Copy table from source table into TransportDB.mdb with Select * Into [TransportTable] in "FullPath\TransportDB.mdb" From SourceTable.
3. Deliver TransportDB.mdb on destination computer.
4. Copy table from TransportTable into DestTable with Select * Into [DestTable] From [TransportTable] in "FullPath\TransportDB.mdb".
FullPath is the path to TransportDB.mdb and is different on source and dest computers.
This way you will use native access methods that should be more reliable and faster than using ADO methods. If you need to perform more complete tasks you should use replication from Microsoft Jet and Replication objects (import this typelib).
©Drkb::02762.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как узнать номер автоинкремента при вставке новой записи? |
We have a table in MsAccess like :
Test, Fields (id=autoinc, name=text);
First we have to have a function like the one below :
Code: Function GetLastInsertID: integer; Begin. // datResult = TADODataSet. DatResult.Active := False; DatResult.CommandText := 'select @@IDENTITY as [ID]'; DatResult.Active := True; Result := datResult.FieldByName('id').AsInteger; DatResult.Active := False; End;
| Примеры работы с репортами. |
In the next small example I'll demonstrate how you can call the report in MS Access:
Code: Var. Access: Variant; Begin. // open the Access application. Try. Access := GetActiveOleObject('Access.Application'); Except. Access := CreateOleObject('Access.Application'); End; Access.Visible := True; // open the database. //The second parameter specifies whether you want to open the database in Exclusive mode. Access.OpenCurrentDatabase('C:\My Documents\Books.mdb', True); // open the report. {The value for the second parameter should be one of. AcViewDesign, acViewNormal, or acViewPreview. acViewNormal, which is the default, prints the report immediately. If you are not using the type library, you can define these values like this: Const. AcViewNormal = $00000000; AcViewDesign = $00000001; AcViewPreview = $00000002; The third parameter is for the name of a query in the current. Database.The fourth parameter is for a SQL WHERE clause - the string must be valid. SQL, minus the WHERE.}. Access.DoCmd.OpenReport('Titles by Author', acViewPreview, EmptyParam, EmptyParam); { ... } // close the database. Access.CloseCurrentDatabase; // close the Access application. {const. AcQuitPrompt = $00000000; AcQuitSaveAll = $00000001; AcQuitSaveNone = $00000002;}. Access.Quit(acQuitSaveAll); End;
| Как удалить таблицу? |
I've been doing extensive work with Client/Server Delphi and MS SQL Server as my back-end database. The operational model that I use for my Client/Server is that the client application acts only as local interface, and that all queries and calculations - even temporary files - are performed or created on the server. Now this presents a couple of problems in that garbage cleanup isn't quite as easy as it is when using local tables as temporary files.
For instance, a lot of my programs create temporary files that I either reference later in the program or that I use as temporary storage for outer joins. Once I'm done with them, I need to delete them. With local tables, it's a snap. Just get a list of the tables, and with a little bit of code that uses some Windows API calls, delete them. Not so easy with SQL Server tables. The reason why is that you have to go through the BDE to accomplish the task - something that's not necessarily very intuitive. Luckily, however, it doesn't involve low-level BDE API calls.
Below is a procedure listing that drops tables from any SQL Server database. After the listing I'll discuss particulars...
Parameter Descriptions.
Code: //var Ses : TSession; //A valid, open session. //DBName : String; //Name of the SQL Server DB. //ArTables : array of String; //An array of table names. //StatMsg : TStatusMsg); //A status message callback. //procedure.
| Extended Stored Procedures with Delphi - Using the ODS (Open Data Services) Api from Delphi. |
Have you ever tried to create a stored procedure on an SQL Server?
What if the functionality you wish to incorporate is not inside the SQL but in your Delphi project? Microsoft provides ODS (Open Data Services) which is an API to help you create such functionality by adding DLL's to your SQL Server that contain Extended Stored Procedures.
This is how you do it....
Answer:
As a special bonus, this article also is a simple solution to encrypting your data inside SQL Server using Extended Stored Procedures.
What do we need?
1. 1. opends60.dll (from a full installation with developer tools of MSSQL or MSDE)
2. 2. MsOdsApi.pas (Header file to use opends60.dll) (included at the end of the article)
3. 3. A Local SQL Server for testing (MSDE will do fine).
NOTE: For cryptography I used the components found under MIT license here:
Http://www.cityinthesky.co.uk/cryptography.html.
Steps:
1. 1. Create a new DLL.
2. 2. Create a unit and in the uses clause add the MsOdsApi unit.
3. 3. In the Library file just under ther Uses clause add Exports and the names of the functions to export:
Code: Library MyEncryptDll; Uses. SysUtils, Classes, Unit1 in 'Unit1.pas'; Exports. Xp_DoEncrypt, Xp_DoDecrypt; Begin. End.
| Самый быстрый способ узнать количество записей в таблице. |
When using the standard dataset.recordcount in my client-server (win nt against sqlserver7 db, targettable has 500.000 records) i can go for lunch and stil be waiting (:-
Answer:
For those of you who don't know why u should not use the standard dataset.recordcount when developing client server database applications.
This article is especialy for those cs db apps against a sqlserver 7 db.
Since the standard dataset.recordcount iterates from begin of the table through the end of the table to result in the recordcount. This is a crime when developing cs db apps (against sqlserver7).
Simply use another way of obtaining the number of records. I use a sql for obtaining the number of records in a sqlserver table.
Drop a tquery on the form.
Provide this tquery with the follow SQL:
SQL:
Code: Select distinct max(itbl.rows). From sysindexes as itbl. Inner join sysobjects as otbl on (itbl.id = otbl.id). Where (otbl.type = 'U') and (otbl.name = :parTableName).
| Как узнать версию сервера? |
This function gets the connected MS SQL Server version. It returns the version info in 3 OUT parameters.
VerNum : double eg. 7.00623.
VerStrShort : string eg. '7.00.623'
VerStrLong : string eg. 'Microsoft SQL Server 7.00 - 7.00.623 (Intel X86) Nov 27 1998 22:20:07 Copyright (c) 1988-1998 Microsoft Corporation Enterprise Edition on Windows NT 5.0 (Build 2195: Service Pack 1)'
I have tested it with MSSQL 7 and MSSQL 2000. I assume it should work for the others. Any feedback and fixes for different versions would be appreciated.
The TQuery parameter that it recieves is a TQuery component that is connected to an open database connection.
Code: Procedure GetSqlVersion(Query: TQuery; Out VerNum: double; Out VerStrShort: string; Out VerStrLong: string); Var. STmp, sValue: string; I: integer; Begin. // @@Version does not return a Cursor. // Read the value from the Record Buffer. // Can be used to read all sys functions from MS Sql. SValue := ''; Query.SQL.Text := 'select @@Version'; Query.Open; SetLength(sValue, Query.RecordSize + 1); Query.GetCurrentRecord(PChar(sValue)); SetLength(sValue, StrLen(PChar(sValue))); Query.Close; If sValue <> '' then. VerStrLong := sValue. Else. Begin. // Don't know this version. VerStrLong := '?'; VerNum := 0.0; VerStrShort := '?.?.?.?'; End; If VerStrLong <> '' then. Begin. STmp := trim(copy(VerStrLong, pos('-', VerStrLong) + 1, 1024)); VerStrShort := copy(sTmp, 1, pos(' ', sTmp) - 1); STmp := copy(VerStrShort, 1, pos('.', VerStrShort)); For i := length(sTmp) + 1 to length(VerStrShort) do. Begin. If VerStrShort[i] <> '.' then. STmp := sTmp + VerStrShort[i]; End; VerNum := StrToFloat(sTmp); End; End;
| Как найти все Alias, укакзывающие на MS SQL Server? |
Code: GetAliases(ComboBox1.Items). Procedure GetAliases(const AList: TStrings); Var. I: Integer; Desc: DBDesc; Buff: array[0..254] of char; Begin. // list all BDE aliases. Session.GetAliasNames(AList); For i := AList.Count - 1 downto 0 do. Begin. StrPCopy(Buff, AList[i]); Check(DbiGetDatabaseDesc(Buff, @Desc)); // no Paradox, please. If StrPas(Desc.szDBType) = 'STANDARD' then. AList.Delete(i). End. End;
| Разработка внешних Stored Procedures. |
Answer:
Writing MS SQL Server Extended Stored Procedures with Delphi.
Microsoft SQL Server 6.5 and 7 have the powerful capability to make functions in DLL's available as stored procedures. Microsoft calls them Extended Stored Procedures. If you've read this article, you know what Extended Stored Procedures are, what you can do with them, and how to install them on a SQL Server. You should also be able to use the object-oriented framework I wrote, which makes writing Extended Stored Procedures with Delphi extremely easy.
I assume you are familiar with SQL Server and with the concept of stored procedures. The code and examples in this article apply both to SQL Server 6.5 and SQL Server 7.
What are Extended Stored Procedures?
Extended Stored Procedures (called xp's afterwards) are part of Microsoft's Open Data Services (ODS) for SQL Server. With ODS you can do three things:
1. Making routines in a DLL available as stored procedures to any SQL Server user.
2. Write procedure server applications. They are similar to xp's, however they run as a separate network server application and could even be running on a different machine (3-tier).
3. Writing gateways to non-SQL Server based environments.
In the following figure a graphical overview of the ODS architecture is given.
| Как выяснить номер версии Oracle? |
This function gets the connected Oracle version. It returns the version info in 3 OUT parameters.
VerNum : double eg. 7.23.
VerStrShort : string eg. '7.2.3.0.0'
VerStrLong : string eg. 'Oracle7 Server Release 7.2.3.0.0 - Production Release'
I have tested it with Oracle 7.2 and 8.17. I assume it should work for the others (not too sure about Oracle 9 though). Any feedback and fixes for different versions would be appreciated.
The TQuery parameter that it recieves is a TQuery component that is connected to an open database connection.
Example :
Code: Var. VNum: double; VShort: string; VLong: string; Begin. GetOraVersion(MySql, VNum, VShort, VLong); Label1.Caption := FloatToStr(VNum); Label2.Caption := VShort; Label3.Caption := VLong; End; Procedure GetOraVersion(Query: TQuery; Out VerNum: double; Out VerStrShort: string; Out VerStrLong: string); Var. STmp: string; CKey: char; I: integer; Begin. Query.SQL.Text := 'select banner from v$version ' + 'where banner like ' + QuotedStr('Oracle%'); Query.Open; If not Query.Eof then. VerStrLong := Query.Fields[0].AsString. Else. Begin. // Don't know this version. VerStrLong := '?'; VerNum := 0.0; VerStrShort := '?.?.?.?'; End; Query.Close; If VerStrLong <> '?' then. Begin. CKey := VerStrLong[7]; // eg. Oracle7 or Oracle8i. VerStrLong[7] := 'X'; // Mask it out. STmp := copy(VerStrLong, pos(cKey, VerStrLong), 1024); VerStrShort := copy(sTmp, 1, pos(' ', sTmp) - 1); STmp := copy(VerStrShort, 1, pos('.', VerStrShort)); For i := length(sTmp) + 1 to length(VerStrShort) do. Begin. If VerStrShort[i] <> '.' then. STmp := sTmp + VerStrShort[i]; End; VerNum := StrToFloat(sTmp); VerStrLong[7] := cKey; // Put correct character back. End; End;
| Как получить текущую дату? |
Code: // make the SQL dependent on type of DBMS. If AppLibrary.Database.DriverName = 'ORACLE' then. SQL.Add('and entry_date < SYSDATE'). Else. SQL.Add('and entry_date < "TODAY"'); End;
| Как найти размер записи? |
Code: Procedure TMainFrm.CalculateRecordSizeClick(Sender: TObject); Var. MaxRecs, RecSize, RecsPerBlock, FreeSpace: Longint; I: Integer; Begin. RecSize := 0; With StrucGrid do. Begin. For i := 0 to pred(RowCount) do. Begin. Case Cells[1, i][1] of. 'A': RecSize := RecSize + StrToInt(Cells[2, i]); 'D', 'T', 'I', '+': RecSize := RecSize + 4; 'N', '$', 'Y', '@': RecSize := RecSize + 8; 'M', 'B', 'F', 'O', 'G': RecSize := RecSize + 10 + StrToInt(Cells[2, i]); 'S': RecSize := RecSize + 2; 'L': RecSize := RecSize + 1; End; End; End; RecsPerBlock := (SpinEdit2.Value - 6) div RecSize; FreeSpace := (SpinEdit2.Value - 6) - (RecSize * RecsPerBlock); MaxRecs := 65536 * RecsPerBlock; ShowMessage('Record Size is: ' + IntToStr(RecSize) + ' bytes' + #13#10. + 'Records per Block: ' + IntToStr(RecsPerBlock) + #13#10. + 'Unused Space per Block: ' + IntToStr(FreeSpace) + ' bytes' + #13#10. + 'Max No of Records in Table: ' + FormatFloat('###############,', MaxRecs)); End;
Как получить дату BIOS?
Code: Unit BiosDate; Interface. Function GetBiosDate: String; Implementation. Function SegOfsToLinear(Segment, Offset: Word): Integer; Begin. Result := (Segment SHL 4) OR Offset; End; Function GetBiosDate: String; Begin. Result := String(PChar(Ptr(SegOfsToLinear($F000, $FFF5)))); End; End.
©Drkb::01357.
Взято из http://forum.sources.ru.
Code: Var. BiosDate: array[0..7] of char absolute. $FFFF5; PCType: byte absolute $FFFFE; Procedure TForm1.FormCreate(Sender: TObject); Var. S: string; Begin. Case PCType of. $FC: S := 'AT'; $FD: S := 'PCjr'; $FE: S := 'XT =8-O'; $FF: S := 'PC'; Else. S := 'Нестандартный'; End; Caption := 'Дата BIOS: ' + BiosDate + ' Тип ПК: ' + S; End;
©Drkb::01358.
Взято с http://delphiworld.narod.ru.
Code: Function GetBiosDate1: String; Var. Buffer : Array[0..8] Of Char; N : DWORD; Begin. ReadProcessMemory(GetCurrentProcess, Ptr($FFFF5), @Buffer, 8, N); Buffer[8] := #0; Result := StrPas(Buffer). End; Function GetBiosDate2: String; Begin. Result := string(pchar(ptr($FFFF5))); End; {Only for Win 95/98/ME).
©Drkb::01359.
Взято с сайта: http://www.swissdelphicenter.ch.
Code: Function GetBIOSDate: string; {получение даты BIOS в Win95}. Var. S: array[0..7] of char; P: pchar; Begin. P := @s; Asm. Push esi. Push edi. Push ecx. Mov esi,$0ffff5. Mov edi,p. Mov cx,8. @@1:mov al,[esi]. Mov [edi],al. Inc edi. Inc esi. Loop @@1. Pop ecx. Pop edi. Pop esi. End; Setstring(result, s, 8); End;
©Drkb::01360.
DelphiWorld 6.0.
| Конвертировать результат запроса в XML и обратно. |
Code: Unit ADOXMLUnit; Interface. Uses. Classes, ADOInt; Function RecordsetToXML(const Recordset: _Recordset): string; Function RecordsetFromXML(const XML: string): _Recordset; Implementation. Uses. ComObj; Function RecordsetToXML(const Recordset: _Recordset): string; Var. RS: Variant; Stream: TStringStream; Begin. Result := ''; If Recordset = nil then. Exit; Stream := TStringStream.Create(''); Try. RS := CreateOleObject('ADODB.Recordset'); RS := Recordset; RS.Save(TStreamAdapter.Create(stream) as IUnknown, adPersistXML); Stream.Position := 0; Result := Stream.DataString; Finally. Stream.Free; End; End; Function RecordsetFromXML(const XML: string): _Recordset; Var. RS: Variant; Stream: TStringStream; Begin. Result := nil; If XML = '' then. Exit; Try. Stream := TStringStream.Create(XML); Stream.Position := 0; RS := CreateOleObject('ADODB.Recordset'); RS.Open(TStreamAdapter.Create(Stream) as IUnknown); Result := IUnknown(RS) as _Recordset; Finally. Stream.Free; End; End; End.
| Конвертировать INI файл в XML. |
Code: Uses XMLIntf, XMLDoc, INIFiles; Procedure INI2XML(const pINIFileName: string; const pXML: IXMLNode; Const AsAttributes: Boolean = true); Var. LINIFile: TIniFile; LSections, lItems: TStringList; ISections, iItems: integer; LNode: IXMLNode; Begin. LINIFile := TIniFile.Create(pINIFileName); Try. LSections := TStringList.Create; Try. LItems := TStringList.Create; Try. LINIFile.ReadSections(lSections); For iSections := 0 to pred(lSections.Count) do. Begin. LItems.Clear; LINIFile.ReadSection(lSections[iSections], lItems); LNode := pXML.AddChild(StringReplace(lSections[iSections], ' ', '', [rfReplaceAll])); For iItems := 0 to pred(lItems.Count) do. Begin. If AsAttributes then. LNode.Attributes[lItems[iItems]] := LINIFile.ReadString(lSections[iSections], lItems[iItems], ''). Else. LNode.AddChild(lItems[iItems]).Text := LINIFile.ReadString(lSections[iSections], lItems[iItems], ''); End; LNode := nil; End; Finally lItems.Free; End; Finally lSections.Free; End; Finally lINIFile.Free; End; End;
| Конвертировать таблицу в XML. |
Code: { SMExport suite's free demo. Data export from dataset into XML-file. Copyright(C) 2000, written by Scalabium, Shkolnik Mike. E-Mail: smexport@scalabium.com. Mshkolnik@yahoo.com. WEB: http://www.scalabium.com. Http://www.geocities.com/mshkolnik. } Unit DS2XML; Interface. Uses. Classes, DB; Procedure DatasetToXML(Dataset: TDataset; FileName: string); Implementation. Uses. SysUtils; Var. SourceBuffer: PChar; Procedure WriteString(Stream: TFileStream; s: string); Begin. StrPCopy(SourceBuffer, s); Stream.Write(SourceBuffer[0], StrLen(SourceBuffer)); End; Procedure WriteFileBegin(Stream: TFileStream; Dataset: TDataset); Function XMLFieldType(fld: TField): string; Begin. Case fld.DataType of. FtString: Result := '"string" WIDTH="' + IntToStr(fld.Size) + '"'; FtSmallint: Result := '"i4"'; //?? FtInteger: Result := '"i4"'; FtWord: Result := '"i4"'; //?? FtBoolean: Result := '"boolean"'; FtAutoInc: Result := '"i4" SUBTYPE="Autoinc"'; FtFloat: Result := '"r8"'; FtCurrency: Result := '"r8" SUBTYPE="Money"'; FtBCD: Result := '"r8"'; //?? FtDate: Result := '"date"'; FtTime: Result := '"time"'; //?? FtDateTime: Result := '"datetime"'; Else. End; If fld.Required then. Result := Result + ' required="true"'; If fld.Readonly then. Result := Result + ' readonly="true"'; End; Var. I: Integer; Begin. WriteString(Stream, '<?xml version="1.0" standalone="yes"?><!-- Generated by SMExport --> ' + '<DATAPACKET Version="2.0">'); WriteString(Stream, '<METADATA><FIELDS>'); {write th metadata}. With Dataset do. For i := 0 to FieldCount - 1 do. Begin. WriteString(Stream, '<FIELD attrname="' + Fields[i].FieldName + '" fieldtype=' + XMLFieldType(Fields[i]) + '/>'); End; WriteString(Stream, '</FIELDS>'); WriteString(Stream, '<PARAMS DEFAULT_ORDER="1" PRIMARY_KEY="1" LCID="1033"/>'); WriteString(Stream, '</METADATA><ROWDATA>'); End; Procedure WriteFileEnd(Stream: TFileStream); Begin. WriteString(Stream, '</ROWDATA></DATAPACKET>'); End; Procedure WriteRowStart(Stream: TFileStream; IsAddedTitle: Boolean); Begin. If not IsAddedTitle then. WriteString(Stream, '<ROW'); End; Procedure WriteRowEnd(Stream: TFileStream; IsAddedTitle: Boolean); Begin. If not IsAddedTitle then. WriteString(Stream, '/>'); End; Procedure WriteData(Stream: TFileStream; fld: TField; AString: ShortString); Begin. If Assigned(fld) and (AString <> '') then. WriteString(Stream, ' ' + fld.FieldName + '="' + AString + '"'); End; Function GetFieldStr(Field: TField): string; Function GetDig(i, j: Word): string; Begin. Result := IntToStr(i); While (Length(Result) < j) do. Result := '0' + Result; End; Var. Hour, Min, Sec, MSec: Word; Begin. Case Field.DataType of. FtBoolean: Result := UpperCase(Field.AsString); FtDate: Result := FormatDateTime('yyyymmdd', Field.AsDateTime); FtTime: Result := FormatDateTime('hhnnss', Field.AsDateTime); FtDateTime: Begin. Result := FormatDateTime('yyyymmdd', Field.AsDateTime); DecodeTime(Field.AsDateTime, Hour, Min, Sec, MSec); If (Hour <> 0) or (Min <> 0) or (Sec <> 0) or (MSec <> 0) then. Result := Result + 'T' + GetDig(Hour, 2) + ':' + GetDig(Min, 2) + ':' + GetDig(Sec, 2) + GetDig(MSec, 3); End; Else. Result := Field.AsString; End; End; Procedure DatasetToXML(Dataset: TDataset; FileName: string); Var. Stream: TFileStream; Bkmark: TBookmark; I: Integer; Begin. Stream := TFileStream.Create(FileName, fmCreate); SourceBuffer := StrAlloc(1024); WriteFileBegin(Stream, Dataset); With DataSet do. Begin. DisableControls; Bkmark := GetBookmark; First; {write a title row}. WriteRowStart(Stream, True); For i := 0 to FieldCount - 1 do. WriteData(Stream, nil, Fields[i].DisplayLabel); {write the end of row}. WriteRowEnd(Stream, True); While (not EOF) do. Begin. WriteRowStart(Stream, False); For i := 0 to FieldCount - 1 do. WriteData(Stream, Fields[i], GetFieldStr(Fields[i])); {write the end of row}. WriteRowEnd(Stream, False); Next; End; GotoBookmark(bkmark); EnableControls; End; WriteFileEnd(Stream); Stream.Free; StrDispose(SourceBuffer); End; End.
| Как экспортировать все таблицы в CSV файл? |
Code: Procedure TMainForm.SaveAllTablesToCSV(DBFileName: string); Var. InfoStr, FileName, RecString, WorkingDirectory: string; OutFileList, TableNameList: TStringList; TableNum, FieldNum: integer; VT: TVarType; Begin. ADOTable1.Active := false; WorkingDirectory := ExtractFileDir(DBFileName); TableNameList := TStringList.Create; OutFileList := TStringList.Create; InfoStr := 'The following files were created' + #13#13; ADOConnection1.GetTableNames(TableNameList, false); For TableNum := 0 to TableNameList.Count - 1 do. Begin. FileName := WorkingDirectory + '\' + TableNameList.Strings[TableNum] + '.CSV'; Caption := 'Saving "' + ExtractFileName(FileName) + '"'; ADOTable1.TableName := TableNameList.Strings[TableNum]; ADOTable1.Active := true; OutFileList.Clear; ADOTable1.First; While not ADOTable1.Eof do. Begin. RecString := ''; For FieldNum := 0 to ADOTable1.FieldCount - 1 do. Begin. VT := VarType(ADOTable1.Fields[FieldNum].Value); Case VT of. // just write the field if not a string. VtInteger, vtExtended, vtCurrency, vtInt64: RecString := RecString + ADOTable1.Fields[FieldNum].AsString. Else. // it IS a string so put quotes around it. RecString := RecString + '"' + ADOTable1.Fields[FieldNum].AsString + '"'; End; { case }. // if not the last field then use a field separator. If FieldNum < (ADOTable1.FieldCount - 1) then. RecString := RecString + ','; End; { for FieldNum }. OutFileList.Add(RecString); ADOTable1.Next; End; { while }. OutFileList.SaveToFile(FileName); InfoStr := InfoStr + FileName + #13; ADOTable1.Active := false; End; { for TableNum }. TableNameList.Free; OutFileList.Free; Caption := 'Done'; ShowMessage(InfoStr); End; Procedure TMainForm.Button1Click(Sender: TObject); Const. ConnStrA = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source='; ConnStrC = ';Persist Security Info=False'; ProvStr = 'Microsoft.Jet.OLEDB.4.0'; Begin. OpenDialog1.InitialDir := ExtractFileDir(ParamStr(0)); If OpenDialog1.Execute then. Try. ADOConnection1.ConnectionString := ConnStrA + OpenDialog1.FileName + ConnStrC; ADOConnection1.Provider := ProvStr; ADOConnection1.Connected := true; ADOTable1.Connection := ADOConnection1; SaveAllTablesToCSV(OpenDialog1.FileName); Except. ShowMessage('Could not Connect to ' + #13 + '"' + OpenDialog1.FileName + '"'); Close; End; End;
| Как подсоединиться к MySQL. |
Perhaps you have already seen the uses clause. You may download mySQL.pas from www.fichtner.net/delphi.
Code: Uses mySQL; Procedure Connect; Var. MyServer: PMysql; Tables: PMYSQL_RES; TableRows: my_ulonglong; Table: PMYSQL_ROW; Begin. MyServer := mysql_init(nil); If myServer <> nil then. Begin. If mysql_options(myServer, MYSQL_OPT_CONNECT_TIMEOUT, '30') = 0 then. Begin. If mysql_real_connect(myServer, 'host', 'user', 'password', 'database', 3306, Nil, CLIENT_COMPRESS) <> nil then. Begin. Tables := mysql_list_tables(myServer, nil); If Tables <> nil then. Begin. TableRows := mysql_num_rows(Tables); While TableRows > 0 do. Begin. Table := mysql_fetch_row(Tables); Tabelle := Table[0]; Dec(TableRows); End; End; End; End; End; End;
Ограничения Foxpro.
Table and Index Files.
Max. # of records per table 1 billion*
Max. # of chars per record 65,000.
Max. # of fields per record 255.
Max. # of open DBFs 225.
Max. # of chars per field 254.
Max. # of chars per index key (IDX) 100.
Max. # of chars per index key (CDX) 240.
Max. # of open index files per table unlimited**
Max. # of open index files in all work areas unlimited**
* The actual file size (in bytes) cannot exceed 2 gigabytes for single-user or exclusively opened multi-user tables. Shared tables with no indexes or .IDX indexes cannot exceed 1 gigabyte. Shared tables with structural .CDX indexes cannot exceed 2 gigabytes.
** Limited by memory. In FoxPro for MS-DOS and FoxPro for Windows, also limited by available MS-DOS file handles. Each .CDX file uses only 1 file handle. The number of MS-DOS file handles is determined by the CONFIG.SYS FILES parameter.
Field Characteristics.
Max. size of character fields 254.
Max. size of numeric fields 20.
Max. # of chars in field names 10.
Digits of precision in numeric computations 16.
©Drkb::02933.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Использование ADO (статья).
Примечание от Vit: Данная статья взята с какой-то книги которую я нашёл на интернете. Причём к сожалению нет указаний ни на название книги, ни на автора, кроме того сия книга была отсканирована не самым тщательным образом и включает в себя огромное количество опечаток и неправильно распознанных слов, особенно в названиях свойств, методов и констант, а так же безобразно отформатированный код. Я попытался исправить максимальное количество таких опечаток настолько насколько это возможно не имея оригинала, но думаю, что часть ошибок всё ещё осталась, кроме того полностью переформатировал все примеры кода. Тем ни менее статью помещаю в DRKB в связи с довольно подробный и очень понятным описанием ADO.
Технология ADO завоевала популярность у разработчиков, благодаря универсальности — базовый' набор интерфейсов OLE DB имеется в каждой современной операционной системе Microsoft. Поэтому для обеспечения доступа приложения к данным достаточно лишь правильно указать провайдер соединения ADO и затем переносить программу на любой компьютер, где имеется требуемая база данных и, конечно, установленная ADO.
Технология Microsoft ActiveX Data Objects обеспечивает универсальный доступ к источникам данных из приложений БД. Такую возможность предоставляют функции набора интерфейсов, созданные на основе общей модели объектов СОМ и описанные в спецификации OLE DB.
Технология ADO и интерфейсы OLE DB обеспечивают для приложений единый способ доступа к источникам данных различных типов. Например, приложение, использующее ADO, может применять одинаково сложные операции и к данным, хранящимся на корпоративном сервере SQL, и к электронным таблицам, и локальным СУБД. Запрос SQL, направленный любому источнику данных через ADO, будет выполнен.
Возникает вопрос: каким образом источники данных смогут выполнить этот запрос?
За серверы БД беспокоиться не стоит, обработка запросов SQL — это их основная обязанность. Но как быть с файловыми последовательностями, электронными таблицами, файлами электронной почты и т. д.? Здесь на помощь приходят механизмы ADO и интерфейсы OLE DB.
OLE DB представляет собой набор специализированных объектов СОМ, инкапсулирующих стандартные функции обработки данных, и специализированные функции конкретных источников данных и интерфейсов, обеспечивающих передачу данных между объектами.
Согласно терминологии ADO, любой источник данных (база данных, электронная таблица, файл) называется хранилищем данных, с которым при помощи провайдера данных взаимодействует приложение. Минимальный набор компонентов приложения может включать объект соединения, объект набора данных, объект процессора запросов.
Примечание.
Объекты OLE DB создаются и функционируют так же, как и другие объекты СОМ. Каждому объекту соответствует идентификатор класса CLSID, хранящийся в системном реестре. Для создания объекта используется метод CoCreateinstance и соответствующая фабрика класса. Объекту соответствует набор интерфейсов, к методам которых можно обращаться после создания объекта.
В результате приложение обращается не прямо к источнику данных, а к объекту OLE DB, который "умеет" представить данные (например, из файла электронной почты) в виде таблицы БД или результата выполнения запроса SQL.
Технология ADO в целом включает в себя не только сами объекты OLE DB, но и механизмы, обеспечивающие взаимодействие объектов с данными и приложениями. На этом уровне важнейшую роль играют провайдеры ADO, координирующие работу приложений с хранилищами данных различных типов.
Такая архитектура позволяет сделать набор объектов и интерфейсов открытым и расширяемым. Набор объектов и соответствующий провайдер может быть создан для любого хранилища данных без внесения изменений в исходную структуру ADO. При этом существенно расширяется само понятие данных — ведь можно разработать набор объектов и интерфейсов и для нетрадиционных табличных данных. Например, это могут быть графические данные геоинформационных систем, древовидные структуры из системных реестров, данные CASE-инструментов и т. д.
Так как технология ADO основана на стандартных интерфейсах СОМ, которые являются системным механизмом Windows, это сокращает общий объем работающего программного кода и позволяет распространять приложения БД без вспомогательных программ и библиотек.
Примечание.
Нижеследующее описание спецификации OLE DB представлено в соответствии с официальной терминологией Microsoft для данной предметной области.
Спецификация OLE DB различает следующие типы объектов:
Перечислитель (Enumerator) выполняет поиск источников данных или других перечислителей. Используется для обеспечения функционирования провайдеров ADO.
Объект-источник данных (Data Source Object) представляет хранилище данных.
Сессия (Session) объединяет совокупность объектов, обращающихся к одному хранилищу данных.
Транзакция (Trasaction) инкапсулирует механизм выполнения транзакции.
Команда (Command) содержит текст команды и обеспечивает ее выполнение. Командой может быть запрос SQL, обращение к таблице БД и т. д.
Набор рядов (Rowset) представляет собой совокупность строк данных, являющихся результатом выполнения команды ADO.
Объект-ошибка (Error) содержит информацию об исключительной ситуации.
Рассмотрим функциональные возможности основных объектов и интерфейсов OLE DB.
©Drkb::02647.
Как получить информацию о BIOS в Windows 9x?
Code: With Memo1.Lines do. Begin. Add('MainBoardBiosName:'+^I+string(Pchar(Ptr($FE061)))); Add('MainBoardBiosCopyRight:'+^I+string(Pchar(Ptr($FE091)))); Add('MainBoardBiosDate:'+^I+string(Pchar(Ptr($FFFF5)))); Add('MainBoardBiosSerialNo:'+^I+string(Pchar(Ptr($FEC71)))); End;
©Drkb::01361.
Взято из http://forum.sources.ru.
| Как назначить пароль на таблицу? |
Code: Uses Unit2; // .. TablePasswort(Table1, 'secret'); Unit Unit2; Interface. Uses. BDE, SysUtils, DBTables, Windows; Function TablePasswort(var table: TTable; password: string): Boolean; Implementation. Function StrToOem(const AnsiStr: string): string; Begin. SetLength(result, Length(AnsiStr)); If Length(result) > 0 then. CharToOem(PChar(AnsiStr), PChar(result)). End; Function TablePasswort(var table: ttable; password: string): Boolean; Var. PTblDesc: pCRTblDesc; HDb: hDBIDb; Begin. Result := false; With table do. Begin. If Active and (not Exclusive) then. Close; If (not Exclusive) then. Exclusive := true; If (not Active) then. Open; HDb := DBHandle; Close. End; GetMem(pTblDesc, sizeof(CRTblDesc)); FillChar(pTblDesc^, sizeof(CRTblDesc), 0); With pTblDesc^ do. Begin. StrPCopy(szTblName, StrToOem(table.tablename)); SzTblType := szParadox; StrPCopy(szPassword, StrToOem(password)); BPack := true; BProtected := true. End; If DbiDoRestructure(hDb, 1, pTblDesc, nil, nil, nil, false) <> DBIERR_NONE then. Exit; If pTblDesc <> nil then. FreeMem(pTblDesc, sizeof(CRTblDesc)); Result := true. End; End.
| Как изменить языковый драйвер в runtime? |
Code: Procedure SetLanguage(Tbl: TTable; Lang: DbiName); Var. POptDesc: pFLDDesc; POptData: pBYTE; HDb: hDbiDb; TblDesc: CRTblDesc; Dir: string; Begin. POptDesc := AllocMem(sizeof(FLDDesc)); POptData := AllocMem(20); SetLength(Dir, dbiMaxNameLen + 1); Tbl.Active := True; Check(DbiGetDirectory(Tbl.DBHandle, False, PChar(Dir))); SetLength(Dir, StrLen(PChar(Dir))); Try. FillChar(TblDesc, sizeof(CRTblDesc), #0); Tbl.DisableControls; Tbl.Close; Check(DbiOpenDatabase(nil, nil, dbiReadWrite, dbiOpenExcl, nil, 0, nil, nil, HDb)); Check(DbiSetDirectory(hDb, PChar(Dir))); POptDesc.iOffset := 0; POptDesc.iLen := Length(Lang) + 1; StrPCopy(pOptDesc.szName, 'LANGDRIVER'); StrPCopy(PChar(pOptData), Lang); TblDesc.iOptParams := 1; TblDesc.pfldOptParams := pOptDesc; TblDesc.pOptData := pOptData; StrPCopy(TblDesc.szTblName, Tbl.TableName); StrCopy(TblDesc.szTblType, szParadox); Check(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, False)); Finally. Check(DbiCloseDatabase(hDb)); FreeMem(pOptDesc, sizeof(FLDDesc)); FreeMem(pOptData, 20); Tbl.EnableControls; End; End;
| Как пересоздать индексы? |
Code: Procedure TForm1.Button4Click(Sender: TObject); Var. AExclusive, aActive: Boolean; Begin. With Table1 do. Begin. AActive := Active; Close; AExclusive := Exclusive; Exclusive := True; Open; Check(DbiRegenIndexes(Table1.Handle)); Close; Exclusive := aExclusive; Active := aActive; Check(DbiSaveChanges(Table1.Handle)); End; End;
Ограничения Paradox.
Table and Index Files.
127 Tables open per system.
64 Record locks on one table (16Bit) per session.
255 Record locks on one table (32Bit) per session.
255 Records in transactions on a table (32 Bit).
512 Open physical files (DB, PX, MB, X??, Y??, VAL, TV).
300 Users in one PDOXUSRS.NET file.
255 Number of fields per table.
255 Size of character fields.
2 Billion records in a table.
2 Billion bytes in .DB (Table) file.
10800 Bytes per record for indexed tables.
32750 Bytes per record for non-indexed tables.
127 Number of secondary indexes per table.
16 Number of fields in an index.
255 Concurrent users per table.
256 Megabytes of data per BLOB field.
100 Passwords per session.
15 Password length.
63 Passwords per table.
159 Fields with validity checks (32 Bit).
63 Fields with validity checks (16 Bit).
©Drkb::02943.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как определить графический формат файла (не используя расширение)? |
You can determine it without extention. Below is a function that reads the file header and determines the type.
Code: Function PhysicalResolveFileType(AStream: TStream): Integer; Var. P: PChar; Begin. Result := 0; If not Assigned(AStream) then. Exit; GetMem(p, 10); Try. AStream.Position := 0; AStream.Read(p[0], 10); {bitmap format}. If (p[0] = #66) and (p[1] = #77) then. Result := 1; {tiff format}. If ((p[0] = #73) and (p[1] = #73) and (p[2] = #42) and (p[3] = #0)) or. ((p[0] = #77) and (p[1] = #77) and (p[2] = #42) and (p[3] = #0)) then. Result := 2; {jpg format}. If (p[6] = #74) and (p[7] = #70) and (p[8] = #73) and (p[9] = #70) then. Result := 3; {png format}. If (p[0] = #137) and (p[1] = #80) and (p[2] = #78) and (p[3] = #71) and. (p[4] = #13) and (p[5] = #10) and (p[6] = #26) and (p[7] = #10) then. Result := 4; {dcx format}. If (p[0] = #177) and (p[1] = #104) and (p[2] = #222) and (p[3] = #58) then. Result := 5; {pcx format}. If p[0] = #10 then. Result := 6; {emf format}. If (p[0] = #215) and (p[1] = #205) and (p[2] = #198) and (p[3] = #154) then. Result := 7; {emf format}. If (p[0] = #1) and (p[1] = #0) and (p[2] = #0) and (p[3] = #0) then. Result := 7; Finally. Freemem(p); End; End;
Как получить средний цвет между двумя цветами?
Code: Function GetColorBetween(StartColor, EndColor: TColor; Pointvalue, Von, Bis: Extended): TColor; Var. F: Extended; R1, r2, r3, g1, g2, g3, b1, b2, b3: Byte; Function CalcColorBytes(fb1, fb2: Byte): Byte; Begin. Result := fb1; If fb1 < fb2 then. Result := FB1 + Trunc(F * (fb2 - fb1)); If fb1 > fb2 then. Result := FB1 - Trunc(F * (fb1 - fb2)); End; Begin. If Pointvalue <= Von then. Begin. Result := StartColor; Exit; End; If Pointvalue >= Bis then. Begin. Result := EndColor; Exit; End; F := (Pointvalue - von) / (Bis - Von); Asm. Mov EAX, Startcolor. Cmp EAX, EndColor. Je @@exit. Mov r1, AL. Shr EAX,8. Mov g1, AL. Shr Eax,8. Mov b1, AL. Mov Eax, Endcolor. Mov r2, AL. Shr EAX,8. Mov g2, AL. Shr EAX,8. Mov b2, AL. Push ebp. Mov al, r1. Mov dl, r2. Call CalcColorBytes. Pop ecx. Push ebp. Mov r3, al. Mov dL, g2. Mov al, g1. Call CalcColorBytes. Pop ecx. Push ebp. Mov g3, Al. Mov dL, B2. Mov Al, B1. Call CalcColorBytes. Pop ecx. Mov b3, al. XOR EAX,EAX. Mov AL, B3. SHL EAX,8. Mov AL, G3. SHL EAX,8. Mov AL, R3. @@Exit: Mov @result, eax. End; End;
©Drkb::03691.
Code: //------------------------------------------------------------------------------ // Function for getting mixed color from two given colors, with a relative. // distance from two colors determined by Position value inside. // MinPosition..MaxPosition range. // Author: Dmitri Papichev (c) 2001. // License type: Freeware. //------------------------------------------------------------------------------ Function GetMixedColor(const StartColor, EndColor: TColor; Const MinPosition, Position, MaxPosition: integer): TColor; Var. Fraction: double; R, G, B, R0, G0, B0, R1, G1, B1: byte; Begin. {process Position out of range situation}. If (MaxPosition < MinPosition) then. Begin. Raise Exception.Create. ('GetMixedColor: MaxPosition is less then MinPosition'); End; {if}. {if Position is outside MinPosition..MaxPosition range, the closest boundary. Is effectively substituted through the adjustment of Fraction}. Fraction := Min(1, Max(0, (Position - MinPosition) / (MaxPosition - MinPosition))); {extract the intensity values}. R0 := GetRValue(StartColor); G0 := GetGValue(StartColor); B0 := GetBValue(StartColor); R1 := GetRValue(EndColor); G1 := GetGValue(EndColor); B1 := GetBValue(EndColor); {calculate the resulting intensity values}. R := R0 + Round((R1 - R0) * Fraction); G := G0 + Round((G1 - G0) * Fraction); B := B0 + Round((B1 - B0) * Fraction); {combine intensities in a resulting color}. Result := RGB(R, G, B); End; {--GetMixedColor--}.
©Drkb::03692.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Дополнительные константы цветов. |
Code: Unit UnitColorConstants; {----------------------------------------------------------------- Unit Name: UnitColorConstants. Version: 2.0. Author: Stewart Moss. Creation Date: Jan 31 2002 11:54 pm. Modification Date: Feb 21 2002. Dependancies: Description: -- This code is copyright (Jan 31 2002) by Stewart Moss. -- All rights Reserved. -----------------------------------------------------------------} Interface. Uses Graphics; Const. ClNicePaleGreen = TColor($CCFFCC); ClPaleRed = TColor($CCCCFF); ClNicePurple = TColor($00E1E100); ClKhaki = TColor($ADAE80); ClMonkeyGreen = TColor($00C0DCC0); ClSkyBlue = TColor($00F1CAA6); ClMedGray = TColor($00A4A0A0); ClCream = TColor($00EFFBFF); ClChocolate = TColor($17335C); ClDarkBrown = TColor($5C3340); ClDarkSlateGrey = TColor($4F4F2F); ClDarkTan = TColor($4F6997); ClDarkWood = TColor($425E85); ClLightWood = TColor($A6C2E9); ClMediumWood = TColor($6480A6); ClNewTan = TColor($9EC7EB); ClSemiSweetChocolate = TColor($26426B); ClSienna = TColor($236B8E); ClWheat = TColor($BFD8D8); ClBrass = TColor($42A6B5); ClBronze = TColor($53788C); ClBronzeII = TColor($3D7DA6); ClCoolCopper = TColor($1987D9); ClCopper = TColor($3373B8); ClQuartz = TColor($F3D9D9); ClFieldSpar = TColor($7592D1); ClGoldenrod = TColor($70DBDB); ClMediumGoldenrod = TColor($AEEAEA); ClBrightGold = TColor($19D9D9); ClGold = TColor($327FCD); ClOldGold = TColor($3BB5CF); ClCoral = TColor($007FFF); ClFirebrick = TColor($23238E); ClIndianRed = TColor($2F2F4E); ClMandarianOrange = TColor($3378E4); ClMediumVioletRed = TColor($9370DB); ClNeonPink = TColor($C76EFF); ClOrangeRed = TColor($0024FF); ClScarlet = TColor($17178C); ClSpicyPink = TColor($AE1CFF); ClThistle = TColor($D8BFD8); ClVioletRed = TColor($9932CC); ClDarkOliveGreen = TColor($2F4F4F); ClForestGreen = TColor($238E23); ClHunterGreen = TColor($215E21); ClMediumForestGreen = TColor($238E6B); ClMediumSeaGreen = TColor($426F42); ClMediumSpringGreen = TColor($00FF7F); ClPaleGreen = TColor($8FBC8F); ClSeaGreen = TColor($688E23); ClSpringGreen = TColor($7FFF00); ClYellowGreen = TColor($32CC99); ClAquamarine = TColor($93DB70); ClCadetBlue = TColor($9F9F5F); ClCornFlowerBlue = TColor($6F4242); ClDarkTurquoise = TColor($DB9370); ClLightBlue = TColor($D9D9C0); ClLightSteelBlue = TColor($BD8F8F); ClMediumAquamarine = TColor($99CD32); ClMediumBlue = TColor($CD3232); ClMediumTurquoise = TColor($DBDB70); ClMidnightBlue = TColor($4F2F2F); ClNeonBlue = TColor($FF4D4D); ClNewMidnightBlue = TColor($9C0000); ClRichBlue = TColor($AB5959); ClSlateBlue = TColor($FF7F00); ClSteelBlue = TColor($8E6B23); ClSummerSky = TColor($DEB038); ClTurquoise = TColor($EAEAAD); ClBlueViolet = TColor($9F5F9F); ClOrchid = TColor($DB70DB); ClDarkPurple = TColor($781F87); ClViolet = TColor($4F2F4F); ClDarkOrchid = TColor($CD3299); ClMediumSlateBlue = TColor($007FFF); ClDarkSlateBlue = TColor($8E236B); ClMediumOrchid = TColor($DB7093); Implementation. End.
| Как найти контрастный цвет к данному? |
Code: Function FindContrastingColor(Color: TColor): TColor; Var. R, G, B: Byte; Begin. R := GetRValue(Color); G := GetGValue(Color); B := GetBValue(Color); If (R < 128) then. R := 255. Else. R := 0; If (G < 128) then. G := 255. Else. G := 0; If (B < 128) then. B := 255. Else. B := 0; Result := RGB(R, G, B); End;
| Как выбрать цвет пользуя TTrackBar. |
Drop three TrackBars on a form. Set Min to 0, Max to 255. Drop a TImage on the form. Then try this code:
Code: { ... } Var. Form1: TForm1; MyColor: LongWord; RedColor: LongWord = $00000000; GreenColor: LongWord = $00000000; BlueColor: LongWord = $00000000; Implementation. {$R *.DFM}. Procedure TForm1.FormCreate(Sender: TObject); Begin. DoImageFill; End; Procedure TForm1.DoImageFill; Begin. MyColor := RedColor or GreenColor or BlueColor; Image1.Canvas.Brush.Color := TColor(MyColor); Image1.Canvas.FillRect(Rect(0, 0, Image1.Width, Image1.Height)); End; Procedure TForm1.RedBarChange(Sender: TObject); Begin. RedColor := RedBar.Position; DoImageFill; End; Procedure TForm1.GreenBarChange(Sender: TObject); Begin. GreenColor := GreenBar.Position shl 8; DoImageFill; End; Procedure TForm1.BlueBarChange(Sender: TObject); Begin. BlueColor := BlueBar.Position shl 16; DoImageFill; End; End.
©Drkb::03696.
Each color value ranges from 0 to 255. Set the three trackbars with this range. You can use the RGB function to create a color from these values.
Code: { ... } Type. TForm1 = class(TForm). RedTrackBar: TTrackBar; GreenTrackBar: TTrackBar; BlueTrackBar: TTrackBar; Panel1: TPanel; Procedure blueTrackBarChange(Sender: TObject); Procedure greenTrackBarChange(Sender: TObject); Procedure redTrackBarChange(Sender: TObject); Public. { Public declarations }. Procedure ChangeColor; End; Procedure TForm1.ChangeColor; Begin. Panel1.Color := RGB(redTrackBar.Position, greenTrackBar.Position, blueTrackBar.Position); End; Procedure TForm1.blueTrackBarChange(Sender: TObject); Begin. ChangeColor; End; Procedure TForm1.greenTrackBarChange(Sender: TObject); Begin. ChangeColor; End; Procedure TForm1.redTrackBarChange(Sender: TObject); Begin. ChangeColor; End;
Как получить информацию о BIOS в Windows NT/2000/XP?
В NT/2000/XP не получится прочитать значения прямо из BIOS, однако, ничего не мешает нам считать нужные значения из реестра.
Code: Procedure TBIOSInfo.GetRegInfoWinNT; Var. Registryv : TRegistry; RegPath : string; Sl : TStrings; Begin. Params.Clear; RegPath := '\HARDWARE\DESCRIPTION\System'; Registryv:=tregistry.Create; Registryv.rootkey:=HKEY_LOCAL_MACHINE; Sl := nil; Try. Registryv.Openkey(RegPath,false); ShowMessage('BIOS Date: '+RegistryV.ReadString('SystemBiosDate')); Sl := ReadMultirowKey(RegistryV,'SystemBiosVersion'); ShowMessage('BIOS Version: '+sl.Text); Except. End; Registryv.Free; If Assigned(sl) then sl.Free; End;
©Drkb::01362.
Code: /следующий метод получает многострочные значения из реестра. //и преобразует их в TStringlist. Function ReadMultirowKey(reg: TRegistry; Key: string): TStrings; Const bufsize = 100; Var. I: integer; S1: string; Sl: TStringList; Bin: array[1..bufsize] of char; Begin. Try. Result := nil; Sl := nil; Sl := TStringList.Create; If not Assigned(reg) then. Raise Exception.Create('TRegistry object not assigned.'); FillChar(bin,bufsize,#0); Reg.ReadBinaryData(Key,bin,bufsize); I := 1; S1 := ''; While i < bufsize do. Begin. If ord(bin[i]) >= 32 then. S1 := s1 + bin[i]. Else. Begin. If Length(s1) > 0 then. Begin. Sl.Add(s1); S1 := ''; End; End; Inc(i); End; Result := sl; Except. Sl.Free; Raise; End; End;
©Drkb::01363.
Взято из http://forum.sources.ru.
| Как создать цветовую паллитру. |
Below are functions that help to create a palette (an identity palette, BTW) from an array of RGBQuads (such as you would find in the palette section of a .BMP file). I stole this from the WinG documentation, and converted it to Delphi. First call ClearSystemPalette, then you can get an identity palette by calling CreateIdentityPalette. If you plan to try palette animation, work in a 256-color mode, and change all the PC_NOCOLLAPSE entries below to PC_RESERVED. Besides creating the palette, the other pieces to the puzzle are:
1. Override the form's GetPalette method, so that it returns the new palette.
2. Select and realize the new palette just before you paint.
OldPal := SelectPalette(Canvas.Handle, NewPalette, False);
RealizePalette(Canvas.Handle);
{ Do your painting here }.
SelectPalette(Canvas.Handle, OldPal, False);
3. Remember to release the palette when you are done using DeleteObject.
4. If you are used using the RGB function to get color values, use the PaletteRGB function in its place.
Code: Function CreateIdentityPalette(const aRGB; nColors: Integer): HPALETTE; Type. QA = array[0..255] of TRGBQUAD; Var. Palette: PLOGPALETTE; PalSize: Word; ScreenDC: HDC; I: Integer; NStaticColors: Integer; NUsableColors: Integer; Begin. PalSize := SizeOf(TLOGPALETTE) + SizeOf(TPALETTEENTRY) * 256; GetMem(Palette, PalSize); Try. With Palette^ do. Begin. PalVersion := $0300; PalNumEntries := 256; ScreenDC := GetDC(0); Try. { For SYSPAL_NOSTATIC, just copy the color table into a PALETTEENTRY. Array and replace the first and last entries with black and white }. If (GetSystemPaletteUse(ScreenDC) = SYSPAL_NOSTATIC) then. Begin. { Fill in the palette with the given values, marking each with PalFlag }. {$R-}. For i := 0 to (nColors - 1) do. With palPalEntry[i], QA(aRGB)[I] do. Begin. PeRed := rgbRed; PeGreen := rgbGreen; PeBlue := rgbBlue; PeFlags := PC_NOCOLLAPSE; End; { Mark any unused entries with PalFlag }. For i := nColors to 255 do. PalPalEntry[i].peFlags := PC_NOCOLLAPSE; { Make sure the last entry is white -- This may replace an entry in the array!}. I := 255; With palPalEntry[i] do. Begin. PeRed := 255; PeGreen := 255; PeBlue := 255; PeFlags := 0; End; { And the first is black -- This may replace an entry in the array!}. With palPalEntry[0] do. Begin. PeRed := 0; PeGreen := 0; PeBlue := 0; PeFlags := 0; End; {$R+}. End. Else. Begin. { For SYSPAL_STATIC, get the twenty static colors into the. Array, then fill in the empty spaces with the given color table }. { Get the static colors from the system palette }. NStaticColors := GetDeviceCaps(ScreenDC, NUMRESERVED); GetSystemPaletteEntries(ScreenDC, 0, 256, palPalEntry); {$R-}. { Set the peFlags of the lower static colors to zero }. NStaticColors := nStaticColors shr 1; For i := 0 to (nStaticColors - 1) do. PalPalEntry[i].peFlags := 0; { Fill in the entries from the given color table}. NUsableColors := nColors - nStaticColors; For I := nStaticColors to (nUsableColors - 1) do. With palPalEntry[i], QA(aRGB)[i] do. Begin. PeRed := rgbRed; PeGreen := rgbGreen; PeBlue := rgbBlue; PeFlags := PC_NOCOLLAPSE; End; { Mark any empty entries as PC_NOCOLLAPSE }. For i := nUsableColors to (255 - nStaticColors) do. PalPalEntry[i].peFlags := PC_NOCOLLAPSE; { Set the peFlags of the upper static colors to zero }. For i := (256 - nStaticColors) to 255 do. PalPalEntry[i].peFlags := 0; End; Finally. ReleaseDC(0, ScreenDC); End; End; { Return the palette }. Result := CreatePalette(Palette^); Finally. FreeMem(Palette, PalSize); End; End; Procedure ClearSystemPalette; Var. Palette: PLOGPALETTE; PalSize: Word; ScreenDC: HDC; I: Word; Const. ScreenPal: HPALETTE = 0; Begin. PalSize := SizeOf(TLOGPALETTE) + SizeOf(TPALETTEENTRY) * 255; {256th = [0] }. GetMem(Palette, PalSize); Try. FillChar(Palette^, PalSize, 0); Palette^.palVersion := $0300; Palette^.palNumEntries := 256; {$R-}. For I := 0 to 255 do. With Palette^.palPalEntry[I] do. PeFlags := PC_NOCOLLAPSE; {$R+}. { Create, select, realize, deselect, and delete the palette }. ScreenDC := GetDC(0); Try. ScreenPal := CreatePalette(Palette^); If ScreenPal <> 0 then. Begin. ScreenPal := SelectPalette(ScreenDC, ScreenPal, FALSE); RealizePalette(ScreenDC); ScreenPal := SelectPalette(ScreenDC, ScreenPal, FALSE); DeleteObject(ScreenPal); End; Finally. ReleaseDC(0, ScreenDC); End; Finally. FreeMem(Palette, PalSize); End; End;
| Как получить доступ к объекту метафайла. |
Below is an example of getting metafile information and enumerating each metafile record :
Code: Function MyEnhMetaFileProc(DC: HDC; {handle to device context}. LpHTable: PHANDLETABLE; {pointer to metafile handle table}. LpEMFR: PENHMETARECORD; {pointer to metafile record}. NObj: integer; {count of objects}. TheForm: TForm1): integer; stdcall; Begin. {draw the metafile record}. PlayEnhMetaFileRecord(dc, lpHTable^, lpEMFR^, nObj); {set to zero to stop metafile enumeration}. Result := 1; End; Procedure TForm1.Button1Click(Sender: TObject); Var. MyMetafile: TMetafile; LpENHMETAHEADER: PENHMETAHEADER; {extra metafile info}. LpENHMETAHEADERSIZE: DWORD; NumMetaRecords: DWORD; Begin. {Create a metafile}. MyMetafile := TMetafile.Create; With TMetafileCanvas.Create(MyMetafile, 0) do. Try. Brush.Color := clRed; Ellipse(0, 0, 100, 100); Ellipse(100, 100, 200, 200); Ellipse(200, 200, 300, 300); Ellipse(300, 300, 400, 400); Ellipse(400, 400, 500, 500); Ellipse(500, 500, 600, 600); Finally. Free; End; {we might as well get some extra metafile info}. LpENHMETAHEADERSIZE := GetEnhMetaFileHeader(MyMetafile.Handle, 0, nil); NumMetaRecords := 0; If (lpENHMETAHEADERSIZE > 0) then. Begin. GetMem(lpENHMETAHEADER, lpENHMETAHEADERSIZE); GetEnhMetaFileHeader(MyMetafile.Handle, lpENHMETAHEADERSIZE, lpENHMETAHEADER); {Here is an example of getting number of metafile records}. NumMetaRecords := lpENHMETAHEADER^.nRecords; {enumerate the records}. EnumEnhMetaFile(Canvas.Handle, MyMetafile.Handle, @MyEnhMetaFileProc, self, Rect(0, 0, 600, 600)); FreeMem(lpENHMETAHEADER, lpENHMETAHEADERSIZE); End; MyMetafile.Free; End;
CUR ---> BMP.
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. HCursor: LongInt; Bitmap: TBitmap; Begin. Bitmap := TBitmap.Create; Bitmap.Width := 32; Bitmap.Height := 32; HCursor := LoadCursorFromFile('test.cur'); DrawIcon(Bitmap.Canvas.Handle, 0, 0, hCursor); Bitmap.SaveToFile('test.bmp'); Bitmap.Free; End;
©Drkb::04274.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как работать с анимированными курсорами? |
Answer:
To use an animated cursor you have several options: load it from a file (using LoadImage or LoadCursorFromFile), load it from a resource (using LoadCursor) or even creating the cursor at runtime (using CreateCursor).
Note:
You should implement custom cursors as resources. Rather than create the cursors at run time, use the LoadCursor, LoadCursorFromFile, or LoadImage function to avoid device dependence, to simplify localization, and to enable applications to share cursor designs.
· Loading a cursor from a file
The easiest way to load a cursor from a file is by using LoadCursorFromFile.
This functions returns a handle to the loaded cursor that you should assign to your application Cursors array.
Code: Var. HCur: HCURSOR; Begin. // Load the cursor from file. HCur := LoadCursorFromFile(PChar('path_to_my_cursor')); // Assign the loaded cursor to application Cursors array. (This cursor will ave the. // number 1 assigned to it. // Remember that predefined cursors start at a negative index, and user defined. // custom cursors are assigned positive indexes. Screen.Cursors[1] := hCur; // Use the cursor as you would use a built-in cursor. Screen.Cursor := 1; End;
Как работать с DWG файлами (AutoCAD)?
Vit: Примечания в коде были на каком-то не то китайском, не то японском языке - удалены!
Code: Unit DWGView; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls; Type. BITMAPINFO256 = record. BmiHeader: BITMAPINFOHEADER; BmiColors: array[0..255] of RGBQUAD; End; Type. TNoPreviewEvent = procedure(Sender: TOBject) of object; TFileErrorEvent = procedure(Sender: TOBject; DWGName: string) of object; TDWGView = class(TImage). Private. FDWGVersion: string; FDWGFile: string; FNoPreviewEvent: TNoPreviewEvent; FOnFileError: TFileErrorEvent; FImage: TImage; Procedure SetDWGFile(const Value: string); Procedure SetFImage(const Value: TImage); Protected. Procedure ReadDWG; Constructor TDWGView; Public. Published. Property Image: TImage read FImage write SetFImage; Property DWGFile: string read FDWGFile write SetDWGFile; Property DWGVersion: string read FDWGVersion; Property OnNoPreview: TNoPreviewEvent read FNoPreviewEvent write FNoPreviewEvent; Property OnFileError: TFileErrorEvent read FOnFileError write FOnFileError; End; Procedure Register; Implementation. Procedure Register; Begin. RegisterComponents('Voice', [TDWGView]); End; Procedure TDWGView.ReadDWG; Var. DWGF: TFileStream; MemF: TMemoryStream; BMPF: TMemoryStream; SentinelF: TMemoryStream; Bif: BITMAPINFO256; Bfh: BITMAPFILEHEADER; PosSentinel: LongInt; LenPreview: Integer; RasterPreview: ShortInt; PosBMP: Integer; LenBMP: Integer; IndexPreview: Integer; TypePreview: Shortint; Begin. If Assigned(FOnFileError) then. FOnFileError(Self, FDWGFile); DWGF := TFileStream.Create(FDWGFile, fmOpenRead); BMPF := TMemoryStream.Create; MemF := TMemoryStream.Create; SentinelF := TMemoryStream.Create; Try. SetLength(FDWGVersion, 6); DWGF.ReadBuffer(FDWGVersion[1], 6); DWGF.Position := 13; DWGF.Read(PosSentinel, 4); DWGF.Position := PosSentinel; SentinelF.CopyFrom(DWGF, 16); DWGF.Read(LenPreview, 4); DWGF.Read(RasterPreview, 1); For IndexPreview := RasterPreview - 1 downto 0 do. Begin. MemF.Position := 0; MemF.CopyFrom(DWGF, 9); MemF.Position := 0; MemF.Read(TypePreview, 1); Case TypePreview of. 1: ; 2: Begin. MemF.Position := 1; MemF.Read(PosBMP, 4); MemF.Read(LenBMP, 4); DWGF.Position := PosBMP; DWGF.ReadBuffer(bif, sizeof(bif)); With bif do. Begin. BmiColors[0].rgbBlue := 0; BmiColors[0].rgbGreen := 0; BmiColors[0].rgbRed := 0; BmiColors[225].rgbBlue := 255; BmiColors[225].rgbGreen := 255; BmiColors[225].rgbRed := 255; End; Bfh.bfType := $4D42; Bfh.bfSize := LenBMP + sizeof(bfh); Bfh.bfReserved1 := 0; Bfh.bfReserved2 := 0; Bfh.bfOffBits := 14 + $28 + 1024; BMPF.Position := 0; BMPF.Write(bfh, sizeof(bfh)); BMPF.WriteBuffer(bif, sizeof(bif)); BMPF.CopyFrom(DWGF, LenBMP - 1064); BMPF.Position := 0; Picture.Bitmap.LoadFromStream(BMPF); End; 3: ; End; End; Finally. SentinelF.Free; MemF.Free; DWGF.Free; BMPF.Free; End; End; Procedure TDWGView.SetDWGFile(const Value: string); Begin. FDWGFile := Value; ReadDWG; End; Procedure TDWGView.SetFImage(const Value: TImage); Begin. FImage := Value; End; Constructor TDWGView.TDWGView; Begin. //TODO: Add your source code here. FDWGFile := ''; FDWGVersion := ''; End; End.
©Drkb::04294.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Компонент для работы с PCX файлами.
Fully supports reading and writing of: 1, 8 and 24 bit PCX images.
Code: /////////////////////////////////////////////////////////////////////// // // // TPCXImage // // ========= // // // // Completed: The 10th of August 2001 // // Author: M. de Haan // // Email: M.deHaan@inn.nl // // Tested: under W95 SP1, NT4 SP6, WIN2000 // // Version: 1.0 // //-------------------------------------------------------------------// // Update: The 14th of August 2001 to version 1.1. // // Reason: Added version check. // // Added comment info on version. // // Changed PCX header ID check. // //-------------------------------------------------------------------// // Update: The 19th of August 2001 to version 2.0. // // Reason: Warning from Delphi about using abstract methods, // // caused by not implementing ALL TGraphic methods. // // (Thanks goes to R.P. Sterkenburg for his diagnostic.) // // Added: SaveToClipboardFormat, LoadFromClipboardFormat, // // GetEmpty. // //-------------------------------------------------------------------// // Update: The 13th of October 2001 to version 2.1. // // Reason: strange errors, read errors, EExternalException, IDE // // hanging, Delphi hanging, Debugger hanging, windows // // hanging, keyboard locked, and so on. // // Changed: Assign procedure. // //-------------------------------------------------------------------// // Update: The 5th of April 2002 to version 2.2. // // Changed: RLE compressor routine. // // Reason: Incompatibility problems with other programs caused // // by the RLE compressor. // // Other programs encode: $C0 as: $C1 $C0. // // ($C0 means: repeat the following byte 0 times // // $C1 means: repeat the following byte 1 time.) // // Changed: File read routine. // // Reason: Now detects unsupported PCX data formats. // // Added: 'Unsupported data format' in exception handler. // // Added: 1 bit PCX support in reading. // // Added: Procedure Convert1BitPCXDataToImage. // // Renamed: Procedure ConvertPCXDataToImage to // // Convert24BitPCXDataToImage. // //-------------------------------------------------------------------// // Update: The 14th of April 2002 to version 2.3. // // Now capable of reading and writing 1 and 24 bit PCX // // images. // // Added: 1 bit PCX support in writing. // // Added: Procedure ConvertImageTo1bitPCXData. // // Changed: Procedure CreatePCXHeader. // // Changed: Procedure TPCXImage.SaveToFile. // //-------------------------------------------------------------------// // Update: The 19th of April 2002 to version 2.4. // // Now capable of reading and writing: 1, 8 and 24 bit // // PCX images. // // Added: 8 bit PCX support in reading and writing. // // Renamed: Procedure ConvertImageTo1And8bitPCXData. // // Renamed: Procedure Convert1And8bitPCXDataToImage. // // Changed: Procedure fSetPalette, fGetPalette. // //-------------------------------------------------------------------// // Update: The 7th of May 2002 to version 2.5. // // Reason: The palette of 8-bit PCX images couldn't be read in // // the calling program. // // Changed: Procedures Assign, AssignTo, fSetPalette, fGetPalette. // // Tested: All formats were tested with the following programs: // // - import in Word 97, // // * (Word ignores the palette of 1 bit PCX images!) // // - import and export in MigroGrafX. // // * (MicroGrafX also ignores the palette of 1 bit PCX // // images.) // // No problems were detected. // // // //===================================================================// // // // The PCX image file format is copyrighted by: // // ZSoft, PC Paintbrush, PC Paintbrush plus // // Trademarks: N/A // // Royalty fees: NONE // // // //===================================================================// // // // The author can not be held responsable for using this software // // in anyway. // // // // The features and restrictions of this component are: // // ---------------------------------------------------- // // // // The reading and writing (import / export) of files / images: // // - PCX version 5 definition, PC Paintbrush 3 and higher, // // - RLE-compressed, // // - 1 and 8 bit PCX images WITH palette and // // - 24 bit PCX images without palette, // // are supported by this component. // // // // Known issues // // ------------ // // // // 1) GetEmpty is NOT tested. // // // // 2) SaveToClipboardFormat is NOT tested. // // // // 3) LoadFromClipboardFormat is NOT tested. // // // // 4) 4 bit PCX images (with palette) are NOT (yet) implemented. // // (I have no 4-bit PCX images to test it on...) // // // /////////////////////////////////////////////////////////////////////// Unit. PCXImage; Interface. Uses. Windows, SysUtils, Classes, Graphics; Const. WIDTH_OUT_OF_RANGE = 'Illegal width entry in PCX file header'; HEIGHT_OUT_OF_RANGE = 'Illegal height entry in PCX file header'; FILE_FORMAT_ERROR = 'Invalid file format'; VERSION_ERROR = 'Only PC Paintbrush (plus) V3.0 and ' + 'higher are supported'; FORMAT_ERROR = 'Illegal identification byte in PCX file' + ' header'; PALETTE_ERROR = 'Invalid palette signature found'; ASSIGN_ERROR = 'Can only Assign a TBitmap or a TPicture'; ASSIGNTO_ERROR = 'Can only AssignTo a TBitmap'; PCXIMAGE_EMPTY = 'The PCX image is empty'; BITMAP_EMPTY = 'The bitmap is empty'; INPUT_FILE_TOO_LARGE = 'The input file is too large to be read'; IMAGE_WIDTH_TOO_LARGE = 'Width of PCX image is too large to handle'; // added 19/08/2001. CLIPBOARD_LOAD_ERROR = 'Loading from clipboard failed'; // added 19/08/2001. CLIPBOARD_SAVE_ERROR = 'Saving to clipboard failed'; // added 14/10/2001. PCX_WIDTH_ERROR = 'Unexpected line length in PCX data'; PCX_HEIGHT_ERROR = 'More PCX data found than expected'; PCXIMAGE_TOO_LARGE = 'PCX image is too large'; // added 5/4/2002. ERROR_UNSUPPORTED = 'Unsupported PCX format'; Const. SPCXImageFile = 'PCX V3.0+ image'; // added 19/08/2001. Var. CF_PCX: WORD; /////////////////////////////////////////////////////////////////////// // // // PCXHeader // // // /////////////////////////////////////////////////////////////////////// Type. QWORD = Cardinal; // Seems more logical to me... Type. FColorEntry = packed record. CeRed: BYTE; CeGreen: BYTE; CeBlue: BYTE; End; // of packed record fColorEntry. Type. TPCXImageHeader = packed record. FID: BYTE; FVersion: BYTE; FCompressed: BYTE; FBitsPerPixel: BYTE; FWindow: packed record. WLeft, WTop, WRight, WBottom: WORD; End; // of packed record fWindow. FHorzResolution: WORD; FVertResolution: WORD; FColorMap: array[0..15] of fColorEntry; FReserved: BYTE; FPlanes: BYTE; FBytesPerLine: WORD; FPaletteInfo: WORD; FFiller: array[0..57] of BYTE; End; // of packed record TPCXImageHeader. /////////////////////////////////////////////////////////////////////// // // // PCXData // // // /////////////////////////////////////////////////////////////////////// Type. TPCXData = object. FData: array of BYTE; End; // of Type TPCXData. /////////////////////////////////////////////////////////////////////// // // // ScanLine // // // /////////////////////////////////////////////////////////////////////// Const. FMaxScanLineLength = $FFF; // Max image width: 4096 pixels. Type. MByteArray = array[0..fMaxScanLineLength] of BYTE; PmByteArray = ^mByteArray; // The "standard" pByteArray from Delphi allocates 32768 bytes, // which is a little bit overdone here, I think... Const. FMaxImageWidth = $FFF; // Max image width: 4096 pixels. Type. XByteArray = array[0..fMaxImageWidth] of BYTE; /////////////////////////////////////////////////////////////////////// // // // PCXPalette // // // /////////////////////////////////////////////////////////////////////// Type. TPCXPalette = packed record. FSignature: BYTE; FPalette: array[0..255] of fColorEntry; End; // of packed record TPCXPalette. /////////////////////////////////////////////////////////////////////// // // // Classes // // // /////////////////////////////////////////////////////////////////////// Type. TPCXImage = class; TPCXFile = class; /////////////////////////////////////////////////////////////////////// // // // PCXFile // // // // File handler // // // /////////////////////////////////////////////////////////////////////// TPCXFile = class(TPersistent). Private. FHeight: Integer; FWidth: Integer; FPCXHeader: TPCXImageHeader; FPCXData: TPCXData; FPCXPalette: TPCXPalette; FColorDepth: QWORD; FPixelFormat: BYTE; // added 5/4/2002. FCurrentPos: QWORD; FHasPalette: Boolean; // added 7/5/2002. Protected. // Protected declarations. Public. // Public declarations. Constructor Create; Destructor Destroy; override; Procedure LoadFromFile(const Filename: string); Procedure LoadFromStream(Stream: TStream); Procedure SaveToFile(const Filename: string); Procedure SaveToStream(Stream: TStream); Published. // Published declarations. // The publishing is done in the TPCXImage section. End; /////////////////////////////////////////////////////////////////////// // // // TPCXImage // // // // Image handler // // // /////////////////////////////////////////////////////////////////////// TPCXImage = class(TGraphic). Private. // Private declarations. FBitmap: TBitmap; FPCXFile: TPCXFile; FRLine: xByteArray; FGLine: xByteArray; FBLine: xByteArray; FP: pmByteArray; FhPAL: HPALETTE; Procedure fConvert24BitPCXDataToImage; Procedure fConvert1And8BitPCXDataToImage; Procedure fConvertImageTo24BitPCXData; Procedure fConvertImageTo1And8BitPCXData(ImageWidthInBytes: QWORD); Procedure fFillDataLines(const fLine: array of BYTE); Procedure fCreatePCXHeader(const byBitsPerPixel: BYTE; Const byPlanes: BYTE; const wBytesPerLine: DWORD); Procedure fSetPalette(const wNumColors: WORD); Procedure fGetPalette(const wNumColors: WORD); Function fGetPixelFormat: TPixelFormat; // Added 07/05/2002. Function fGetBitmap: TBitmap; // Added 07/05/2002. Protected. // Protected declarations. Procedure Draw(ACanvas: TCanvas; const Rect: TRect); override; Function GetHeight: Integer; override; Function GetWidth: Integer; override; Procedure SetHeight(Value: Integer); override; Procedure SetWidth(Value: Integer); override; Function GetEmpty: Boolean; override; Public. // Public declarations. Constructor Create; override; Destructor Destroy; override; Procedure Assign(Source: TPersistent); override; Procedure AssignTo(Dest: TPersistent); override; Procedure LoadFromFile(const Filename: string); override; Procedure LoadFromStream(Stream: TStream); override; Procedure SaveToFile(const Filename: string); override; Procedure SaveToStream(Stream: TStream); override; Procedure LoadFromClipboardFormat(AFormat: WORD; AData: THandle; APalette: HPALETTE); override; Procedure SaveToClipboardFormat(var AFormat: WORD; Var AData: THandle; var APalette: HPALETTE); override; Published. // Published declarations. Property Height: Integer. Read GetHeight write SetHeight; Property Width: Integer. Read GetWidth write SetWidth; Property PixelFormat: TPixelFormat. Read fGetPixelFormat; Property Bitmap: TBitmap. Read fGetBitmap; // Added 7/5/2002. End; Implementation. /////////////////////////////////////////////////////////////////////// // // // TPCXImage // // // // Image handler // // // /////////////////////////////////////////////////////////////////////// Constructor TPCXImage.Create; Begin. Inherited Create; // Init HPALETTE. FhPAL := 0; // Create a private bitmap to hold the image. If not Assigned(fBitmap) then. FBitmap := TBitmap.Create; // Create the PCXFile. If not Assigned(fPCXFile) then. FPCXFile := TPCXFile.Create; End; //--------------------------------------------------------------------- Destructor TPCXImage.Destroy; Begin. // Reversed order of create. // Free fPCXFile. FPCXFile.Free; // Free private bitmap. FBitmap.Free; // Delete palette. If fhPAL <> 0 then. DeleteObject(fhPAL); // Distroy all the other things. Inherited Destroy; End; //--------------------------------------------------------------------- Procedure TPCXImage.SetHeight(Value: Integer); Begin. If Value >= 0 then. FBitmap.Height := Value; End; //--------------------------------------------------------------------- Procedure TPCXImage.SetWidth(Value: Integer); Begin. If Value >= 0 then. FBitmap.Width := Value; End; //--------------------------------------------------------------------- Function TPCXImage.GetHeight: Integer; Begin. Result := fPCXFile.fHeight; End; //--------------------------------------------------------------------- Function TPCXImage.GetWidth: Integer; Begin. Result := fPCXFile.fWidth; End; //--------------------------------------------------------------------- Function TPCXImage.fGetBitmap: TBitmap; Begin. Result := fBitmap; End; //-------------------------------------------------------------------// // The credits for this procedure go to his work of TGIFImage by // // Reinier P. Sterkenburg // // Added 19/08/2001 // //-------------------------------------------------------------------// // NOT TESTED! Procedure TPCXImage.LoadFromClipboardFormat(AFormat: WORD; ADAta: THandle; APalette: HPALETTE); Var. Size: QWORD; Buf: Pointer; Stream: TMemoryStream; BMP: TBitmap; Begin. If (AData = 0) then. AData := GetClipBoardData(AFormat); If (AData <> 0) and (AFormat = CF_PCX) then. Begin. Size := GlobalSize(AData); Buf := GlobalLock(AData); Try. Stream := TMemoryStream.Create; Try. Stream.SetSize(Size); Move(Buf^, Stream.Memory^, Size); Self.LoadFromStream(Stream); Finally. Stream.Free; End; Finally. GlobalUnlock(AData); End; End. Else if (AData <> 0) and (AFormat = CF_BITMAP) then. Begin. BMP := TBitmap.Create; Try. BMP.LoadFromClipboardFormat(AFormat, AData, APalette); Self.Assign(BMP); Finally. BMP.Free; End; End. Else. Raise Exception.Create(CLIPBOARD_LOAD_ERROR); End; //-------------------------------------------------------------------// // The credits for this procedure go to his work of TGIFImage by // // Reinier P. Sterkenburg // // Added 19/08/2001 // //-------------------------------------------------------------------// // NOT TESTED! Procedure TPCXImage.SaveToClipboardFormat(var AFormat: WORD; Var AData: THandle; var APalette: HPALETTE); Var. Stream: TMemoryStream; Data: THandle; Buf: Pointer; Begin. If Empty then. Exit; // First store the bitmap to the clipboard. FBitmap.SaveToClipboardFormat(AFormat, AData, APalette); // Then try to save the PCX. Stream := TMemoryStream.Create; Try. SaveToStream(Stream); Stream.Position := 0; Data := GlobalAlloc(HeapAllocFlags, Stream.Size); Try. If Data <> 0 then. Begin. Buf := GlobalLock(Data); Try. Move(Stream.Memory^, Buf^, Stream.Size); Finally. GlobalUnlock(Data); End; If SetClipBoardData(CF_PCX, Data) = 0 then. Raise Exception.Create(CLIPBOARD_SAVE_ERROR); End; Except. GlobalFree(Data); Raise; End; Finally. Stream.Free; End; End; //-------------------------------------------------------------------// // NOT TESTED! Function TPCXImage.GetEmpty: Boolean; // Added 19/08/2002. Begin. If Assigned(fBitmap) then. Result := fBitmap.Empty. Else. Result := (fPCXFile.fHeight = 0) or (fPCXFile.fWidth = 0); End; //--------------------------------------------------------------------- Procedure TPCXImage.SaveToFile(const Filename: string); Var. FPCX: TFileStream; W, WW: QWORD; Begin. If (fBitmap.Width = 0) or (fBitmap.Height = 0) then. Raise Exception.Create(BITMAP_EMPTY); W := fBitmap.Width; WW := W div 8; If (W mod 8) > 0 then. Inc(WW); Case fBitmap.PixelFormat of. Pf1bit: Begin. // Fully supported by PCX and by this component. FCreatePCXHeader(1, 1, WW); FConvertImageTo1And8BitPCXData(WW); FGetPalette(2); End; Pf4bit: Begin. // I don't have 4-bit PCX images to test with. // It will be treated as a 24 bit image. FCreatePCXHeader(8, 3, W); FConvertImageTo24BitPCXData; End; Pf8bit: Begin. // Fully supported by PCX and by this component. FCreatePCXHeader(8, 1, W); FConvertImageTo1And8BitPCXData(W); FGetPalette(256); End; Pf15bit: Begin. // Is this supported in PCX? // It will be treated as a 24 bit image. FCreatePCXHeader(8, 3, W); FConvertImageTo24BitPCXData; End; Pf16bit: Begin. // Is this supported in PCX? // It will be treated as a 24 bit image. FCreatePCXHeader(8, 3, W); FConvertImageTo24BitPCXData; End; Pf24bit: Begin. // Fully supported by PCX and by this component. FCreatePCXHeader(8, 3, W); FConvertImageTo24BitPCXData; End; Pf32bit: Begin. // Not supported by PCX. FCreatePCXHeader(8, 3, W); FConvertImageTo24BitPCXData; End; Else. Begin. FCreatePCXHeader(8, 3, W); FConvertImageTo24BitPCXData; End; // of else. End; // of Case. FPCX := TFileStream.Create(Filename, fmCreate); Try. FPCX.Position := 0; SaveToStream(fPCX); Finally. FPCX.Free; End; // of finally. SetLength(fPCXFile.fPCXData.fData, 0); End; // of Procedure SaveToFile. //-------------------------------------------------------------------// Procedure TPCXImage.AssignTo(Dest: TPersistent); Var. BAssignToError: Boolean; Begin. BAssignToError := True; If Dest is TBitmap then. Begin. // The old AssignTo procedure was like this. // But then the palette was couldn't be accessed in the calling. // program for some reason. // -------------------------- // (Dest as TBitmap).Assign(fBitmap); // If fBitmap.Palette <> 0 then. // (Dest as TBitmap).Palette := CopyPalette(fBitmap.Palette); // -------------------------- // Do the assigning. (Dest as TBitmap).Assign(fBitmap); If fPCXFile.fHasPalette then. (Dest as TBitmap).Palette := CopyPalette(fhPAL); // Now the calling program can access the palette. // (if it has one)! BAssignToError := False; End; If Dest is TPicture then. Begin. (Dest as TPicture).Graphic.Assign(fBitmap); BAssignToError := False; End; If bAssignToError then. Raise Exception.Create(ASSIGNTO_ERROR); // You can write other assignments here, if you want... End; //-------------------------------------------------------------------// Procedure TPCXImage.Assign(Source: TPersistent); Var. IX, iY: DWORD; BAssignError: Boolean; Begin. BAssignError := True; If (Source is TBitmap) then. Begin. FBitmap.Assign(Source as TBitmap); If (Source as TBitmap).Palette <> 0 then. Begin. FhPAL := CopyPalette((Source as TBitmap).Palette); FBitmap.Palette := fhPAL; End; BAssignError := False; End; If (Source is TPicture) then. Begin. IX := (Source as TPicture).Width; IY := (Source as TPicture).Height; FBitmap.Width := iX; FBitmap.Height := iY; FBitmap.Canvas.Draw(0, 0, (Source as TPicture).Graphic); BAssignError := False; End; // You can write other assignments here, if you want... If bAssignError then. Raise Exception.Create(ASSIGN_ERROR); End; //--------------------------------------------------------------------- Procedure TPCXImage.Draw(ACanvas: TCanvas; const Rect: TRect); Begin. // Faster. // ACanvas.Draw(0,0,fBitmap); // Slower. ACanvas.StretchDraw(Rect, fBitmap); End; //--------------------------------------------------------------------- Procedure TPCXImage.LoadFromFile(const Filename: string); Begin. FPCXFile.LoadFromFile(Filename); // added 5/4/2002. Case fPCXFile.fPixelFormat of. 1: fConvert1And8BitPCXDataToImage; 8: fConvert1And8BitPCXDataToImage; 24: fConvert24BitPCXDataToImage; End; End; //--------------------------------------------------------------------- Procedure TPCXImage.SaveToStream(Stream: TStream); Begin. FPCXFile.SaveToStream(Stream); End; //--------------------------------------------------------------------- Procedure TPCXImage.LoadFromStream(Stream: TStream); Begin. FPCXFile.LoadFromStream(Stream); End; /////////////////////////////////////////////////////////////////////// // // // Called by RLE compressor // // // /////////////////////////////////////////////////////////////////////// Procedure TPCXImage.fFillDataLines(const fLine: array of BYTE); Var. By: BYTE; Cnt: WORD; I: QWORD; W: QWORD; Begin. I := 0; By := fLine[0]; Cnt := $C1; W := fBitmap.Width; Repeat. Inc(I); If By = fLine[I] then. Begin. Inc(Cnt); If Cnt = $100 then. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := BYTE(Pred(Cnt)); Inc(fPCXFile.fCurrentPos); FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By; Inc(fPCXFile.fCurrentPos); Cnt := $C1; By := fLine[I]; End; End; If (By <> fLine[I]) then. Begin. If (Cnt = $C1) then. Begin. // If (By < $C1) then. If (By < $C0) then // changed 5/4/2002. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By; Inc(fPCXFile.fCurrentPos); End. Else. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := BYTE(Cnt); Inc(fPCXFile.fCurrentPos); FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By; Inc(fPCXFile.fCurrentPos); End; End. Else. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := BYTE(Cnt); Inc(fPCXFile.fCurrentPos); FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By; Inc(fPCXFile.fCurrentPos); End; Cnt := $C1; By := fLine[I]; End; Until I = W - 1; // Write the last byte(s). If (Cnt > $C1) then. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := BYTE(Cnt); Inc(fPCXFile.fCurrentPos); End; If (Cnt = $C1) and (By > $C0) then. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := BYTE(Cnt); Inc(fPCXFile.fCurrentPos); End; FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By; Inc(fPCXFile.fCurrentPos); End; //-------------------------------------------------------------------// // RLE Compression algorithm // //-------------------------------------------------------------------// Procedure TPCXImage.fConvertImageTo24BitPCXData; // Renamed 5/4/2002. Var. H, W: QWORD; X, Y: QWORD; I: QWORD; Begin. H := fBitmap.Height; W := fBitmap.Width; FPCXFile.fCurrentPos := 0; SetLength(fPCXFile.fPCXData.fData, 6 * H * W); // To be sure... FBitmap.PixelFormat := pf24bit; // Always do this if you're using. // ScanLine! For Y := 0 to H - 1 do. Begin. FP := fBitmap.ScanLine[Y]; I := 0; For X := 0 to W - 1 do. Begin. FRLine[X] := fP[I]; Inc(I); // Extract a red line. FGLine[X] := fP[I]; Inc(I); // Extract a green line. FBLine[X] := fP[I]; Inc(I); // Extract a blue line. End; FFillDataLines(fBLine); // Compress the blue line. FFillDataLines(fGLine); // Compress the green line. FFillDataLines(fRLine); // Compress the red line. End; // Correct the length of fPCXData.fData. SetLength(fPCXFile.fPCXData.fData, fPCXFile.fCurrentPos); End; //-------------------------------------------------------------------// Procedure TPCXImage.fConvertImageTo1And8BitPCXData(ImageWidthInBytes: QWORD); Var. H, W, X, Y: QWORD; OldByte, newByte: BYTE; Cnt: BYTE; Begin. H := fBitmap.Height; W := ImageWidthInBytes; FPCXFile.fCurrentPos := 0; SetLength(fPCXFile.fPCXData.fData, 2 * H * W); // To be sure... OldByte := 0; // Otherwise the compiler issues a warning about. // oldByte not being initialized... Cnt := $C1; For Y := 0 to H - 1 do. Begin. FP := fBitmap.ScanLine[Y]; For X := 0 to W - 1 do. Begin. NewByte := fP[X]; If X > 0 then. Begin. If (Cnt = $FF) then. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := Cnt; Inc(fPCXFile.fCurrentPos); FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := oldByte; Inc(fPCXFile.fCurrentPos); Cnt := $C1; End. Else if newByte = oldByte then. Inc(Cnt); If newByte <> oldByte then. Begin. If (Cnt > $C1) or (oldByte >= $C0) then. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := Cnt; Inc(fPCXFile.fCurrentPos); Cnt := $C1; End; FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := oldByte; Inc(fPCXFile.fCurrentPos); End; End; OldByte := newByte; End; // Write last byte of line. If (Cnt > $C1) or (oldByte >= $C0) then. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := Cnt; Inc(fPCXFile.fCurrentPos); Cnt := $C1; End; FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := oldByte; Inc(fPCXFile.fCurrentPos); End; // Write last byte of image. If (Cnt > $C1) or (oldByte >= $C0) then. Begin. FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := Cnt; Inc(fPCXFile.fCurrentPos); // Cnt := 1; End; FPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := oldByte; Inc(fPCXFile.fCurrentPos); // Correct the length of fPCXData.fData. SetLength(fPCXFile.fPCXData.fData, fPCXFile.fCurrentPos); End; //-------------------------------------------------------------------// // RLE Decompression algorithm // //-------------------------------------------------------------------// Procedure TPCXImage.fConvert24BitPCXDataToImage; // Renamed 5/4/2002. Var. I: QWORD; By: BYTE; Cnt: BYTE; H, W: QWORD; X, Y: QWORD; K, L: QWORD; Begin. H := fPCXFile.fPCXHeader.fWindow.wBottom - FPCXFile.fPCXHeader.fWindow.wTop + 1; W := fPCXFile.fPCXHeader.fWindow.wRight - FPCXFile.fPCXHeader.fWindow.wLeft + 1; Y := 0; // First line of image. FBitmap.Width := W; // Set bitmap width. FBitmap.Height := H; // Set bitmap height. FBitmap.PixelFormat := pf24bit; // Always do this if you're using. // ScanLine! I := 0; // Pointer to data byte of fPXCFile. Repeat. // Process the red line. // ProcessLine(fRLine,W); X := 0; // Pointer to position in Red / Green / Blue line. Repeat. By := fPCXFile.fPCXData.fData[I]; Inc(I); // one byte. If By < $C1 then. If X <= W then // added 5/4/2002. Begin. FRLine[X] := By; Inc(X); End; // multiple bytes (RLE). If By > $C0 then. Begin. Cnt := By and $3F; By := fPCXFile.fPCXData.fData[I]; Inc(I); //FillChar(fRLine[J],Cnt,By); //Inc(J,Cnt); For K := 1 to Cnt do. If X <= W then // added 5/4/2002. Begin. FRLine[X] := By; Inc(X); End; End; Until X >= W; // Process the green line. // ProcessLine(fGLine,W); X := 0; Repeat. By := fPCXFile.fPCXData.fData[I]; Inc(I); // one byte. If By < $C1 then. If X <= W then // added 5/4/2002. Begin. FGLine[X] := By; Inc(X); End; // multiple bytes (RLE). If By > $C0 then. Begin. Cnt := By and $3F; By := fPCXFile.fPCXData.fData[I]; Inc(I); For K := 1 to Cnt do. If X <= W then // added 5/4/2002. Begin. FGLine[X] := By; Inc(X); End; End; Until X >= W; // Process the blue line. // ProcessLine(fBLine,W); X := 0; Repeat. By := fPCXFile.fPCXData.fData[I]; Inc(I); // one byte. If By < $C1 then. If X <= W then // added 5/4/2002. Begin. FBLine[X] := By; Inc(X); End; // multiple bytes (RLE). If By > $C0 then. Begin. Cnt := By and $3F; By := fPCXFile.fPCXData.fData[I]; Inc(I); For K := 1 to Cnt do. If X <= W then // added 5/4/2002. Begin. FBLine[X] := By; Inc(X); End; End; Until X >= W; // Write the just processed data RGB lines to the bitmap. FP := fBitmap.ScanLine[Y]; L := 0; For X := 0 to W - 1 do. Begin. FP[L] := fBLine[X]; Inc(L); FP[L] := fGLine[X]; Inc(L); FP[L] := fRLine[X]; Inc(L); End; Inc(Y); // Process the next RGB line. Until Y >= H; SetLength(fPCXFile.fPCXData.fData, 0); End; //-------------------------------------------------------------------// Procedure TPCXImage.fConvert1And8BitPCXDataToImage; // added 5/4/2002. Var. I, J: QWORD; By: BYTE; Cnt: BYTE; H, W, WW: QWORD; X, Y: QWORD; Begin. H := fPCXFile.fPCXHeader.fWindow.wBottom - FPCXFile.fPCXHeader.fWindow.wTop + 1; W := fPCXFile.fPCXHeader.fWindow.wRight - FPCXFile.fPCXHeader.fWindow.wLeft + 1; FBitmap.Width := W; // Set bitmap width. FBitmap.Height := H; // Set bitmap height. WW := W; // 1 bit PCX. If fPCXFile.fPixelFormat = 1 then. Begin. // All 1 bit images have a palette. FBitmap.PixelFormat := pf1bit; // Always do this if you're using. // ScanLine! WW := W div 8; // Correct width for pf1bit. If W mod 8 > 0 then. Begin. Inc(WW); FBitMap.Width := WW * 8; End; FSetPalette(2); End; // 8 bit PCX. If fPCXFile.fPixelFormat = 8 then. Begin. // All 8 bit images have a palette! // This is how to set the palette of a bitmap. // 1. First set the bitmap to pf8bit; // 2. then set the palette of the bitmap; // 3. then set the pixels with ScanLine or with Draw. // If you do it with StretchDraw, it won't work. Don't ask me why. // If you don't do it in this order, it won't work either! You'll. // get strange colors. FBitmap.PixelFormat := pf8bit; // Always do this if you're using. // ScanLine! FSetPalette(256); End; I := 0; Y := 0; Repeat. FP := fBitmap.ScanLine[Y]; X := 0; // Pointer to position in line. Repeat. By := fPCXFile.fPCXData.fData[I]; Inc(I); // one byte. If By < $C1 then. If X <= WW then. Begin. FP[X] := By; Inc(X); End; // multiple bytes (RLE). If By > $C0 then. Begin. Cnt := By and $3F; By := fPCXFile.fPCXData.fData[I]; Inc(I); For J := 1 to Cnt do. If X <= WW then. Begin. FP[X] := By; Inc(X); End; End; Until X >= WW; Inc(Y); // Next line. Until Y >= H; End; //--------------------------------------------------------------------- Procedure TPCXImage.fCreatePCXHeader(const byBitsPerPixel: BYTE; Const byPlanes: BYTE; const wBytesPerLine: DWORD); Var. H, W: WORD; Begin. W := fBitmap.Width; H := fBitmap.Height; // PCX header. FPCXFile.fPCXHeader.fID := BYTE($0A); // BYTE (1). FPCXFile.fPCXHeader.fVersion := BYTE(5); // BYTE (2). FPCXFile.fPCXHeader.fCompressed := BYTE(1); // BYTE (3). // 0 = uncompressed, 1 = compressed. // Only RLE compressed files are supported by this component. FPCXFile.fPCXHeader.fBitsPerPixel := BYTE(byBitsPerPixel); // BYTE (4). FPCXFile.fPCXHeader.fWindow.wLeft := WORD(0); // WORD (5,6). FPCXFile.fPCXHeader.fWindow.wTop := WORD(0); // WORD (7,8). FPCXFile.fPCXHeader.fWindow.wRight := WORD(W - 1); // WORD (9,10). FPCXFile.fPCXHeader.fWindow.wBottom := WORD(H - 1); // WORD (11,12). FPCXFile.fPCXHeader.fHorzResolution := WORD(72); // WORD (13,14). FPCXFile.fPCXHeader.fVertResolution := WORD(72); // WORD (15,16). FillChar(fPCXFile.fPCXHeader.fColorMap, 48, 0); // Array of Byte. // (17..64). FPCXFile.fPCXHeader.fReserved := BYTE(0); // BYTE (65). FPCXFile.fPCXHeader.fPlanes := BYTE(byPlanes); // BYTE (66). FPCXFile.fPCXHeader.fBytesPerLine := WORD(wBytesPerLine); // WORD (67,68). // must be even. // rounded above. FPCXFile.fPCXHeader.fPaletteInfo := WORD(1); // WORD (69,70). FillChar(fPCXFile.fPCXHeader.fFiller, 58, 0); // Array of Byte. // (71..128). FPCXFile.fPixelFormat := fPCXFile.fPCXHeader.fPlanes * FPCXFile.fPCXHeader.fBitsPerPixel; FPCXFile.fColorDepth := 1 shl fPCXFile.fPixelFormat; End; //--------------------------------------------------------------------- (* // From Delphi 5.0, graphics.pas. Function CopyPalette(Palette: HPALETTE): HPALETTE; Var. PaletteSize : Integer; LogPal : TMaxLogPalette; Begin. Result := 0; If Palette = 0 then. Exit; PaletteSize := 0; If GetObject(Palette,SizeOf(PaletteSize),@PaletteSize) = 0 then. Exit; If PaletteSize = 0 then. Exit; With LogPal do. Begin. PalVersion := $0300; PalNumEntries := PaletteSize; GetPaletteEntries(Palette,0,PaletteSize,palPalEntry); End; Result := CreatePalette(PLogPalette(@LogPal)^); End; *) //--------------------------------------------------------------------- // From Delphi 5.0, graphics.pas. (* Procedure TPCXImage.fSetPixelFormat(Value : TPixelFormat); Const. BitCounts : Array [pf1Bit..pf32Bit] of BYTE = (1,4,8,16,16,24,32); Var. DIB : TDIBSection; Pal : HPALETTE; DC : hDC; KillPal : Boolean; Begin. If Value = GetPixelFormat then. Exit; Case Value of. PfDevice : Begin. HandleType := bmDDB; Exit; End; PfCustom : InvalidGraphic(@SInvalidPixelFormat); Else. FillChar(DIB,sizeof(DIB), 0); DIB.dsbm := FImage.FDIB.dsbm; KillPal := False; With DIB, dsbm,dsbmih do. Begin. BmBits := nil; BiSize := SizeOf(DIB.dsbmih); BiWidth := bmWidth; BiHeight := bmHeight; BiPlanes := 1; BiBitCount := BitCounts[Value]; Pal := FImage.FPalette; Case Value of. Pf4Bit : Pal := SystemPalette16; Pf8Bit : Begin. DC := GDICheck(GetDC(0)); Pal := CreateHalftonePalette(DC); KillPal := True; ReleaseDC(0, DC); End; Pf16Bit : Begin. BiCompression := BI_BITFIELDS; DsBitFields[0] := $F800; DsBitFields[1] := $07E0; DsBitFields[2] := $001F; End; End; // of Case. Try. CopyImage(Handle, Pal, DIB); PaletteModified := (Pal <> 0); Finally. If KillPal then. DeleteObject(Pal); End; // of Try. Changed(Self); End; // of With. End; // of Case. End; // of Procedure. *) //--------------------------------------------------------------------- Procedure TPCXImage.fSetPalette(const wNumColors: WORD); (* From Delphi 5.0, graphics.pas. Type. TPalEntry = packed record. PeRed : BYTE; PeGreen : BYTE; PeBlue : BYTE; End; Type. TagLOGPALETTE = packed record. PalVersion : WORD; PalNumEntries : WORD; PalPalEntry : Array[0..255] of TPalEntry. End; Type. TMAXLogPalette = tagLOGPALETTE; PMAXLogPalette = ^TMAXLogPalette; Type. PRGBQuadArray = ^TRGBQuadArray; TRGBQuadArray = Array[BYTE] of TRGBQuad; Type. PRGBQuadArray = ^TRGBQuadArray; TRGBQuadArray = Array[BYTE] of TRGBQuad; *) Var. Pal: TMaxLogPalette; W: WORD; Begin. Pal.palVersion := $300; // The "Magic" number. Pal.palNumEntries := wNumColors; For W := 0 to 255 do. Begin. Pal.palPalEntry[W].peRed := FPCXFile.fPCXPalette.fPalette[W].ceRed; Pal.palPalEntry[W].peGreen := FPCXFile.fPCXPalette.fPalette[W].ceGreen; Pal.palPalEntry[W].peBlue := FPCXFile.fPCXPalette.fPalette[W].ceBlue; Pal.palPalEntry[W].peFlags := 0; End; (* Must we delete the old palette first here? I dont know. If fhPAL <> 0 then. DeleteObject(fhPAL); *) FhPAL := CreatePalette(PLogPalette(@pal)^); If fhPAL <> 0 then. FBitmap.Palette := fhPAL; End; //--------------------------------------------------------------------- Function TPCXImage.fGetPixelFormat: TPixelFormat; // Only pf1bit, pf4bit and pf8bit images have a palette. // pf15bit, pf16bit, pf24bit and pf32bit images have no palette. // You can change the palette of pf1bit images in windows. // The foreground color and the background color of pf1bit images. // do not have to be black and white. You can choose any tow colors. // The palette of pf4bit images is fixed. // The palette entries 0..9 and 240..255 of pf8bit images are reserved. // in windows. Begin. Result := pfDevice; Case fPCXFile.fPixelFormat of. 01: Result := pf1bit; // Implemented WITH palette. // 04 : Result := pf4bit; // Not yet implemented in this component, // is however implemented in PCX format. 08: Result := pf8bit; // Implemented WITH palette. // 15 : Result := pf15bit; // Not implemented in PCX format? // 16 : Result := pf16bit; // Not implemented in PCX format? 24: Result := pf24bit; // Implemented, has no palette. // 32 : Result := pf32bit; // Not implemented in PCX format. End; End; //--------------------------------------------------------------------- Procedure TPCXImage.fGetPalette(const wNumColors: WORD); Var. Pal: TMaxLogPalette; W: WORD; Begin. FPCXFile.fPCXPalette.fSignature := $0C; Pal.palVersion := $300; // The "Magic" number. Pal.palNumEntries := wNumColors; GetPaletteEntries(CopyPalette(fBitmap.Palette), 0, wNumColors, Pal.palPalEntry); For W := 0 to 255 do. If W < wNumColors then. Begin. FPCXFile.fPCXPalette.fPalette[W].ceRed := Pal.palPalEntry[W].peRed; FPCXFile.fPCXPalette.fPalette[W].ceGreen := Pal.palPalEntry[W].peGreen; FPCXFile.fPCXPalette.fPalette[W].ceBlue := Pal.palPalEntry[W].peBlue; End. Else. Begin. FPCXFile.fPCXPalette.fPalette[W].ceRed := 0; FPCXFile.fPCXPalette.fPalette[W].ceGreen := 0; FPCXFile.fPCXPalette.fPalette[W].ceBlue := 0; End; End; //===================================================================== /////////////////////////////////////////////////////////////////////// // // // TPCXFile // // // /////////////////////////////////////////////////////////////////////// Constructor TPCXFile.Create; Begin. Inherited Create; FHeight := 0; FWidth := 0; FCurrentPos := 0; End; //--------------------------------------------------------------------- Destructor TPCXFile.Destroy; Begin. SetLength(fPCXData.fData, 0); Inherited Destroy; End; //--------------------------------------------------------------------- Procedure TPCXFile.LoadFromFile(const Filename: string); Var. FPCXStream: TFileStream; Begin. FPCXStream := TFileStream.Create(Filename, fmOpenRead); Try. FPCXStream.Position := 0; LoadFromStream(fPCXStream); Finally. FPCXStream.Free; End; End; //--------------------------------------------------------------------- Procedure TPCXFile.SaveToFile(const Filename: string); Var. FPCXStream: TFileStream; Begin. FPCXStream := TFileStream.Create(Filename, fmCreate); Try. FPCXStream.Position := 0; SaveToStream(fPCXStream); Finally. FPCXStream.Free; End; End; //--------------------------------------------------------------------- Procedure TPCXFile.LoadFromStream(Stream: TStream); Var. FFileLength: Cardinal; Begin. // Read the PCX header. Stream.Read(fPCXHeader, SizeOf(fPCXHeader)); // Check the ID byte. If fPCXHeader.fID <> $0A then. Raise Exception.Create(FORMAT_ERROR); (* Check PCX version byte. ====================== Versionbyte = 0 => PC PaintBrush V2.5. Versionbyte = 2 => PC Paintbrush V2.8 with palette information. Versionbyte = 3 => PC Paintbrush V2.8 without palette information. Versionbyte = 4 => PC Paintbrush for Windows. Versionbyte = 5 => PC Paintbrush V3 and up, and PC Paintbrush Plus. With 24 bit image support. *) // Check the PCX version. If fPCXHeader.fVersion <> 5 then. Raise Exception.Create(VERSION_ERROR); // Calculate width. FWidth := fPCXHeader.fWindow.wRight - fPCXHeader.fWindow.wLeft + 1; If fWidth < 0 then. Raise Exception.Create(WIDTH_OUT_OF_RANGE); // Calculate height. FHeight := fPCXHeader.fWindow.wBottom - fPCXHeader.fWindow.wTop + 1; If fHeight < 0 then. Raise Exception.Create(HEIGHT_OUT_OF_RANGE); // Is it too large? If fWidth > fMaxImageWidth then. Raise Exception.Create(IMAGE_WIDTH_TOO_LARGE); // Calculate pixelformat. FPixelFormat := fPCXHeader.fPlanes * fPCXHeader.fBitsPerPixel; // Calculate number of colors. FColorDepth := 1 shl fPixelFormat; // Is this image supported? If not (fPixelFormat in [1, 8, 24]) then. Raise Exception.Create(ERROR_UNSUPPORTED); // The lines following are NOT tested!!! (* If fColorDepth <= 16 then. For I := 0 to fColorDepth - 1 do. Begin. If fPCXHeader.fVersion = 3 then. Begin. FPCXPalette.fPalette[I].R := fPCXHeader.fColorMap[I].R shl 2; FPCXPalette.fPalette[I].G := fPCXHeader.fColorMap[I].G shl 2; FPCXPalette.fPalette[I].B := fPCXHeader.fColorMap[I].B shl 2; End. Else. Begin. FPCXPalette.fPalette[I].R := fPCXHeader.fColorMap[I].R; FPCXPalette.fPalette[I].G := fPCXHeader.fColorMap[I].G; FPCXPalette.fPalette[I].B := fPCXHeader.fColorMap[I].B; End; End; *) // Calculate number of data bytes. // If fFileLength > fMaxDataFileLength then. // Raise Exception.Create(INPUT_FILE_TOO_LARGE); If fPixelFormat = 24 then. Begin. FFileLength := Stream.Size - Stream.Position; SetLength(fPCXData.fData, fFileLength); // Read the data. Stream.Read(fPCXData.fData[0], fFileLength); FHasPalette := False; End; If fPixelFormat in [1, 8] then. Begin. FFileLength := Stream.Size - Stream.Position - 769; SetLength(fPCXData.fData, fFileLength); // Correct number of data bytes. Stream.Read(fPCXData.fData[0], fFilelength); // Read the palette. Stream.Read(fPCXPalette, SizeOf(fPCXPalette)); FHasPalette := True; // Check palette signature byte. If fPCXPalette.fSignature <> $0C then. Raise Exception.Create(PALETTE_ERROR); End; End; //--------------------------------------------------------------------- Procedure TPCXFile.SaveToStream(Stream: TStream); Begin. FHasPalette := False; Stream.Write(fPCXHeader, SizeOf(fPCXHeader)); Stream.Write(fPCXData.fData[0], fCurrentPos); If fPixelFormat in [1, 8] then. Begin. Stream.Write(fPCXPalette, SizeOf(fPCXPalette)); FHasPalette := True; End; End; //--------------------------------------------------------------------- // Register PCX format. Initialization. TPicture.RegisterFileFormat('PCX', sPCXImageFile, TPCXImage); CF_PCX := RegisterClipBoardFormat('PCX Image'); TPicture.RegisterClipBoardFormat(CF_PCX, TPCXImage); //--------------------------------------------------------------------- // Unregister PCX format. Finalization. TPicture.UnRegisterGraphicClass(TPCXImage); //--------------------------------------------------------------------- End.
©Drkb::04303.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
TXT ---> GIF.
Code: Procedure TxtToGif(txt, FileName: string); Var. Temp: TBitmap; GIF: TGIFImage; Begin. Temp := TBitmap.Create; Try. Temp.Height := 400; Temp.Width := 60; Temp.Transparent := True; Temp.Canvas.Brush.Color := colFondo.ColorValue; Temp.Canvas.Font.Name := Fuente.FontName; Temp.Canvas.Font.Color := colFuente.ColorValue; Temp.Canvas.TextOut(10, 10, txt); Imagen.Picture.Assign(nil); GIF := TGIFImage.Create; Try. // Convert the bitmap to a GIF. GIF.Assign(Temp); // Save the GIF. GIF.SaveToFile(FileName); // Display the GIF. Imagen.Picture.Assign(GIF); Finally. GIF.Free; End; Finally. Temp.Destroy; End; End;
©Drkb::04281.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Работа с TGA файлами. |
Code: Const. FERRORMSG2 = 'Sorry, Unsupported Compressed(RLE) File Format'; FERRORMSG3 = 'Sorry, Unsupported More Than 256 Colours File Format'; Type. TArrBuff = array[1..512] of Byte; TPalette_Cell = record. B2, g2, r2: byte; End; TPal = array[0..255] of TPalette_Cell; TPPal = ^TPal; TTGA_Header = record // Targa(TGA) HEADER // IDLength, ColorMap, ImageType: byte; ClrMapSpes: array[1..5] of byte; XAwal, YAwal, Width, Height: SmallInt; BpPixel, ImageDescription: byte; End; Var. Pal: TPPal; PFile: file; Buffer: TArrBuff; FTgaHeader: TTGA_Header; Procedure THPTGA.ReadImageData2Bitmap; Var. I, j, idx: integer; Begin. Seek(pFile, sizeof(FtgaHeader) + FtgaHeader.IDLength + 768); For i := FtgaHeader.Height - 1 downto FtgaHeader.YAwal do. Begin. BlockRead(pFile, buffer, FtgaHeader.Width); For j := FtgaHeader.XAwal to FtgaHeader.Width - 1 do. Begin. Idx := j - FtgaHeader.XAwal + 1; SetPixel(Bitmap.Canvas.Handle, j, i, rgb(pal^[buffer[idx]].r2, Pal^[buffer[idx]].g2, pal^[buffer[idx]].b2)); End; End; End; Procedure THPTGA.LoadFromFile(const FileName: string); Begin. AssignFile(pFile, FileName); {$I-}Reset(pFile, 1); {$I+}. If (IOResult = 0) then. Begin. Try. BlockRead(pFile, FtgaHeader, SizeOf(FtgaHeader)); // checking unsupported features here. If (FtgaHeader.ImageType > 3) then. Begin. MessageBox(Application.Handle, FERRORMSG2, 'TGA Viewer Error', MB_ICONHAND); Exit; End; If (FtgaHeader.BpPixel > 8) then. Begin. MessageBox(Application.Handle, FERRORMSG3, 'TGA Viewer Error', MB_ICONHAND); Exit; End; GetMem(pal, 768); Try. Bitmap.Width := FtgaHeader.Width; Bitmap.Height := FtgaHeader.Height; // if use Color-Map and Uncompressed then read it. If (FtgaHeader.ImageType = 1) then. BlockRead(pFile, pal^, 768); ReadImageData2Bitmap; Finally. FreeMem(pal); End; Finally. CloseFile(pFile); End; End. Else. MessageBox(Application.Handle, 'Error Opening File', 'TGA Viewer Error', MB_ICONHAND); End;
BMP ---> AVI (для TAnimate).
TAnimate is a rather nice component. However if you don't want to use the built in AVI files and want to create your own AVI files from BMP files, then you may have a problem as there is no tool in Delphi to do this.
While browsing the web for information on AVI file formats I came upon a site www.shrinkwrapvb.com/avihelp/avihelp.htm that is maintained by Ray Mercer. In this tutorial he explains how to manipulate,read and write AVI files. I was particularly interested in "Step 5" in which he shows a utility that takes a list of BMP files that creates an AVI file which can be used by the TAnimate component. The only problem was that the examples are in Visual Basic, thus a conversion to Delphi was required.
I have posted this procedure.
CreateAVI(const FileName : string; BMPFileList : TStrings; FramesPerSec : integer = 10);
To keep the text of the example simple and readable I have left out most to the error checking (try except etc.). You can also play with the AVISaveOptions dialog box, but I can only seem to get it to work with "Full Frames Uncompressed" with BMP files. Can anyone shed some light on this ?
Errors you should check for are ..
All files are valid BMP files and are of the same size.
All Blockreads are valid with no read errors.
Ray has a downloadable EXE that works quite nicely, however I am about to write my own utility that incorporates the following ...
Multiline file selection.
Listbox line reordering (drag/drop).
Sort File list.
Layout Save and Load .
AVI Preview.
(I have beta version 1.0.0.0 ready, if anyone wants a copy of exe or source code, drop me a mail at mheydon@pgbison.co.za).
For further info on AVI files I recommend you vist Ray's site at http://www.shrinkwrapvb.com/avihelp/avihelp.htm it really is a well written tutorial (even if it is in Visual Basic).
Code: Const. // AVISaveOptions Dialog box flags. ICMF_CHOOSE_KEYFRAME = 1; // show KeyFrame Every box. ICMF_CHOOSE_DATARATE = 2; // show DataRate box. ICMF_CHOOSE_PREVIEW = 4; // allow expanded preview dialog. ICMF_CHOOSE_ALLCOMPRESSORS = 8; // don't only show those that. // can handle the input format. // or input data. AVIIF_KEYFRAME = 10; Type. AVI_COMPRESS_OPTIONS = packed record. FccType: DWORD; // stream type, for consistency. FccHandler: DWORD; // compressor. DwKeyFrameEvery: DWORD; // keyframe rate. DwQuality: DWORD; // compress quality 0-10,000. DwBytesPerSecond: DWORD; // bytes per second. DwFlags: DWORD; // flags... see below. LpFormat: DWORD; // save format. CbFormat: DWORD; LpParms: DWORD; // compressor options. CbParms: DWORD; DwInterleaveEvery: DWORD; // for non-video streams only. End; AVI_STREAM_INFO = packed record. FccType: DWORD; FccHandler: DWORD; DwFlags: DWORD; DwCaps: DWORD; WPriority: word; WLanguage: word; DwScale: DWORD; DwRate: DWORD; DwStart: DWORD; DwLength: DWORD; DwInitialFrames: DWORD; DwSuggestedBufferSize: DWORD; DwQuality: DWORD; DwSampleSize: DWORD; RcFrame: TRect; DwEditCount: DWORD; DwFormatChangeCount: DWORD; SzName: array[0..63] of char; End; BITMAPINFOHEADER = packed record. BiSize: DWORD; BiWidth: DWORD; BiHeight: DWORD; BiPlanes: word; BiBitCount: word; BiCompression: DWORD; BiSizeImage: DWORD; BiXPelsPerMeter: DWORD; BiYPelsPerMeter: DWORD; BiClrUsed: DWORD; BiClrImportant: DWORD; End; BITMAPFILEHEADER = packed record. BfType: word; //"magic cookie" - must be "BM". BfSize: integer; BfReserved1: word; BfReserved2: word; BfOffBits: integer; End; // DLL External declarations. Function AVISaveOptions(Hwnd: DWORD; uiFlags: DWORD; nStreams: DWORD; PPavi: Pointer; plpOptions: Pointer): boolean; Stdcall; external 'avifil32.dll'; Function AVIFileCreateStream(pFile: DWORD; pPavi: Pointer; pSi: Pointer): integer; Stdcall; external 'avifil32.dll'; Function AVIFileOpen(pPfile: Pointer; szFile: PChar; uMode: DWORD; ClSid: DWORD): integer; Stdcall; external 'avifil32.dll'; Function AVIMakeCompressedStream(psCompressed: Pointer; psSource: DWORD; LpOptions: Pointer; pclsidHandler: DWORD): integer; Stdcall; external 'avifil32.dll'; Function AVIStreamSetFormat(pAvi: DWORD; lPos: DWORD; lpGormat: Pointer; CbFormat: DWORD): integer; Stdcall; external 'avifil32.dll'; Function AVIStreamWrite(pAvi: DWORD; lStart: DWORD; lSamples: DWORD; LBuffer: Pointer; cBuffer: DWORD; dwFlags: DWORD; PlSampWritten: DWORD; plBytesWritten: DWORD): integer; Stdcall; external 'avifil32.dll'; Function AVISaveOptionsFree(nStreams: DWORD; ppOptions: Pointer): integer; Stdcall; external 'avifil32.dll'; Function AVIFileRelease(pFile: DWORD): integer; stdcall; external 'avifil32.dll'; Procedure AVIFileInit; stdcall; external 'avifil32.dll'; Procedure AVIFileExit; stdcall; external 'avifil32.dll'; Function AVIStreamRelease(pAvi: DWORD): integer; stdcall; external 'avifil32.dll'; Function mmioStringToFOURCCA(sz: PChar; uFlags: DWORD): integer; Stdcall; external 'winmm.dll'; // ============================================================================ // Main Function to Create AVI file from BMP file listing. // ============================================================================ Procedure CreateAVI(const FileName: string; IList: TStrings; FramesPerSec: integer = 10); Var. Opts: AVI_COMPRESS_OPTIONS; POpts: Pointer; PFile, ps, psCompressed: DWORD; Strhdr: AVI_STREAM_INFO; I: integer; BFile: file; M_Bih: BITMAPINFOHEADER; M_Bfh: BITMAPFILEHEADER; M_MemBits: packed array of byte; M_MemBitMapInfo: packed array of byte; Begin. DeleteFile(FileName); Fillchar(Opts, SizeOf(Opts), 0); FillChar(strhdr, SizeOf(strhdr), 0); Opts.fccHandler := 541215044; // Full frames Uncompressed. AVIFileInit; Pfile := 0; POpts := @Opts; If AVIFileOpen(@pFile, PChar(FileName), OF_WRITE or OF_CREATE, 0) = 0 then. Begin. // Determine Bitmap Properties from file item[0] in list. AssignFile(BFile, IList[0]); Reset(BFile, 1); BlockRead(BFile, m_Bfh, SizeOf(m_Bfh)); BlockRead(BFile, m_Bih, SizeOf(m_Bih)); SetLength(m_MemBitMapInfo, m_bfh.bfOffBits - 14); SetLength(m_MemBits, m_Bih.biSizeImage); Seek(BFile, SizeOf(m_Bfh)); BlockRead(BFile, m_MemBitMapInfo[0], length(m_MemBitMapInfo)); CloseFile(BFile); Strhdr.fccType := mmioStringToFOURCCA('vids', 0); // stream type video. Strhdr.fccHandler := 0; // def AVI handler. Strhdr.dwScale := 1; Strhdr.dwRate := FramesPerSec; // fps 1 to 30. Strhdr.dwSuggestedBufferSize := m_Bih.biSizeImage; // size of 1 frame. SetRect(strhdr.rcFrame, 0, 0, m_Bih.biWidth, m_Bih.biHeight); If AVIFileCreateStream(pFile, @ps, @strhdr) = 0 then. Begin. // if you want user selection options then call following line. // (but seems to only like "Full frames Uncompressed option). // AVISaveOptions(Application.Handle, // ICMF_CHOOSE_KEYFRAME or ICMF_CHOOSE_DATARATE, // 1,@ps,@pOpts); // AVISaveOptionsFree(1,@pOpts); If AVIMakeCompressedStream(@psCompressed, ps, @opts, 0) = 0 then. Begin. If AVIStreamSetFormat(psCompressed, 0, @m_memBitmapInfo[0], Length(m_MemBitMapInfo)) = 0 then. Begin. For i := 0 to IList.Count - 1 do. Begin. AssignFile(BFile, IList[i]); Reset(BFile, 1); Seek(BFile, m_bfh.bfOffBits); BlockRead(BFile, m_MemBits[0], m_Bih.biSizeImage); Seek(BFile, SizeOf(m_Bfh)); BlockRead(BFile, m_MemBitMapInfo[0], length(m_MemBitMapInfo)); CloseFile(BFile); If AVIStreamWrite(psCompressed, i, 1, @m_MemBits[0], M_Bih.biSizeImage, AVIIF_KEYFRAME, 0, 0) <> 0 then. Begin. ShowMessage('Error during Write AVI File'); Break; End; End; End; End; End; AVIStreamRelease(ps); AVIStreamRelease(psCompressed); AVIFileRelease(pFile); End; AVIFileExit; M_MemBitMapInfo := nil; M_memBits := nil; End;
©Drkb::04264.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Получение Gaussian Blur. |
Автор: Den is Com.
Ну вот, добрались и до фильтров. В неформальных испытаниях этот код оказался вдвое быстрее, чем это делает Adobe Photoshop. Мне кажется есть множество фильтров, которые можно переделать или оптимизировать для быстроты обработки изображений.
Ядро гауссовой функции exp(-(x^2 + y^2)) есть разновидность формулы f(x)*g(y), которая означает, что мы можем выполнить двумерную свертку, делая последовательность одномерных сверток - сначала мы свертываем каждую строчку изображения, затем - каждую колонку. Хороший повод для ускорения (N^2 становится N*2). Любая свертка требует некоторого место для временного хранения результатов - ниже в коде программа BlurRow как раз распределяет и освобождает память для каждой колонки. Вероятно это должно ускорить обработку изображения, правда не ясно насколько.
Поле "size" в записи TKernel ограничено значением 200. Фактически, если вы хотите использовать еще больший радиус, это не вызовет проблем - попробуйте со значениями radius = 3, 5 или другими. Для большого количества данных методы свертки на поверку оказываются эффективнее преобразований Фурье (как показали опыты).
Еще один комментарий все же необходим: гауссово размывание имеет одно магическое свойство, а именно - вы можете сначала размыть каждую строчку (применить фильтр), затем каждую колонку - фактически получается значительно быстрее, чем двумерная свертка.
Во всяком случае вы можете сделать так:
Code: Unit GBlur2; Interface. Uses Windows, Graphics; Type. PRGBTriple = ^TRGBTriple; TRGBTriple = packed record. B: byte; //легче для использования чем типа rgbtBlue... G: byte; R: byte; End; PRow = ^TRow; TRow = array[0..1000000] of TRGBTriple; PPRows = ^TPRows; TPRows = array[0..1000000] of PRow; Const. MaxKernelSize = 100; Type. TKernelSize = 1..MaxKernelSize; TKernel = record. Size: TKernelSize; Weights: array[-MaxKernelSize..MaxKernelSize] of single; End; //идея заключается в том, что при использовании TKernel мы игнорируем. //Weights (вес), за исключением Weights в диапазоне -Size..Size. Procedure GBlur(theBitmap: TBitmap; radius: double); Implementation. Uses SysUtils; Procedure MakeGaussianKernel(var K: TKernel; radius: double; MaxData, DataGranularity: double); //Делаем K (гауссово зерно) со среднеквадратичным отклонением = radius. //Для текущего приложения мы устанавливаем переменные MaxData = 255, //DataGranularity = 1. Теперь в процедуре установим значение. //K.Size так, что при использовании K мы будем игнорировать Weights (вес). //с наименее возможными значениями. (Малый размер нам на пользу, //поскольку время выполнения напрямую зависит от. //значения K.Size.). Var. J: integer; Temp, delta: double; KernelSize: TKernelSize; Begin. For j := Low(K.Weights) to High(K.Weights) do. Begin. Temp := j / radius; K.Weights[j] := exp(-temp * temp / 2); End; //делаем так, чтобы sum(Weights) = 1: Temp := 0; For j := Low(K.Weights) to High(K.Weights) do. Temp := temp + K.Weights[j]; For j := Low(K.Weights) to High(K.Weights) do. K.Weights[j] := K.Weights[j] / temp; //теперь отбрасываем (или делаем отметку "игнорировать". //для переменной Size) данные, имеющие относительно небольшое значение - //это важно, в противном случае смазавание происходим с малым радиусом и. //той области, которая "захватывается" большим радиусом... KernelSize := MaxKernelSize; Delta := DataGranularity / (2 * MaxData); Temp := 0; While (temp < delta) and (KernelSize > 1) do. Begin. Temp := temp + 2 * K.Weights[KernelSize]; Dec(KernelSize); End; K.Size := KernelSize; //теперь для корректности возвращаемого результата проводим ту же. //операцию с K.Size, так, чтобы сумма всех данных была равна единице: Temp := 0; For j := -K.Size to K.Size do. Temp := temp + K.Weights[j]; For j := -K.Size to K.Size do. K.Weights[j] := K.Weights[j] / temp; End; Function TrimInt(Lower, Upper, theInteger: integer): integer; Begin. If (theInteger <= Upper) and (theInteger >= Lower) then. Result := theInteger. Else if theInteger > Upper then. Result := Upper. Else. Result := Lower; End; Function TrimReal(Lower, Upper: integer; x: double): integer; Begin. If (x < upper) and (x >= lower) then. Result := trunc(x). Else if x > Upper then. Result := Upper. Else. Result := Lower; End; Procedure BlurRow(var theRow: array of TRGBTriple; K: TKernel; P: PRow); Var. J, n, LocalRow: integer; Tr, tg, tb: double; //tempRed и др. W: double; Begin. For j := 0 to High(theRow) do. Begin. Tb := 0; Tg := 0; Tr := 0; For n := -K.Size to K.Size do. Begin. W := K.Weights[n]; //TrimInt задает отступ от края строки... With theRow[TrimInt(0, High(theRow), j - n)] do. Begin. Tb := tb + w * b; Tg := tg + w * g; Tr := tr + w * r; End; End; With P[j] do. Begin. B := TrimReal(0, 255, tb); G := TrimReal(0, 255, tg); R := TrimReal(0, 255, tr); End; End; Move(P[0], theRow[0], (High(theRow) + 1) * Sizeof(TRGBTriple)); End; Procedure GBlur(theBitmap: TBitmap; radius: double); Var. Row, Col: integer; TheRows: PPRows; K: TKernel; ACol: PRow; P: PRow; Begin. If (theBitmap.HandleType <> bmDIB) or (theBitmap.PixelFormat <> pf24Bit) then. Raise. Exception.Create('GBlur может работать только с 24-битными изображениями'); MakeGaussianKernel(K, radius, 255, 1); GetMem(theRows, theBitmap.Height * SizeOf(PRow)); GetMem(ACol, theBitmap.Height * SizeOf(TRGBTriple)); //запись позиции данных изображения: For Row := 0 to theBitmap.Height - 1 do. TheRows[Row] := theBitmap.Scanline[Row]; //размываем каждую строчку: P := AllocMem(theBitmap.Width * SizeOf(TRGBTriple)); For Row := 0 to theBitmap.Height - 1 do. BlurRow(Slice(theRows[Row]^, theBitmap.Width), K, P); //теперь размываем каждую колонку. ReAllocMem(P, theBitmap.Height * SizeOf(TRGBTriple)); For Col := 0 to theBitmap.Width - 1 do. Begin. //- считываем первую колонку в TRow: For Row := 0 to theBitmap.Height - 1 do. ACol[Row] := theRows[Row][Col]; BlurRow(Slice(ACol^, theBitmap.Height), K, P); //теперь помещаем обработанный столбец на свое место в данные изображения: For Row := 0 to theBitmap.Height - 1 do. TheRows[Row][Col] := ACol[Row]; End; FreeMem(theRows); FreeMem(ACol); ReAllocMem(P, 0); End; End.
| Как изменить яркость и контраст? |
You must change the RBG values of the pixels. For 1, 4 and 8 bit bitmaps, you must edit the palette. For 15 - 32 bit bitmaps, you must edit the pixel direct. For larger bitmaps you should precalulate a table and set the RGB values from this table.
Red := BCTable[Red];
Green := BCTable[Green];
Blue := BCTable[Blue];
You can find the calculation of the table below. The rest is standard source code, look at EFG's Computer Lab for any solution.
I define the brightness and contrast value between 0..255. Other definitions are possible, change BMax, CMax, BNorm and CNorm.
Code: Type. TBCTable = array[Byte] of Byte; Const. RGBCount = 256; RGBMax = 255; RGBHalf = 128; RGBMin = 0; BMax = 128; { Maximal value brightness 100% - 0% = 0% - - 100% }. CMax = 128; { Maximal value contrast 100% - 0% = 0% - - 100% }. BNorm = 128; { Normal value brightness 0% }. CNorm = 128; { Normal value contrast 0% }. Procedure CalcBCTable(var ABCTable: TBCTable; ABrightness, AContrast: Integer); Var. I, v: Integer; BOffset: Integer; M, D: Integer; Begin. Dec(ABrightness, BNorm); Dec(AContrast, CNorm); { precalculation brightness assistance values }. BOffset := ((ABrightness) * RGBMax div BMax); { precalculation contrast assistance values }. If AContrast < CMax then. Begin { because Division by 0 on 100% }. If AContrast <= 0 then. Begin { decrement contrast }. M := CMax + AContrast; D := CMax; End. Else. Begin { increment contrast }. M := CMax; D := CMax - AContrast; End; End. Else. Begin. M := 0; D := 1; End; For i := RGBMin to RGBMax do. Begin. { calculate contrast }. If AContrast < CMax then. Begin. V := ((i - RGBHalf) * M) div D + RGBHalf; { restrict to byte range }. If v < RGBMin then. V := RGBMin. Else if v > RGBMax then. V := RGBMax; End. Else. Begin { contrast = 100% }. If i < RGBHalf then. V := RGBMin. Else. V := RGBMax; End; { calculate brightness }. Inc(v, BOffset); { restrict to byte range }. If v < RGBMin then. V := RGBMin. Else if v > RGBMax then. V := RGBMax; ABCTable[i] := v; End; End;
| Как сделать colorize? |
Code: Function Colorize(RGB, Luma: Cardinal); Var. L, r, g, b: Single; Begin. Result := Luma; If Luma = 0 then { it's all black anyway}. Exit; L := Luma / 255; R := RGB and $FF * l; G := RGB shr 8 and $FF * l; B := RGB shr 16 and $FF * l; Result := Round(b) shl 16 or Round(g) shl 8 or Round(r); End;
| Как сделать Twist / Swirl эффект. |
Code: { ... } Try. Try. Begin. B := TBitmap.Create; TBufr := TBitmap.Create; CopyMe(b, Image1.Picture.Graphic); {copy image to b}. Twist(100); End; Finally. Begin. B.Free; TBufr.Free; End; End; Except. Raise ESomeErrorWarning.Create('Kaboom!'); End; { ... }
| Как сделать wipe эффект. |
Code: Unit ClockWipe; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls; Type. TPkt = array[0..361] of TPoint; Type. TForm1 = class(TForm). Image1: TImage; Image2: TImage; Button1: TButton; Procedure FormPaint(Sender: TObject); Procedure Button1Click(Sender: TObject); Private. Procedure ClockWipe(re: TRect; Bmp: TBitmap); Procedure SetPolygonRegion(Pkt: TPkt; PktCount: Integer; Bmp: TBitmap); Function GetArcPoint(cPoint: TPoint; radius, winkel: Integer): TPoint; Public. { Public Declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.Button1Click(Sender: TObject); Begin. Canvas.Draw(0, 0, Image1.Picture.Bitmap); ClockWipe(Image2.Picture.Bitmap.Canvas.ClipRect, Image2.Picture.Bitmap); End; Procedure TForm1.FormPaint(Sender: TObject); Begin. Canvas.Draw(0, 0, Image1.Picture.Bitmap); End; Procedure TForm1.ClockWipe(re: TRect; Bmp: TBitmap); Var. Radius, winkel, cv: Integer; CP: TPoint; Pkt: TPkt; Begin. Radius := Round(Sqrt(Sqr((re.right - re.left) div 2) + Sqr((re.bottom - re.top) div 2))); CP := Point((re.right - re.left) div 2, (re.bottom - re.top) div 2); Pkt[0] := cP; For winkel := 0 to 360 do. Pkt[winkel + 1] := GetArcPoint(cP, radius, winkel + 90); For cv := 0 to 361 do. If (cv - 1) / 20 = (cv - 1) div 20 then. Begin. Sleep(50); SetPolygonRegion(Pkt, cv + 1, Image2.Picture.Bitmap); End; End; Procedure TForm1.SetPolygonRegion(Pkt: TPkt; PktCount: Integer; Bmp: TBitmap); Var. Region: HRGN; Begin. Region := CreatePolygonRGN(Pkt, PktCount, WINDING); If Region <> 0 then. Begin. SelectClipRgn(Canvas.handle, Region); Canvas.Draw(0, 0, Bmp); SelectClipRgn(Canvas.handle, 0); DeleteObject(Region); End; End; Function TForm1.GetArcPoint(cPoint: TPoint; radius, winkel: Integer): TPoint; Begin. Result.x := Round(cPoint.x + radius * Cos(winkel * 2 * pi / 360)); Result.y := Round(cPoint.y - radius * Sin(winkel * 2 * pi / 360)); End; End.
| Работа с удалёнными записями. |
Code: Procedure TForm1.Table1AfterOpen(DataSet: TDataset); Begin. SetDelete(Table1, TRUE); End; Procedure SetDelete(oTable: TTable; Value: Boolean); Var. Rslt: DBIResult; SzErrMsg: DBIMSG; Begin. Try. OTable.DisableControls; Try. Rslt := DbiSetProp(hDBIObj(oTable.Handle), curSOFTDELETEON, LongInt(Value)); If rslt <> DBIERR_NONE then. Begin. DbiGetErrorString(rslt, szErrMsg); Raise Exception.Create(StrPas(szErrMsg)); End; Except. On E: EDBEngineError do. ShowMessage(E.Message); On E: Exception do. ShowMessage(E.Message); End; Finally. OTable.Refresh; OTable.EnableControls; End; End;
Как сделать 24bit dithering?
Code: { ... } Type. PIntegerArray = ^TIntegerArray; TIntegerArray = array[0..maxInt div sizeof(integer) - 2] of integer; TColor3 = packed record. B, g, r: byte; End; TColor3Array = array[0..maxInt div sizeof(TColor3) - 2] of TColor3; PColor3Array = ^TColor3Array; Procedure Swap(var p1, p2: PIntegerArray); Var. T: PIntegerArray; Begin. T := p1; P1 := p2; P2 := t; End; Function clamp(x, min, max: integer): integer; Begin. Result := x; If result < min then. Result := min; Else. If result > max then. Result := max; End; Procedure Dither(bmpS, bmpD: TBitmap); Var. BmpS, bmpD: TBitmap; ScanlS, scanlD: PColor3Array; Error1R, error1G, error1B, Error2R, error2G, error2B: PIntegerArray; X, y: integer; Dx: integer; C, cD: TColor3; SR, sG, sB: integer; DR, dG, dB: integer; ER, eG, eB: integer; Begin. BmpD.Width := bmpS.Width; BmpD.Height := bmpS.Height; BmpS.PixelFormat := pf24bit; BmpD.PixelFormat := pf24bit; Error1R := AllocMem((bmpS.Width + 2) * sizeof(integer)); Error1G := AllocMem((bmpS.Width + 2) * sizeof(integer)); Error1B := AllocMem((bmpS.Width + 2) * sizeof(integer)); Error2R := AllocMem((bmpS.Width + 2) * sizeof(integer)); Error2G := AllocMem((bmpS.Width + 2) * sizeof(integer)); Error2B := AllocMem((bmpS.Width + 2) * sizeof(integer)); {dx holds the delta for each iteration as we zigzag, it'll change between 1 and -1}. Dx := 1; For y := 0 to bmpS.Height - 1 do. Begin. ScanlS := bmpS.ScanLine[y]; ScanlD := bmpD.ScanLine[y]; If dx > 0 then. X := 0. Else. X := bmpS.Width - 1; While (x >= 0) and (x < bmpS.Width) do. Begin. C := scanlS[x]; SR := c.r; SG := c.g; SB := c.b; ER := error1R[x + 1]; EG := error1G[x + 1]; EB := error1B[x + 1]; DR := (sR * 16 + eR) div 16; DG := (sR * 16 + eR) div 16; DB := (sR * 16 + eR) div 16; {actual downsampling}. DR := clamp(dR, 0, 255) and (255 shl 4); DG := clamp(dR, 0, 255) and (255 shl 4); DB := clamp(dR, 0, 255) and (255 shl 4); CD.r := dR; CD.g := dG; CD.b := dB; ScanlD[x] := cD; ER := sR - dR; EG := sG - dG; EB := sB - dB; Inc(error1R[x + 1 + dx], (eR * 7)); {next}. Inc(error1G[x + 1 + dx], (eG * 7)); Inc(error1B[x + 1 + dx], (eB * 7)); Inc(error2R[x + 1], (eR * 5)); {top}. Inc(error2G[x + 1], (eG * 5)); Inc(error2B[x + 1], (eB * 5)); Inc(error2R[x + 1 + dx], (eR * 1)); {diag forward}. Inc(error2G[x + 1 + dx], (eG * 1)); Inc(error2B[x + 1 + dx], (eB * 1)); Inc(error2R[x + 1 - dx], (eR * 3)); {diag backward}. Inc(error2G[x + 1 - dx], (eG * 3)); Inc(error2B[x + 1 - dx], (eB * 3)); Inc(x, dx); End; Dx := dx * -1; Swap(error1R, error2R); Swap(error1G, error2G); Swap(error1B, error2B); FillChar(error2R^, sizeof(integer) * (bmpS.Width + 2), 0); FillChar(error2G^, sizeof(integer) * (bmpS.Width + 2), 0); FillChar(error2B^, sizeof(integer) * (bmpS.Width + 2), 0); End; FreeMem(error1R); FreeMem(error1G); FreeMem(error1B); FreeMem(error2R); FreeMem(error2G); FreeMem(error2B); End;
©Drkb::03873.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Как сделать greyscale dithering?
Code: Procedure Greyscale(dib8, dib24: TFastDIB; Colors: Byte); Type. TDiv3 = array[0..767] of Byte; TScale = array[0..255] of Byte; TLineErrors = array[-1.. - 1] of DWord; PDiv3 = ^TDiv3; PScale = ^TScale; PLineErrors = ^TLineErrors; Var. X, y, i, Ln, Nxt: Integer; Pc: PFColor; Pb: PByte; Lines: array[0..1] of PLineErrors; Div3: PDiv3; Scale: PScale; Pti: PDWord; Dir: ShortInt; Begin. Dib8.FillColors(0, Colors, tfBlack, tfWhite); New(Div3); Pb := Pointer(Div3); For i := 0 to 255 do. Begin. Pb^ := i; Inc(pb); Pb^ := i; Inc(pb); Pb^ := i; Inc(pb); End; New(Scale); Pb := Pointer(Scale); X := (Colors shl 16) shr 8; Y := x; For i := 0 to 255 do. Begin. Pb^ := y shr 16; Inc(y, x); Inc(pb); End; GetMem(Lines[0], 24 * (dib24.Width + 2)); GetMem(Lines[1], 24 * (dib24.Width + 2)); Pc := PFColor(dib24.Bits); For x := 0 to dib24.Width - 1 do. Begin. Lines[0, x] := Div3[pc.r + pc.g + pc.b] * 16; Inc(pc); End; Pc := Ptr(Integer(pc) + dib24.Gap); Dir := 1; For y := 1 to dib24.Height do. Begin. Nxt := y mod 2; Ln := 1 - Nxt; If y < dib24.Height then. Begin. For x := 0 to dib24.Width - 1 do. Begin. Lines[Nxt, x] := Div3[pc.r + pc.g + pc.b] * 16; Inc(pc); End; Pc := Ptr(Integer(pc) + dib24.Gap); End; X := 0; If dir = -1 then. X := dib24.Width - 1; Pti := @Lines[Ln, x]; Pb := @dib8.Pixels8[y - 1, x]; While ((x > -1) and (x < dib24.Width)) do. Begin. Pti^ := pti^ div 16; If pti^ > 255 then. Pti^ := 255. Else if pti^ < 0 then. Pti^ := 0; Pb^ := Scale[pti^]; I := pti^ - dib8.Colors[pb^].r; If i <> 0 then. Begin. Inc(Lines[Ln, x + dir], i * 7); Inc(Lines[Nxt, x - dir], i * 3); Inc(Lines[Nxt, x], i * 5); Inc(Lines[Nxt, x + dir], i); End; Inc(pb, dir); Inc(pti, dir); Inc(x, dir); End; Inc(pb, dib8.Gap); Dir := -dir; End; Dispose(Lines[0]); Dispose(Lines[1]); Dispose(Scale); Dispose(Div3); End;
©Drkb::03874.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как добавить когерентный шум? |
Code: {Coherent noise function over 1, 2 or 3 dimensions by Ken Perlin}. Unit perlin; Interface. Function noise1(arg: double): double; Function noise2(vec0, vec1: double): double; Function noise3(vec0, vec1, vec2: double): double; Function PNoise1(x, alpha, beta: double; n: integer): double; Function PNoise2(x, y, alpha, beta: double; n: integer): double; Function PNoise3(x, y, z, alpha, beta: double; n: integer): double; {High Alpha: smoother intensity change, lower contrast. Low Alpha: rapid intensity change, higher contrast. High Beta: coarse, big spots. Low Beta: fine, small spots}. Implementation. Uses. SysUtils; Const. DefB = $100; DefBM = $FF; DefN = $1000; Var. Start: boolean = true; P: array[0..defB + defB + 2 - 1] of integer; G3: array[0..defB + defB + 2 - 1, 0..2] of double; G2: array[0..defB + defB + 2 - 1, 0..1] of double; G1: array[0..defB + defB + 2 - 1] of double; Function s_curve(t: double): double; Begin. Result := t * t * (3.0 - 2.0 * t); End; Function lerp(t, a, b: double): double; Begin. Result := a + t * (b - a); End; Procedure setup(veci: double; var b0, b1: integer; var r0, r1: double); Var. T: double; Begin. T := veci + defN; B0 := trunc(t) and defBM; B1 := (b0 + 1) and defBM; R0 := t - int(t); R1 := r0 - 1.0; End; Procedure normalize2(var v0, v1: double); Var. S: double; Begin. S := sqrt(v0 * v0 + v1 * v1); V0 := v0 / s; V1 := v1 / s; End; Procedure normalize3(var v0, v1, v2: double); Var. S: double; Begin. S := sqrt(v0 * v0 + v1 * v1 + v2 * v2); V0 := v0 / s; V1 := v1 / s; V2 := v2 / s; End; Procedure init; Var. I, j, k: integer; Begin. For i := 0 to defB - 1 do. Begin. P[i] := i; G1[i] := (random(defB + defB) - defB) / defB; For j := 0 to 1 do. G2[i, j] := (random(defB + defB) - defB) / defB; Normalize2(g2[i, 0], g2[i, 1]); For j := 0 to 2 do. G3[i, j] := (random(defB + defB) - defB) / defB; Normalize3(g3[i, 0], g3[i, 1], g3[i, 2]); End; I := defB; While i > 0 do. Begin. K := p[i]; J := random(defB); P[i] := p[j]; P[j] := k; Dec(i); End; For i := 0 to defB + 1 do. Begin. P[defB + i] := p[i]; G1[defB + i] := g1[i]; For j := 0 to 1 do. G2[defB + i, j] := g2[i, j]; For j := 0 to 2 do. G3[defB + i, j] := g3[i, j]; End; End; Function noise1(arg: double): double; Var. Bx0, bx1: integer; Rx0, rx1, sx, u, v: double; Begin. If start then. Begin. Init; Start := false; End; Bx0 := trunc(arg + defN) and defBM; Bx1 := (bx0 + 1) and defBM; Rx0 := frac(arg + defN); Rx1 := rx0 - 1.0; Sx := rx0 * rx0 * (3.0 - 2.0 * rx0); U := rx0 * g1[p[bx0]]; V := rx1 * g1[p[bx1]]; Result := u + sx * (v - u); End; Function noise2(vec0, vec1: double): double; Var. I, j, bx0, bx1, by0, by1, b00, b10, b01, b11: integer; Rx0, rx1, ry0, ry1, sx, sy, a, b, u, v: double; Begin. If start then. Begin. Init; Start := false; End; Bx0 := trunc(vec0 + defN) and defBM; Bx1 := (bx0 + 1) and defBM; Rx0 := frac(vec0 + defN); Rx1 := rx0 - 1.0; By0 := trunc(vec1 + defN) and defBM; By1 := (by0 + 1) and defBM; Ry0 := frac(vec1 + defN); Ry1 := ry0 - 1.0; I := p[bx0]; J := p[bx1]; B00 := p[i + by0]; B10 := p[j + by0]; B01 := p[i + by1]; B11 := p[j + by1]; Sx := rx0 * rx0 * (3.0 - 2.0 * rx0); Sy := ry0 * ry0 * (3.0 - 2.0 * ry0); U := rx0 * g2[b00, 0] + ry0 * g2[b00, 1]; V := rx1 * g2[b10, 0] + ry0 * g2[b10, 1]; A := u + sx * (v - u); U := rx0 * g2[b01, 0] + ry1 * g2[b01, 1]; V := rx1 * g2[b11, 0] + ry1 * g2[b11, 1]; B := u + sx * (v - u); Result := a + sy * (b - a); End; Function noise3orig(vec0, vec1, vec2: double): double; Var. I, j, bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11: integer; Rx0, rx1, ry0, ry1, rz0, rz1, sx, sy, sz, a, b, c, d, u, v: double; Begin. If start then. Begin. Start := false; Init; End; Setup(vec0, bx0, bx1, rx0, rx1); Setup(vec1, by0, by1, ry0, ry1); Setup(vec2, bz0, bz1, rz0, rz1); I := p[bx0]; J := p[bx1]; B00 := p[i + by0]; B10 := p[j + by0]; B01 := p[i + by1]; B11 := p[j + by1]; Sx := s_curve(rx0); Sy := s_curve(ry0); Sz := s_curve(rz0); U := rx0 * g3[b00 + bz0, 0] + ry0 * g3[b00 + bz0, 1] + rz0 * g3[b00 + bz0, 2]; V := rx1 * g3[b10 + bz0, 0] + ry0 * g3[b10 + bz0, 1] + rz0 * g3[b10 + bz0, 2]; A := lerp(sx, u, v); U := rx0 * g3[b01 + bz0, 0] + ry1 * g3[b01 + bz0, 1] + rz0 * g3[b01 + bz0, 2]; V := rx1 * g3[b11 + bz0, 0] + ry1 * g3[b11 + bz0, 1] + rz0 * g3[b11 + bz0, 2]; B := lerp(sx, u, v); C := lerp(sy, a, b); U := rx0 * g3[b00 + bz1, 0] + ry0 * g3[b00 + bz1, 1] + rz1 * g3[b00 + bz1, 2]; V := rx1 * g3[b10 + bz1, 0] + ry0 * g3[b10 + bz1, 1] + rz1 * g3[b10 + bz1, 2]; A := lerp(sx, u, v); U := rx0 * g3[b01 + bz1, 0] + ry1 * g3[b01 + bz1, 1] + rz1 * g3[b01 + bz1, 2]; V := rx1 * g3[b11 + bz1, 0] + ry1 * g3[b11 + bz1, 1] + rz1 * g3[b11 + bz1, 2]; B := lerp(sx, u, v); D := lerp(sy, a, b); Result := lerp(sz, c, d); End; Function noise3(vec0, vec1, vec2: double): double; Var. I, j, bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11: integer; Rx0, rx1, ry0, ry1, rz0, rz1, sx, sy, sz, a, b, c, d, u, v: double; Begin. If start then. Begin. Start := false; Init; End; Bx0 := trunc(vec0 + defN) and defBM; Bx1 := (bx0 + 1) and defBM; Rx0 := frac(vec0 + defN); Rx1 := rx0 - 1.0; By0 := trunc(vec1 + defN) and defBM; By1 := (by0 + 1) and defBM; Ry0 := frac(vec1 + defN); Ry1 := ry0 - 1.0; Bz0 := trunc(vec2 + defN) and defBM; Bz1 := (bz0 + 1) and defBM; Rz0 := frac(vec2 + defN); Rz1 := rz0 - 1.0; I := p[bx0]; J := p[bx1]; B00 := p[i + by0]; B10 := p[j + by0]; B01 := p[i + by1]; B11 := p[j + by1]; Sx := rx0 * rx0 * (3.0 - 2.0 * rx0); Sy := ry0 * ry0 * (3.0 - 2.0 * ry0); Sz := rz0 * rz0 * (3.0 - 2.0 * rz0); U := rx0 * g3[b00 + bz0, 0] + ry0 * g3[b00 + bz0, 1] + rz0 * g3[b00 + bz0, 2]; V := rx1 * g3[b10 + bz0, 0] + ry0 * g3[b10 + bz0, 1] + rz0 * g3[b10 + bz0, 2]; A := u + sx * (v - u); U := rx0 * g3[b01 + bz0, 0] + ry1 * g3[b01 + bz0, 1] + rz0 * g3[b01 + bz0, 2]; V := rx1 * g3[b11 + bz0, 0] + ry1 * g3[b11 + bz0, 1] + rz0 * g3[b11 + bz0, 2]; B := u + sx * (v - u); C := a + sy * (b - a); U := rx0 * g3[b00 + bz1, 0] + ry0 * g3[b00 + bz1, 1] + rz1 * g3[b00 + bz1, 2]; V := rx1 * g3[b10 + bz1, 0] + ry0 * g3[b10 + bz1, 1] + rz1 * g3[b10 + bz1, 2]; A := u + sx * (v - u); U := rx0 * g3[b01 + bz1, 0] + ry1 * g3[b01 + bz1, 1] + rz1 * g3[b01 + bz1, 2]; V := rx1 * g3[b11 + bz1, 0] + ry1 * g3[b11 + bz1, 1] + rz1 * g3[b11 + bz1, 2]; B := u + sx * (v - u); D := a + sy * (b - a); Result := c + sz * (d - c); End; {Harmonic summing functions}. {In what follows "alpha" is the weight when the sum is formed. Typically it is 2. As this. Approaches 1 the function is noisier. "beta" is the harmonic scaling/spacing, typically 2. Persistance = 1/alpha. Beta = frequency. N = octaves}. Function PNoise1(x, alpha, beta: double; n: integer): double; Var. I: integer; Val, sum, p, scale: double; Begin. Sum := 0; Scale := 1; P := x; For i := 0 to n - 1 do. Begin. Val := noise1(p); Sum := sum + val / scale; Scale := scale * alpha; P := p * beta; End; Result := sum; End; Function PNoise2(x, y, alpha, beta: double; n: integer): double; Var. I: integer; Val, sum, px, py, scale: double; Begin. Sum := 0; Scale := 1; Px := x; Py := y; For i := 0 to n - 1 do. Begin. Val := noise2(px, py); Sum := sum + val / scale; Scale := scale * alpha; Px := px * beta; Py := py * beta; End; Result := sum; End; Function PNoise3(x, y, z, alpha, beta: double; n: integer): double; Var. I: integer; Val, sum, px, py, pz, scale: double; Begin. Sum := 0; Scale := 1; Px := x; Py := y; Pz := z; For i := 0 to n - 1 do. Begin. Val := noise3(px, py, pz); Sum := sum + val / scale; Scale := scale * alpha; Px := px * beta; Py := py * beta; Pz := pz * beta; End; Result := sum; End; End.
| Как определить лежит ли точка внутри полигона? |
The main procedure is called ExploreLine. In this procedure Fst and Lst may be two consecutively points in the polyline. Srch is the point searched.
Code: { ... } Const {global}. BigM = 1000000; Function Pend(Pi, Pf: TPoint): Real; Begin. If (Pf.X = Pi.X) then. Result := BigM {for a vertical line}. Else. Result := (Pf.Y - Pi.Y) / (Pf.X - Pi.X); End; Function Dist(Pi, Pf: TPoint): Real; Begin. Result := sqrt(sqr(Pi.Y - Pf.Y) + sqr(Pi.X - Pf.X)). End; Function CalcPoint(Pi, Pf: TPoint; d: Word): TPoint; Var. K, m: Real; { k=d / (1 + m2)Ѕ }. Begin. M := Pend(Pi, Pf); K := d / (Sqrt(1 + Sqr(m))); If ((Pf.X - Pi.X) < 0) then. Begin. Result.X := Pi.X - Round(k); Result.Y := Pi.Y - Round(m * k); End. Else. Begin. Result.X := Pi.X + Round(k); Result.Y := Pi.Y + Round(m * k); End; End; Function ExploreLine(Srch, Fst, Lst: TPoint): Boolean; Var. P: Word; Any: TPoint; Lim, dis: Real; Begin. Lim := Dist(Lst, Fst); P := 1; Any := Fst; Repeat. Result := TestPoint(Srch, Any); Dis := Dist(Any, Fst); Any := CalcPoint(Fst, Lst, Rad * p); Inc(p); Until. (Result)rr(dis >= lim); End;
| Как начертить hexagon? |
Code: Procedure PlotPolygon(const Canvas: TCanvas; const N: Integer; const R: Single; Const XC: Integer; const YC: Integer); Type. TPolygon = array of TPoint; Var. Polygon: TPolygon; I: Integer; C: Extended; S: Extended; A: Single; Begin. SetLength(Polygon, N); A := 2 * Pi / N; For I := 0 to (N - 1) do. Begin. SinCos(I * A, S, C); Polygon[I].X := XC + Round(R * C); Polygon[I].Y := YC + Round(R * S); End; Canvas.Polygon(Polygon); End; Procedure TForm1.Button1Click(Sender: TObject); Var. W: Single; H: Single; X: Integer; Y: Integer; Const. N = 6; R = 10; Begin. W := 1.5 * R; H := R * Sqrt(3); For X := 0 to Round(ClientWidth / W) do. For Y := 0 to Round(ClientHeight / H) do. If Odd(X) then. PlotPolygon(Canvas, N, R, Round(X * W), Round((Y + 0.5) * H)). Else. PlotPolygon(Canvas, N, R, Round(X * W), Round(Y * H)); End;
©Drkb::03906.
Code: Unit HexGrid; Interface. Uses. Windows, Messages, SysUtils, Classes, Controls, ExtCtrls, Math; Type. TOrientation = (hxVertical, hxhorizontal); THexGrid = class(TCustomPanel). Private. FOrientation: TOrientation; FHexSize: Integer; FPoints: array[0..5] of TPoint; FDisplayCaption: Boolean; Procedure ChangedDimensions; Procedure SetOrientation(Value: TOrientation); Procedure SetHexSize(const Value: Integer); Procedure DrawVerticalGrid; Procedure DrawhorizontalGrid; Procedure SetDisplayCaption(Value: Boolean); Protected. Public. Constructor Create(AOwner: TComponent); override; Procedure Paint; override; Property Orientation: TOrientation read FOrientation write SetOrientation; Published. Property Align; Property Alignment; Property Anchors; Property AutoSize; Property BevelInner; Property BevelOuter; Property BevelWidth; Property BiDiMode; Property BorderWidth; Property BorderStyle; Property Caption; Property Color; Property Constraints; Property Ctl3D; Property UseDockManager default True; Property DockSite; Property DragCursor; Property DragKind; Property DragMode; Property Enabled; Property FullRepaint; Property Font; Property Locked; Property ParentBiDiMode; Property ParentColor; Property ParentCtl3D; Property ParentFont; Property ParentShowHint; Property PopupMenu; Property ShowHint; Property TabOrder; Property TabStop; Property Visible; Property OnCanResize; Property OnClick; Property OnConstrainedResize; Property OnContextPopup; Property OnDockDrop; Property OnDockOver; Property OnDblClick; Property OnDragDrop; Property OnDragOver; Property OnEndDock; Property OnEndDrag; Property OnEnter; Property OnExit; Property OnGetSiteInfo; Property OnMouseDown; Property OnMouseMove; Property OnMouseUp; Property OnResize; Property OnStartDock; Property OnStartDrag; Property OnUnDock; Property Left; Property Top; Property Width; Property Height; Property Cursor; Property Hint; Property HelpType; Property HelpKeyword; Property HelpContext; Property HexSize: Integer read FHexSize write SetHexSize; Property DisplayCaption: Boolean read FDisplayCaption write SetDisplayCaption; End; Procedure Register; Implementation. Procedure Register; Begin. RegisterComponents('Samples', [THexGrid]); End; Procedure THexGrid.ChangedDimensions; Var. I: Integer; Begin. For I := 0 to High(FPoints) do. Begin. FPoints[I].X := 0; FPoints[I].Y := 0; End; If Orientation = hxhorizontal then. Begin. FPoints[0].X := Hexsize div 4; FPoints[1].X := HexSize - (Hexsize div 4); FPoints[2].X := HexSize; FPoints[2].Y := HexSize div 2; FPoints[3].X := HexSize - (Hexsize div 4); FPoints[3].Y := HexSize; FPoints[4].X := HexSize div 4; FPoints[4].Y := HexSize; FPoints[5].Y := HexSize div 2; End; If Orientation = hxVertical then. Begin. FPoints[0].X := HexSize div 2; FPoints[1].X := HexSize; FPoints[1].Y := HexSize div 4; FPoints[2].X := HexSize; FPoints[2].Y := HexSize - (Hexsize div 4); FPoints[3].X := HexSize div 2; FPoints[3].Y := HexSize; FPoints[4].Y := HexSize - (Hexsize div 4); FPoints[5].Y := HexSize div 4; End; End; Procedure THexGrid.SetOrientation(Value: TOrientation); Begin. If FOrientation <> Value then. Begin. FOrientation := Value; ChangedDimensions; Invalidate; End; End; Procedure THexGrid.SetHexSize(const Value: Integer); Begin. If FHexSize <> Value then. Begin. FHexSize := Value; ChangedDimensions; Invalidate; End; End; Constructor THexGrid.Create(AOwner: TComponent); Begin. Inherited; FOrientation := hxVertical; FHexSize := 64; ChangedDimensions; Width := 128; Height := 128; End; Procedure THexGrid.Paint; Begin. Inherited; If Orientation = hxhorizontal then. DrawhorizontalGrid. Else. DrawVerticalGrid; End; Procedure THexGrid.DrawhorizontalGrid; Var. I: Integer; X, Y, Offset: Integer; FHex: array[0..5] of TPoint; Begin. X := 0; Y := 0; Offset := 0; While X + HexSize < Width do. Begin. Y := 0; While Y + HexSize < Height do. Begin. With Self.Canvas do. Begin. For I := 0 to High(FPoints) do. Begin. FHex[I].X := X + FPoints[I].X; FHex[I].Y := Y + FPoints[I].Y + Offset; End; Polygon(FHex); End; Y := Y + HexSize; End; If Offset = 0 then. Offset := (0 - (HexSize div 2)). Else. Offset := 0; X := X + (HexSize - (HexSize div 4)); End; End; Procedure THexGrid.DrawVerticalGrid; Var. I: Integer; X, Y, Offset: Integer; FHex: array[0..5] of TPoint; Begin. X := 0; Y := 0; Offset := 0; While Y + HexSize < Height do. Begin. X := 0; While X + HexSize < Width do. Begin. With Self.Canvas do. Begin. For I := 0 to High(FPoints) do. Begin. FHex[I].X := X + FPoints[I].X + Offset; FHex[I].Y := Y + FPoints[I].Y; End; Polygon(FHex); End; X := X + HexSize; End; If Offset = 0 then. Offset := (0 - (HexSize div 2)). Else. Offset := 0; Y := Y + (HexSize - (HexSize div 4)); End; End; Procedure THexGrid.SetDisplayCaption(Value: Boolean); Begin. End; End.
Как повернуть элипс?
Code: Procedure TForm1.EllipseAngle(ACanvas: TCanvas; XCenter, YCenter, XRadius, YRadius: Integer; Angle: Integer); Const. Step = 49; Var. RX, RY: Integer; I: Integer; Theta: Double; SAngle, CAngle: Double; RotAngle: Double; XC, YC: Integer; Kf: Double; X, Y: Double; XRot, YRot: Integer; Points: array[0..Step] of TPoint; Begin. RotAngle := Angle * PI / 180; Kf := (360 * PI / 180) / Step; SAngle := Sin(RotAngle); CAngle := Cos(RotAngle); For i := 0 to Step do. Begin. Theta := i * Kf; X := XCenter + XRadius * Cos(Theta); Y := YCenter + YRadius * Sin(Theta); XRot := Round(XCenter + (X - XCenter) * CAngle - (Y - YCenter) * SAngle); YRot := Round(YCenter + (X - XCenter) * SAngle + (Y - YCenter) * CAngle); Points[i] := Point(XRot, YRot); End; ACanvas.Polygon(Points); End;
©Drkb::03908.
Code: Procedure RotatedEllipse(aCanvas: TCanvas; X1, Y1, X2, Y2: Integer); Var. T, O: TXForm; {in unit Windows}. Begin. { ... } SetGraphicsMode(aCanvas.Handle, GM_Advanced); GetWorldTransform(aCanvas.Handle, O); {Angle in degree}. T.eM11 := 1 * Cos(w / 360 * Pi * 2); T.eM22 := 1 * Cos(w / 360 * Pi * 2); T.eM12 := 1 * Sin(w / 360 * Pi * 2); T.eM21 := 1 * -Sin(w / 360 * Pi * 2); T.eDX := Round((X1 + X2) / 2); T.eDY := Round((Y1 + Y2) / 2); ModifyWorldTransform(aCanvas.Handle, T, MWT_LEFTMULTIPLY); Canvas.Ellipse(X1, Y1, X2, Y2); SetWorldTransform(TheDraw.Handle, O); End;
©Drkb::03909.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как изменить размеры полигона? |
Code: { ... } Type. TPolygon = array of TPoint; Procedure ZoomPolygon(var Polygon: TPolygon; const Center: TPoint; const Scale: Double); Var. I: Integer; Begin. For I := 0 to High(Polygon) do. Begin. Polygon[I].X := Round(Scale * (Polygon[I].X - Center.X) + Center.X); Polygon[I].Y := Round(Scale * (Polygon[I].Y - Center.Y) + Center.Y); End; End;
| Вывод текста с эффектами. |
How to make TextOut with 3d-Effect or hollow Text.
Answer:
Make a new Application and take this Proc bellow for the OnPaint-Event of the Form. The TextOutput will look like written with a kaligraf.If You replace the for loop in the proc with a single call of textout you can use this code to write "hollow" text. Try it with different Pen-Styles too!
Code: Procedure TForm1.FormPaint(Sender: TObject); Var. HFnt: HFONT; Fontname, Txt: PChar; Sze: Size; C: Integer; Byt: Byte; Begin. Fontname := 'Arial'; Txt := 'Mediakueche'; HFnt := CreateFont(90, 60, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH + FF_DONTCARE, Fontname); SelectObject(Canvas.Handle, hfnt); SetBkMode(Canvas.Handle, TRANSPARENT); GetTextExtentPoint32(Canvas.Handle, txt, length(txt), sze); BeginPath(Canvas.Handle); C := 1; For c := 0 to 4 do. Begin. TextOut(Canvas.Handle, 5 + c, 10 + c, Txt, length(Txt)); End; EndPath(Canvas.Handle); // Canvas.pen.Style := psDot; StrokePath(Canvas.Handle); SetBkMode(Canvas.Handle, OPAQUE); DeleteObject(SelectObject(Canvas.Handle, GetStockObject(WHITE_BRUSH))); SelectObject(Canvas.Handle, GetStockObject(SYSTEM_FONT)); DeleteObject(HFnt); End;
Как получить handle на editbox в Internet Explorer?
Code: Var. Hndl: HWND; Main: HWND; Begin. Main := FindWindow('IEFrame', nil); If main <> 0 then. Begin. Hndl := findwindowex(main, 0, 'Worker', nil); If hndl <> 0 then. Begin. Hndl := findwindowex(hndl, 0, 'ReBarWindow32', nil); If hndl <> 0 then. Begin. Hndl := findwindowex(hndl, 0, 'ComboBoxEx32', nil); If hndl <> 0 then. Begin. Hndl := findwindowex(hndl, 0, 'ComboBox', nil); If hndl <> 0 then. Begin. Hndl := findwindowex(hndl, 0, 'Edit', nil);
©Drkb::03511.
Code: Unit Unit1; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Type. TForm1 = class(TForm). Label1: TLabel; Button1: TButton; Procedure Button1Click(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. Procedure FindIEEditHandle; End; Var. Form1: TForm1; EditHandle: THandle; Implementation. {$R *.DFM}. Function EnumIEChildProc(AHandle: hWnd; AnObject: TObject): BOOL; stdcall; Var. TmpS: string; TheClassName: string; TheWinText: string; Begin. Result := True; SetLength(theClassName, 256); GetClassName(AHandle, PChar(theClassName), 255); SetLength(theWinText, 256); GetWindowText(AHandle, PChar(theWinText), 255); TmpS := StrPas(PChar(theClassName)); If theWinText <> EmptyStr then. TmpS := tmpS + '"' + StrPas(PChar(theWinText)) + '"' Else. TmpS := tmpS + '""'; If Pos('Edit', tmpS) > 0 then. Begin. EditHandle := AHandle; End; End; Function IEWindowEnumProc(AHandle: hWnd; AnObject: TObject): BOOL; stdcall; {callback for EnumWindows.}. Var. TheClassName: string; TheWinText: string; TmpS: string; Begin. Result := True; SetLength(theClassName, 256); GetClassName(AHandle, PChar(theClassName), 255); SetLength(theWinText, 256); GetWindowText(AHandle, PChar(theWinText), 255); TmpS := StrPas(PChar(theClassName)); If theWinText <> EmptyStr then. TmpS := tmpS + '"' + StrPas(PChar(theWinText)) + '"' Else. TmpS := tmpS + '""'; If Pos('IEFrame', tmpS) > 0 then. Begin. EnumChildWindows(AHandle, @EnumIEChildProc, longInt(0)); End; End; Procedure TForm1.FindIEEditHandle; Begin. Screen.Cursor := crHourGlass; Try. EnumWindows(@IEWindowEnumProc, LongInt(0)); Finally. Screen.Cursor := crDefault; End; End; Procedure TForm1.Button1Click(Sender: TObject); Begin. FindIEEditHandle; If EditHandle > 0 then. Label1.Caption := IntToStr(EditHandle). Else. Label1.Caption := 'Not Found'; End; End.
©Drkb::03512.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| MS Exchange API. |
MS Exchange API via CDO (Collaboration Data Objects).
CDO (Collaboration Data Objects) Base Library.
( Talking to MS-Exchange server.).
This is a vast subject that is beyond the scope of this article to detail all here. This library provides the basic building blocks for someone who wants to develop using CDO. There are many references on the Net, but your best source is the CDO.HLP file that ships on the Exchange CD or site http://www.cdolive.com/start.htmhttp://www.cdolive.com/start.htm. The cdolive.com site is an excellent reference site which discusses all aspects including installation, versions and also downloads. (CDO.HLP is downloadable from here).
My basic class provides the following functionality ..
Utility functions and methods.
Code: Function CdoNothing(Obj : OleVariant) : boolean; Function CdoDefaultProfile : string; Function VarNothing : IDispatch; Procedure CdoDisposeList(WorkList : TList); Procedure CdoDisposeObjects(WorkStrings : TStrings); Procedure CdoDisposeNodes(WorkData : TTreeNodes);
| Пример FTP сервера. |
Code: Unit Unit1; Interface. Uses. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, IdBaseComponent, IdComponent, IdUDPBase, IdUDPServer, IdTrivialFTPServer, StdCtrls, IdUDPClient, IdTrivialFTP; Type. TForm1 = class(TForm). IdTrivialFTPServer1: TIdTrivialFTPServer; IdTrivialFTP1: TIdTrivialFTP; Button1: TButton; Button2: TButton; Procedure FormCreate(Sender: TObject); Procedure IdTrivialFTPServer1ReadFile(Sender: TObject; Var FileName: string; const PeerInfo: TPeerInfo; Var GrantAccess: Boolean; var AStream: TStream; Var FreeStreamOnComplete: Boolean); Procedure IdTrivialFTPServer1TransferComplete(Sender: TObject; Const Success: Boolean; const PeerInfo: TPeerInfo; AStream: TStream; Const WriteOperation: Boolean); Procedure IdTrivialFTPServer1WriteFile(Sender: TObject; Var FileName: string; const PeerInfo: TPeerInfo; Var GrantAccess: Boolean; var AStream: TStream; Var FreeStreamOnComplete: Boolean); Procedure Button1Click(Sender: TObject); Procedure Button2Click(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. TFTPPath: string; End; Var. Form1: TForm1; Implementation. {$R *.dfm}. Procedure TForm1.FormCreate(Sender: TObject); Begin. IdTrivialFTPServer1.ThreadedEvent := True; IdTrivialFTPServer1.Active := True; { Set the path to where the files will be stored/retreived }. TFTPPath := IncludeTrailingPathDelimiter('C:\Temp'); End; Procedure TForm1.IdTrivialFTPServer1ReadFile(Sender: TObject; Var FileName: string; const PeerInfo: TPeerInfo; Var GrantAccess: Boolean; var AStream: TStream; Var FreeStreamOnComplete: Boolean); Var. FS: TFileStream; Begin. FreeStreamOnComplete := True; Try. { Convert UNIX style filenames to WINDOWS style }. While Pos('/', Filename) <> 0 do. Filename[Pos('/', Filename)] := '\'; { Assure that the filename DOES NOT CONTAIN any path information }. Filename := ExtractFileName(Filename); { Check if file exists }. If FileExists(TFTPPath + Filename) then. Begin. { Open file in READ ONLY mode }. FS := TFileStream.Create(TFTPPath + Filename, FmOpenRead or fmShareDenyWrite); { Assign stream to variable }. AStream := FS; { Set parameters }. GrantAccess := True; End. Else. Begin. GrantAccess := False; End; Except. { On errors, deny access }. GrantAccess := False; If Assigned(FS) then. FreeAndNil(FS); End; End; Procedure TForm1.IdTrivialFTPServer1WriteFile(Sender: TObject; Var FileName: string; const PeerInfo: TPeerInfo; Var GrantAccess: Boolean; var AStream: TStream; Var FreeStreamOnComplete: Boolean); Var. FS: TFileStream; Begin. Try. { Convert UNIX style filenames to WINDOWS style }. While Pos('/', Filename) <> 0 do. Filename[Pos('/', Filename)] := '\'; { Assure that the filename DOES NOT CONTAIN any path information }. Filename := ExtractFileName(Filename); { Open file in WRITE ONLY mode }. FS := TFileStream.Create(TFTPPath + Filename, FmCreate or fmShareExclusive); { Copy all the data }. AStream := FS; { Set parameters }. FreeStreamOnComplete := True; GrantAccess := True; Except. { On errors, deny access }. GrantAccess := False; If Assigned(FS) then. FreeAndNil(FS); End; End; Procedure TForm1.IdTrivialFTPServer1TransferComplete(Sender: TObject; Const Success: Boolean; const PeerInfo: TPeerInfo; AStream: TStream; Const WriteOperation: Boolean); Begin. // Success = TRUE if the read/write operation was successfull. // WriteOperation = TRUE if the client SENT a file to the server. Try. { Close the FileStream }. If Assigned(AStream) then. FreeAndNil(AStream); Except. End; End; // Example of how to DOWNLOAD a file from the server. Procedure TForm1.Button1Click(Sender: TObject); Var. ST: TMemoryStream; Begin. ST := TMemoryStream.Create; IdTrivialFTP1.Get('testfile.dat', ST); If Assigned(ST) then. Begin. ShowMessage('Filesize=' + IntToStr(ST.Size)); FreeAndNil(ST); End; End; // Example of how to UPLOAD a file to the server. Procedure TForm1.Button2Click(Sender: TObject); Var. ST: TMemoryStream; I: Integer; S: string; Begin. { Create stream }. ST := TMemoryStream.Create; { Initialize data }. S := 'This is a test file. It whould appear in the ' + 'TFTP Server upload directory.' + #13#10; { Store in stream }. ST.Write(S[1], Length(S)); ST.Position := 0; { Send Stream to TFTP Server }. IdTrivialFTP1.Put(ST, 'textfile.txt'); { Free Stream }. If Assigned(ST) then. FreeAndNil(ST); { Show a dialog }. ShowMessage('Done!'); End; End.
| Пример простейшего HTTP сервера. |
Code: Unit uMainForm; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, IdBaseComponent, IdComponent, IdTCPServer, IdHTTPServer, StdCtrls, ExtCtrls, HTTPApp; Type. TfrmServer = class(TForm). HttpServer: TIdHTTPServer; ChkActive: TCheckBox; Label1: TLabel; EdtRootFolder: TEdit; BtnGetFolder: TButton; Label2: TLabel; EdtDefaultDoc: TEdit; LstLog: TListBox; Bevel1: TBevel; BtnClearLog: TButton; Procedure btnGetFolderClick(Sender: TObject); Procedure FormCreate(Sender: TObject); Procedure chkActiveClick(Sender: TObject); Procedure btnClearLogClick(Sender: TObject); Procedure httpServerCommandGet(AThread: TIdPeerThread; RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo); Procedure pgpEHTMLHTMLTag(Sender: TObject; Tag: TTag; Const TagString: string; TagParams: TStrings; Var ReplaceText: string); Private. Procedure Log(Data: string); Procedure LogServerState; Public. End; Var. FrmServer: TfrmServer; Implementation. Uses. ShlObj, FileCtrl; {$R *.DFM}. // copied from the last "Latium Software - Pascal Newsletter #33". Function BrowseCallbackProc(Wnd: HWND; uMsg: UINT; LParam, lpData: LPARAM): Integer stdcall; Var. Buffer: array[0..MAX_PATH - 1] of char; Begin. Case uMsg of. BFFM_INITIALIZED: If lpData <> 0 then. SendMessage(Wnd, BFFM_SETSELECTION, 1, lpData); BFFM_SELCHANGED: Begin. SHGetPathFromIDList(PItemIDList(lParam), Buffer); SendMessage(Wnd, BFFM_SETSTATUSTEXT, 0, Integer(@Buffer)); End; End; Result := 0; End; // copied from the last "Latium Software - Pascal Newsletter #33". Function BrowseForFolder(Title: string; RootCSIDL: integer = 0; InitialFolder: string = ''): string; Var. BrowseInfo: TBrowseInfo; Buffer: array[0..MAX_PATH - 1] of char; ResultPItemIDList: PItemIDList; Begin. With BrowseInfo do. Begin. HwndOwner := Application.Handle; If RootCSIDL = 0 then. PidlRoot := nil. Else. SHGetSpecialFolderLocation(hwndOwner, RootCSIDL, PidlRoot); PszDisplayName := @Buffer; LpszTitle := PChar(Title); UlFlags := BIF_RETURNONLYFSDIRS or BIF_STATUSTEXT; Lpfn := BrowseCallbackProc; LParam := Integer(Pointer(InitialFolder)); IImage := 0; End; Result := ''; ResultPItemIDList := SHBrowseForFolder(BrowseInfo); If ResultPItemIDList <> nil then. Begin. SHGetPathFromIDList(ResultPItemIDList, Buffer); Result := Buffer; GlobalFreePtr(ResultPItemIDList); End; With BrowseInfo do. If pidlRoot <> nil then. GlobalFreePtr(pidlRoot); End; // clear log file. Procedure TfrmServer.btnClearLogClick(Sender: TObject); Begin. LstLog.Clear; End; // got http server root folder. Procedure TfrmServer.btnGetFolderClick(Sender: TObject); Var. NewFolder: string; Begin. NewFolder := BrowseForFolder('Web Root Folder', 0, edtRootFolder.Text); If NewFolder <> '' then. If DirectoryExists(NewFolder) then. EdtRootFolder.Text := NewFolder; End; // de-activate http server. Procedure TfrmServer.chkActiveClick(Sender: TObject); Begin. If chkActive.Checked then. Begin. // root folder must exists. If AnsiLastChar(edtRootFolder.Text)^ = '\' then. EdtRootFolder.Text := Copy(edtRootFolder.Text, 1, Pred(Length(edtRootFolder.Text))); ChkActive.Checked := DirectoryExists(edtRootFolder.Text); If not chkActive.Checked then. ShowMessage('Root Folder does not exist.'); End; // de-/activate server. HttpServer.Active := chkActive.Checked; // log to list box. LogServerState; // set interactive state for user fields. EdtRootFolder.Enabled := not chkActive.Checked; EdtDefaultDoc.Enabled := not chkActive.Checked; End; // prepare ! Procedure TfrmServer.FormCreate(Sender: TObject); Begin. EdtRootFolder.Text := ExtractFilePath(Application.ExeName) + 'WebSite'; ForceDirectories(edtRootFolder.Text); End; // incoming client request for download. Procedure TfrmServer.httpServerCommandGet(AThread: TIdPeerThread; RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo); Var. I: Integer; RequestedDocument, FileName, CheckFileName: string; EHTMLParser: TPageProducer; Begin. // requested document. RequestedDocument := RequestInfo.Document; // log request. Log('Client: ' + RequestInfo.RemoteIP + ' request for: ' + RequestedDocument); // 001. If Copy(RequestedDocument, 1, 1) <> '/' then. // invalid request. Raise Exception.Create('invalid request: ' + RequestedDocument); // 002. // convert all '/' to '\' FileName := RequestedDocument; I := Pos('/', FileName); While I > 0 do. Begin. FileName[I] := '\'; I := Pos('/', FileName); End; // locate requested file. FileName := edtRootFolder.Text + FileName; Try. // check whether file or folder was requested. If AnsiLastChar(FileName)^ = '\' then. // folder - reroute to default document. CheckFileName := FileName + edtDefaultDoc.Text. Else. // file - use it. CheckFileName := FileName; If FileExists(CheckFileName) then. Begin. // file exists. If LowerCase(ExtractFileExt(CheckFileName)) = '.ehtm' then. Begin. // Extended HTML - send through internal tag parser. EHTMLParser := TPageProducer.Create(Self); Try. // set source file name. EHTMLParser.HTMLFile := CheckFileName; // set event handler. EHTMLParser.OnHTMLTag := pgpEHTMLHTMLTag; // parse ! ResponseInfo.ContentText := EHTMLParser.Content; Finally. EHTMLParser.Free; End; End. Else. Begin. // return file as-is. // log. Log('Returning Document: ' + CheckFileName); // open file stream. ResponseInfo.ContentStream := TFileStream.Create(CheckFileName, fmOpenRead or fmShareCompat); End; End; Finally. If Assigned(ResponseInfo.ContentStream) then. Begin. // response stream does exist. // set length. ResponseInfo.ContentLength := ResponseInfo.ContentStream.Size; // write header. ResponseInfo.WriteHeader; // return content. ResponseInfo.WriteContent; // free stream. ResponseInfo.ContentStream.Free; ResponseInfo.ContentStream := nil; End. Else if ResponseInfo.ContentText <> '' then. Begin. // set length. ResponseInfo.ContentLength := Length(ResponseInfo.ContentText); // write header. ResponseInfo.WriteHeader; // return content. End. Else. Begin. If not ResponseInfo.HeaderHasBeenWritten then. Begin. // set error code. ResponseInfo.ResponseNo := 404; ResponseInfo.ResponseText := 'Document not found'; // write header. ResponseInfo.WriteHeader; End; // return content. ResponseInfo.ContentText := 'The document requested is not availabe.'; ResponseInfo.WriteContent; End; End; End; Procedure TfrmServer.Log(Data: string); Begin. LstLog.Items.Add(DateTimeToStr(Now) + ' - ' + Data); End; Procedure TfrmServer.LogServerState; Begin. If httpServer.Active then. Log(httpServer.ServerSoftware + ' is active'). Else. Log(httpServer.ServerSoftware + ' is not active'); End; Procedure TfrmServer.pgpEHTMLHTMLTag(Sender: TObject; Tag: TTag; Const TagString: string; TagParams: TStrings; var ReplaceText: string); Var. LTag: string; Begin. LTag := LowerCase(TagString); If LTag = 'date' then. ReplaceText := DateToStr(Now). Else if LTag = 'time' then. ReplaceText := TimeToStr(Now). Else if LTag = 'datetime' then. ReplaceText := DateTimeToStr(Now). Else if LTag = 'server' then. ReplaceText := httpServer.ServerSoftware; End; End.
| Как выключить удалённый компьютер? |
Code: {----------------------------------------------------------------------------- Unit Name: formClient. Author: Stewart Moss. Creation Date: 27 February, 2002 (16:30). Documentation Date: 27 February, 2002 (16:30). Version 1.0. ----------------------------------------------------------------------------- Description: This is to demonstrate shutting down a machine over the network. ** Tobias R. requests the article "How to send a shutdown command in a network?" ** This is not really what you want. I think you are looking for some kind. Of IPC or RPC command. But this will work. Each machine needs to run. A copy of this server. It uses the standard delphi ServerSocket found in the "ScktComp" unit. Create a form (name frmClient) with a TServerSocket on it (name ServerSocket). Set the Port property of ServerSocket to 5555. Add a TMemo called Memo1. It listens on port 5555 using TCP/IP. It has a very simple protocol. Z = Show message with "Z". B = Beep. S = Shutdown windows. Run the program.. Then from the command prompt type in. "telnet localhost 5555". Type in one of the three commands above. (all in uppercase) and the server will respond. Copyright 2002 by Stewart Moss. All rights reserved. -----------------------------------------------------------------------------} Unit formClient; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ScktComp, StdCtrls; Type. TfrmClient = class(TForm). ServerSocket: TServerSocket; Memo1: TMemo; Procedure ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket); Procedure FormCreate(Sender: TObject); Procedure FormClose(Sender: TObject; var Action: TCloseAction); Private. { Private declarations }. Public. { Public declarations }. End; Var. FrmClient: TfrmClient; Implementation. {$R *.DFM}. Procedure TfrmClient.ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket); Var. Incomming: string; Begin. // read off the socket. Incomming := Socket.ReceiveText; Memo1.Lines.Add(incomming); If Incomming = 'S' then // Shutdown Protocol. ExitWindowsEx(EWX_FORCE or EWX_SHUTDOWN, 0); If Incomming = 'B' then // Beep Protocol. Beep; If Incomming = 'Z' then // Z protocol. Showmessage('Z'); End; Procedure TfrmClient.FormCreate(Sender: TObject); Begin. ServerSocket.Active := true; End; Procedure TfrmClient.FormClose(Sender: TObject; var Action: TCloseAction); Begin. ServerSocket.Active := false; End; End.
Как сделать свои собственные сообщения при компилляции?
Формат команды:
{$MESSAGE HINT|WARN|ERROR|FATAL 'text string' }.
Например, добавление следующих строк приведёт к появлению:
Code: {$MESSAGE 'Появился новый hint!'}. {$MESSAGE Hint 'И это тоже hint!'}. {$MESSAGE Warn 'А это уже Warning'}. {$MESSAGE Error 'Эта строка вызовет ошибку компиляции!'}. {$MESSAGE Fatal 'А это фатальная ошибка компиляции!'}.
Пример:
Code: Destructor TumbSelectionTempTable.Destroy; Begin. // Clear the temp tables. {$MESSAGE Warn ' - remember to free all allocated objects'}. ClearAllOuterWorldFold; If FSubjectsTempTableCreated then. DropTempTable(FTableName); FOuterWorldsFolded.Free; FQuery.Free; Inherited; End;
Работает только в Дельфи 6/7.
©Drkb::00010.
| Как работать со сканером? |
Code: //////////////////////////////////////////////////////////////////////// // // // Delphi Scanner Support Framework // // // // Copyright (C) 1999 by Uli Tessel // // // //////////////////////////////////////////////////////////////////////// // // // Modified and rewritten as a Delphi component by: // // // // M. de Haan // // // // June 2002 // // // //////////////////////////////////////////////////////////////////////// Unit. TWAIN; Interface. Uses. SysUtils, // Exceptions. Forms, // TMessageEvent. Windows, // HMODULE. Graphics, // TBitmap. IniFiles, // Inifile. Controls, // TCursor. Classes; // Class. Const. // Messages. MSG_GET = $0001; // Get one or more values. MSG_GETCURRENT = $0002; // Get current value. MSG_GETDEFAULT = $0003; // Get default (e.g. power up) value. MSG_GETFIRST = $0004; // Get first of a series of items, // e.g. Data Sources. MSG_GETNEXT = $0005; // Iterate through a series of items. MSG_SET = $0006; // Set one or more values. MSG_RESET = $0007; // Set current value to default value. MSG_QUERYSUPPORT = $0008; // Get supported operations on the. // capacities. // Messages used with DAT_NULL. // --------------------------- MSG_XFERREADY = $0101; // The data source has data ready. MSG_CLOSEDSREQ = $0102; // Request for the application to close. // the Data Source. MSG_CLOSEDSOK = $0103; // Tell the application to save the. // state. MSG_DEVICEEVENT = $0104; // Some event has taken place. // Messages used with a pointer to a DAT_STATUS structure. // ------------------------------------------------------ MSG_CHECKSTATUS = $0201; // Get status information. // Messages used with a pointer to DAT_PARENT data. // ----------------------------------------------- MSG_OPENDSM = $0301; // Open the Data Source Manager. MSG_CLOSEDSM = $0302; // Close the Data Source Manager. // Messages used with a pointer to a DAT_IDENTITY structure. // -------------------------------------------------------- MSG_OPENDS = $0401; // Open a Data Source. MSG_CLOSEDS = $0402; // Close a Data Source. MSG_USERSELECT = $0403; // Put up a dialog of all Data Sources. // The user can select a Data Source. // Messages used with a pointer to a DAT_USERINTERFACE structure. // ------------------------------------------------------------- MSG_DISABLEDS = $0501; // Disable data transfer in the Data. // Source. MSG_ENABLEDS = $0502; // Enable data transfer in the Data. // Source. MSG_ENABLEDSUIONLY = $0503; // Enable for saving Data Source state. // only. // Messages used with a pointer to a DAT_EVENT structure. // ----------------------------------------------------- MSG_PROCESSEVENT = $0601; // Messages used with a pointer to a DAT_PENDINGXFERS structure. // ------------------------------------------------------------ MSG_ENDXFER = $0701; MSG_STOPFEEDER = $0702; // Messages used with a pointer to a DAT_FILESYSTEM structure. // ---------------------------------------------------------- MSG_CHANGEDIRECTORY = $0801; MSG_CREATEDIRECTORY = $0802; MSG_DELETE = $0803; MSG_FORMATMEDIA = $0804; MSG_GETCLOSE = $0805; MSG_GETFIRSTFILE = $0806; MSG_GETINFO = $0807; MSG_GETNEXTFILE = $0808; MSG_RENAME = $0809; MSG_COPY = $080A; MSG_AUTOMATICCAPTUREDIRECTORY = $080B; // Messages used with a pointer to a DAT_PASSTHRU structure. // -------------------------------------------------------- MSG_PASSTHRU = $0901; Const. DG_CONTROL = $0001; // data pertaining to control. DG_IMAGE = $0002; // data pertaining to raster images. Const. // Data Argument Types for the DG_CONTROL Data Group. DAT_CAPABILITY = $0001; // TW_CAPABILITY. DAT_EVENT = $0002; // TW_EVENT. DAT_IDENTITY = $0003; // TW_IDENTITY. DAT_PARENT = $0004; // TW_HANDLE, // application win handle in Windows. DAT_PENDINGXFERS = $0005; // TW_PENDINGXFERS. DAT_SETUPMEMXFER = $0006; // TW_SETUPMEMXFER. DAT_SETUPFILEXFER = $0007; // TW_SETUPFILEXFER. DAT_STATUS = $0008; // TW_STATUS. DAT_USERINTERFACE = $0009; // TW_USERINTERFACE. DAT_XFERGROUP = $000A; // TW_UINT32. DAT_IMAGEMEMXFER = $0103; // TW_IMAGEMEMXFER. DAT_IMAGENATIVEXFER = $0104; // TW_UINT32, loword is hDIB, PICHandle. DAT_IMAGEFILEXFER = $0105; // Null data. Const. // Condition Codes: Application gets these by doing DG_CONTROL. // DAT_STATUS MSG_GET. TWCC_CUSTOMBASE = $8000; TWCC_SUCCESS = 00; // It worked! TWCC_BUMMER = 01; // Failure due to unknown causes. TWCC_LOWMEMORY = 02; // Not enough memory to perform operation. TWCC_NODS = 03; // No Data Source. TWCC_MAXCONNECTIONS = 04; // Data Source is connected to maximum. // number of possible applications. TWCC_OPERATIONERROR = 05; // Data Source or Data Source Manager. // reported error, application. // shouldn't report an error. TWCC_BADCAP = 06; // Unknown capability. TWCC_BADPROTOCOL = 09; // Unrecognized MSG DG DAT combination. TWCC_BADVALUE = 10; // Data parameter out of range. TWCC_SEQERROR = 11; // DG DAT MSG out of expected sequence. TWCC_BADDEST = 12; // Unknown destination Application / // Source in DSM_Entry. TWCC_CAPUNSUPPORTED = 13; // Capability not supported by source. TWCC_CAPBADOPERATION = 14; // Operation not supported by. // capability. TWCC_CAPSEQERROR = 15; // Capability has dependancy on other. // capability. TWCC_DENIED = 16; // File System operation is denied. // (file is protected). TWCC_FILEEXISTS = 17; // Operation failed because file. // already exists. TWCC_FILENOTFOUND = 18; // File not found. TWCC_NOTEMPTY = 19; // Operation failed because directory. // is not empty. TWCC_PAPERJAM = 20; // The feeder is jammed. TWCC_PAPERDOUBLEFEED = 21; // The feeder detected multiple pages. TWCC_FILEWRITEERROR = 22; // Error writing the file (meant for. // things like disk full conditions). TWCC_CHECKDEVICEONLINE = 23; // The device went offline prior to or. // during this operation. Const. // Flags used in TW_MEMORY structure. TWMF_APPOWNS = $01; TWMF_DSMOWNS = $02; TWMF_DSOWNS = $04; TWMF_POINTER = $08; TWMF_HANDLE = $10; Const. // Flags for country, which seems to be equal to their telephone. // number. TWCY_AFGHANISTAN = 1001; TWCY_ALGERIA = 0213; TWCY_AMERICANSAMOA = 0684; TWCY_ANDORRA = 0033; TWCY_ANGOLA = 1002; TWCY_ANGUILLA = 8090; TWCY_ANTIGUA = 8091; TWCY_ARGENTINA = 0054; TWCY_ARUBA = 0297; TWCY_ASCENSIONI = 0247; TWCY_AUSTRALIA = 0061; TWCY_AUSTRIA = 0043; TWCY_BAHAMAS = 8092; TWCY_BAHRAIN = 0973; TWCY_BANGLADESH = 0880; TWCY_BARBADOS = 8093; TWCY_BELGIUM = 0032; TWCY_BELIZE = 0501; TWCY_BENIN = 0229; TWCY_BERMUDA = 8094; TWCY_BHUTAN = 1003; TWCY_BOLIVIA = 0591; TWCY_BOTSWANA = 0267; TWCY_BRITAIN = 0006; TWCY_BRITVIRGINIS = 8095; TWCY_BRAZIL = 0055; TWCY_BRUNEI = 0673; TWCY_BULGARIA = 0359; TWCY_BURKINAFASO = 1004; TWCY_BURMA = 1005; TWCY_BURUNDI = 1006; TWCY_CAMAROON = 0237; TWCY_CANADA = 0002; TWCY_CAPEVERDEIS = 0238; TWCY_CAYMANIS = 8096; TWCY_CENTRALAFREP = 1007; TWCY_CHAD = 1008; TWCY_CHILE = 0056; TWCY_CHINA = 0086; TWCY_CHRISTMASIS = 1009; TWCY_COCOSIS = 1009; TWCY_COLOMBIA = 0057; TWCY_COMOROS = 1010; TWCY_CONGO = 1011; TWCY_COOKIS = 1012; TWCY_COSTARICA = 0506; TWCY_CUBA = 0005; TWCY_CYPRUS = 0357; TWCY_CZECHOSLOVAKIA = 0042; TWCY_DENMARK = 0045; TWCY_DJIBOUTI = 1013; TWCY_DOMINICA = 8097; TWCY_DOMINCANREP = 8098; TWCY_EASTERIS = 1014; TWCY_ECUADOR = 0593; TWCY_EGYPT = 0020; TWCY_ELSALVADOR = 0503; TWCY_EQGUINEA = 1015; TWCY_ETHIOPIA = 0251; TWCY_FALKLANDIS = 1016; TWCY_FAEROEIS = 0298; TWCY_FIJIISLANDS = 0679; TWCY_FINLAND = 0358; TWCY_FRANCE = 0033; TWCY_FRANTILLES = 0596; TWCY_FRGUIANA = 0594; TWCY_FRPOLYNEISA = 0689; TWCY_FUTANAIS = 1043; TWCY_GABON = 0241; TWCY_GAMBIA = 0220; TWCY_GERMANY = 0049; TWCY_GHANA = 0233; TWCY_GIBRALTER = 0350; TWCY_GREECE = 0030; TWCY_GREENLAND = 0299; TWCY_GRENADA = 8099; TWCY_GRENEDINES = 8015; TWCY_GUADELOUPE = 0590; TWCY_GUAM = 0671; TWCY_GUANTANAMOBAY = 5399; TWCY_GUATEMALA = 0502; TWCY_GUINEA = 0224; TWCY_GUINEABISSAU = 1017; TWCY_GUYANA = 0592; TWCY_HAITI = 0509; TWCY_HONDURAS = 0504; TWCY_HONGKONG = 0852; TWCY_HUNGARY = 0036; TWCY_ICELAND = 0354; TWCY_INDIA = 0091; TWCY_INDONESIA = 0062; TWCY_IRAN = 0098; TWCY_IRAQ = 0964; TWCY_IRELAND = 0353; TWCY_ISRAEL = 0972; TWCY_ITALY = 0039; TWCY_IVORYCOAST = 0225; TWCY_JAMAICA = 8010; TWCY_JAPAN = 0081; TWCY_JORDAN = 0962; TWCY_KENYA = 0254; TWCY_KIRIBATI = 1018; TWCY_KOREA = 0082; TWCY_KUWAIT = 0965; TWCY_LAOS = 1019; TWCY_LEBANON = 1020; TWCY_LIBERIA = 0231; TWCY_LIBYA = 0218; TWCY_LIECHTENSTEIN = 0041; TWCY_LUXENBOURG = 0352; TWCY_MACAO = 0853; TWCY_MADAGASCAR = 1021; TWCY_MALAWI = 0265; TWCY_MALAYSIA = 0060; TWCY_MALDIVES = 0960; TWCY_MALI = 1022; TWCY_MALTA = 0356; TWCY_MARSHALLIS = 0692; TWCY_MAURITANIA = 1023; TWCY_MAURITIUS = 0230; TWCY_MEXICO = 0003; TWCY_MICRONESIA = 0691; TWCY_MIQUELON = 0508; TWCY_MONACO = 0033; TWCY_MONGOLIA = 1024; TWCY_MONTSERRAT = 8011; TWCY_MOROCCO = 0212; TWCY_MOZAMBIQUE = 1025; TWCY_NAMIBIA = 0264; TWCY_NAURU = 1026; TWCY_NEPAL = 0977; TWCY_NETHERLANDS = 0031; TWCY_NETHANTILLES = 0599; TWCY_NEVIS = 8012; TWCY_NEWCALEDONIA = 0687; TWCY_NEWZEALAND = 0064; TWCY_NICARAGUA = 0505; TWCY_NIGER = 0227; TWCY_NIGERIA = 0234; TWCY_NIUE = 1027; TWCY_NORFOLKI = 1028; TWCY_NORWAY = 0047; TWCY_OMAN = 0968; TWCY_PAKISTAN = 0092; TWCY_PALAU = 1029; TWCY_PANAMA = 0507; TWCY_PARAGUAY = 0595; TWCY_PERU = 0051; TWCY_PHILLIPPINES = 0063; TWCY_PITCAIRNIS = 1030; TWCY_PNEWGUINEA = 0675; TWCY_POLAND = 0048; TWCY_PORTUGAL = 0351; TWCY_QATAR = 0974; TWCY_REUNIONI = 1031; TWCY_ROMANIA = 0040; TWCY_RWANDA = 0250; TWCY_SAIPAN = 0670; TWCY_SANMARINO = 0039; TWCY_SAOTOME = 1033; TWCY_SAUDIARABIA = 0966; TWCY_SENEGAL = 0221; TWCY_SEYCHELLESIS = 1034; TWCY_SIERRALEONE = 1035; TWCY_SINGAPORE = 0065; TWCY_SOLOMONIS = 1036; TWCY_SOMALI = 1037; TWCY_SOUTHAFRICA = 0027; TWCY_SPAIN = 0034; TWCY_SRILANKA = 0094; TWCY_STHELENA = 1032; TWCY_STKITTS = 8013; TWCY_STLUCIA = 8014; TWCY_STPIERRE = 0508; TWCY_STVINCENT = 8015; TWCY_SUDAN = 1038; TWCY_SURINAME = 0597; TWCY_SWAZILAND = 0268; TWCY_SWEDEN = 0046; TWCY_SWITZERLAND = 0041; TWCY_SYRIA = 1039; TWCY_TAIWAN = 0886; TWCY_TANZANIA = 0255; TWCY_THAILAND = 0066; TWCY_TOBAGO = 8016; TWCY_TOGO = 0228; TWCY_TONGAIS = 0676; TWCY_TRINIDAD = 8016; TWCY_TUNISIA = 0216; TWCY_TURKEY = 0090; TWCY_TURKSCAICOS = 8017; TWCY_TUVALU = 1040; TWCY_UGANDA = 0256; TWCY_USSR = 0007; TWCY_UAEMIRATES = 0971; TWCY_UNITEDKINGDOM = 0044; TWCY_USA = 0001; TWCY_URUGUAY = 0598; TWCY_VANUATU = 1041; TWCY_VATICANCITY = 0039; TWCY_VENEZUELA = 0058; TWCY_WAKE = 1042; TWCY_WALLISIS = 1043; TWCY_WESTERNSAHARA = 1044; TWCY_WESTERNSAMOA = 1045; TWCY_YEMEN = 1046; TWCY_YUGOSLAVIA = 0038; TWCY_ZAIRE = 0243; TWCY_ZAMBIA = 0260; TWCY_ZIMBABWE = 0263; TWCY_ALBANIA = 0355; TWCY_ARMENIA = 0374; TWCY_AZERBAIJAN = 0994; TWCY_BELARUS = 0375; TWCY_BOSNIAHERZGO = 0387; TWCY_CAMBODIA = 0855; TWCY_CROATIA = 0385; TWCY_CZECHREPUBLIC = 0420; TWCY_DIEGOGARCIA = 0246; TWCY_ERITREA = 0291; TWCY_ESTONIA = 0372; TWCY_GEORGIA = 0995; TWCY_LATVIA = 0371; TWCY_LESOTHO = 0266; TWCY_LITHUANIA = 0370; TWCY_MACEDONIA = 0389; TWCY_MAYOTTEIS = 0269; TWCY_MOLDOVA = 0373; TWCY_MYANMAR = 0095; TWCY_NORTHKOREA = 0850; TWCY_PUERTORICO = 0787; TWCY_RUSSIA = 0007; TWCY_SERBIA = 0381; TWCY_SLOVAKIA = 0421; TWCY_SLOVENIA = 0386; TWCY_SOUTHKOREA = 0082; TWCY_UKRAINE = 0380; TWCY_USVIRGINIS = 0340; TWCY_VIETNAM = 0084; Const. // Flags for languages. TWLG_DAN = 000; // Danish. TWLG_DUT = 001; // Dutch. TWLG_ENG = 002; // English. TWLG_FCF = 003; // French Canadian. TWLG_FIN = 004; // Finnish. TWLG_FRN = 005; // French. TWLG_GER = 006; // German. TWLG_ICE = 007; // Icelandic. TWLG_ITN = 008; // Italian. TWLG_NOR = 009; // Norwegian. TWLG_POR = 010; // Portuguese. TWLG_SPA = 011; // Spannish. TWLG_SWE = 012; // Swedish. TWLG_USA = 013; TWLG_AFRIKAANS = 014; TWLG_ALBANIA = 015; TWLG_ARABIC = 016; TWLG_ARABIC_ALGERIA = 017; TWLG_ARABIC_BAHRAIN = 018; TWLG_ARABIC_EGYPT = 019; TWLG_ARABIC_IRAQ = 020; TWLG_ARABIC_JORDAN = 021; TWLG_ARABIC_KUWAIT = 022; TWLG_ARABIC_LEBANON = 023; TWLG_ARABIC_LIBYA = 024; TWLG_ARABIC_MOROCCO = 025; TWLG_ARABIC_OMAN = 026; TWLG_ARABIC_QATAR = 027; TWLG_ARABIC_SAUDIARABIA = 028; TWLG_ARABIC_SYRIA = 029; TWLG_ARABIC_TUNISIA = 030; TWLG_ARABIC_UAE = 031; // United Arabic Emirates. TWLG_ARABIC_YEMEN = 032; TWLG_BASQUE = 033; TWLG_BYELORUSSIAN = 034; TWLG_BULGARIAN = 035; TWLG_CATALAN = 036; TWLG_CHINESE = 037; TWLG_CHINESE_HONGKONG = 038; TWLG_CHINESE_PRC = 039; // People's Republic of China. TWLG_CHINESE_SINGAPORE = 040; TWLG_CHINESE_SIMPLIFIED = 041; TWLG_CHINESE_TAIWAN = 042; TWLG_CHINESE_TRADITIONAL = 043; TWLG_CROATIA = 044; TWLG_CZECH = 045; TWLG_DANISH = TWLG_DAN; TWLG_DUTCH = TWLG_DUT; TWLG_DUTCH_BELGIAN = 046; TWLG_ENGLISH = TWLG_ENG; TWLG_ENGLISH_AUSTRALIAN = 047; TWLG_ENGLISH_CANADIAN = 048; TWLG_ENGLISH_IRELAND = 049; TWLG_ENGLISH_NEWZEALAND = 050; TWLG_ENGLISH_SOUTHAFRICA = 051; TWLG_ENGLISH_UK = 052; TWLG_ENGLISH_USA = TWLG_USA; TWLG_ESTONIAN = 053; TWLG_FAEROESE = 054; TWLG_FARSI = 055; TWLG_FINNISH = TWLG_FIN; TWLG_FRENCH = TWLG_FRN; TWLG_FRENCH_BELGIAN = 056; TWLG_FRENCH_CANADIAN = TWLG_FCF; TWLG_FRENCH_LUXEMBOURG = 057; TWLG_FRENCH_SWISS = 058; TWLG_GERMAN = TWLG_GER; TWLG_GERMAN_AUSTRIAN = 059; TWLG_GERMAN_LUXEMBOURG = 060; TWLG_GERMAN_LIECHTENSTEIN = 061; TWLG_GERMAN_SWISS = 062; TWLG_GREEK = 063; TWLG_HEBREW = 064; TWLG_HUNGARIAN = 065; TWLG_ICELANDIC = TWLG_ICE; TWLG_INDONESIAN = 066; TWLG_ITALIAN = TWLG_ITN; TWLG_ITALIAN_SWISS = 067; TWLG_JAPANESE = 068; TWLG_KOREAN = 069; TWLG_KOREAN_JOHAB = 070; TWLG_LATVIAN = 071; TWLG_LITHUANIAN = 072; TWLG_NORWEGIAN = TWLG_NOR; TWLG_NORWEGIAN_BOKMAL = 073; TWLG_NORWEGIAN_NYNORSK = 074; TWLG_POLISH = 075; TWLG_PORTUGUESE = TWLG_POR; TWLG_PORTUGUESE_BRAZIL = 076; TWLG_ROMANIAN = 077; TWLG_RUSSIAN = 078; TWLG_SERBIAN_LATIN = 079; TWLG_SLOVAK = 080; TWLG_SLOVENIAN = 081; TWLG_SPANISH = TWLG_SPA; TWLG_SPANISH_MEXICAN = 082; TWLG_SPANISH_MODERN = 083; TWLG_SWEDISH = TWLG_SWE; TWLG_THAI = 084; TWLG_TURKISH = 085; TWLG_UKRANIAN = 086; TWLG_ASSAMESE = 087; TWLG_BENGALI = 088; TWLG_BIHARI = 089; TWLG_BODO = 090; TWLG_DOGRI = 091; TWLG_GUJARATI = 092; TWLG_HARYANVI = 093; TWLG_HINDI = 094; TWLG_KANNADA = 095; TWLG_KASHMIRI = 096; TWLG_MALAYALAM = 097; TWLG_MARATHI = 098; TWLG_MARWARI = 099; TWLG_MEGHALAYAN = 100; TWLG_MIZO = 101; TWLG_NAGA = 102; TWLG_ORISSI = 103; TWLG_PUNJABI = 104; TWLG_PUSHTU = 105; TWLG_SERBIAN_CYRILLIC = 106; TWLG_SIKKIMI = 107; TWLG_SWEDISH_FINLAND = 108; TWLG_TAMIL = 109; TWLG_TELUGU = 110; TWLG_TRIPURI = 111; TWLG_URDU = 112; TWLG_VIETNAMESE = 113; Const. TWRC_SUCCESS = 0; TWRC_FAILURE = 1; // Application may get TW_STATUS for. // info on failure. TWRC_CHECKSTATUS = 2; // tried hard to get the status. TWRC_CANCEL = 3; TWRC_DSEVENT = 4; TWRC_NOTDSEVENT = 5; TWRC_XFERDONE = 6; TWRC_ENDOFLIST = 7; // After MSG_GETNEXT if nothing left. TWRC_INFONOTSUPPORTED = 8; TWRC_DATANOTAVAILABLE = 9; Const. TWON_ONEVALUE = $05; // indicates TW_ONEVALUE container. TWON_DONTCARE8 = $FF; Const. ICAP_XFERMECH = $0103; Const. TWTY_UINT16 = $0004; // Means: item is a TW_UINT16. Const. // ICAP_XFERMECH values (SX_ means Setup XFer). TWSX_NATIVE = 0; TWSX_FILE = 1; TWSX_MEMORY = 2; TWSX_FILE2 = 3; Type. TW_UINT16 = WORD; // unsigned short TW_UINT16. PTW_UINT16 = ^TW_UINT16; TTWUInt16 = TW_UINT16; PTWUInt16 = pTW_UINT16; Type. TW_BOOL = WORDBOOL; // unsigned short TW_BOOL. PTW_BOOL = ^TW_BOOL; TTWBool = TW_BOOL; PTWBool = pTW_BOOL; Type. TW_STR32 = array[0..33] of Char; // char TW_STR32[34]. PTW_STR32 = ^TW_STR32; TTWStr32 = TW_STR32; PTWStr32 = pTW_STR32; Type. TW_STR255 = array[0..255] of Char; // char TW_STR255[256]. PTW_STR255 = ^TW_STR255; TTWStr255 = TW_STR255; PTWStr255 = pTW_STR255; Type. TW_INT16 = SmallInt; // short TW_INT16. PTW_INT16 = ^TW_INT16; TTWInt16 = TW_INT16; PTWInt16 = pTW_INT16; Type. TW_UINT32 = ULONG; // unsigned long TW_UINT32. PTW_UINT32 = ^TW_UINT32; TTWUInt32 = TW_UINT32; PTWUInt32 = pTW_UINT32; Type. TW_HANDLE = THandle; TTWHandle = TW_HANDLE; TW_MEMREF = Pointer; TTWMemRef = TW_MEMREF; Type. // DAT_PENDINGXFERS. Used with MSG_ENDXFER to indicate additional. // data. TW_PENDINGXFERS = packed record. Count: TW_UINT16; Case Boolean of. False: (EOJ: TW_UINT32); True: (Reserved: TW_UINT32); End; PTW_PENDINGXFERS = ^TW_PENDINGXFERS; TTWPendingXFERS = TW_PENDINGXFERS; PTWPendingXFERS = pTW_PENDINGXFERS; Type. // DAT_EVENT. For passing events down from the application to the DS. TW_EVENT = packed record. PEvent: TW_MEMREF; // Windows pMSG or Mac pEvent. TWMessage: TW_UINT16; // TW msg from data source, e.g. // MSG_XFERREADY. End; PTW_EVENT = ^TW_EVENT; TTWEvent = TW_EVENT; PTWEvent = pTW_EVENT; Type. // TWON_ONEVALUE. Container for one value. TW_ONEVALUE = packed record. ItemType: TW_UINT16; Item: TW_UINT32; End; PTW_ONEVALUE = ^TW_ONEVALUE; TTWOneValue = TW_ONEVALUE; PTWOneValue = pTW_ONEVALUE; Type. // DAT_CAPABILITY. Used by application to get/set capability from/in. // a data source. TW_CAPABILITY = packed record. Cap: TW_UINT16; // id of capability to set or get, e.g. // CAP_BRIGHTNESS. ConType: TW_UINT16; // TWON_ONEVALUE, _RANGE, _ENUMERATION or. // _ARRAY. HContainer: TW_HANDLE; // Handle to container of type Dat. End; PTW_CAPABILITY = ^TW_CAPABILITY; TTWCapability = TW_CAPABILITY; PTWCapability = pTW_CAPABILITY; Type. // DAT_STATUS. Application gets detailed status info from a data. // source with this. TW_STATUS = packed record. ConditionCode: TW_UINT16; // Any TWCC_xxx constant. Reserved: TW_UINT16; // Future expansion space. End; PTW_STATUS = ^TW_STATUS; TTWStatus = TW_STATUS; PTWStatus = pTW_STATUS; Type. // No DAT needed. Used to manage memory buffers. TW_MEMORY = packed record. Flags: TW_UINT32; // Any combination of the TWMF_ constants. Length: TW_UINT32; // Number of bytes stored in buffer TheMem. TheMem: TW_MEMREF; // Pointer or handle to the allocated memory. // buffer. End; PTW_MEMORY = ^TW_MEMORY; TTWMemory = TW_MEMORY; PTWMemory = pTW_MEMORY; Const. // ICAP_IMAGEFILEFORMAT values (FF_means File Format. TWFF_TIFF = 0; // Tagged Image File Format. TWFF_PICT = 1; // Macintosh PICT. TWFF_BMP = 2; // Windows Bitmap. TWFF_XBM = 3; // X-Windows Bitmap. TWFF_JFIF = 4; // JPEG File Interchange Format. TWFF_FPX = 5; // Flash Pix. TWFF_TIFFMULTI = 6; // Multi-page tiff file. TWFF_PNG = 7; // Portable Network Graphic. TWFF_SPIFF = 8; TWFF_EXIF = 9; Type. // DAT_SETUPFILEXFER. Sets up DS to application data transfer via a. // file. TW_SETUPFILEXFER = packed record. FileName: TW_STR255; Format: TW_UINT16; // Any TWFF_xxx constant. VRefNum: TW_INT16; // Used for Mac only. End; PTW_SETUPFILEXFER = ^TW_SETUPFILEXFER; TTWSetupFileXFER = TW_SETUPFILEXFER; PTWSetupFileXFER = pTW_SETUPFILEXFER; Type. // DAT_SETUPFILEXFER2. Sets up DS to application data transfer via a. // file. }. TW_SETUPFILEXFER2 = packed record. FileName: TW_MEMREF; // Pointer to file name text. FileNameType: TW_UINT16; // TWTY_STR1024 or TWTY_UNI512. Format: TW_UINT16; // Any TWFF_xxx constant. VRefNum: TW_INT16; // Used for Mac only. ParID: TW_UINT32; // Used for Mac only. End; PTW_SETUPFILEXFER2 = ^TW_SETUPFILEXFER2; TTWSetupFileXFER2 = TW_SETUPFILEXFER2; PTWSetupFileXFER2 = pTW_SETUPFILEXFER2; Type. // DAT_SETUPMEMXFER. Sets up Data Source to application data. // transfer via a memory buffer. TW_SETUPMEMXFER = packed record. MinBufSize: TW_UINT32; MaxBufSize: TW_UINT32; Preferred: TW_UINT32; End; PTW_SETUPMEMXFER = ^TW_SETUPMEMXFER; TTWSetupMemXFER = TW_SETUPMEMXFER; PTWSetupMemXFER = pTW_SETUPMEMXFER; Type. TW_VERSION = packed record. MajorNum: TW_UINT16; // Major revision number of the software. MinorNum: TW_UINT16; // Incremental revision number of the. // software. Language: TW_UINT16; // e.g. TWLG_SWISSFRENCH. Country: TW_UINT16; // e.g. TWCY_SWITZERLAND. Info: TW_STR32; // e.g. "1.0b3 Beta release". End; PTW_VERSION = ^TW_VERSION; PTWVersion = pTW_VERSION; TTWVersion = TW_VERSION; Type. TW_IDENTITY = packed record. Id: TW_UINT32; // Unique number. In Windows, // application hWnd. Version: TW_VERSION; // Identifies the piece of code. ProtocolMajor: TW_UINT16; // Application and DS must set to. // TWON_PROTOCOLMAJOR. ProtocolMinor: TW_UINT16; // Application and DS must set to. // TWON_PROTOCOLMINOR. SupportedGroups: TW_UINT32; // Bit field OR combination of DG_ // constants. Manufacturer: TW_STR32; // Manufacturer name, e.g. // "Hewlett-Packard". ProductFamily: TW_STR32; // Product family name, e.g. // "ScanJet". ProductName: TW_STR32; // Product name, e.g. "ScanJet Plus". End; PTW_IDENTITY = ^TW_IDENTITY; Type. // DAT_USERINTERFACE. Coordinates UI between application and data. // source. TW_USERINTERFACE = packed record. ShowUI: TW_BOOL; // TRUE if DS should bring up its UI. ModalUI: TW_BOOL; // For Mac only - true if the DS's UI is modal. HParent: TW_HANDLE; // For Windows only - Application handle. End; PTW_USERINTERFACE = ^TW_USERINTERFACE; TTWUserInterface = TW_USERINTERFACE; PTWUserInterface = pTW_USERINTERFACE; //////////////////////////////////////////////////////////////////////// // // // END OF TWAIN TYPES AND CONSTANTS // // // //////////////////////////////////////////////////////////////////////// Const. TWAIN_DLL_Name = 'TWAIN_32.DLL'; DSM_Entry_Name = 'DSM_Entry'; Ini_File_Name = 'WIN.INI'; CrLf = #13 + #10; Resourcestring // Errorstrings: ERR_DSM_ENTRY_NOT_FOUND = 'Unable to find the entry of the Data ' + 'Source Manager in: TWAIN_32.DLL'; ERR_TWAIN_NOT_LOADED = 'Unable to load or find: TWAIN_32.DLL'; ERR_DSM_CALL_FAILED = 'A call to the Data Source Manager failed ' + 'in module %s'; ERR_UNKNOWN = 'A call to the Data Source Manager failed ' + 'in module %s: Code %.04x'; ERR_DSM_OPEN = 'Unable to close the Data Source Manager. ' + 'Maybe a source is still in use'; ERR_STATUS = 'Unable to get the status'; ERR_DSM = 'Data Source Manager error in module %s:' + CrLf + '%s'; ERR_DS = 'Data Source error in module %s:' + CrLf + '%s'; Type. ETwainError = class(Exception); TImageType = (ffTIFF, ffPICT, ffBMP, ffXBM, ffJFIF, ffFPX, FfTIFFMULTI, ffPNG, ffSPIFF, ffEXIF, ffUNKNOWN); TTransferType = (xfNative, xfMemory, xfFile); TLanguageType = (lgDutch, lgEnglish, LgFrench, lgGerman, LgAmerican, lgItalian, LgSpanish, lgNorwegian, LgFinnish, lgDanish, LgRussian, lgPortuguese, LgSwedish, lgPolish, LgGreek, lgTurkish); TCountryType = (ctNetherlands, ctEngland, CtFrance, ctGermany, CtUSA, ctSpain, CtItaly, ctDenmark, CtFinland, ctNorway, CtRussia, ctPortugal, CtSweden, ctPoland, CtGreece, ctTurkey); TTWAIN = class(TComponent). Private. // Private declarations. FBitmap: TBitmap; // the actual bmp used for. // scanning, must be. // removed. HDSMDLL: HMODULE; // = 0, the library handle: // will stay global. AppId: TW_IDENTITY; // our (Application) ID. // (may stay global). DsId: TW_IDENTITY; // Data Source ID (will. // become member of DS. // class). FhWnd: HWND; // = 0, maybe will be. // removed, use. // application.handle. // instead. FXfer: TTransferType; // = xfNative; BDataSourceManagerOpen: Boolean; // = False, flag, may stay. // global. BDataSourceOpen: Boolean; // = False, will become. // member of DS class. BDataSourceEnabled: Boolean; // = False, will become. // member of DS class. FScanReady: TNotifyEvent; // notifies that the scan. // is ready. SDefaultSource: string; // remember old data source. FOldOnMessageHandler: TMessageEvent; // Save old OnMessage event. FShowUI: Boolean; // Show User Interface. FSetupFileXfer: TW_SETUPFILEXFER; // Not used yet. FSetupMemoryXfer: TW_SETUPMEMXFER; // Not used yet. FMemory: TW_MEMORY; // Not used yet. Function fLoadTwain: Boolean; Procedure fUnloadTwain; Function fNativeXfer: Boolean; Function fMemoryXfer: Boolean; // Not used yet. Function fFileXfer: Boolean; // Not used yet. Function fGetDestination: TTransferType; Procedure fSetDestination(dest: TTransferType); Function Condition2String(ConditionCode: TW_UINT16): string; Procedure RaiseLastDataSourceManagerCondition(module: string); Procedure RaiseLastDataSourceCondition(module: string); Procedure TwainCheckDataSourceManager(res: TW_UINT16; Module: string); Procedure TwainCheckDataSource(res: TW_UINT16; Module: string); Function CallDataSourceManager(pOrigin: pTW_IDENTITY; DG: TW_UINT32; DAT: TW_UINT16; MSG: TW_UINT16; PData: TW_MEMREF): TW_UINT16; Function CallDataSource(DG: TW_UINT32; DAT: TW_UINT16; MSG: TW_UINT16; PData: TW_MEMREF): TW_UINT16; Procedure XferMech; Procedure fSetProductname(pn: string); Function fGetProductname: string; Procedure fSetManufacturer(mf: string); Function fGetManufacturer: string; Procedure fSetProductFamily(pf: string); Function fGetProductFamily: string; Procedure fSetLanguage(lg: TLanguageType); Function fGetLanguage: TLanguageType; Procedure fSetCountry(ct: TCountryType); Function fGetCountry: TCountryType; Procedure SaveDefaultSourceEntry; Procedure RestoreDefaultSourceEntry; Procedure fSetCursor(cr: TCursor); Function fGetCursor: TCursor; Procedure fSetImageType(it: TImageType); Function fGetImageType: TImageType; Procedure fSetFilename(fn: string); Function fGetFilename: string; Procedure fSetVersionInfo(vi: string); Function fGetVersionInfo: string; Procedure fSetVersionMajor(vmaj: WORD); Procedure fSetVersionMinor(vmin: WORD); Function fGetVersionMajor: WORD; Function fGetVersionMinor: WORD; Protected. Procedure ScanReady; dynamic; // Notifies when image transfer is. // ready. Procedure fNewOnMessageHandler(var Msg: TMsg; Var Handled: Boolean); virtual; Public. // Public declarations. Constructor Create(AOwner: TComponent); override; Destructor Destroy; override; Procedure Acquire(aBmp: TBitmap); Procedure OpenDataSource; Procedure CloseDataSource; Procedure InitTWAIN; Procedure OpenDataSourceManager; Procedure CloseDataSourceManager; Function IsDataSourceManagerOpen: Boolean; Procedure EnableDataSource; // Procedure TWEnableDSUIOnly(ShowUI : Boolean); Procedure DisableDataSource; Function IsDataSourceOpen: Boolean; Function IsDataSourceEnabled: Boolean; Procedure SelectDataSource; Function IsTwainDriverAvailable: Boolean; Function ProcessSourceMessage(var Msg: TMsg): Boolean; Published. // Published declarations. // Properties, methods. Property Destination: TTransferType. Read fGetDestination write fSetDestination; Property TwainDriverFound: Boolean. Read IsTwainDriverAvailable; Property Productname: string. Read fGetProductname write fSetProductname; Property Manufacturer: string. Read fGetManufacturer write fSetManufacturer; Property ProductFamily: string. Read fGetProductFamily write fSetProductFamily; Property Language: TLanguageType. Read fGetLanguage write fSetLanguage; Property Country: TCountryType. Read fGetCountry write fSetCountry; Property ShowUI: Boolean. Read fShowUI write fShowUI; Property Cursor: TCursor. Read fGetCursor write fSetCursor; Property FileFormat: TImageType. Read fGetImageType write fSetImageType; Property Filename: string. Read fGetFilename write fSetFilename; Property VersionInfo: string. Read fGetVersionInfo write fSetVersionInfo; Property VersionMajor: WORD. Read fGetVersionMajor write fSetVersionMajor; Property VersionMinor: WORD. Read fGetVersionMinor write fSetVersionMinor; // Events. Property OnScanReady: TNotifyEvent. Read fScanReady write fScanReady; End; Procedure Register; Type. DSMENTRYPROC = function(pOrigin: pTW_IDENTITY; PDest: pTW_IDENTITY; DG: TW_UINT32; DAT: TW_UINT16; MSG: TW_UINT16; PData: TW_MEMREF): TW_UINT16; stdcall; TDSMEntryProc = DSMENTRYPROC; Type. DSENTRYPROC = function(pOrigin: pTW_IDENTITY; DG: TW_UINT32; DAT: TW_UINT16; MSG: TW_UINT16; PData: TW_MEMREF): TW_UINT16; stdcall; TDSEntryProc = DSENTRYPROC; Var. DS_Entry: TDSEntryProc = nil; // Initialize. DSM_Entry: TDSMEntryProc = nil; // Initialize. Implementation. //--------------------------------------------------------------------- Constructor TTWAIN.Create(AOwner: TComponent); Begin. Inherited Create(AOwner); // Initialize variables. AppID.Version.Info := 'Twain component'; AppID.Version.Country := TWCY_USA; AppID.Version.Language := TWLG_USA; AppID.Productname := 'SimpelSoft TWAIN module'; // This is the one that you are. // going to see in the UI. AppID.ManuFacturer := 'SimpelSoft'; AppID.ProductFamily := 'SimpelSoft components'; AppID.Version.MajorNum := 1; AppID.Version.MinorNum := 0; // appID.ID := Application.Handle; FSetFilename('C:\TWAIN.BMP'); // fSetupFileXfer.FileName := 'C:\TWAIN.TMP': FSetImageType(ffBMP); // fSetupFileXfer.Format := TWFF_BMP; // fSetupFileXfer.VRefNum := xx; // For Mac. // fSetupMemoryXfer.MinBufSize := xx; // fSetupMemoryXfer.MaxBufSize := yy; // fSetupMemoryXfer.Preferred := zz; FMemory.Flags := TWFF_BMP; // fMemory.Length := SizeOf(Mem); // fMemory.TheMem := @Mem; // fhWnd := Application.Handle; FShowUI := True; HDSMDLL := 0; SDefaultSource := ''; FXfer := xfNative; BDataSourceManagerOpen := False; BDataSourceOpen := False; BDataSourceEnabled := False; End; //--------------------------------------------------------------------- Destructor TTWAIN.Destroy; Begin. If bDataSourceEnabled then. DisableDataSource; If bDataSourceOpen then. CloseDataSource; If bDataSourceManagerOpen then. CloseDataSourceManager; FUnLoadTwain; // Loose the TWAIN_32.DLL. If sDefaultSource <> '' then. RestoreDefaultSourceEntry; // Write old entry back in WIN.INI. Application.OnMessage := fOldOnMessageHandler; // Restore old OnMessage. // handler. Inherited Destroy; End; //--------------------------------------------------------------------- Function TTWAIN.fGetVersionMajor: WORD; Begin. Result := appID.Version.MajorNum; End; //--------------------------------------------------------------------- Function TTWAIN.fGetVersionMinor: WORD; Begin. Result := appID.Version.MinorNum; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetVersionMajor(vmaj: WORD); Begin. AppID.Version.MajorNum := vmaj; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetVersionMinor(vmin: WORD); Begin. AppID.Version.MinorNum := vmin; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetVersionInfo(vi: string); Var. I, L: Integer; Begin. FillChar(appID.Version.Info, SizeOf(appID.Version.Info), #0); L := Length(vi); If L = 0 then. Exit; If L > 32 then. L := 32; For I := 1 to L do. AppID.Version.Info[I - 1] := vi[I]; End; //--------------------------------------------------------------------- Function TTWAIN.fGetVersionInfo: string; Var. I: Integer; Begin. Result := ''; I := 0; If appID.Version.Info[I] <> #0 then. Repeat. Result := Result + appID.Version.Info[I]; Inc(I); Until appID.Version.Info[I] = #0; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetImageType(it: TImageType); Begin. FSetupFileXfer.Format := TWFF_BMP; // Initialize. FMemory.Flags := TWFF_BMP; // Initialize. Case it of. FfTIFF: Begin. FSetupFileXfer.Format := TWFF_TIFF; FMemory.Flags := TWFF_TIFF; End; FfPICT: Begin. FSetupFileXfer.Format := TWFF_PICT; FMemory.Flags := TWFF_PICT; End; FfBMP: Begin. FSetupFileXfer.Format := TWFF_BMP; FMemory.Flags := TWFF_BMP; End; FfXBM: Begin. FSetupFileXfer.Format := TWFF_XBM; FMemory.Flags := TWFF_XBM; End; FfJFIF: Begin. FSetupFileXfer.Format := TWFF_JFIF; FMemory.Flags := TWFF_JFIF; End; FfFPX: Begin. FSetupFileXfer.Format := TWFF_FPX; FMemory.Flags := TWFF_FPX; End; FfTIFFMULTI: Begin. FSetupFileXfer.Format := TWFF_TIFFMULTI; FMemory.Flags := TWFF_TIFFMULTI; End; FfPNG: Begin. FSetupFileXfer.Format := TWFF_PNG; FMemory.Flags := TWFF_PNG; End; FfSPIFF: Begin. FSetupFileXfer.Format := TWFF_SPIFF; FMemory.Flags := TWFF_SPIFF; End; FfEXIF: Begin. FSetupFileXfer.Format := TWFF_EXIF; FMemory.Flags := TWFF_EXIF; End; End; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetFilename(fn: string); Var. L, I: Integer; Begin. FillChar(fSetupFileXfer.FileName, SizeOf(fSetupFileXfer.Filename), #0); L := Length(fn); If L > 0 then. For I := 1 to L do. FSetupFileXfer.Filename[I - 1] := fn[I]; End; //--------------------------------------------------------------------- Function TTWAIN.fGetFilename: string; Var. I: Integer; Begin. Result := ''; I := 0; If fSetupFileXfer.Filename[I] <> #0 then. Repeat. Result := Result + fSetupFileXfer.Filename[I]; Inc(I); Until fSetupFileXfer.Filename[I] = #0; End; //--------------------------------------------------------------------- Function TTWAIN.fGetImageType: TImageType; Begin. Result := ffUNKNOWN; // Initialize. Case fSetupFileXfer.Format of. TWFF_TIFF: Result := ffTIFF; TWFF_PICT: Result := ffPICT; TWFF_BMP: Result := ffBMP; TWFF_XBM: Result := ffXBM; TWFF_JFIF: Result := ffJFIF; TWFF_FPX: Result := ffFPX; TWFF_TIFFMULTI: Result := ffTIFFMULTI; TWFF_PNG: Result := ffPNG; TWFF_SPIFF: Result := ffSPIFF; TWFF_EXIF: Result := ffEXIF; End; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetCursor(cr: TCursor); Begin. Screen.Cursor := cr; End; //--------------------------------------------------------------------- Function TTWAIN.fGetCursor: TCursor; Begin. Result := Screen.Cursor; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetCountry(ct: TCountryType); Begin. Case ct of. CtDenmark: appID.Version.Country := TWCY_DENMARK; CtNetherlands: appID.Version.Country := TWCY_NETHERLANDS; CtEngland: appID.Version.Country := TWCY_BRITAIN; CtFinland: appID.Version.Country := TWCY_FINLAND; CtFrance: appID.Version.Country := TWCY_FRANCE; CtGermany: appID.Version.Country := TWCY_GERMANY; CtItaly: appID.Version.Country := TWCY_ITALY; CtNorWay: appID.Version.Country := TWCY_NORWAY; CtSpain: appID.Version.Country := TWCY_SPAIN; CtUSA: appID.Version.Country := TWCY_USA; CtRussia: appID.Version.Country := TWCY_RUSSIA; CtPortugal: appID.Version.Country := TWCY_PORTUGAL; CtSweden: appID.Version.Country := TWCY_SWEDEN; CtPoland: appID.Version.Country := TWCY_POLAND; CtGreece: appID.Version.Country := TWCY_GREECE; CtTurkey: appID.Version.Country := TWCY_TURKEY; End; End; //--------------------------------------------------------------------- Function TTWAIN.fGetCountry: TCountryType; Begin. Result := ctNetherlands; // Initialize. Case appID.Version.Country of. TWCY_NETHERLANDS: Result := ctNetherlands; TWCY_DENMARK: Result := ctDenmark; TWCY_BRITAIN: Result := ctEngland; TWCY_FINLAND: Result := ctFinland; TWCY_FRANCE: Result := ctFrance; TWCY_GERMANY: Result := ctGermany; TWCY_NORWAY: Result := ctNorway; TWCY_ITALY: Result := ctItaly; TWCY_SPAIN: Result := ctSpain; TWCY_USA: Result := ctUSA; TWCY_RUSSIA: Result := ctRussia; TWCY_PORTUGAL: Result := ctPortugal; TWCY_SWEDEN: Result := ctSweden; TWCY_TURKEY: Result := ctTurkey; TWCY_GREECE: Result := ctGreece; TWCY_POLAND: Result := ctPoland; End; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetLanguage(lg: TLanguageType); Begin. Case lg of. LgDanish: appID.Version.Language := TWLG_DAN; LgDutch: appID.Version.Language := TWLG_DUT; LgEnglish: appID.Version.Language := TWLG_ENG; LgFinnish: appID.Version.Language := TWLG_FIN; LgFrench: appID.Version.Language := TWLG_FRN; LgGerman: appID.Version.Language := TWLG_GER; LgNorwegian: appID.Version.Language := TWLG_NOR; LgItalian: appID.Version.Language := TWLG_ITN; LgSpanish: appID.Version.Language := TWLG_SPA; LgAmerican: appID.Version.Language := TWLG_USA; LgRussian: appID.Version.Language := TWLG_RUSSIAN; LgPortuguese: appID.Version.Language := TWLG_POR; LgSwedish: appID.Version.Language := TWLG_SWE; LgPolish: appID.Version.Language := TWLG_POLISH; LgGreek: appID.Version.Language := TWLG_GREEK; LgTurkish: appID.Version.Language := TWLG_TURKISH; End; End; //--------------------------------------------------------------------- Function TTWAIN.fGetLanguage: TLanguageType; Begin. Result := lgDutch; // Initialize. Case appID.Version.Language of. TWLG_DAN: Result := lgDanish; TWLG_DUT: Result := lgDutch; TWLG_ENG: Result := lgEnglish; TWLG_FIN: Result := lgFinnish; TWLG_FRN: Result := lgFrench; TWLG_GER: Result := lgGerman; TWLG_ITN: Result := lgItalian; TWLG_NOR: Result := lgNorwegian; TWLG_SPA: Result := lgSpanish; TWLG_USA: Result := lgAmerican; TWLG_RUSSIAN: Result := lgRussian; TWLG_POR: Result := lgPortuguese; TWLG_SWE: Result := lgSwedish; TWLG_POLISH: Result := lgPolish; TWLG_GREEK: Result := lgGreek; TWLG_TURKISH: Result := lgTurkish; End; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetManufacturer(mf: string); Var. I, L: Integer; Begin. FillChar(appID.Manufacturer, SizeOf(appID.Manufacturer), #0); L := Length(mf); If L = 0 then. Exit; If L > 32 then. L := 32; For I := 1 to L do. AppID.Manufacturer[I - 1] := mf[I]; End; //--------------------------------------------------------------------- Function TTWAIN.fGetManufacturer: string; Var. I: Integer; Begin. Result := ''; I := 0; If appID.Manufacturer[I] <> #0 then. Repeat. Result := Result + appID.Manufacturer[I]; Inc(I); Until appID.Manufacturer[I] = #0; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetProductname(pn: string); Var. I, L: Integer; Begin. FillChar(appID.Productname, SizeOf(appID.Productname), #0); L := Length(pn); If L = 0 then. Exit; If L > 32 then. L := 32; For I := 1 to L do. AppID.Productname[I - 1] := pn[I]; End; //--------------------------------------------------------------------- Function TTWAIN.fGetProductName: string; Var. I: Integer; Begin. Result := ''; I := 0; If appID.ProductName[I] <> #0 then. Repeat. Result := Result + appID.ProductName[I]; Inc(I); Until appID.ProductName[I] = #0; End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetProductFamily(pf: string); Var. I, L: Integer; Begin. FillChar(appID.ProductFamily, SizeOf(appID.ProductFamily), #0); L := Length(pf); If L = 0 then. Exit; If L > 32 then. L := 32; For I := 1 to L do. AppID.ProductFamily[I - 1] := pf[I]; End; //--------------------------------------------------------------------- Function TTWAIN.fGetProductFamily: string; Var. I: Integer; Begin. Result := ''; I := 0; If appID.ProductFamily[I] <> #0 then. Repeat. Result := Result + appID.ProductFamily[I]; Inc(I); Until appID.ProductFamily[I] = #0; End; //--------------------------------------------------------------------- Procedure TTWAIN.ScanReady; Begin. If Assigned(fScanReady) then. FScanReady(Self); End; //--------------------------------------------------------------------- Procedure TTWAIN.fSetDestination(dest: TTransferType); Begin. FXfer := dest; End; //--------------------------------------------------------------------- Function TTWAIN.fGetDestination: TTransferType; Begin. Result := fXfer; End; //---------------------------------------------------------------------- Function UpCaseStr(const s: string): string; Var. I, L: Integer; Begin. Result := s; L := Length(Result); If L > 0 then. Begin. For I := 1 to L do. Result[I] := UpCase(Result[I]); End; // Result := s; // Minor bug, changed 23/05/03. End; //---------------------------------------------------------------------- // Internal routine. //---------------------------------------------------------------------- Function GetWinDir: string; Var. WD: array[0..MAX_PATH] of Char; L: WORD; Begin. WD := #0; GetWindowsDirectory(WD, MAX_PATH); Result := StrPas(WD); L := Length(Result); // Remove the "\" if any. If L > 0 then. If Result[L] = '\' then. Result := Copy(Result, 1, L - 1); End; //---------------------------------------------------------------------- // Internal routine. //---------------------------------------------------------------------- Procedure FileFindSubDir(const ffsPath: string; Var ffsBo: Boolean); Var. Sr: TSearchRec; Begin. If FindFirst(ffsPath + '\*.*', faAnyFile, sr) = 0 then. Repeat. If sr.Name <> '.' then. If sr.Name <> '..' then. If sr.Attr and faDirectory = faDirectory then. Begin. FileFindSubDir(ffsPath + '\' + sr.name, ffsBo); End. Else. Begin. If UpCaseStr(ExtractFileExt(sr.Name)) = '.DS' then. If UpCaseStr(sr.Name) <> 'WIATWAIN.DS' then. FfsBo := True; End; Until FindNext(sr) <> 0; // Error if SysUtils is not added in front of FindClose! SysUtils.FindClose(sr); End; //---------------------------------------------------------------------- Function TTWAIN.IsTwainDriverAvailable: Boolean; Var. Sr: TSearchRec; S: string; Bo: Boolean; Begin. // This routine might not be failsafe! // Under circumstances the twain drivers found in the directory. // %WINDOWS%\TWAIN_32\*.ds and below could be not properly installed! Bo := False; S := GetWinDir + '\TWAIN_32'; FileFindSubDir(s, Bo); Result := Bo; End; //--------------------------------------------------------------------- Procedure TTWAIN.SaveDefaultSourceEntry; Var. WinIni: TIniFile; Begin. If sDefaultSource <> '' then. Exit; WinIni := TIniFile.Create(Ini_File_Name); SDefaultSource := WinIni.ReadString('TWAIN', 'DEFAULT SOURCE', ''); WinIni.Free; End; //--------------------------------------------------------------------- Procedure TTWAIN.RestoreDefaultSourceEntry; Var. WinIni: TIniFile; Begin. If sDefaultSource = '' then. Exit; // It is not changed by this component or it is not there... WinIni := TIniFile.Create(Ini_File_Name); WinIni.WriteString('TWAIN', 'DEFAULT SOURCE', sDefaultSource); WinIni.Free; SDefaultSource := ''; End; //--------------------------------------------------------------------- Procedure TTWAIN.InitTWAIN; Begin. AppID.ID := Application.Handle; FHwnd := Application.Handle; FLoadTwain; // Load TWAIN_32.DLL. FOldOnMessageHandler := Application.OnMessage; // Save old pointer. Application.OnMessage := fNewOnMessageHandler; // Set to our handler. OpenDataSourceManager; // Open DS. End; //--------------------------------------------------------------------- Function TTWAIN.fLoadTwain: Boolean; Begin. If HDSMDLL = 0 then. Begin. HDSMDLL := LoadLibrary(TWAIN_DLL_Name); DSM_Entry := GetProcAddress(HDSMDLL, DSM_Entry_Name); // if @DSM_Entry = nil then. // raise ETwainError.Create(SErrDSMEntryNotFound); End; Result := (HDSMDLL <> 0); End; //--------------------------------------------------------------------- Procedure TTWAIN.fUnloadTwain; Begin. If HDSMDLL <> 0 then. Begin. DSM_Entry := nil; FreeLibrary(HDSMDLL); HDSMDLL := 0; End; End; //--------------------------------------------------------------------- Function TTWAIN.Condition2String(ConditionCode: TW_UINT16): string; Begin. // Texts copied from PDF Documentation: Rework needed. Case ConditionCode of. TWCC_BADCAP: Result := 'Capability not supported by source or operation (get,' + CrLf + 'set) is not supported on capability, or capability had' + CrLf + 'dependencies on other capabilities and cannot be' + CrLf + 'operated upon at this time'; TWCC_BADDEST: Result := 'Unknown destination in DSM_Entry.'; TWCC_BADPROTOCOL: Result := 'Unrecognized operation triplet.'; TWCC_BADVALUE: Result := 'Data parameter out of supported range.'; TWCC_BUMMER: Result := 'General failure. Unload Source immediately.'; TWCC_CAPUNSUPPORTED: Result := 'Capability not supported by ' + 'Data Source.'; TWCC_CAPBADOPERATION: Result := 'Operation not supported on ' + 'capability.'; TWCC_CAPSEQERROR: Result := 'Capability has dependencies on other capabilities and ' + CrLf + 'cannot be operated upon at this time.'; TWCC_DENIED: Result := 'File System operation is denied (file is protected).'; TWCC_PAPERDOUBLEFEED, TWCC_PAPERJAM: Result := 'Transfer failed because of a feeder error'; TWCC_FILEEXISTS: Result := 'Operation failed because file already exists.'; TWCC_FILENOTFOUND: Result := 'File not found.'; TWCC_LOWMEMORY: Result := 'Not enough memory to complete the operation.'; TWCC_MAXCONNECTIONS: Result := 'Data Source is connected to maximum supported number of ' + CrLf + 'applications.'; TWCC_NODS: Result := 'Data Source Manager was unable to find the specified Data ' + 'Source.'; TWCC_NOTEMPTY: Result := 'Operation failed because directory is not empty.'; TWCC_OPERATIONERROR: Result := 'Data Source or Data Source Manager reported an error to the' + CrLf + 'user and handled the error. No application action ' + 'required.'; TWCC_SEQERROR: Result := 'Illegal operation for current Data Source Manager' + CrLf + 'and Data Source state.'; TWCC_SUCCESS: Result := 'Operation was succesful.'; Else. Result := Format('Unknown condition %.04x', [ConditionCode]); End; End; /////////////////////////////////////////////////////////////////////// // RaiseLastDSMCondition (idea: like RaiseLastWin32Error) // // Tries to get the status from the DSM and raises an exception // // with it. // /////////////////////////////////////////////////////////////////////// Procedure TTWAIN.RaiseLastDataSourceManagerCondition(module: string); Var. Status: TW_STATUS; Begin. Assert(@DSM_Entry <> nil); If DSM_Entry(@appId, nil, DG_CONTROL, DAT_STATUS, MSG_GET, @status) <> TWRC_SUCCESS then. Raise ETwainError.Create(ERR_STATUS). Else. Raise ETwainError.CreateFmt(ERR_DSM, [module, Condition2String(status.ConditionCode)]); End; /////////////////////////////////////////////////////////////////////// // RaiseLastDSCondition // // same again, but for the actual DS // // (should be a method of DS) // /////////////////////////////////////////////////////////////////////// Procedure TTWAIN.RaiseLastDataSourceCondition(module: string); Var. Status: TW_STATUS; Begin. Assert(@DSM_Entry <> nil); If DSM_Entry(@appId, @dsID, DG_CONTROL, DAT_STATUS, MSG_GET, @status) <> TWRC_SUCCESS then. Raise ETwainError.Create(ERR_STATUS). Else. Raise ETwainError.CreateFmt(ERR_DSM, [module, Condition2String(status.ConditionCode)]); End; /////////////////////////////////////////////////////////////////////// // TwainCheckDSM (idea: like Win32Check or GDICheck in Graphics.pas) // /////////////////////////////////////////////////////////////////////// Procedure TTWAIN.TwainCheckDataSourceManager(res: TW_UINT16; Module: string); Begin. If res <> TWRC_SUCCESS then. Begin. If res = TWRC_FAILURE then. RaiseLastDataSourceManagerCondition(module). Else. Raise ETwainError.CreateFmt(ERR_UNKNOWN, [module, res]); End; End; /////////////////////////////////////////////////////////////////////// // TwainCheckDS // // same again, but for the actual DS // // (should be a method of DS) // /////////////////////////////////////////////////////////////////////// Procedure TTWAIN.TwainCheckDataSource(res: TW_UINT16; Module: string); Begin. If res <> TWRC_SUCCESS then. Begin. If res = TWRC_FAILURE then. RaiseLastDataSourceCondition(module). Else. Raise ETwainError.CreateFmt(ERR_UNKNOWN, [module, res]); End; End; /////////////////////////////////////////////////////////////////////// // CallDSMEntry: // // Short form for DSM Calls: appId is not needed as parameter // /////////////////////////////////////////////////////////////////////// Function TTWAIN.CallDataSourceManager(pOrigin: pTW_IDENTITY; DG: TW_UINT32; DAT: TW_UINT16; MSG: TW_UINT16; PData: TW_MEMREF): TW_UINT16; Begin. Assert(@DSM_Entry <> nil); Result := DSM_Entry(@appID, POrigin, DG, DAT, MSG, PData); If (Result <> TWRC_SUCCESS) and (DAT <> DAT_EVENT) then. Begin. End; End; /////////////////////////////////////////////////////////////////////// // Short form for (actual) DS Calls. appId and dsID are not needed // // (this should be a DS class method) // /////////////////////////////////////////////////////////////////////// Function TTWAIN.CallDataSource(DG: TW_UINT32; DAT: TW_UINT16; MSG: TW_UINT16; PData: TW_MEMREF): TW_UINT16; Begin. Assert(@DSM_Entry <> nil); Result := DSM_Entry(@appID, @dsID, DG, DAT, MSG, PData); End; /////////////////////////////////////////////////////////////////////// // A lot of the following code is a conversion from the // // twain example program (and some comments are copied, too) // // (The error handling is done differently) // // Most functions should be moved to a DSM or DS class // /////////////////////////////////////////////////////////////////////// Procedure TTWAIN.OpenDataSourceManager; Begin. If not bDataSourceManagerOpen then. Begin. Assert(appID.ID <> 0); If not fLoadTwain then. Raise ETwainError.Create(ERR_TWAIN_NOT_LOADED); // appID.Id := fhWnd; // appID.Version.MajorNum := 1; // appID.Version.MinorNum := 0; // appID.Version.Language := TWLG_USA; // appID.Version.Country := TWCY_USA; // appID.Version.Info := 'Twain Component'; AppID.ProtocolMajor := 1; // TWON_PROTOCOLMAJOR; AppID.ProtocolMinor := 7; // TWON_PROTOCOLMINOR; AppID.SupportedGroups := DG_IMAGE or DG_CONTROL; // appID.Productname := 'HP ScanJet 5p'; // appId.ProductFamily := 'ScanJet'; // appId.Manufacturer := 'Hewlett-Packard'; TwainCheckDataSourceManager(CallDataSourceManager(nil, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, @fhWnd), 'OpenDataSourceManager'); BDataSourceManagerOpen := True; End; End; //--------------------------------------------------------------------- Procedure TTWAIN.CloseDataSourceManager; Begin. If bDataSourceOpen then. Raise ETwainError.Create(ERR_DSM_OPEN); If bDataSourceManagerOpen then. Begin. // This call performs one important function: // - tells the SM which application, appID.id, is requesting SM to. // close. // - be sure to test return code, failure indicates SM did not. // close !! TwainCheckDataSourceManager(CallDataSourceManager(nil, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, @fhWnd), 'CloseDataSourceManager'); BDataSourceManagerOpen := False; End; FUnLoadTwain; // Loose the DLL. If sDefaultSource <> '' then. RestoreDefaultSourceEntry; End; //--------------------------------------------------------------------- Function TTWAIN.IsDataSourceManagerOpen: Boolean; Begin. Result := bDataSourceManagerOpen; End; //--------------------------------------------------------------------- Procedure TTWAIN.OpenDataSource; Begin. Assert(bDataSourceManagerOpen, 'Data Source Manager must be open'); If not bDataSourceOpen then. Begin. TwainCheckDataSourceManager(CallDataSourceManager(nil, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, @dsID), 'OpenDataSource'); BDataSourceOpen := True; End; End; //--------------------------------------------------------------------- Procedure TTWAIN.CloseDataSource; Begin. Assert(bDataSourceManagerOpen, 'Data Source Manager must be open'); If bDataSourceOpen then. Begin. TwainCheckDataSourceManager(CallDataSourceManager(nil, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, @dsID), 'CloseDataSource'); BDataSourceOpen := False; End; End; //--------------------------------------------------------------------- Procedure TTWAIN.EnableDataSource; Var. TwUI: TW_USERINTERFACE; Begin. Assert(bDataSourceOpen, 'Data Source must be open'); If not bDataSourceEnabled then. Begin. FillChar(twUI, SizeOf(twUI), #0); TwUI.hParent := fhWnd; TwUI.ShowUI := fShowUI; TwUI.ModalUI := True; TwainCheckDataSourceManager(CallDataSourceManager(@dsID, DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS, @twUI), 'EnableDataSource'); BDataSourceEnabled := True; End; End; //--------------------------------------------------------------------- Procedure TTWAIN.DisableDataSource; Var. TwUI: TW_USERINTERFACE; Begin. Assert(bDataSourceOpen, 'Data Source must be open'); If bDataSourceEnabled then. Begin. TwUI.hParent := fhWnd; TwUI.ShowUI := TW_BOOL(TWON_DONTCARE8); (*!!!!*). TwainCheckDataSourceManager(CallDataSourceManager(@dsID, DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS, @twUI), 'DisableDataSource'); BDataSourceEnabled := False; End; End; //--------------------------------------------------------------------- Function TTWAIN.IsDataSourceOpen: Boolean; Begin. Result := bDataSourceOpen; End; //--------------------------------------------------------------------- Function TTWAIN.IsDataSourceEnabled: Boolean; Begin. Result := bDataSourceEnabled; End; //--------------------------------------------------------------------- Procedure TTWAIN.SelectDataSource; Var. NewDSIdentity: TW_IDENTITY; TwRC: TW_UINT16; Begin. SaveDefaultSourceEntry; Assert(not bDataSourceOpen, 'Data Source must be closed'); TwainCheckDataSourceManager(CallDataSourceManager(nil, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, @NewDSIdentity), 'SelectDataSource1'); TwRC := CallDataSourceManager(nil, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, @NewDSIdentity); Case twRC of. TWRC_SUCCESS: dsID := NewDSIdentity; // log in new Source. TWRC_CANCEL: ; // keep the current Source. Else. TwainCheckDataSourceManager(twRC, 'SelectDataSource2'); End; End; (******************************************************************* Functions from CAPTEST.C. *******************************************************************) Procedure TTWAIN.XferMech; Var. Cap: TW_CAPABILITY; PVal: pTW_ONEVALUE; Begin. FXfer := xfNative; // Override. Cap.Cap := ICAP_XFERMECH; Cap.ConType := TWON_ONEVALUE; Cap.hContainer := GlobalAlloc(GHND, SizeOf(TW_ONEVALUE)); Assert(cap.hContainer <> 0); Try. Pval := pTW_ONEVALUE(GlobalLock(cap.hContainer)); Assert(pval <> nil); Try. Pval.ItemType := TWTY_UINT16; Case fXfer of. XfMemory: pval.Item := TWSX_MEMORY; XfFile: pval.Item := TWSX_FILE; XfNative: pval.Item := TWSX_NATIVE; End; Finally. GlobalUnlock(cap.hContainer); End; TwainCheckDataSource(CallDataSource(DG_CONTROL, DAT_CAPABILITY, MSG_SET, @cap), 'XferMech'); Finally. GlobalFree(cap.hContainer); End; End; /////////////////////////////////////////////////////////////////////// Function TTWAIN.ProcessSourceMessage(var Msg: TMsg): Boolean; Var. TwRC: TW_UINT16; Event: TW_EVENT; Pending: TW_PENDINGXFERS; Begin. Result := False; If bDataSourceManagerOpen and bDataSourceOpen then. Begin. Event.pEvent := @Msg; Event.TWMessage := 0; TwRC := CallDataSource(DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, @event); Case event.TWMessage of. MSG_XFERREADY: Begin. Case fXfer of. XfNative: fNativeXfer; XfMemory: fMemoryXfer; XfFile: fFileXfer; End; TwainCheckDataSource(CallDataSource(DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER, @pending), 'Check for Pending Transfers'); If pending.Count > 0 then. TwainCheckDataSource(CallDataSource( DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET, @pending), 'Abort Pending Transfers'); DisableDataSource; CloseDataSource; ScanReady; // Event. End; MSG_CLOSEDSOK, MSG_CLOSEDSREQ: Begin. DisableDataSource; CloseDataSource; ScanReady // Event. End; End; Result := not (twRC = TWRC_NOTDSEVENT); End; End; //--------------------------------------------------------------------- Procedure TTWAIN.Acquire(aBmp: TBitmap); Begin. // fOldOnMessageHandler := Application.OnMessage; // Save old pointer. // Application.OnMessage := fNewOnMessageHandler; // Set to our handler. // OpenDataSourceManager; // Open DS. FBitmap := aBmp; OpenDataSourceManager; OpenDataSource; XferMech; // Must be written for xfMemory and xfFile. EnableDataSource; End; //--------------------------------------------------------------------- // Must be written! Function TTWAIN.fMemoryXfer: Boolean; Var. TwRC: TW_UINT16; Begin. Result := False; TwRC := CallDataSource(DG_IMAGE, DAT_IMAGEMEMXFER, MSG_GET, Nil); Case twRC of. TWRC_XFERDONE: Result := True; TWRC_CANCEL: ; TWRC_FAILURE: ; End; End; //--------------------------------------------------------------------- // Must be written! Function TTWAIN.fFileXfer: Boolean; Var. TwRC: TW_UINT16; Begin. // Not yet implemented! Result := False; TwRC := CallDataSource(DG_IMAGE, DAT_IMAGEFILEXFER, MSG_GET, Nil); Case twRC of. TWRC_XFERDONE: Result := True; TWRC_CANCEL: ; TWRC_FAILURE: ; End; End; //--------------------------------------------------------------------- Function TTWAIN.fNativeXfer: Boolean; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Function DibNumColors(dib: Pointer): Integer; Var. Lpbi: PBITMAPINFOHEADER; Lpbc: PBITMAPCOREHEADER; Bits: Integer; Begin. Lpbi := dib; Lpbc := dib; If lpbi.biSize <> SizeOf(BITMAPCOREHEADER) then. Begin. If lpbi.biClrUsed <> 0 then. Begin. Result := lpbi.biClrUsed; Exit; End; Bits := lpbi.biBitCount; End. Else. Bits := lpbc.bcBitCount; Case bits of. 1: Result := 2; 4: Result := 16; // 4? 8: Result := 256; // 8? Else. Result := 0; End; End; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Var. TwRC: TW_UINT16; HDIB: TW_UINT32; HBmp: HBITMAP; LpDib: ^TBITMAPINFO; LpBits: PChar; ColorTableSize: Integer; Dc: HDC; Begin. Result := False; TwRC := CallDataSource(DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, @hDIB); Case twRC of. TWRC_XFERDONE: Begin. LpDib := GlobalLock(hDIB); Try. ColorTableSize := (DibNumColors(lpDib) * SizeOf(RGBQUAD)); LpBits := PChar(lpDib); Inc(lpBits, lpDib.bmiHeader.biSize); Inc(lpBits, ColorTableSize); Dc := GetDC(0); Try. HBMP := CreateDIBitmap(dc, lpdib.bmiHeader, CBM_INIT, lpBits, lpDib^, DIB_RGB_COLORS); FBitmap.Handle := hBMP; Result := True; Finally. ReleaseDC(0, dc); End; Finally. GlobalUnlock(hDIB); GlobalFree(hDIB); End; End; TWRC_CANCEL: ; TWRC_FAILURE: RaiseLastDataSourceManagerCondition('Native Transfer'); End; End; //--------------------------------------------------------------------- Procedure TTWAIN.fNewOnMessageHandler(var Msg: TMsg; Var Handled: Boolean); Begin. Handled := ProcessSourceMessage(Msg); If Assigned(fOldOnMessageHandler) then. FOldOnMessageHandler(Msg, Handled). End;
©Drkb::01616.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
The setup program for Imaging (tool that ships with Windows > 98) installs the Image.
Scan control (OCX) and the 32-bit TWAIN DLLs.
All you have to do is to import this ActiveX control in Delphi and generate a component wrapper:
Import the ActiveX Control "Kodak Image Scan Control".
(Select Component|Import ActiveX Control...).
Now add a TImgScan Component from the Register "ActiveX" to your form.
Change the following Properties in the Object Inspector:
FileType = 3 - BMP_Bitmap.
PageOption = 4 - OverwritePages.
ScanTo = 2 - FileOnly.
Code: FileType = 3 - BMP_Bitmap. PageOption = 4 - OverwritePages. ScanTo = 2 - FileOnly. {***} Procedure TForm1.Button1Click(Sender: TObject); Begin. If imgScan1.ScannerAvailable then. Try. ImgScan1.Image := 'c:\Scanner.bmp'; ImgScan1.OpenScanner; ImgScan1.Zoom := 100; ImgScan1.StartScan; Application.ProcessMessages; Finally. ImgScan1.CloseScanner; { Show the scanned image in Image1 }. ImgScan1.Picture.LoadFromFile(Image1.Image); End; End;
Как очистить буфер клавиатуры?
Code: Procedure EmptyKeyQueue; Var. Msg: TMsg; Begin. While PeekMessage(msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE or PM_NOYIELD) do. ; End; Begin. EmptyKeyQueue; End.
©Drkb::01522.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Как найти список параллельных портов?
Code: Function PortExists(const PortName: string): Boolean; Var. HPort: HWND; Begin. Result := False; HPort := CreateFile(PChar(PortName), {name}. GENERIC_READ or GENERIC_WRITE, {access attributes}. 0, {no sharing}. Nil, {no security}. OPEN_EXISTING, {creation action}. FILE_ATTRIBUTE_NORMAL or. FILE_FLAG_OVERLAPPED, {attributes}. 0); {no template}. If hPort <> INVALID_HANDLE_VALUE then. Begin. CloseHandle(hPort); Result := True; End; End; {Parallel Ports}. For i := 1 to 9 do. Begin. If PortExists('LPT' + IntToStr(i)) then. List.Append('Ports: Printer Port (LPT' + IntTostr(i) + ')'); End;
©Drkb::01348.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Печать содержимого TMemo или TListbox. |
Следующая функция имеет один параметр в виде объекта TStrings и печатает каждую строку на принтер, установленный в системе по умолчанию. Так как эта функция использует TStrings, то она будет работать с различными компонентами, которые содержат свойство типа TStrings, такие как TDBMemo или TOutline:
Code: Uses Printers; Procedure PrintStrings(Strings: TStrings); Var. Prn: TextFile; I: word; Begin. AssignPrn(Prn); Try. Rewrite(Prn); Try. For i := 0 to Strings.Count - 1 do. Writeln(Prn, Strings.Strings[i]); Finally. CloseFile(Prn); End; Except. On EInOutError do. MessageDlg('Error Printing text.', mtError, [mbOk], 0); End; End;
Как получить размер физической установленной памяти?
Code: Uses. Windows, SysUtils; Function DisplayRam: string; Var. Info: TMemoryStatus; Begin. Info.dwLength := SizeOf(TMemoryStatus); GlobalMemoryStatus(Info); Result := Format('%d MB RAM', [(Info.dwTotalPhys shr 20) + 1]); End;
©Drkb::01618.
Code: Function Physmem: string; Var. MemStat: TMemoryStatus; Begin. MemStat.dwLength := sizeof(MemStat); GlobalMemoryStatus(MemStat); Result := inttoStr(memstat.dwTotalPhys div 1024); End; Function PhysmemFree: string; Var. MemStat: TMemoryStatus; Begin. MemStat.dwLength := sizeof(MemStat); GlobalMemoryStatus(MemStat); Result := inttoStr(memstat.dwAvailPhys div 1024); End; Function MemLoad: string; Var. MemStat: TMemoryStatus; Begin. MemStat.dwLength := sizeof(MemStat); GlobalMemoryStatus(MemStat); Result := inttoStr(memstat.dwMemoryLoad); End; Function TotalPageFile: string; Var. MemStat: TMemoryStatus; Begin. MemStat.dwLength := sizeof(MemStat); GlobalMemoryStatus(MemStat); Result := inttoStr(memstat.dwTotalPageFile div 1024); End; Function AvailPageFile: string; Var. MemStat: TMemoryStatus; Begin. MemStat.dwLength := sizeof(MemStat); GlobalMemoryStatus(MemStat); Result := inttoStr(memstat.dwAvailPageFile div 1024); End; Function VirTotPageFile: string; Var. MemStat: TMemoryStatus; Begin. MemStat.dwLength := sizeof(MemStat); GlobalMemoryStatus(MemStat); Result := inttoStr(memstat.dwTotalVirtual div 1024); End; Function AvailVir: string; Var. MemStat: TMemoryStatus; Begin. MemStat.dwLength := sizeof(MemStat); GlobalMemoryStatus(MemStat); Result := inttoStr(memstat.dwAvailVirtual div 1024); End;
©Drkb::01619.
Code: Uses. Windows; Function TMyApp.GlobalMemoryStatus(Index: Integer): Integer; Var. MemoryStatus: TMemoryStatus. Begin. With MemoryStatus do. Begin. DwLength := SizeOf(TMemoryStatus); Windows.GlobalMemoryStatus(MemoryStatus); Case Index of. 1: Result := dwMemoryLoad; 2: Result := dwTotalPhys div 1024; 3: Result := dwAvailPhys div 1024; 4: Result := dwTotalPageFile div 1024; 5: Result := dwAvailPageFile div 1024; 6: Result := dwTotalVirtual div 1024; 7: Result := dwAvailVirtual div 1024; Else. Result := 0; End; End; End;
©Drkb::01620.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Как получить инфу о SCSI дисках?
Code: Program ScsiSN; // PURPOSE: Simple console application that display SCSI harddisk serial number. {$APPTYPE CONSOLE}. Uses. Windows, SysUtils; //------------------------------------------------------------- Function GetDeviceHandle(sDeviceName: string): THandle; Begin. Result := CreateFile(PChar('\\.\' + sDeviceName), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, Nil, OPEN_EXISTING, 0, 0). End; //------------------------------------------------------------- Function ScsiHddSerialNumber(DeviceHandle: THandle): string; {$ALIGN ON}. Type. TScsiPassThrough = record. Length: Word; ScsiStatus: Byte; PathId: Byte; TargetId: Byte; Lun: Byte; CdbLength: Byte; SenseInfoLength: Byte; DataIn: Byte; DataTransferLength: ULONG; TimeOutValue: ULONG; DataBufferOffset: DWORD; SenseInfoOffset: ULONG; Cdb: array[0..15] of Byte; End; TScsiPassThroughWithBuffers = record. Spt: TScsiPassThrough; BSenseBuf: array[0..31] of Byte; BDataBuf: array[0..191] of Byte; End; {ALIGN OFF}. Var. DwReturned: DWORD; Len: DWORD; Buffer: array[0..SizeOf(TScsiPassThroughWithBuffers) + SizeOf(TScsiPassThrough) - 1] of Byte; Sptwb: TScsiPassThroughWithBuffers absolute Buffer; Begin. Result := ''; FillChar(Buffer, SizeOf(Buffer), #0); With sptwb.spt do. Begin. Length := SizeOf(TScsiPassThrough); CdbLength := 6; // CDB6GENERIC_LENGTH. SenseInfoLength := 24; DataIn := 1; // SCSI_IOCTL_DATA_IN. DataTransferLength := 192; TimeOutValue := 2; DataBufferOffset := PChar(@sptwb.bDataBuf) - PChar(@sptwb); SenseInfoOffset := PChar(@sptwb.bSenseBuf) - PChar(@sptwb); Cdb[0] := $12; // OperationCode := SCSIOP_INQUIRY; Cdb[1] := $01; // Flags := CDB_INQUIRY_EVPD; Vital product data. Cdb[2] := $80; // PageCode Unit serial number. Cdb[4] := 192; // AllocationLength. End; Len := sptwb.spt.DataBufferOffset + sptwb.spt.DataTransferLength; If DeviceIoControl(DeviceHandle, $0004D004, @sptwb, SizeOf(TScsiPassThrough), @sptwb, len, dwReturned, nil). And ((PChar(@sptwb.bDataBuf) + 1)^ = #$80) then. SetString(Result, PChar(@sptwb.bDataBuf) + 4, Ord((PChar(@sptwb.bDataBuf) + 3)^)); End; /============================================================= Var. HDevice: THandle = 0; SSerNum, sDeviceName: string; Begin. SDeviceName := ParamStr(1); If sDeviceName = '' then. Begin. WriteLn; WriteLn('Display SCSI-2 device serial number.'); WriteLn; WriteLn('Using:'); WriteLn; If Win32Platform = VER_PLATFORM_WIN32_NT then // Windows NT/2000. WriteLn(' ScsiSN PhysicalDrive0'). Else. WriteLn(' ScsiSN C:'); WriteLn(' ScsiSN Cdrom0'); WriteLn(' ScsiSN Tape0'); WriteLn; Exit; End; HDevice := GetDeviceHandle(sDeviceName); If hDevice = INVALID_HANDLE_VALUE then. WriteLn('Error on GetDeviceHandle: ', SysErrorMessage(GetLastError)). Else. Try. SSerNum := ScsiHddSerialNumber(hDevice); If sSerNum = '' then. WriteLn('Error on DeviceIoControl: ', SysErrorMessageGetLastError)). Else. WriteLn('Device ' + sDeviceName. + ' serial number = "', sSerNum, '"'); Finally. CloseHandle(hDevice); End; End.
For more information about SCSI commands:
Ftp://ftp.t10.org/t10/drafts/scsi-1/
Ftp://ftp.t10.org/t10/drafts/spc/
Ftp://ftp.t10.org/t10/drafts/spc2/
©Drkb::01374.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как работать с Web камерой? |
First of all, get the SDK at http://developer.logitech.comhttp://developer.logitech.com.
| Как можно работать с объектами не заботясь об их разрушении? |
Вначале сделаем интерфейс для нашего объекта:
Code: Type. IAutoClean = interface. ['{61D9CBA6-B1CE-4297-9319-66CC86CE6922}']. End; TAutoClean = class(TInterfacedObject, IAutoClean). Private. FObj: TObject; Public. Constructor Create(AObj: TObject); Destructor Destroy; override; End; Implementation. Constructor TAutoClean.Create(AObj: TObject); Begin. FObj := AObj; End; Destructor TAutoClean.Destroy; Begin. FreeAndNil(FObj); Inherited; End;
А теперь будем использовать его вместо объекта:
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. A: IAutoClean; //must declare as local variable, so when this procedure finished, it's out of scope. O: TOpenDialog; //any component. Begin. O := TOpenDialog.Create(self); A := TAutoClean.Create(o); If o.Execute then. ShowMessage(o.FileName); End;
| Ещё примеры экспертов. |
This article introduces you to the world of Delphi Experts. Delphi Experts are DLLs, that will be loaded during the startup sequence of Delphi. This article first appeared on Delphi-PRAXiShttp://www.delphipraxis.net/viewtopic.php?t=5300 in German.
Скрытые настройки Дельфи.
| Скрытые настройки Дельфи. |
Some undocumented registry settings of Delphi 5 (which -slightly adapted- might also work with Delphi 4 and below) modify the behavior of the Delphi component palette in a manner you may like!
Most values are stored as strings, and boolean values are represented as "1" for true and "0" for false. All values are stored in.
HKEY_CURRENT_USER.
As always, use of this information is at your own risk... ;-).
Software\Borland\Delphi\5.0\Extras\AutoPaletteSelect.
Will cause a tab on the component palette to be automatically selected when the mouse is hovering over it. If the mouse is in the top two- thirds (2/3) of the tab, the palette for that tab will automatically be displayed.
Software\Borland\Delphi\5.0\Extras\AutoPaletteScroll.
Will make you scroll left and right automatically whenever the mouse is positioned over the relevant arrow.
Software\Borland\Delphi\5.0\Editor\Options\NoCtrlAltKeys.
Disables menu item Ctrl+Alt key sequences for international keyboards.
Software\Borland\Delphi\5.0\Form Design\AlwaysEnableMiddleEast.
Forces Right-to-Left text in the form designer (?).
Software\Borland\Delphi\5.0\Extras\FontNamePropertyDisplayFontNames.
Display the fonts in the object inspector dropdown in the font's actual style (slow with many fonts installed). See also DsgnIntf.FontNamePropertyDisplayFontNames in D5.
Software\Borland\Delphi\5.0\Compiling\ShowCodeInsiteErrors.
Show compilation errors found by CodeInsite in the message view window.
Software\Borland\Delphi\5.0\Globals\PropValueColor.
Fill in with a string like "clGreen" to change the color of the right half (properties) of the Object Inspector.
Software\Borland\Delphi\5.0\Disabled Packages.
This is the place you put Delphi Direct :).
Software\Borland\Delphi\5.0\Globals\TwoDigitYearCenturyWindow.
Default value for TwoDigitYearCenturyWindow (see the help file).
Software\Borland\Delphi\5.0\Component Templates\CCLibDir.
Alternative component templates directory (shared/network).
Software\Borland\Delphi\5.0\FormDesign\DefaultFont="Arial,8" [D4] or "Arial,8,Bold" [D5].
The default for new forms (you might prefer using the repository's default form checkbox instead).
Software\Borland\Delphi\5.0\Wizards.
Alternate key to store Expert/Wizard DLLs to load at startup.
Software\Borland\Delphi\5.0\Debugging\DontPromptForJITDebugger.
Don't ask to change the current JIT debugger (?).
Software\Borland\Delphi\5.0\Version Control\VCSManager.
The DLL used for the version control interface in the IDE.
Software\Borland\Delphi\5.0\Globals\PrivateDir.
A way to specify an alternative directory for the location for the Delphi configuration files when running the application from a network drive or the CD-ROM.
Software\Borland\Delphi\5.0\Main Window\Palette Visible.
Software\Borland\Delphi\5.0\Main Window\Speedbar Visible.
Software\Borland\Delphi\5.0\Main Window\Palette Hints.
Software\Borland\Delphi\5.0\Main Window\Speedbar Hints.
Software\Borland\Delphi\5.0\Main Window\Split Position.
These seem to have no effect at runtime, but are read by the IDE. The actually used values come from.
HKEY_CURRENT_USER\Software\Borland\Delphi\5.0\Toolbars.
Software\Borland\Delphi\5.0\ProjectManager\Dockable.
Software\Borland\Delphi\5.0\PropertyInspector\Dockable.
Software\Borland\Delphi\5.0\CallStackWindow\Dockable.
Software\Borland\Delphi\5.0\ModuleWindow\Dockable.
Read but unused settings. Used values come from DSK files.
There are lots of other interesting registry keys that aren't modifiable in the IDE, but they all have values written by default, so you can find and play with them much easier.
©Drkb::00051.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Запущен ли Softice? |
Code: //SoftIce in W9x. Function IsSoftIce95Loaded: boolean; Var. HFile: Thandle; Begin. Result := false; HFile := CreateFileA('\.SICE', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); If (hFile <> INVALID_HANDLE_VALUE) then. Begin. CloseHandle(hFile); Result := TRUE; End; End; // SoftIce in NT OS. Function IsSoftIceNTLoaded: boolean; Var. HFile: Thandle; Begin. Result := false; HFile := CreateFileA('\.NTICE', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); If (hFile <> INVALID_HANDLE_VALUE) then. Begin. CloseHandle(hFile); Result := TRUE; End; End; //to detect it. If IsSoftIce95Loaded or IsSoftIceNTLoaded then. Application.Terminate. {if you insert a "Nag" (Message telling him he uses SoftIce) then a amatuer cracker w'll find this protection in notime}. //bestway of using this thing is in "project Unit".
| Как добавить горизонтальную полосу прокрутки (scrollbar) в TListBox? |
В Delphi компонент TListBox автоматически включает в себя вертикальный scrollbar. Полоска прокрутки появляется в том случае, если все элементы списка не помещаются в видимую область списка. Однако, list box не показывает горизонтального скролбара, когда ширина элементов превышает ширину списка. Конечно же существует способ добавить горизонтальную полосу прокрутки.
Добавьте следующий код в событие Вашей формы OnCreate.
Code: Procedure TForm1.FormCreate(Sender: TObject); Var. I, MaxWidth: integer; Begin. MaxWidth := 0; For i := 0 to LB1.Items.Count - 1 do. If MaxWidth < LB1.Canvas.TextWidth(LB1.Items.Strings[i]) then. MaxWidth := LB1.Canvas.TextWidth(LB1.Items.Strings[i]); SendMessage(LB1.Handle, LB_SETHORIZONTALEXTENT, MaxWidth+2, 0); End;
| Как получить/изменить громкость? |
Code: Procedure GetVolume(var volL, volR: Word); Var. HWO: HWAVEOUT; WaveF: TWAVEFORMATEX; Vol: DWORD; Begin. VolL := 0; VolR := 0; // init TWAVEFORMATEX. FillChar(waveF, SizeOf(waveF), 0); // open WaveMapper = std output of playsound. WaveOutOpen(@hWO, WAVE_MAPPER, @waveF, 0, 0, 0); // get volume. WaveOutGetVolume(hWO, @vol); VolL := vol and $FFFF; VolR := vol shr 16; WaveOutClose(hWO); End; Procedure SetVolume(const volL, volR: Word); Var. HWO: HWAVEOUT; WaveF: TWAVEFORMATEX; Vol: DWORD; Begin. // init TWAVEFORMATEX. FillChar(waveF, SizeOf(waveF), 0); // open WaveMapper = std output of playsound. WaveOutOpen(@hWO, WAVE_MAPPER, @waveF, 0, 0, 0); Vol := volL + volR shl 16; // set volume. WaveOutSetVolume(hWO, vol); WaveOutClose(hWO); End;
| Как получить информацию о дорожке аудио-CD? |
Code: Unit frmMain; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, MMSystem; Type. TForm1 = class(TForm). Memo1: TMemo; Button2: TButton; Button3: TButton; Procedure Button2Click(Sender: TObject); Procedure Button3Click(Sender: TObject); Private. Function IsAudioCD(Drive: char): bool; Public. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Function TForm1.IsAudioCD(Drive: char): bool; Var. DrivePath: string; MaximumComponentLength: DWORD; FileSystemFlags: DWORD; VolumeName: string; Begin. Result := false; DrivePath := Drive + ':\'; If GetDriveType(PChar(DrivePath)) = DRIVE_CDROM then. Begin. SetLength(VolumeName, 64); GetVolumeInformation(PChar(DrivePath), PChar(VolumeName), Length(VolumeName), nil, MaximumComponentLength, FileSystemFlags, nil, 0); If lStrCmp(PChar(VolumeName), 'Audio CD') = 0 then. Result := True; End; End; Procedure TForm1.Button2Click(Sender: TObject); Begin. If IsAudioCD(' D ') then. Showmessage('Cd is an audio cd'). Else. Showmessage('Cd is not an audio cd'); End; Procedure TForm1.Button3Click(Sender: TObject); Type. TDWord = record. High: Word; Low: Word; End; Var. Msp: TMCI_INFO_PARMS; MediaString: array[0..255] of char; Ret: longint; I: integer; StatusParms: TMCI_STATUS_PARMS; MciSetParms: TMCI_SET_PARMS; MciOpenParms: TMCI_OPEN_PARMS; ADeviceID: MCIDEVICEID; Function GetTheDeviceID: MCIDEVICEID; Begin. FillChar(MciOpenParms, SizeOf(MciOpenParms), #0); Try. MciOpenParms.lpstrDeviceType := 'cdaudio'; Ret := mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE + MCI_OPEN_SHAREABLE, LongInt(@MciOpenParms)); Result := MciOpenParms.wDeviceID; Except. On E: Exception do. Begin. Result := 0; Showmessage('error receiving deviceIDt' + #13 + SysErrorMessage(GetLastError). + #13 + E.Message); End; End; End; Function GetTrackInfo(const uMsg: UInt; const fdwCommand: DWord; Const dwItem: DWord; const dwTrack: DWord): string; Begin. Result := 'Did not work...'; FillChar(MediaString, SizeOf(MediaString), #0); FillChar(StatusParms, SizeOf(StatusParms), #0); StatusParms.dwItem := dwItem; StatusParms.dwTrack := dwTrack; Ret := mciSendCommand(aDeviceID, uMsg, fdwCommand, longint(@StatusParms)); If Ret = 0 then. Result := IntToStr(StatusParms.dwReturn); End; Procedure SetTimeInfo; Begin. FillChar(MciSetParms, SizeOf(MciSetParms), #0); MciSetParms.dwTimeFormat := MCI_FORMAT_MSF; Ret := mciSendCommand(aDeviceID {Mp.DeviceId}, MCI_SET, MCI_SET_TIME_FORMAT, Longint(@MciSetParms)); If Ret <> 0 then. Showmessage('Error convering timeformat...'); End; Begin. Memo1.Clear; ADeviceID := GetTheDeviceID; Application.ProcessMessages; Memo1.Lines.Add('Track info :'); SetTimeInfo; Memo1.Lines.Add('Tracks: ' + GetTrackInfo(MCI_STATUS, MCI_STATUS_ITEM, MCI_STATUS_NUMBER_OF_TRACKS, 0)); Memo1.Lines.Add(' '); For I := 1 to StrToInt(GetTrackInfo(MCI_STATUS, MCI_STATUS_ITEM, MCI_STATUS_NUMBER_OF_TRACKS, 0)) do. Begin. Memo1.Lines.Add('Track ' + IntToStr(I) + ' : ' + IntToStr(MCI_MSF_MINUTE. (StrToInt(GetTrackInfo(MCI_STATUS, MCI_STATUS_ITEM + MCI_TRACK, MCI_STATUS_LENGTH, I)))) + ':' + IntToStr(MCI_MSF_SECOND(StrToInt(GetTrackInfo(MCI_STATUS, MCI_STATUS_ITEM + MCI_TRACK, MCI_STATUS_LENGTH, I))))); End; Application.ProcessMessages; End; End.
| Как проиграть wav из ресурса не сохраняя его в файл? |
Code: { ... } Var. FindHandle, ResHandle: THandle; ResPtr: Pointer; Begin. FindHandle := FindResource(HInstance, 'Name of your resource', 'WAVE'); If FindHandle <> 0 then. Begin. ResHandle := LoadResource(HInstance, FindHandle); If ResHandle <> 0 then. Begin. ResPtr := LockResource(ResHandle); If ResPtr <> nil then. SndPlaySound(PChar(ResPtr), snd_ASync or snd_Memory); UnlockResource(ResHandle); End; FreeResource(FindHandle); End; End;
| Как прочитать заголовок wav файла? |
Code: Type. TWaveHeader = record. Ident1: array[0..3] of Char; // Must be "RIFF". Len: DWORD; // Remaining length after this header. Ident2: array[0..3] of Char; // Must be "WAVE". Ident3: array[0..3] of Char; // Must be "fmt ". Reserv: DWORD; // Reserved 4 bytes. WFormatTag: Word; // format type. NChannels: Word; // number of channels (i.e. mono, stereo, etc.). NSamplesPerSec: DWORD; //sample rate. NAvgBytesPerSec: DWORD; //for buffer estimation. NBlockAlign: Word; //block size of data. WBitsPerSample: Word; //number of bits per sample of mono data. CbSize: Word; //the count in bytes of the size of. Ident4: array[0..3] of Char; //Must be "data". End;
| Как разрезать wav файл? |
Code: Type. TWaveHeader = record. Ident1: array[0..3] of Char; // Must be "RIFF". Len: DWORD; // remaining length after this header. Ident2: array[0..3] of Char; // Must be "WAVE". Ident3: array[0..3] of Char; // Must be "fmt ". Reserv: DWORD; // Reserved Size. WFormatTag: Word; // format type. NChannels: Word; // number of channels (i.e. mono, stereo, etc.). NSamplesPerSec: DWORD; //sample rate. NAvgBytesPerSec: DWORD; //for buffer estimation. NBlockAlign: Word; //block size of data. WBitsPerSample: Word; //number of bits per sample of mono data. CbSize: Word; //the count in bytes of the size of. Ident4: array[0..3] of Char; //Must be "data". End;
| Как проиграть ноту? |
This is a simple class that plays a formatted musical string. It is reminiscent of the old GWBASIC days whereby one could play a string of notes via the PC speaker. I know that WAV and MIDI files are available in todays technology, but sometimes one does not need all that overhead. The class is useful for certain types of alarms (specially if the user has his sound card volume muted) or simple "Cell Phone" like jingles. The trick of the matter in Delphi is that the standard DELPHI implementation of BEEP takes no arguments and has only one sound. However the WIN API BEEP() takes two arguments.
Ie.
BOOL Beep(
DWORD dwFreq, // sound frequency, in hertz.
DWORD dwDuration // sound duration, in milliseconds.
);
Parameters.
DwFreq.
Windows NT:
Specifies the frequency, in hertz, of the sound. This parameter must be in the range 37 through 32,767 (0x25 through 0x7FFF).
Windows 95:
The parameter is ignored.
DwDuration.
Windows NT:
Specifies the duration, in milliseconds, of the sound.
Windows 95:
The parameter is ignored.
As can be seen it appears that BEEP() is NOT supported on WIN95, but is OK from there upwards. (I have not tested it on WIN95, but assume you will just get a monotone ???? - anyone for comment).
It is easily called by prefixing the unit.
Ie. Windows.Beep(Freq,Duration).
The format of the "Music String" is a comma delimited (",<" terminated) string in the following formats. (The string is CASE-INSENSITIVE and [] means optional with defaults).
A..G[+ or -][0..5][/BEATS] and.
@[/BEATS].
Where A..G is the Note to be played.
+ or - is optional Sharp or Flat designator respectively. (default is normal NULL).
0..5 is optional Octave range (default = 1).
/BEATS is number of 100ms to hold the note (default = 1).
Where @ is a musical pause.
/BEATS is the number of beats to pause for (default = 1).
Where ,< is the END OF STRING terminator.
Properties:
DefaultOctave : Used if no 0..5 designator specified in format. (System Default = 1).
BetweenNotesPause : Use to set number MS gap between notes (faster or slower default = 100ms).
Simple Example:
Code: Procedure TForm1.Button3Click(Sender: TObject); Var. Organ: TMusicPlayer; Begin. Organ := TMusicPlayer.Create; Organ.Play('A,C,C+,D/3,C,A,C,A,@,F,D/4,<'); Organ.Play('A,A3/2,G4,G/3,@/2,D-0/4,<'); Organ.Free; End;
| Как прочитать свойства видеофайла? |
Below is some code to get some of the data. To use the DirectDraw/ DirectShow calls you need either the older DSHOW.PAS (DX6) or more current DirectShow.pas header conversion from the Project JEDI web site:
Code: Type. TDSMediaInfo = record. SurfaceDesc: TDDSurfaceDesc; Pitch: integer; PixelFormat: TPixelFormat; MediaLength: Int64; AvgTimePerFrame: Int64; FrameCount: integer; Width: integer; Height: integer; FileSize: Int64; End; Function GetHugeFileSize(const FileName: string): int64; Var. FileHandle: hFile; Begin. FileHandle := FileOpen(FileName, fmOpenRead or fmShareDenyNone); Try. LARGE_INTEGER(Result).LowPart := GetFileSize(FileHandle, @LARGE_INTEGER(Result).HighPart); If LARGE_INTEGER(Result).LowPart = $FFFFFFFF then. Win32Check(GetLastError = NO_ERROR); Finally. FileClose(FileHandle); End; End; Function GetMediaInfo(FileName: WideString): TDSMediaInfo; Var. DirectDraw: IDirectDraw; AMStream: IAMMultiMediaStream; MMStream: IMultiMediaStream; PrimaryVidStream: IMediaStream; DDStream: IDirectDrawMediaStream; GraphBuilder: IGraphBuilder; MediaSeeking: IMediaSeeking; TimeStart, TimeStop: Int64; DesiredSurface: TDDSurfaceDesc; DDSurface: IDirectDrawSurface; Begin. If FileName = '' then. Raise Exception.Create('No File Name Specified'); OleCheck(DirectDrawCreate(nil, DirectDraw, nil)); DirectDraw.SetCooperativeLevel(GetDesktopWindow(), DDSCL_NORMAL); Result.FileSize := GetHugeFileSize(FileName); AMStream := IAMMultiMediaStream(CreateComObject(CLSID_AMMultiMediaStream)); OleCheck(AMStream.Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, nil)); OleCheck(AMStream.AddMediaStream(DirectDraw, MSPID_PrimaryVideo, 0, IMediaStream(nil^))); OleCheck(AMStream.OpenFile(PWideChar(FileName), AMMSF_NOCLOCK)); AMStream.GetFilterGraph(GraphBuilder); MediaSeeking := GraphBuilder as IMediaSeeking; MediaSeeking.GetDuration(Result.MediaLength); MMStream := AMStream as IMultiMediaStream; OleCheck(MMStream.GetMediaStream(MSPID_PrimaryVideo, PrimaryVidStream)); DDStream := PrimaryVidStream as IDirectDrawMediaStream; DDStream.GetTimePerFrame(Result.AvgTimePerFrame); {Result.FrameCount := Result.MediaLength div Result.AvgTimePerFrame;}. { TODO : Test for better accuracy }. Result.FrameCount := Round(Result.MediaLength / Result.AvgTimePerFrame); Result.MediaLength := Result.FrameCount * Result.AvgTimePerFrame; ZeroMemory(@DesiredSurface, SizeOf(DesiredSurface)); DesiredSurface.dwSize := Sizeof(DesiredSurface); OleCheck(DDStream.GetFormat(TDDSurfaceDesc(nil^), IDirectDrawPalette(nil^), DesiredSurface, DWord(nil^))); Result.SurfaceDesc := DesiredSurface; DesiredSurface.ddsCaps.dwCaps := DesiredSurface.ddsCaps.dwCaps or. DDSCAPS_OFFSCREENPLAIN or DDSCAPS_SYSTEMMEMORY; DesiredSurface.dwFlags := DesiredSurface.dwFlags or DDSD_CAPS or DDSD_PIXELFORMAT; {Create a surface here to get vital statistics}. OleCheck(DirectDraw.CreateSurface(DesiredSurface, DDSurface, nil)); OleCheck(DDSurface.GetSurfaceDesc(DesiredSurface)); Result.Pitch := DesiredSurface.lPitch; If DesiredSurface.ddpfPixelFormat.dwRGBBitCount = 24 then. Result.PixelFormat := pf24bit. Else if DesiredSurface.ddpfPixelFormat.dwRGBBitCount = 32 then. Result.PixelFormat := pf32bit; Result.Width := DesiredSurface.dwWidth; Result.Height := DesiredSurface.dwHeight; End;
| Как показать видео на полном экране? |
Code: Procedure TForm1.Button1Click(Sender: TObject); Const. LongName: PChar = 'f:\media\ANIM1.MPG'; {Your complete FileName}. Var. Ret, shortName: PChar; Err: DWord; Begin. {Getting the short Name (8:3) of selected file}. ShortName := strAlloc(521); GetShortPathName(longName, shortname, 512); {Sending a close Command to the MCI}. Ret := strAlloc(255); Err := mciSendString(pchar('close movie'), 0, 0, 0); {No error check because at the first call there is no MCI device to close}. {Open a new MCI Device with the selected movie file}. Err := mciSendString(pchar('open ' + shortName + ' alias movie'), 0, 0, 0); ShortName := nil; {If an Error was traced then display a MessageBox with the mciError string}. If err <> 0 then. Begin. MciGetErrorString(err, ret, 255); MessageDlg(ret, mtInformation, [mbOk], 0); End; {Sending the "play fullscreen command to the Windows MCI}. Err := mciSendString(pchar('play movie fullscreen'), 0, 0, 0); {Use the following line instead of the above one if you want to play. It in screen mode}. Err := mciSendString(pchar('play movie'), 0, 0, 0); {If an Error was traced then display a MessageBox with the mciError string}. If err <> 0 then. Begin. MciGetErrorString(err, ret, 255); MessageDlg(ret, mtInformation, [mbOk], 0); End; Ret := nil; End;
| Как перетащить целую колонку из Stringgrid в Listbox? |
После того, как поместите TListBox на форму, необходимо изменить свойство Style в TListBox на lbOwnerDrawFixed. Если не изменить свойство Style, то событие OnDrawItem никогда не вызовется. Теперь поместите следующий код в обработчик события OnDrawItem Вашего TListBox:
Code: Procedure TForm1.ListBox1DrawItem. (Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); Var. MyColor: TColor; MyBrush: TBrush; Begin. MyBrush := TBrush.Create; With (Control as TListBox).Canvas do. Begin. If not Odd(Index) then. MyColor := clSilver. Else. MyColor := clYellow; MyBrush.Style := bsSolid; MyBrush.Color := myColor; Windows.FillRect(handle, Rect, myBrush.Handle); Brush.Style := bsClear; TextOut(Rect.Left, Rect.Top, (Control as TListBox).Items[Index]); MyBrush.Free; End; End;
| Как узнать установлен ли activeX на машине? |
Code: { ... } Var. StrOLE: string; Begin. StrOLE = "YourCOMServer.Application" {your ProgID}. If (CLSIDFromProgID(PWideChar(WideString(strOLE), ClassID) = S_OK) then. Begin. { ... } End; End;
©Drkb::04348.
Code: { ... } Const. CKEY = '\SOFTWARE\Classes\CLSID\%s\InprocServer32' Var. SKey: string; SComServer: string; Exists: boolean; Reg: TRegistry; Begin. Reg := TRegistry.Create; Try. Reg.RootKey := HKEY_LOCAL_MACHINE; SKey := format(cKEY, [GuidToString(ClassID)]); If Reg.OpenKey(sKey, False) then. Begin. SComServer := Reg.ReadString(''); If FileExists(sComServer) then. Begin. { ... } End; End; Finally. Reg.free; End; End;
| Как работать с PDF файлами? |
Let's see how to to show an Adobe Acrobat (.PDF) file in a Delphi application. All you need to do is the Acrobat ActiveX control (pdf.ocx and pdf.tlb), which you you can get for free from Adobe.
Here's How:
· Start Delphi and select Component | Import ActiveX Control...
· Look for the 'Acrobat Control for ActiveX (Version x.x)' and simply click on Install.
· Select the Component palette location in which you want to place selected library.
· Maybe the best is to leave the ActiveX option selected.
· Click on Install.
· Select a package where the new component must be installed or create a new package for the new TPdf control. Click on OK.
· Delphi will prompt you whether you want to rebuild the modified/new package or not. Click on Yes.
· After the package is compiled, Delphi will show you a message saying that the new TPdf component was registered and already available as part of the VCL.
· Close the package detail window, allowing Delphi to save the changes to it.
· The component is now available in the ActiveX tab (if you didn't change this setting in step 4)
· Drop the component on a form.
· Select the TPdf component you just dropped on a blank form.
· Using the object inspector, set the src property to the name of an existing PDF file on your system. Now all you have to do is resize the component and read the PDF file from your Delphi application.
Tips:
If you do not have the Acrobat ActiveX control, download it nowhttp://www.adobe.com/prodindex/acrobat/readstep.html! It will be required for tip to work.
Last step (Step 15) can be done in runtime, so you can open and close files programmatically, as well as resize the control.
Closing acrobat reader on formdestroy:
Code: Procedure Tfrm_doc_pdf.FormDestroy(Sender: TObject); Var. XHWND: integer; Begin. XHWND := findwindow(nil, 'Acrobat Reader'); Sendmessage(xHWND, WM_CLOSE, 0, 0); End;
| Как добавить собственную панель? |
Code: Function AddExplorerBar(BarTitle, Url: string; BarSize: Int64; Horizontal: Boolean): string; Const. EXPLORERBAR_ID = '{4D5C8C2A-D075-11d0-B416-00C04FB90376}'; VERTICAL_BAR = '{00021493-0000-0000-C000-000000000046}'; HORIZONTAL_BAR = '{00021494-0000-0000-C000-000000000046}'; Var. GUID: TGUID; SysDir, ID: string; Reg: TRegistry; Begin. CreateGuid(GUID); ID := GuidToString(GUID); Reg := TRegistry.Create; With Reg do. Try. RootKey := HKEY_CLASSES_ROOT; OpenKey('\CLSID\' + ID, True); WriteString('', 'BarTitle'); CloseKey; CreateKey('\CLSID\' + ID + '\Implemented Categories'); If HORIZONTAL then. CreateKey('\CLSID\' + ID + '\Implemented Categories\' + HORIZONTAL_BAR). Else. CreateKey('\CLSID\' + ID + '\Implemented Categories\' + VERTICAL_BAR); SetLength(SysDir, 255); GetSysDirectory(PChar(SysDir), 255); SysDir := PChar(SysDir) + '\SHDOCVW.DLL'; OpenKey('\CLSID\' + ID + '\InProcServer32', True); Writestring('', SysDir); WriteString('Threadingmodel', 'Apartment'); CloseKey; OpenKey('\CLSID\' + ID + '\Instance', True); WriteString('CLSID', EXPLORERBAR_ID); CloseKey; OpenKey('\CLSID\' + ID + '\Instance\InitPropertyBag', True); WriteString('Url', URL); CloseKey; RootKey := HKEY_LOCAL_MACHINE; OpenKey('Software\Microsoft\Internet Explorer\Explorer Bars\' + ID, True); WriteBinaryData('BarSize', BarSize, SizeOf(BarSize)); CloseKey; OpenKey('\Software\IE5Tools\Explorer Bars\', True); WriteString(BarTitle, ID); CloseKey; OpenKey('\Software\Microsoft\Internet Explorer\Toolbar', True). WriteString(ID, ''); CloseKey; Finally. Free; End; Result := ID; End;
| Как добавить свой пункт меню? |
Code: Function AddMenuItem(ConnType: TconnType; MenuText, StatusBarText, GuidOrPath: string; HelpMenu: Boolean): string; Var. GUID: TGUID; ID: string; Reg: TRegistry; Begin. CreateGuid(GUID); ID := GuidToString(GUID); Reg := TRegistry.Create; With Reg do. Begin. RootKey := HKEY_LOCAL_MACHINE; OpenKey('\Software\Microsoft\Internet Explorer\Extensions\' + ID, True); If HelpMenu then. WriteString('MenuCostumize', 'help'); WriteString('CLSID', '{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}'); WriteString('MenuText', MenuText); WriteString('MenuStatusBar', StatusBarText); Case ConnType of. EXECUTABLE: WriteString('Exec', GuidOrPath); COM_OBJECT: WriteString('ClsidExtension', GuidOrPath); SCRIPT: WriteString('Script', GuidOrPath); End; CloseKey; OpenKey('\Software\IE5Tools\Menu Items\', True); WriteString(MenuText, ID); CloseKey; Free; End; Result := ID; End;
| Как добавить кнопку? |
Code: Type. TConnType = (COM_OBJECT, EXPLORER_BAR, SCRIPT, EXECUTABLE); Function AddBandToolbarBtn(Visible: Boolean; ConnType: TConnType; BtnText, HotIcon, Icon, GuidOrPath: string): string; Var. GUID: TGUID; Reg: TRegistry; ID: string; Begin. CreateGuid(GUID); ID := GuidToString(GUID); Reg := TRegistry.Create; With Reg do. Try. RootKey := HKEY_LOCAL_MACHINE; OpenKey('\Software\Microsoft\Internet Explorer\Extensions\' + ID, True); If Visible then. WriteString('Default Visible', 'Yes'). Else. WriteString('Default Visible', 'No'); WriteString('ButtonText', BtnText); WriteString('HotIcon', HotIcon); WriteString('Icon', Icon); Case ConnType of. COM_OBJECT: Begin. WriteString('CLSID', '{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}'); WriteString('ClsidExtension', GuidOrPath); End; EXPLORER_BAR: Begin. WriteString('CLSID', '{E0DD6CAB-2D10-11D2-8F1A-0000F87ABD16}'); WriteString('BandCLSID', GuidOrPath); End; EXECUTABLE: Begin. WriteString('CLSID', '{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}'); WriteString('Exec', GuidOrPath); End; SCRIPT: Begin. WriteString('CLSID', '{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}'); WriteString('Script', GuidOrPath); End; End; CloseKey; OpenKey('\Software\IE5Tools\ToolBar Buttons\', True); WriteString(BtnText, ID); CloseKey; Finally. Free; End; Result := ID; End;
| Как узнать доступен ли DCOM? |
Code: Function IsDCOMEnabled: Boolean; Var. Ts: string; R: TRegistry; Begin. R := TRegistry.Create; R.RootKey := HKEY_LOCAL_MACHINE; R.OpenKey('Software\Microsoft\OLE', False); Ts := AnsiUpperCase(R.ReadString('EnableDCOM')); R.Free; Result := (Ts = 'Y'); End;
| Проверка правописания и синонимов при помощи компонентов MS Office. |
This is the VCL for Spell Checking and Synonyms using MS Word COM interface. It can correct and replace words in a Text String,TMemo or TRichEdit using a built in replacement editor, or can be controlled by user dialog. I see there are other callable functions in the interface, which I have not implemented. Anyone see a use for any of them ?.
They are ...
Code: Property PartOfSpeechList: OleVariant read Get_PartOfSpeechList; Property AntonymList: OleVariant read Get_AntonymList; Property RelatedExpressionList: OleVariant read Get_RelatedExpressionList; Property RelatedWordList: OleVariant read Get_RelatedWordList;
| Как вставить конец страницы? |
Code: { ... } Excel.ActiveWindow.View := xlPageBreakPreview; WS.HPageBreaks.Add(WS.Cells.Item[78, 1]); { ... }
| Как вставить картинку. |
Answer:
If WS is your worksheet:
Code: { ... } WS.Shapes.AddPicture('C:\Pictures\Small.Bmp', EmptyParam, EmptyParam, 10, 160, EmptyParam, EmptyParam);
| Как вставить растровое изображение в компонент ListBox? |
Для этого необходимо установить в инспекторе объектов поле Style в lbOwnerDrawFixed, при фиксированной высоте строки, или в lbOwnerDrawVariable, при переменной, и установить собственный обработчик события для OnDrawItem. В этом обработчике и надо рисовать растровое изображение.
Пример:
Рисуются изображения размером 32*16 (размер стандартного глифа для Delphi). Очень полезно при поиске нужного изображения для кнопок!
Установить в инспекторе объектов для ListBox поле ItemHeight = 19, а поле Color = clBtnFace.
Code: { Загрузить список файлов в ListBox1 при нажатии на кнопку Load (например)}. Procedure TForm1.bLoadClick(Sender: TObject); VAR S : String; Begin. ListBox1.Clear; {чистим список}. S := '*.bmp'#0; {задаем шаблон}. ListBox1.Perform(LB_DIR, DDL_ReadWrite, Longint(@S[1])); {заполняем список}. End; ............ {Отобразить изображения и имена файлов в ListBox}. Procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: DrawState); Var. Bitmap: TBitmap; Offset: Integer; BMPRect: TRect; Begin. With (Control as TListBox).Canvas do. Begin. FillRect(Rect); Bitmap := TBitmap.Create; Bitmap.LoadFromFile(ListBox1.Items[Index]); Offset := 0; If Bitmap <> nil then. Begin. BMPRect := Bounds(Rect.Left + 2, Rect.Top + 2, (Rect.Bottom - Rect.Top - 2) * 2, Rect.Bottom - Rect.Top - 2); {StretchDraw(BMPRect, Bitmap); Можно просто нарисовать, но лучше сначала убрать фон}. BrushCopy(BMPRect, Bitmap, Bounds(0, 0, Bitmap.Width, Bitmap.Height), Bitmap.Canvas.Pixels[0, Bitmap.Height - 1]); Offset := (Rect.Bottom - Rect.Top + 1) * 2; End; TextOut(Rect.Left + Offset, Rect.Top, ListBox1.Items[Index]); Bitmap.Free; End; End;
| Как вставить свой пункт меню? |
Code: { ... } Var. CBar: CommandBar; MenuItem: OleVariant; { ... } { Add an item to the File menu }. CBar := Word.CommandBars['File']; MenuItem := CBar.Controls.Add(msoControlButton, EmptyParam, EmptyParam, EmptyParam, True) as CommandBarButton; MenuItem.Caption := 'NewMenuItem'; MenuItem.DescriptionText := 'Does nothing'; {Note that a VB macro with the right name must exist before you assign it to the item!}. MenuItem.OnAction := 'VBMacroName'; { ... }
| Как поменять ссылку в тексте? |
Code: { ... } Doc := Word.ActiveDocument; For x := 1 to Doc.Hyperlinks.Count do. Begin. Doc.Hyperlinks.Item(x).Address; End; { ... }
| Как узнать существует ли страница (worksheet)? |
Code: { ... } WB := Excel.Workbooks[1]; For Idx := 1 to WB.Worksheets.Count do. If WB.Worksheets[Idx].Name = 'first' then. Showmessage('Found the worksheet'); { ... }
Как проверить инсталлирован ли MS Word?
Code: Uses. ..., Registry; Function IsMicrosoftWordInstalled: Boolean; Var. Reg: TRegistry; S: string; Begin. Reg := TRegistry.Create; With Reg do. Begin. RootKey := HKEY_CLASSES_ROOT; Result := KeyExists('Word.Application'); Free; End; End;
©Drkb::04417.
Code: Function MSWordIsInstalled: Boolean; Begin. Result := AppIsInstalled('Word.Application'); End; Function AppIsInstalled(strOLEObject: string): Boolean; Var. ClassID: TCLSID; Begin. Result := (CLSIDFromProgID(PWideChar(WideString(strOLEObject)), ClassID) = S_OK). End;
©Drkb::04418.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Как определить установлен ли на компьютере Word, запустить его и загрузить в него текст из программы?
Пример:
Code: Var. MsWord: Variant; ... Try. // Если Word уже запущен. MsWord := GetActiveOleObject('Word.Application'); // Взять ссылку на запущенный OLE объект. Except. Try. // Word не запущен, запустить. MsWord := CreateOleObject('Word.Application'); // Создать ссылку на зарегистрированный OLE объект. MsWord.Visible := True; Except. ShowMessage('Не могу запустить Microsoft Word'); Exit; End; End; End; ... MSWord.Documents.Add; // Создать новый документ. MsWord.Selection.Font.Bold := True; // Установить жирный шрифт. MsWord.Selection.Font.Size := 12; // установить 12 кегль. MsWord.Selection.TypeText('Текст');
©Drkb::04419.
Источник: http://dmitry9.nm.ru/info/
| Как сохранить текст MS Word в другом формате? |
Open a new Application and place:
A button named Button3,
A RitchText object named WordEditor.
And an OpenDialog component.
From now on, you can browse for any *.doc file and load it into the RitchText object.
NOTE: Format:=6 instructs Word to save the file as RTF. Extension is not enough.
Other File Formats:
Argument Format File Format.
0 Normal (Word format).
1 Document Template.
2 Text Only (extended characters saved in ANSI character set).
3 Text+Breaks (plain text with line breaks; extended characters saved in ANSI character set).
4 Text Only (PC-8) (extended characters saved in IBM PC character set).
5 Text+Breaks (PC-8) (text with line breaks; extended characters saved in IBM PC character set).
6 Rich-text format (RTF).
Code: Procedure TImport_Form.ToolButton3Click(Sender: TObject); Var. WordApp: Variant; Begin. If OpenDialog1.Execute then. Begin. Edit1.Text := ExtractFileName(OpenDialog1.FileName); StatusBar1.SimpleText := OpenDialog1.FileName; WordApp := CreateOleObject('Word.Basic'); If not VarIsEmpty(WordApp) then. Begin. WordApp.FileOpen(OpenDialog1.FileName); WordApp.FileSaveAs(Name := 'c:\temp_bb.rtf', Format := 6); WordApp.AppClose; WordApp := Unassigned; WordEditor.Lines.LoadFromFile('c:\temp_bb.rtf'); End. Else. ShowMessage('Could not start MS Word'); End; End;
| Как скопировать страницу? |
Code: { ... } Var. After: OleVariant; Sh: _Worksheet; Begin. Sh := Excel.Worksheets['Sheet1'] as _Worksheet; After := Excel.Workbooks[1].Sheets[3]; Sh.Copy(EmptyParam, After, lcid); { ... }
Как заменить значение переменных для текста MS Word?
Code: Uses. Office97; {or Office2000, OfficeXP, Office_TLB}. Var. VDoc, PropName, DocName: OleVariant; VDoc := Word.ActiveDocument; { ... } { Set a document property }. PropName := 'MyOpinionOfThisDocument'; VDoc.CustomDocumentProperties.Add(PropName, False, msoPropertyTypeString, 'Utter drivel', EmptyParam); { Read a document property }. Caption := VDoc.CustomDocumentProperties[PropName].Value; { ... }
©Drkb::04421.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как создать DLL для MS Excel? |
Problem/Question/Abstract:
How do I make delphi functions available to Excel users?
I have seen many articles telling how to control Excel from within Delphi. However, it is also appealing to give Excel users (which tend to be far less programming oriented guys) the power of tools built with Dephi, its flexibility and velocity.
Answer:
The idea is very simple and is based upon the variable types that are common to Excel's VBA and to Delphi. Those include 32 bit integer, double precision floating point and, mainly, Excel ranges.
I found that Excel sometimes interprets incorrectly simple types when passed by reference and thus I limmited their usage to value parameters.
On the other hand, ranges can only be passed by reference and can be read from but not written to. This means that, within Delphi, you must use the reserved word CONST instead of VAR.
First, I defined within a simple unit a set of functions that convert simple Variant types to simple types and viceversa. Those are IntToVar,Double and VarTodouble (the real unit also includes a StrToVar function but not a VarToStr since this one is already included in the System unit), and are used within the procedures that do the real work (RangeToMatrix, RangeToVector,VectorToMatrix and VectortoRange).
All these functions (along with some others that you might find useful) are put together in a unit called "_Variants" whose source code is copied here (with some slight modifications).
In the real unit you will find that there fucntions that provide conversion between Excel ranges and SDL delphi component suite which I have found to be quite useful (refer to www.lohninger.com).
I shall restrict the examples, however to standard types.
Lets take first a simple function:
This function, called gamma_alfa, takes as input the mean and the variance of a population and returns the alfa parameter of a gamma distribution.
In Excel's VBA it is declared as.
Declare Function gamma_alfa Lib "c:\archivos\del_files\f_auxiliares_delphi" Alias "gamma_alfa_XL" (ByVal media As Double, ByVal varianza As Double) As Double.
Note the lib statement that refers to name that the DLL actually has.
Note also the ByVal modifiers used for declaring the variables as well as the "as double" statements.
These mean that both the input and the output will be simple types of type double.
In Delphi, the function is declared as.
Function gamma_alfa(media, varianza : double) : Double;stdcall;
Note the stdcall at the end of the declaration. This is to ensure that Delphi will use the Microsoft calling convention.
Also note the inconsistency between the delphi function's name and the "alias" statement in VBA.
This is set in the export clause of the DLL:
Code: Exports ..., Gamma_alfa name 'gamma_alfa_XL', ...;
Как узнать версию MS Word?
Code: { ... } MsWord := CreateOleObject('Word.Basic'); Try. {Return Application Info. This call is the same for English and. French Microsoft Word.}. Lang := MsWord.AppInfo(Integer(16)); Except. Try. {For German Microsoft Word the procedure name is translated}. Lang := MsWord.AnwInfo(Integer(16)); Except. Try. {For Swedish Microsoft Word the procedure name is translated}. Lang := MsWord.PrgmInfo(Integer(16)); Except. Try. {For Dutch Microsoft Word the procedure name is translated}. Lang := MsWord.ToepasInfo(Integer(16)); Except. {If this procedure does not exist there is a different translation. Of Microsoft Word}. ShowMessage('Microsoft Word version is not German, French, Dutch, Swedish. Or English.'); Exit; End; End; End; End; ShowMessage(Lang); { ... }
©Drkb::04422.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как создать таблицу в MS Word? |
If Doc is a TWordDocument, for example:
Code: { ... } Var. Tbl: Table; R: Range; Direction: OleVariant; { ... } Direction := wdCollapseEnd; R := Doc.Range; R.Collapse(Direction); Tbl := Doc.Tables.Add(R, 2, 4, EmptyParam, EmptyParam); Tbl.Cell(1, 1).Range.Text := 'Row 1, Col 1'; Tbl.Cell(1, 2).Range.Text := 'Row 1, Col 2';
| Как принимать перетаскиваемые файлы из проводника? |
Вот пример с TListbox на форме:
Code: Type. TForm1 = class(TForm). ListBox1: TListBox; Procedure FormCreate(Sender: TObject); Protected. Procedure WMDROPFILES (var Msg: TMessage); message WM_DROPFILES; Private. Public. End; Var. Form1: TForm1; Implementation. Uses shellapi; {$R *.DFM}. Procedure TForm1.FormCreate(Sender: TObject); Begin. DragAcceptFiles(Form1.Handle, true); End; Procedure TForm1.WMDROPFILES (var Msg: TMessage); Var. I, Amount, Size: integer; Filename: PChar; Begin. Inherited; Amount := DragQueryFile(Msg.WParam, $FFFFFFFF, Filename, 255); For i := 0 to (Amount - 1) do. Begin. Size := DragQueryFile(Msg.WParam, i , nil, 0) + 1; Filename:= StrAlloc(size); DragQueryFile(Msg.WParam,i , Filename, size); Listbox1.items.add(StrPas(Filename)); StrDispose(Filename); End; DragFinish(Msg.WParam); End;
| Как поменять border страницы? |
Code: { ... } Var. Rng: OleVariant; LeftEdge: Border; { ... } WS.Range['A5', 'D5'].Borders.Item[xlEdgeTop].Weight := xlThick; WS.Range['A5', 'D5'].Borders.Item[xlEdgeTop].Color := clYellow; WS.Range['A5', 'D5'].Borders.Item[xlEdgeBottom].Linestyle := xlDouble; WS.Range['A5', 'D5'].Borders.Item[xlEdgeBottom].Color := clYellow; { ... } { ... } WS.Evaluate('B6, C6, D6, E6, F6').Borders.Item[xlEdgeLeft].Line. Style := xlContinuous; Rng := WS.Range['A1', 'A1']; Rng.BorderAround(xlContinuous, xlThin, Color := clFuchsia); LeftEdge := WS.Range['B2', 'B5'].Borders.Item[xlEdgeLeft]; LeftEdge.Linestyle := xlContinuous; LeftEdge.Weight := 3; LeftEdge.Color := clLime; { ... }
| Как осуществить поиск ячейки по её значению? |
Code: { ... } Var. Rnge: OleVariant; { ... } Rnge := WS.Cells; Rnge := Rnge.Find('Is this text on the sheet?'); If Pointer(IDispatch(Rnge)) <> nil then. {The text was found somewhere, so colour it pink}. Rnge.Interior.Color := clFuchsia;
| Как добавить текст к закладке? |
Code: Uses. ComObj; Procedure TForm1.Button1Click(Sender: TObject); Const. // Word Document to open. YourWordDocument = 'c:\test\worddoc.doc'; Var. BookmarkName, Doc, R: OleVariant; Begin. // Start a Word instance. Try. WordApp := CreateOleObject('Word.Application'); Except. ShowMessage('Could not start MS Word!'); End; // Open your Word document. WordApp.Documents.Open(YourWordDocument); Doc := WordApp.ActiveDocument; // name of your bookmark. BookmarkName := 'MyBookMark'; // Check if bookmark exists. If Doc.Bookmarks.Exists(BookmarkName) then. Begin. R := Doc.Bookmarks.Item(BookmarkName).Range; // Add text at our bookmark. R.InsertAfter('Text in bookmark'); // You make a text formatting like changing its color. R.Font.Color := clRed; End; // Save your document and quit Word. If not VarIsEmpty(WordApp) then. Begin. WordApp.DisplayAlerts := 0; WordApp.Documents.Item(1).Save; WordApp.Quit; BookmarkName := Unassigned; R := Unassigned; WordApp := Unassigned; End; End;
| Как работать с адресной книгой Lotus Notes? |
Code: Unit Unit2; Interface. Uses. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Domino_TLB, Menus, ComCtrls; Const. PASSWD = 'ur70'; Type. TForm2 = class(TForm). TV_INFO: TTreeView; MainMenu1: TMainMenu; File1: TMenuItem; Create1: TMenuItem; Init1: TMenuItem; AddressBook1: TMenuItem; Scan1: TMenuItem; Procedure Create1Click(Sender: TObject); Procedure Init1Click(Sender: TObject); Procedure Scan1Click(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. End; Var. Form2: TForm2; Session: TNotesSession; Implementation. {$R *.dfm}. Procedure TForm2.Create1Click(Sender: TObject); Begin. Session := TNotesSession.Create(nil); End; Procedure TForm2.Init1Click(Sender: TObject); Begin. Session.Initialize(PASSWD); End; Procedure TForm2.Scan1Click(Sender: TObject); Var. NotesDb: NotesDatabase; AddrBook: NotesDatabase; People, People2: NotesView; Person, Person2: NotesDocument; View: NotesView; Item: NotesItem; AddrBooks: OleVariant; Views: OleVariant; Items: OleVariant; X, y, z: integer; View_name: string; Tn, tc: TTreeNode; Begin. NotesDb := Session.GetDatabase('', 'names.nsf', False); AddrBooks := Session.AddressBooks; For x := 0 to VarArrayHighBound(AddrBooks, 1) - VarArrayLowBound(AddrBooks, 1) do. Begin. AddrBook := NotesDatabase(IUnknown(AddrBooks[x])); If (addrBook.IsPrivateAddressBook) then. Begin. AddrBook.Open; End. Else. AddrBook := nil; If (addrBook <> nil) then. Begin. Views := addrBook.Views; For y := 0 to VarArrayHighBound(Views, 1) - VarArrayLowBound(Views, 1) do. Begin. View := NotesView(IUnknown(Views[y])); View_name := View.Name; Tn := tv_info.Items.AddNode(nil, nil, view_name, nil, naAdd); If copy(view_name, 1, 1) = '$' then. View_name := copy(view_name, 2, length(view_name) - 1); People := addrBook.GetView(view_name); Person := people.GetFirstDocument; If Person <> nil then. Begin. Items := Person.Items; For z := 0 to VarArrayHighBound(Items, 1) - VarArrayLowBound(Items, 1) do. Begin. Item := NotesItem(IUnknown(Items[z])); Tc := tv_info.Items.AddChild(tn, Item.Name); People := addrBook.GetView(view_name); Person := people.GetFirstDocument; While (Person <> nil) do. Begin. Try. Try. Tv_info.Items.AddChild(tc, Person.GetFirstItem(Item.Name).Text. {Item.Text}); Except. End; Finally. Person := People.GetNextDocument(Person); End; End; End; End; End; End; End; End; End.
| Как работать с Shapes. |
Code: { ... } Var. Pic: Word2000.Shape; Left, Top: OleVariant; { ... } {To add a pic and make it appear behind text}. Left := 100; Top := 100; Pic := Doc.Shapes.AddPicture('C:\Small.bmp', EmptyParam, EmptyParam, Left, Top, EmptyParam, EmptyParam, EmptyParam); Pic.WrapFormat.Type_ := wdWrapNone; Pic.ZOrder(msoSendBehindText); {To get a watermark effect}. Pic.PictureFormat.Brightness := 0.75; Pic.PictureFormat.Contrast := 0.20; {To make any white in a picture transparent}. Pic.PictureFormat.TransparencyColor := clWhite; Pic.PictureFormat.TransparentBackground := msoTrue; Pic.Fill.Visible := msoFalse; { ... }
| Как добавить текст в header документа? |
Code: { ... } ADoc := WordApp.Documents.Add(EmptyParam, EmptyParam); ADoc.Sections.Item(1).Headers.Item(wdHeaderFooterPrimary).Range.Text := 'This is a header'; { ... }
| Как добавить текст в footer документа? |
Footer:
Code: { ... } ADoc := WordApp.Documents.Add(EmptyParam, EmptyParam); ADoc.Sections.Item(1).Footers.Item(wdHeaderFooterPrimary).Range.Text := 'This is a footer'; { ... }
| Как работать с объектом Excel вставленном в Word документ? |
Code: { ... } Var. AWordApplication: WordApplication; AWordDocument: WordDocument; AWorkBook: ExcelWorkBook; AWorkSheet: ExcelWorkSheet; AInlineShape: InlineShape; AFileName: OleVariant; TrueParam: OleVariant; Begin. AWordApplication := CoWordApplication.Create; Try. FalseParam := False; AFileName := 'c:\wordexcel.doc'; AWordDocument := AWordApplication.Documents.Open(AFileName, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam); AInlineShape := AWordDocument.InlineShapes.Item(1); AInlineShape.Activate; AWorkBook := AWordDocument.InlineShapes.Item(1).OLEFormat.Object_ as. ExcelWorkBook; AWorkSheet := AWorkBook.ActiveSheet as ExcelWorkSheet; ShowMessage(AWorkSheet.Cells.Item[2, 1].Text); Finally. AWordApplication.Quit(FalseParam, EmptyParam, EmptyParam); AWordApplication := nil; AWordDocument := nil; End; End;
| Как сделать поиск/замену в документе? |
You should use a variant because the Find.Execute method is a bit buggy. Something like this, for example:
Code: { ... } Var. Rnge: OleVariant; { ... } Rnge := Doc.Content; Rnge.Find.Execute('old', Wrap := wdFindContinue, ReplaceWith := 'new', Replace := WdReplaceAll); { ... }
©Drkb::04429.
Code: { ... } { Create the OLE Object }. WordApp := CreateOLEObject('Word.Application'); WordApp.Documents.Open(yourDocFile); WordApp.Selection.Find.ClearFormatting; WordApp.Selection.Find.Text := yourOldStr; WordApp.Selection.Find.Replacement.Text := yourNewStr; WordApp.Selection.Find.Forward := True; WordApp.Selection.Find.Wrap := 1; {wdFindContinue}. WordApp.Selection.Find.Format := False; WordApp.Selection.Find.MatchCase := False; WordApp.Selection.Find.MatchWholeWord := False; WordApp.Selection.Find.MatchWildcards := True; WordApp.Selection.Find.MatchSoundsLike := False; WordApp.Selection.Find.MatchAllWordForms := False; WordApp.Selection.Find.Execute(Replace := 2); {wdReplaceAll}. {Or as alternative: WordApp.Selection.Find.Execute(Replace := 1); for one replace}. WordApp.ActiveDocument.SaveAs(yourNewDocFile); WordApp.Quit; WordApp := Unassigned; { ... }
©Drkb::04430.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
Code: { **** UBPFD *********** by kladovka.net.ru **** >> Заменить строки в файле Word. Функция заменяет файлы в документе word с опциями поиска и замены. Зависимости: ComObj. Автор: [NIKEL], nikel@pisem.net, Norilsk. Copyright: Some help. Дата: 15 сентября 2002 г. ********************************************** } Uses ComObj; // Флаги замены. Type. TWordReplaceFlags = set of (wrfReplaceAll, wrfMatchCase, wrfMatchWildcards); Function WordStringReplace(ADocument: TFileName; SearchString, ReplaceString: string; Flags: TWordReplaceFlags): Boolean; Const. WdFindContinue = 1; WdReplaceOne = 1; WdReplaceAll = 2; WdDoNotSaveChanges = 0; Var. WordApp: OLEVariant; Begin. Result := False; { Существует ли файл }. If not FileExists(ADocument) then. Begin. ShowMessage('Файл не найден!'); Exit; End; { Создаем OLE объект }. Try. WordApp := CreateOLEObject('Word.Application'); Except. On E: Exception do. Begin. E.Message := 'Word недоступен'; Raise; End; End; Try. { Прячем Word }. WordApp.Visible := False; { Открываем документ }. WordApp.Documents.Open(ADocument); { Инициализируем параметры}. WordApp.Selection.Find.ClearFormatting; WordApp.Selection.Find.Text := SearchString; WordApp.Selection.Find.Replacement.Text := ReplaceString; WordApp.Selection.Find.Forward := True; WordApp.Selection.Find.Wrap := wdFindContinue; WordApp.Selection.Find.Format := False; WordApp.Selection.Find.MatchCase := wrfMatchCase in Flags; WordApp.Selection.Find.MatchWholeWord := False; WordApp.Selection.Find.MatchWildcards := wrfMatchWildcards in Flags; WordApp.Selection.Find.MatchSoundsLike := False; WordApp.Selection.Find.MatchAllWordForms := False; { Ищем}. If wrfReplaceAll in Flags then. WordApp.Selection.Find.Execute(Replace := wdReplaceAll). Else. WordApp.Selection.Find.Execute(Replace := wdReplaceOne); { Сохраняем word }. WordApp.ActiveDocument.SaveAs(ADocument); { Всё нормально }. Result := True; { Закрываем document }. WordApp.ActiveDocument.Close(wdDoNotSaveChanges); Finally. { Закрываем Word }. WordApp.Quit; WordApp := Unassigned; End; End;
| 12 AM - это полдень или полночь? |
Очень-очень странные эти англоязычные люди... И счёт времени у них странный!
| Как добавить TCheckBox в TStringGrid? |
Автор: Joel E. Cant.
Пример демонстрирует добавление любого количества чекбоксов в StringGrid. В этом примере необходимо добавить TPanel, а в саму панель включить TstringGrid. Так же необходимо добавить невидимый TcheckBox на форму. Затем добавьте5 колонок и 4 строки в объект StringGrid.
Code: Procedure TForm1.CheckBox1Click(Sender: TObject); Begin. ShowMessage('There it is!!'); End; // Заполняем заголовок StringGrid. Procedure TForm1.FormCreate(Sender: TObject); Begin. StringGrid1.Cells[0,0] := 'A Simple'; StringGrid1.Cells[1,0] := 'Way'; StringGrid1.Cells[2,0] := 'To'; StringGrid1.Cells[3,0] := 'Do It'; StringGrid1.Cells[4,0] := 'Check !!'; AddCheckBoxes; // добавляем чекбоксы... End; Procedure TForm1.AddCheckBoxes; Var i: Integer; NewCheckBox: TCheckBox; Begin. Clean_previus_buffer; // очищаем неиспользуемые чекбоксы... For i := 1 to 4 do. Begin. StringGrid1.Cells[0,i] := 'a'; StringGrid1.Cells[1,i] := 'b'; StringGrid1.Cells[2,i] := 'c'; StringGrid1.Cells[3,i] := 'd'; NewCheckBox := TCheckBox.Create(Application); NewCheckBox.Width := 0; NewCheckBox.Visible := false; NewCheckBox.Caption := 'OK'; NewCheckBox.Color := clWindow; NewCheckBox.Tag := i; NewCheckBox.OnClick := CheckBox1.OnClick; //Связываем предыдущее событие OnClick. // с существующим TCheckBox. NewCheckBox.Parent := Panel1; StringGrid1.Objects[4,i] := NewCheckBox; StringGrid1.RowCount := i; End; Set_checkbox_alignment; // расположение чекбоксов в ячейках таблицы... End; Procedure TForm1.clean_previus_buffer; Var NewCheckBox: TCheckBox; I: Integer; Begin. For i := 1 to StringGrid1.RowCount do. Begin. NewCheckBox := (StringGrid1.Objects[4,i] as TCheckBox); If NewCheckBox <> nil then. Begin. NewCheckBox.Visible := false; StringGrid1.Objects[4,i] := nil; End; End; End; Procedure TForm1.set_checkbox_alignment; Var NewCheckBox: TCheckBox; Rect: TRect; I: Integer; Begin. For i := 1 to StringGrid1.RowCount do. Begin. NewCheckBox := (StringGrid1.Objects[4,i] as TCheckBox); If NewCheckBox <> nil then. Begin. Rect := StringGrid1.CellRect(4,i); // получаем размер ячейки для чекбокса. NewCheckBox.Left := StringGrid1.Left + Rect.Left+2; NewCheckBox.Top := StringGrid1.Top + Rect.Top+2; NewCheckBox.Width := Rect.Right - Rect.Left; NewCheckBox.Height := Rect.Bottom - Rect.Top; NewCheckBox.Visible := True; End; End; End; Procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); Begin. If not (gdFixed in State) then set_checkbox_alignment; End;
| Как работать с WordBasic? |
Code: Uses OleAuto; Var. MSWord: Variant; Begin. MsWord := CreateOleObject('Word.Basic'); MsWord.FileNewDefault; MsWord.TogglePortrait; End;
| Как конвертировать RFC дату и обратно? |
Code: Function DateTimeToRfcTime( Dt: TDateTime; IDiff: integer; BlnGMT: boolean = false): string; {* Explanation: IDiff is the local offset to GMT in minutes. If blnGMT then Result is UNC time else local time. E.g. local time zone: ET = GMT - 5hr = -300 minutes. Dt is TDateTime of 3 Jan 2001 5:45am. BlnGMT = true -> Result = 'Wed, 03 Jan 2001 05:45:00 GMT' BlnGMT = false -> Result = 'Wed, 03 Jan 2001 05:45:00 -0500' *} Const. Weekday: array[1..7] of string = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); Month: array[1..12] of string = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); Var. IDummy: Word; IYear: Word; IMonth: Word; IDay: Word; IHour: Word; IMinute: Word; ISecond: Word; StrZone: string; Begin. If blnGMT then. Begin. Dt := dt - iDiff / 1440; StrZone := 'GMT'; End. Else. Begin. IDiff := (iDiff div 60) * 100 + (iDiff mod 60); If iDiff < 0 then. StrZone := Format('-%.4d', [-iDiff]). Else. StrZone := Format('+%.4d', [iDiff]); End; DecodeDate(dt, iYear, iMonth, iDay); DecodeTime(dt, iHour, iMinute, iSecond, iDummy); Result := Format('%s, %.2d %s %4d %.2d:%.2d:%.2d %s', [ Weekday[DayOfWeek(dt)], iDay, Month[iMonth], iYear, IHour, iMinute, iSecond, strZone]); End; Function RfcTimeToDateTime( StrTime: string; BlnGMT: boolean = true): TDateTime; {* Explanation: If blnGMT then Result is UNC time else local time. E.g. local time zone: ET = GMT - 5hr = -0500. StrTime = 'Wed, 03 Jan 2001 05:45:00 -0500' BlnGMT = true -> FormatDateTime('...', Result) = '03.01.2001 10:45:00' BlnGMT = false -> FormatDateTime('...', Result) = '03.01.2001 05:45:00' *} Const. Wd = 'sun#mon#tue#wed#thu#fri#sat'; Month = 'janfebmaraprmayjunjulaugsepoctnovdec'; Var. S: string; Dd: Word; Mm: Word; Yy: Word; Hh: Word; Nn: Word; Ss: Word; Begin. S := LowerCase(Copy(strTime, 1, 3)); If Pos(s, wd) > 0 then. Delete(strTime, 1, Pos(' ', strTime)); S := Trim(Copy(strTime, 1, Pos(' ', strTime))); Delete(strTime, 1, Length(s) + 1); Dd := StrToIntDef(s, 0); S := LowerCase(Copy(strTime, 1, 3)); Delete(strTime, 1, 4); Mm := (Pos(s, month) div 3) + 1; S := Copy(strTime, 1, 4); Delete(strTime, 1, 5); Yy := StrToIntDef(s, 0); Result := EncodeDate(yy, mm, dd); S := strTime[1] + strTime[2]; Hh := StrToIntDef(strTime[1] + strTime[2], 0); Nn := StrToIntDef(strTime[4] + strTime[5], 0); Ss := StrToIntDef(strTime[7] + strTime[8], 0); Delete(strTime, 1, 9); Result := Result + EncodeTime(hh, nn, ss, 0); If (CompareText(strTime, 'gmt') <> 0) and blnGMT then. Begin. Hh := StrToIntDef(strTime[2] + strTime[3], 0); Nn := StrToIntDef(strTime[4] + strTime[5], 0); If strTime[1] = '+' then. Result := Result - EncodeTime(hh, nn, 0, 0). Else. Result := Result + EncodeTime(hh, nn, 0, 0); End; End;
| Как подсчитать возраст по дню рождения? |
Code: { BrthDate: Date of birth }. Function TFFuncs.CalcAge(brthdate: TDateTime): Integer; Var. Month, day, year, bmonth, bday, byear: word; Begin. DecodeDate(BrthDate, byear, bmonth, bday); If bmonth = 0 then. Result := 0. Else. Begin. DecodeDate(Date, year, month, day); Result := year - byear; If (100 * month + day) < (100 * bmonth + bday) then. Result := result - 1; End; End;
©Drkb::00193.
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. Month, Day, Year, CurrentMonth, CurrentDay, CurrentYear: word; Age: integer; Begin. DecodeDate(DateTimePicker1.Date, Year, Month, Day); DecodeDate(Date, CurrentYear, CurrentMonth, CurrentDay); If (Year = CurrentYear) and (Month = CurrentMonth) and (Day = CurrentDay) then. Age := 0. Else. Begin. Age := CurrentYear - Year; If (Month > CurrentMonth) then. Dec(Age). Else if Month = CurrentMonth then. If (Day > CurrentDay) then. Dec(Age); End; Label1.Caption := IntToStr(Age); End;
| Проверка правильности даты. |
Code: Function DateExists(Date: string; Separator: char): Boolean; Var. OldDateSeparator: Char; Begin. Result := True; OldDateSeparator := DateSeparator; DateSeparator := Separator; Try. Try. StrToDate(Date); Except. Result := False; End; Finally. DateSeparator := OldDateSeparator; End; End; Procedure TForm1.FormCreate(Sender: TObject); Begin. If DateExists('35.3.2001', '.') then. Begin. {your code}. End; End;
| Конвертируем Unix дату. |
The value is a Unix Time, defined as seconds since 1970-01-01T00:00:00,0Z. Important is the Letter Z, you live in Sweden, in consequence you must add 1 hour for StandardDate and 2 hours for DaylightDate to the date. The infos you can get with GetTimeZoneInformation. But you must determine, which Bias (Standard or Daylight) is valid for the date (in this case -60). You can convert the date value with the function below.
The Date for 977347109 is 2000-12-20T22:18:29+01:00.
Code: Const. UnixDateDelta = 25569; { 1970-01-01T00:00:00,0 }. SecPerMin = 60; SecPerHour = SecPerMin * 60; SecPerDay = SecPerHour * 24; MinDayFraction = 1 / (24 * 60); {Convert Unix time to TDatetime}. Function UnixTimeToDateTime(AUnixTime: DWord; ABias: Integer): TDateTime; Begin. Result := UnixDateDelta + (AUnixTime div SecPerDay) { Days }. + ((AUnixTime mod SecPerDay) / SecPerDay) { Seconds }. - ABias * MinDayFraction { Bias to UTC in minutes }; End; {Convert Unix time to String with locale settings}. Function UnixTimeToStr(AUnixTime: DWord; ABias: Integer): string; Begin. Result := FormatDateTime('ddddd hh:nn:ss', UnixTimeToDateTime(AUnixTime, ABias)); End; {Convert TDateTime to Unix time}. Function DateTimeToUnixTime(ADateTime: TDateTime; ABias: Integer): DWord; Begin. Result := Trunc((ADateTime - UnixDateDelta) * SecPerDay) + ABias * SecPerMin; End; Procedure TForm1.Button4Click(Sender: TObject); Begin. Label1.Caption := UnixTimeToStr(977347109, -60); End;
| Как получить дату по Юлианскому календарю? |
Code: Function julian(year, month, day: Integer): real; Var. Yr, mth: Integer; Noleap, leap, days, yrs: Real; Begin. If year < 0 then yr := year + 1 else yr := year; Mth := month; If (month < 3) then. Begin. Mth := mth + 12; Yr := yr - 1; End; Yrs := 365.25 * yr; If ((yrs < 0) and (frac(yrs) <> 0)) then yrs := int(yrs) - 1 else yrs := int(yrs); Days := int(yrs) + int(30.6001 * (mth + 1)) + day - 723244.0; If days < -145068.0 then julian := days. Else. Begin. Yrs := yr / 100.0; If ((yrs < 0) and (frac(yrs) <> 0)) then yrs := int(yrs) - 1; Noleap := int(yrs); Yrs := noleap / 4.0; If ((yrs < 0) and (frac(yrs) <> 0)) then yrs := int(yrs) - 1; Leap := 2 - noleap + int(yrs); Julian := days + leap; End; End;
| Как поместить ComboBox в ячейку StringGrid? |
Следующий пример демонстрирует всплывающий ComboBox в качестве местного редактора для компонента TStringGrid:
Code: Procedure TForm1.FormCreate(Sender: TObject); Begin. {Высоту у combobox не получится установить, поэтому мы будем}. {подгонять размер у грида под размер combobox!}. StringGrid1.DefaultRowHeight := ComboBox1.Height; {Скрываем combobox}. ComboBox1.Visible := False; End; Procedure TForm1.ComboBox1Change(Sender: TObject); Begin. {Получаем выбранный элемент из ComboBox и помещаем его в грид}. StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row] := ComboBox1.Items[ComboBox1.ItemIndex]; ComboBox1.Visible := False; StringGrid1.SetFocus; End; Procedure TForm1.ComboBox1Exit(Sender: TObject); Begin. {Получаем выбранный элемент из ComboBox и помещаем его в грид}. StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row] := ComboBox1.Items[ComboBox1.ItemIndex]; ComboBox1.Visible := False; StringGrid1.SetFocus; End; Procedure TForm1.StringGrid1SelectCell(Sender: TObject; Col, Row: Integer; var CanSelect: Boolean); Var. R: TRect; Begin. If ((Col = 3) AND. (Row <> 0)) then begin. {Размер и расположение combobox подгоняем под ячейку}. R := StringGrid1.CellRect(Col, Row); R.Left := R.Left + StringGrid1.Left; R.Right := R.Right + StringGrid1.Left; R.Top := R.Top + StringGrid1.Top; R.Bottom := R.Bottom + StringGrid1.Top; ComboBox1.Left := R.Left + 1; ComboBox1.Top := R.Top + 1; ComboBox1.Width := (R.Right + 1) - R.Left; ComboBox1.Height := (R.Bottom + 1) - R.Top; {Показываем combobox}. ComboBox1.Visible := True; ComboBox1.SetFocus; End; CanSelect := True; End;
| Как получить UNC путь к файлу? |
Code: Function GetUNCName(PathStr: string): string; Var. BufSize: DWord; Buf: TUniversalNameInfo; Msg: string; Begin. BufSize := SizeOf(TUniversalNameInfo); If (WNetGetUniversalName(PChar(PathStr), UNIVERSAL_NAME_INFO_LEVEL, Buf, bufSize) > 0) then. Case GetLastError of. ERROR_BAD_DEVICE: msg := 'ERROR_BAD_DEVICE'; ERROR_CONNECTION_UNAVAIL: msg := 'ERROR_CONNECTION_UNAVAIL'; ERROR_EXTENDED_ERROR: msg := 'ERROR_EXTENDED_ERROR'; ERROR_MORE_DATA: msg := 'ERROR_MORE_DATA'; ERROR_NOT_SUPPORTED: msg := 'ERROR_NOT_SUPPORTED'; ERROR_NO_NET_OR_BAD_PATH: msg := 'ERROR_NO_NET_OR_BAD_PATH'; ERROR_NO_NETWORK: msg := 'ERROR_NO_NETWORK'; ERROR_NOT_CONNECTED: msg := 'ERROR_NOT_CONNECTED'; End. Else. Msg := buf.lpUniversalName; Result := msg; End;
| Использование классовых методов для выявления утечек памяти. |
Class Methods aply to the class level, in other words you donґt need an instance to call the method.
I wish we could define class objects as well, but they doesnґt exist in Object Pascal, so we will do a trick, we are going to define a variable in the implementation section of the unit, this variable will hold the number of instances the class will have in a moment in time. Object Oriented purist might claim about it, but it works, nobody is perfect (not even Delphi!).
For example say you need to create instances of a class named TFoo, so you create the following Unit.
We will define two class procedures: AddInstance(to increse the counter of instances) and ReleaseInstance(to decrese the number of instances), these are called in the constructor and the destructor acordingly. Finally we define a class function NumOfInstances which returns the actual number of instances.
Add a Initilialization and a Finalization section to the Unit, in the Finalization section ask if the number of instances is <> 0, if this is the case you known that you didinґt destroy all the objects that you created.
Code: Unit U_Foo; Interface. Uses. Classes, Windows, SysUtils; Type. TFoo = class. Private. Class procedure AddInstance; Class procedure ReleaseInstance; Public. Constructor Create; Destructor Destroy; override; Class function NumOfInstances: Integer; End; Implementation. Var. TFoo_Instances: Integer = 0; { TFoo }. Class procedure TFoo.AddInstance; Begin. Inc(TFoo_Instances); End; //end of TFoo.AddInstance. Constructor TFoo.Create; Begin. AddInstance; End; //end of TFoo.Create. Destructor TFoo.Destroy; Begin. ReleaseInstance; Inherited; End; //end of TFoo.Destroy. Class function TFoo.NumOfInstances: Integer; Begin. Result := TFoo_Instances; End; //end of TFoo.NumOfInstances. Class procedure TFoo.ReleaseInstance; Begin. Dec(TFoo_Instances); End; //end of TFoo.ReleaseInstance. Initialization. Finalization. If TFoo_Instances <> 0 then. MessageBox(0, PChar(Format('%d instances of TFoo active', [TFoo_Instances])), 'Warning', MB_OK or MB_ICONWARNING); End.
| Дельфи IAutoComplete интерфейс. |
Code: Unit uAutoComplete; Interface. Uses. Windows, SysUtils, Controls, Classes, ActiveX, ComObj, stdctrls, Forms, Messages; Const. IID_IAutoComplete: TGUID = '{00bb2762-6a77-11d0-a535-00c04fd7d062}'; IID_IAutoComplete2: TGUID = '{EAC04BC0-3791-11d2-BB95-0060977B464C}'; CLSID_IAutoComplete: TGUID = '{00BB2763-6A77-11D0-A535-00C04FD7D062}'; IID_IACList: TGUID = '{77A130B0-94FD-11D0-A544-00C04FD7d062}'; IID_IACList2: TGUID = '{470141a0-5186-11d2-bbb6-0060977b464c}'; CLSID_ACLHistory: TGUID = '{00BB2764-6A77-11D0-A535-00C04FD7D062}'; CLSID_ACListISF: TGUID = '{03C036F1-A186-11D0-824A-00AA005B4383}'; CLSID_ACLMRU: TGUID = '{6756a641-de71-11d0-831b-00aa005b4383}'; Type. IACList = interface(IUnknown). ['{77A130B0-94FD-11D0-A544-00C04FD7d062}']. Function Expand(pszExpand: POLESTR): HResult; stdcall; End; Const. {Options for IACList2}. ACLO_NONE = 0; {don't enumerate anything}. ACLO_CURRENTDIR = 1; {enumerate current directory}. ACLO_MYCOMPUTER = 2; {enumerate MyComputer}. ACLO_DESKTOP = 4; {enumerate Desktop Folder}. ACLO_FAVORITES = 8; {enumerate Favorites Folder}. ACLO_FILESYSONLY = 16; {enumerate only the file system}. Type. IACList2 = interface(IACList). ['{470141a0-5186-11d2-bbb6-0060977b464c}']. Function SetOptions(dwFlag: DWORD): HResult; stdcall; Function GetOptions(var pdwFlag: DWORD): HResult; stdcall; End; IAutoComplete = interface(IUnknown). ['{00bb2762-6a77-11d0-a535-00c04fd7d062}']. Function Init(hwndEdit: HWND; const punkACL: IUnknown; pwszRegKeyPath, PwszQuickComplete: POLESTR): HResult; stdcall; Function Enable(fEnable: BOOL): HResult; stdcall; End; Const. {Options for IAutoComplete2}. ACO_NONE = 0; ACO_AUTOSUGGEST = $1; ACO_AUTOAPPEND = $2; ACO_SEARCH = $4; ACO_FILTERPREFIXES = $8; ACO_USETAB = $10; ACO_UPDOWNKEYDROPSLIST = $20; ACO_RTLREADING = $40; Type. IAutoComplete2 = interface(IAutoComplete). ['{EAC04BC0-3791-11d2-BB95-0060977B464C}']. Function SetOptions(dwFlag: DWORD): HResult; stdcall; Function GetOptions(out pdwFlag: DWORD): HResult; stdcall; End; TEnumString = class(TInterfacedObject, IEnumString). Private. FStrings: TStringList; FCurrIndex: integer; Public. {IEnumString}. Function Next(celt: Longint; out elt; pceltFetched: PLongint): HResult; stdcall; Function Skip(celt: Longint): HResult; stdcall; Function Reset: HResult; stdcall; Function Clone(out enm: IEnumString): HResult; stdcall; {VCL}. Constructor Create; Destructor Destroy; override; End; TACOption = (acAutoAppend, acAutoSuggest, acUseArrowKey); TACOptions = set of TACOption; TACSource = (acsList, acsHistory, acsMRU, acsShell); TACEdit = class(TEdit). Private. FACList: TEnumString; FAutoComplete: IAutoComplete; FACEnabled: boolean; FACOptions: TACOptions; FACSource: TACSource; Function GetACStrings: TStringList; Procedure SetACEnabled(const Value: boolean); Procedure SetACOptions(const Value: TACOptions); Procedure SetACSource(const Value: TACSource); Protected. Procedure CreateWnd; override; Procedure DestroyWnd; override; Public. Constructor Create(AOwner: TComponent); override; Destructor Destroy; override; Published. Property ACStrings: TStringList read GetACStrings; Property ACEnabled: boolean read FACEnabled write SetACEnabled; Property ACOptions: TACOptions read FACOptions write SetACOptions; Property ACSource: TACSource read FACSource write SetACSource; End; Implementation. { IUnknownInt }. Function TEnumString.Clone(out enm: IEnumString): HResult; Begin. Result := E_NOTIMPL; Pointer(enm) := nil; End; Constructor TEnumString.Create; Begin. Inherited Create; FStrings := TStringList.Create; FCurrIndex := 0; End; Destructor TEnumString.Destroy; Begin. FStrings.Free; Inherited; End; Function TEnumString.Next(celt: Integer; out elt; pceltFetched: PLongint): HResult; Var. I: Integer; WStr: WideString; Begin. I := 0; While (I < celt) and (FCurrIndex < FStrings.Count) do. Begin. WStr := FStrings[FCurrIndex]; TPointerList(elt)[I] := CoTaskMemAlloc(2 * (Length(wStr) + 1)); StringToWideChar(wStr, TPointerList(elt)[I], 2 * (Length(wStr) + 1)); Inc(I); Inc(FCurrIndex); End; If pceltFetched <> nil then. PceltFetched^ := I; If I = celt then. Result := S_OK. Else. Result := S_FALSE; End; Function TEnumString.Reset: HResult; Begin. FCurrIndex := 0; Result := S_OK; End; Function TEnumString.Skip(celt: Integer): HResult; Begin. If (FCurrIndex + celt) <= FStrings.Count then. Begin. Inc(FCurrIndex, celt); Result := S_OK; End. Else. Begin. FCurrIndex := FStrings.Count; Result := S_FALSE; End; End; { TACEdit }. Constructor TACEdit.Create(AOwner: TComponent); Begin. Inherited; FACList := TEnumString.Create; FACEnabled := true; FACOptions := [acAutoAppend, acAutoSuggest, acUseArrowKey]; End; Procedure TACEdit.CreateWnd; Var. Dummy: IUnknown; Strings: IEnumString; Begin. Inherited; If HandleAllocated then. Begin. Try. Dummy := CreateComObject(CLSID_IAutoComplete); If (Dummy <> nil) and (Dummy.QueryInterface(IID_IAutoComplete, FAutoComplete) = S_OK) then. Begin. Case FACSource of. AcsHistory: Strings := CreateComObject(CLSID_ACLHistory) as IEnumString; AcsMRU: Strings := CreateComObject(CLSID_ACLMRU) as IEnumString; AcsShell: Strings := CreateComObject(CLSID_ACListISF) as IEnumString; Else. Strings := FACList as IEnumString; End; If S_OK = FAutoComplete.Init(Handle, Strings, nil, nil) then. Begin. SetACEnabled(FACEnabled); SetACOptions(FACOptions); End; End; Except. {CLSID_IAutoComplete is not available}. End; End; End; Destructor TACEdit.Destroy; Begin. FACList := nil; Inherited; End; Procedure TACEdit.DestroyWnd; Begin. If (FAutoComplete <> nil) then. Begin. FAutoComplete.Enable(false); FAutoComplete := nil; End; Inherited; End; Function TACEdit.GetACStrings: TStringList; Begin. Result := FACList.FStrings; End; Procedure TACEdit.SetACEnabled(const Value: boolean); Begin. If (FAutoComplete <> nil) then. Begin. FAutoComplete.Enable(FACEnabled); End; FACEnabled := Value; End; Procedure TACEdit.SetACOptions(const Value: TACOptions); Const. Options: array[TACOption] of integer = (ACO_AUTOAPPEND, ACO_AUTOSUGGEST, ACO_UPDOWNKEYDROPSLIST); Var. Option: TACOption; Opt: DWORD; AC2: IAutoComplete2; Begin. If (FAutoComplete <> nil) then. Begin. If S_OK = FAutoComplete.QueryInterface(IID_IAutoComplete2, AC2) then. Begin. Opt := ACO_NONE; For Option := Low(Options) to High(Options) do. Begin. If (Option in FACOptions) then. Opt := Opt or DWORD(Options[Option]); End; AC2.SetOptions(Opt); End; End; FACOptions := Value; End; Procedure TACEdit.SetACSource(const Value: TACSource); Begin. If FACSource <> Value then. Begin. FACSource := Value; RecreateWnd; End; End; Initialization. Finalization. End.
| Как проверить правильность штрих-кода? |
Solve 1:
I want to publish a code for checksum calculation by modulus 10 which is used in the barcodes. I must say that this "mod10" is specifical so readf an article if you're interested.
This algorithm is very popular for UPC barcodes (Universal Product Code), hash code or serial number generation for applications etc...
The basic algorithm:
1. 1. add the values of the digits in the odd positions (1, 3, 5...)
2. 2. multiply this result by 3
3. 3. add the values of the digits in the even positions (2, 4, 6...)
4. 4. sum the results of steps 2 and 3
5. 5. the check digit is the smallest number which, when added to the result in step 4, produces a multiple of 10.
Small example. Assume the source data is 08137919805.
1. 1. 0+1+7+1+8+5=22
2. 2. 22*3=66
3. 3. 8+3+9+9+0=29
4. 4. 66+29=95
5. 5. 95+??=100 where ?? is a 5 (our checksum)
My implementation in the Pascal:
Code: Function Mod10(const Value: string): Integer; Var. I, intOdd, intEven: Integer; Begin. {add all odd seq numbers}. IntOdd := 0; I := 1; While (i <= Length(Value)) do. Begin. Inc(intOdd, StrToIntDef(Value[i], 0)); Inc(i, 2); End; {add all even seq numbers}. IntEven := 0; I := 2; While (i <= Length(Value)) do. Begin. Inc(intEven, StrToIntDef(Value[i], 0)); Inc(i, 2); End; Result := 3 * intOdd + intEven; {modulus by 10 to get}. Result := Result mod 10; If Result <> 0 then. Result := 10 - Result. End;
| Как конвертировать кодовую страницу? |
All Systems (Win 95+ and WinNT4+) with MS Internet Explorer 4 and newer have a library named mlang.dll in the Winnt\System32 directory. Usually you can tell Delphi to simply import these COM Libraries. This one however, Delphi did not. I started to convert the "most wanted" interface for myself. The results I present you here.
First I give you the code for the conversion unit, that allows you simply convert any text from code page interpretation into another one. Following I will shortly discuss the code and give you a sample of how to use it.
UCodePageConverter.
Code: {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Unit Name : uCodePageConverter. * Autor : Daniel Wischnewski. * Copyright : Copyright © 2002 by gate(n)etwork. All Right Reserved. * Urheber : Daniel Wischnewski. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} Unit uCodePageConverter; Interface. Uses. Windows; Const. IID_MLangConvertCharset: TGUID = '{D66D6F98-CDAA-11D0-B822-00C04FC9B31F}'; CLASS_MLangConvertCharset: TGUID = '{D66D6F99-CDAA-11D0-B822-00C04FC9B31F}'; Type. TagMLCONVCHARF = DWORD; Const. MLCONVCHARF_AUTODETECT: tagMLCONVCHARF = 1; MLCONVCHARF_ENTITIZE: tagMLCONVCHARF = 2; Type. TagCODEPAGE = UINT; Const. CODEPAGE_Thai: tagCODEPAGE = 0874; CODEPAGE_Japanese: tagCODEPAGE = 0932; CODEPAGE_Chinese_PRC: tagCODEPAGE = 0936; CODEPAGE_Korean: tagCODEPAGE = 0949; CODEPAGE_Chinese_Taiwan: tagCODEPAGE = 0950; CODEPAGE_UniCode: tagCODEPAGE = 1200; CODEPAGE_Windows_31_EastEurope: tagCODEPAGE = 1250; CODEPAGE_Windows_31_Cyrillic: tagCODEPAGE = 1251; CODEPAGE_Windows_31_Latin1: tagCODEPAGE = 1252; CODEPAGE_Windows_31_Greek: tagCODEPAGE = 1253; CODEPAGE_Windows_31_Turkish: tagCODEPAGE = 1254; CODEPAGE_Hebrew: tagCODEPAGE = 1255; CODEPAGE_Arabic: tagCODEPAGE = 1256; CODEPAGE_Baltic: tagCODEPAGE = 1257; Type. IMLangConvertCharset = interface. ['{D66D6F98-CDAA-11D0-B822-00C04FC9B31F}']. Function Initialize( UiSrcCodePage: tagCODEPAGE; uiDstCodePage: tagCODEPAGE; DwProperty: tagMLCONVCHARF. ): HResult; stdcall; Function GetSourceCodePage( Out puiSrcCodePage: tagCODEPAGE. ): HResult; stdcall; Function GetDestinationCodePage( Out puiDstCodePage: tagCODEPAGE. ): HResult; stdcall; Function GetProperty(out pdwProperty: tagMLCONVCHARF): HResult; stdcall; Function DoConversion( PSrcStr: PChar; pcSrcSize: PUINT; pDstStr: PChar; pcDstSize: PUINT. ): HResult; stdcall; Function DoConversionToUnicode( PSrcStr: PChar; pcSrcSize: PUINT; pDstStr: PWChar; pcDstSize: PUINT. ): HResult; stdcall; Function DoConversionFromUnicode( PSrcStr: PWChar; pcSrcSize: PUINT; pDstStr: PChar; pcDstSize: PUINT. ): HResult; stdcall; End; CoMLangConvertCharset = class. Class function Create: IMLangConvertCharset; Class function CreateRemote(const MachineName: string): IMLangConvertCharset; End; Implementation. Uses. ComObj; { CoMLangConvertCharset }. Class function CoMLangConvertCharset.Create: IMLangConvertCharset; Begin. Result := CreateComObject(CLASS_MLangConvertCharset) as IMLangConvertCharset; End; Class function CoMLangConvertCharset.CreateRemote( Const MachineName: string. ): IMLangConvertCharset; Begin. Result := CreateRemoteComObject( MachineName, CLASS_MLangConvertCharset. ) as IMLangConvertCharset; End; End.
Конвертация римских цифр в арабские.
| Конвертация римских цифр в арабские. |
Code: Function RomanToDec(const Value: string): integer; Var. I, lastValue, curValue: integer; Begin. Result := 0; LastValue := 0; For i := Length(Value) downto 1 do. Begin. Case UpCase(Value[i]) of. 'C': CurValue := 100; 'D': CurValue := 500; 'I': CurValue := 1; 'L': CurValue := 50; 'M': CurValue := 1000; 'V': CurValue := 5; 'X': CurValue := 10; Else. Raise Exception.CreateFmt('Invalid character: %s', [Value[i]]); End; If curValue < lastValue then. Dec(Result, curValue). Else. Inc(Result, curValue); LastValue := curValue; End; End;
©Drkb::04058.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как узнать физические координаты каретки в пикселях? |
Code: {TRichEdit}. Var. Pt: TPoint; Begin. With richedit1 do. Begin. Perform(messages.EM_POSFROMCHAR, WPARAM(@pt), selstart); Label1.caption := Format('(%d, %d)', [pt.x, pt.y]); End; End; {TMemo and TEdit}. Var. R: LongInt; Begin. With memo1 do. Begin. R := Perform(messages.EM_POSFROMCHAR, selstart, 0); If r >= 0 then. Begin. Label1.caption := IntToStr(HiWord(r)); Label2.caption := IntToStr(LoWord(r)); End; End; End;
| Как сделать Memo с закруглёнными краями? |
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. Rgn: HRGN; R: TRect; Begin. R := memo1.ClientRect; Rgn := CreateRoundRectRgn(r.Left, r.top, r.right, r.bottom, 20, 20); Memo1.BorderStyle := bsNone; Memo1.Perform(EM_GETRECT, 0, lparam(@r)); InflateRect(r, -5, -5); Memo1.Perform(EM_SETRECTNP, 0, lparam(@r)); SetWindowRgn(memo1.Handle, rgn, true); End;
| Как сделать различные подсказки для каждой ячейки в StringGrid? |
Следующий пример демонстрирует отслеживаение движения мышки в компоненте TStringGrid. Если мышка переместится на другую ячейку в гриде, то будет показано новое окно подсказки с номером колонки и строки данной ячейки:
Code: Type. TForm1 = class(TForm). StringGrid1: TStringGrid; Procedure StringGrid1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); Procedure FormCreate(Sender: TObject); Private. { Private declarations }. Col : integer; Row : integer; Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.FormCreate(Sender: TObject); Begin. StringGrid1.Hint := '0 0'; StringGrid1.ShowHint := True; End; Procedure TForm1.StringGrid1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); Var. R : integer; C : integer; Begin. StringGrid1.MouseToCell(X, Y, C, R); If ((Row <> r) or. (Col <> c)) then begin. Row := r; Col := c; Application.CancelHint; StringGrid1.Hint := IntToStr(r) + #32 + IntToStr(c); End; End;
| Как сделать pop-up Combobox по позиции курсора в Memo? |
Code: Unit CBoxInMemo; Interface. Uses. Windows, Classes, Controls, Graphics, Forms, StdCtrls; Type. TFrmCboxInMemo = class(TForm). Button1: TButton; Memo1: TMemo; Label1: TLabel; ComboBox1: TComboBox; Procedure Button1Click(Sender: TObject); Procedure ComboBox1Exit(Sender: TObject); Procedure ComboBox1Click(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. End; Var. FrmCboxInMemo: TFrmCboxInMemo; Implementation. {$R *.DFM}. Procedure TFrmCboxInMemo.Button1Click(Sender: TObject); Var. ClientPos: TPoint; LineHeight: Integer; TmpFont: TFont; Begin. GetCaretPos(clientPos); {Use the following calculation of line height only if you want your combobox. To appear below the char position you are referencing.}. TmpFont := Canvas.Font; Canvas.Font := Memo1.Font; LineHeight := Canvas.TextHeight('Xy'); Canvas.Font := tmpFont; With ComboBox1 do. Begin. {Adjustment of Top by lineHeight only necessary if combobox is to appear below line.}. Top := clientPos.Y + Memo1.Top + lineHeight; Left := clientPos.X + Memo1.Left; Visible := true; SetFocus; End; End; Procedure TFrmCboxInMemo.ComboBox1Exit(Sender: TObject); Begin. ComboBox1.Visible := false; End; Procedure TFrmCboxInMemo.ComboBox1Click(Sender: TObject); Begin. ComboBox1.Visible := false; End; End.
| Как загрузить юникоды в мемо? |
Code: Procedure LoadUnicodeFile(const filename: string; strings: TStrings); Procedure SwapWideChars(p: PWideChar); Begin. While p^ <> #0000 do. Begin. P^ := WideChar(Swap(Word(p^))); Inc(p); End; End; Var. Ms: TMemoryStream; Wc: WideChar; PWc: PWideChar; Begin. Ms := TMemoryStream.Create; Try. Ms.LoadFromFile(filename); Ms.Seek(0, soFromend); Wc := #0000; Ms.Write(wc, sizeof(wc)); PWC := ms.Memory; If pWc^ = #$FEFF then {normal byte order mark}. Inc(pWc). Else if pWc^ = #$FFFE then. Begin {byte order is big-endian}. SwapWideChars(pWc); Inc(pWc); End. Else. ; {no byte order mark}. Strings.Text := WideChartoString(pWc); Finally. Ms.free; End; End;
| Как изменить кодовую страницу шрифта принтера? |
Code: Uses Sysutils, Printers; Procedure TForm1.Button1Click(Sender: TObject); Var. Dosya: TextFile. Begin. With Printer do. Begin. AssignPrn(Dosya); Rewrite(Dosya); Printer.Canvas.Font.Name := 'Courier New'; Printer.Canvas.Font.Style := [fsBold]; Printer.Canvas.Font.Size := 18; //****for Turkish special characters. Writeln(Dosya, '?ьi??ц?'); //****set Font CharSet to Turkish(162). Printer.Canvas.Font.Charset := 162; Writeln(Dosya, '?ьi??ц?'); CloseFile(Dosya); End; End; The following table lists the predefined constants provided for standard character sets: Type. TFontCharset = 0..255;
| Как прервать печать и заставить печатать свой файл? |
Code: Uses. Printers; {$R *.DFM}. Procedure StartPrintToFile(filename: string); Var. CTitle: array[0..31] of Char; DocInfo: TDocInfo; Begin. With Printer do. Begin. BeginDoc; { Abort job just started on API level. }. EndPage(Canvas.handle); Windows.AbortDoc(Canvas.handle); { Restart it with a print file as destination. }. StrPLCopy(CTitle, Title, SizeOf(CTitle) - 1); FillChar(DocInfo, SizeOf(DocInfo), 0); With DocInfo do. Begin. CbSize := SizeOf(DocInfo); LpszDocName := CTitle; LpszOutput := PChar(filename); End; StartDoc(Canvas.handle, DocInfo); StartPage(Canvas.handle); End; End; Procedure TForm1.Button1Click(Sender: TObject); Begin. StartPrintToFile('C:\temp\temp.prn'); Try. Printer.Canvas.TextOut(100, 100, 'Hello World.'); Finally. Printer.endDoc; End; End;
| Как сделать предпросмотр? |
Code: Unit printpreview; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls; Type. TForm1 = class(TForm). Panel1: TPanel; Panel2: TPanel; PreviewPaintbox: TPaintBox; Label1: TLabel; Label2: TLabel; LeftMarginEdit: TEdit; TopMarginEdit: TEdit; Label3: TLabel; Label4: TLabel; RightMarginEdit: TEdit; Label5: TLabel; BottomMarginEdit: TEdit; ApplyMarginsButton: TButton; OrientationRGroup: TRadioGroup; Label6: TLabel; ZoomEdit: TEdit; ZoomUpDown: TUpDown; Procedure LeftMarginEditKeyPress(Sender: TObject; var Key: Char); Procedure FormCreate(Sender: TObject); Procedure PreviewPaintboxPaint(Sender: TObject); Procedure ApplyMarginsButtonClick(Sender: TObject); Private. { Private declarations }. PreviewText: string; Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. Uses printers; {$R *.DFM}. Procedure TForm1.LeftMarginEditKeyPress(Sender: TObject; var Key: Char); Begin. If not (Key in ['0'..'9', #9, DecimalSeparator]) then. Key := #0; End; Procedure TForm1.FormCreate(Sender: TObject); Var. S: string; Procedure loadpreviewtext; Var. Sl: TStringList; Begin. Sl := TStringList.Create; Try. Sl.Loadfromfile(Extractfilepath(application.exename) + 'printpreview.pas'); PreviewText := sl.Text; Finally. Sl.free. End; End; Begin. {Initialize the margin edits with a margin of 0.75 inch}. S := FormatFloat('0.00', 0.75); LeftMarginEdit.Text := S; TopMarginEdit.Text := S; RightMarginEdit.Text := S; BottomMarginEdit.Text := S; {Initialize the orientation radio group}. If Printer.Orientation = poPortrait then. OrientationRGroup.ItemIndex := 0. Else. OrientationRGroup.ItemIndex := 1; {load test text for display}. LoadPreviewtext; End; Procedure TForm1.PreviewPaintboxPaint(Sender: TObject); Var. Pagewidth, pageheight: Double; {printer page dimension in inch}. PrinterResX, printerResY: Integer; {printer resolution in dots/inch}. MinmarginX, minmarginY: Double; {nonprintable margin in inch}. Outputarea: TRect; {print area in 1/1000 inches}. Scale: Double; {conversion factor, pixels per 1/1000 inch}. Procedure InitPrintSettings; Function GetMargin(S: string; inX: Boolean): Double; Begin. Result := StrToFloat(S); If InX then. Begin. If Result < minmarginX then. Result := minmarginX; End. Else. Begin. If Result < minmarginY then. Result := minmarginY; End; End; Begin. PrinterResX := GetDeviceCaps(printer.handle, LOGPIXELSX); PrinterResY := GetDeviceCaps(printer.handle, LOGPIXELSY); Pagewidth := GetDeviceCaps(printer.handle, PHYSICALWIDTH) / printerResX; Pageheight := GetDeviceCaps(printer.handle, PHYSICALHEIGHT) / printerResY; MinmarginX := GetDeviceCaps(printer.handle, PHYSICALOFFSETX) / printerResX; MinmarginY := GetDeviceCaps(printer.handle, PHYSICALOFFSETY) / printerResY; Outputarea.Left := Round(GetMargin(LeftMarginEdit.Text, true) * 1000); Outputarea.Top := Round(GetMargin(TopMarginEdit.Text, false) * 1000); Outputarea.Right := Round((pagewidth - GetMargin(RightMarginEdit.Text, true)) * 1000); Outputarea.Bottom := Round((pageheight - GetMargin(BottomMarginEdit.Text, false)). * 1000); End; Procedure ScaleCanvas(Canvas: TCanvas; widthavail, heightavail: Integer); Var. Needpixelswidth, needpixelsheight: Integer; {dimensions of preview at current zoom factor in pixels}. Orgpixels: TPoint; {origin of preview in pixels}. Begin. {set up a coordinate system for the canvas that uses 1/1000 inch as unit, Honors the zoom factor and maintains the MM_TEXT orientation of the. Coordinate axis (origin in top left corner, positive Y axis points down}. Scale := Screen.PixelsPerInch / 1000; {Apply zoom factor}. Scale := scale * StrToInt(Zoomedit.text) / 100; {figure out size of preview}. Needpixelswidth := Round(pagewidth * 1000 * scale); Needpixelsheight := Round(pageheight * 1000 * scale); If needpixelswidth >= widthavail then. Orgpixels.X := 0. Else. Orgpixels.X := (widthavail - needpixelswidth) div 2; If needpixelsheight >= heightavail then. Orgpixels.Y := 0. Else. Orgpixels.Y := (heightavail - needpixelsheight) div 2; {change mapping mode to MM_ISOTROPIC}. SetMapMode(canvas.handle, MM_ISOTROPIC); {move viewport origin to orgpixels}. SetViewportOrgEx(canvas.handle, orgpixels.x, orgpixels.y, nil); {scale the window}. SetViewportExtEx(canvas.handle, Round(1000 * scale), Round(1000 * scale), nil); SetWindowExtEx(canvas.handle, 1000, 1000, nil); End; Begin. If OrientationRGroup.ItemIndex = 0 then. Printer.Orientation := poPortrait. Else. Printer.Orientation := poLandscape; InitPrintsettings; With Sender as TPaintBox do. Begin. ScaleCanvas(Canvas, ClientWidth, ClientHeight); {specify font height in 1/1000 inch}. Canvas.Font.Height := Round(font.height / font.pixelsperinch * 1000); {paint page white}. Canvas.Brush.Color := clWindow; Canvas.Brush.Style := bsSolid; Canvas.FillRect(Rect(0, 0, Round(pagewidth * 1000), Round(pageheight * 1000))); {draw the text}. DrawText(canvas.handle, PChar(PreviewText), Length(PreviewText), Outputarea, DT_WORDBREAK or DT_LEFT); {Draw thin gray lines to mark borders}. Canvas.Pen.Color := clGray; Canvas.Pen.Style := psSolid; Canvas.Pen.Width := 10; With Canvas do. Begin. MoveTo(outputarea.left - 100, outputarea.top); LineTo(outputarea.right + 100, outputarea.top); MoveTo(outputarea.left - 100, outputarea.bottom); LineTo(outputarea.right + 100, outputarea.bottom); MoveTo(outputarea.left, outputarea.top - 100); LineTo(outputarea.left, outputarea.bottom + 100); MoveTo(outputarea.right, outputarea.top - 100); LineTo(outputarea.right, outputarea.bottom + 100); End; End; End; Procedure TForm1.ApplyMarginsButtonClick(Sender: TObject); Begin. PreviewPaintbox.Invalidate; End; End.
| Как прочитать название порта принтера? |
Code: { ... } Uses. Printers, winspool; Function GetCurrentPrinterHandle: THandle; Const. Defaults: TPrinterDefaults = (pDatatype: nil; pDevMode: nil; DesiredAccess: PRINTER_ACCESS_USE or PRINTER_ACCESS_ADMINISTER); Var. Device, Driver, Port: array[0..255] of char; HDeviceMode: THandle; Begin. Printer.GetPrinter(Device, Driver, Port, hDeviceMode); If not OpenPrinter(@Device, Result, @Defaults) then. RaiseLastWin32Error; End; Procedure TForm1.Button1Click(Sender: TObject); Procedure Display(const prefix: string; S: PChar); Begin. Memo1.lines.add(prefix + string(S)); End; Var. PInfo: PPrinterInfo2; BytesNeeded: DWORD; HPrinter: THandle; I: Integer; Begin. For i := 0 to printer.Printers.Count - 1 do. Begin. Printer.PrinterIndex := i; HPrinter := GetCurrentPrinterHandle; Try. GetPrinter(hPrinter, 2, nil, 0, @bytesNeeded); PInfo := AllocMem(bytesNeeded); Try. GetPrinter(hPrinter, 2, pInfo, bytesNeeded, @bytesNeeded); Display('ServerName: ', pInfo^.pServerName); Display('PrinterName: ', pInfo^.pPrinterName); Display('ShareName: ', pInfo^.pShareName); Display('PortName: ', pInfo^.pPortName); Finally. FreeMem(pInfo); End; Finally. ClosePrinter(hPrinter); End; End; End;
| Как распечатать bitmap? |
Code: Procedure StretchPrint(R: TRect; ABitmap: Graphics.TBitmap); Var. Dc: HDC; IsDcPalDevice: Bool; HDibHeader: THandle; PDibHeader: pointer; HBits: THandle; PBits: pointer; Ppal: PLOGPALETTE; Pal: hPalette; Oldpal: hPalette; I: integer; Begin. Pal := 0; OldPal := 0; {Get the screen dc}. Dc := GetDc(0); {Allocate memory for a DIB structure}. HDibHeader := GlobalAlloc(GHND, sizeof(TBITMAPINFO) + (sizeof(TRGBQUAD) * 256)); {get a pointer to the alloced memory}. PDibHeader := GlobalLock(hDibHeader); {fill in the dib structure with info on the way we want the DIB}. FillChar(pDibHeader^, sizeof(TBITMAPINFO) + (sizeof(TRGBQUAD) * 256), #0); PBITMAPINFOHEADER(pDibHeader)^.biSize := sizeof(TBITMAPINFOHEADER); PBITMAPINFOHEADER(pDibHeader)^.biPlanes := 1; PBITMAPINFOHEADER(pDibHeader)^.biBitCount := 8; PBITMAPINFOHEADER(pDibHeader)^.biWidth := ABitmap.width; PBITMAPINFOHEADER(pDibHeader)^.biHeight := ABitmap.height; PBITMAPINFOHEADER(pDibHeader)^.biCompression := BI_RGB; {find out how much memory for the bits}. GetDIBits(dc, ABitmap.Handle, 0, ABitmap.height, nil, TBitmapInfo(pDibHeader^), DIB_RGB_COLORS); {Alloc memory for the bits}. HBits := GlobalAlloc(GHND, PBitmapInfoHeader(pDibHeader)^.BiSizeImage); {Get a pointer to the bits}. PBits := GlobalLock(hBits); {Call fn again, but this time give us the bits!}. GetDIBits(dc, ABitmap.Handle, 0, ABitmap.height, pBits, PBitmapInfo(pDibHeader)^, DIB_RGB_COLORS); {Release the screen dc}. ReleaseDc(0, dc); {Just incase the printer drver is a palette device}. IsDcPalDevice := false; If GetDeviceCaps(Printer.Canvas.Handle, RASTERCAPS) and RC_PALETTE = RC_PALETTE then. Begin. {Create palette from dib}. GetMem(pPal, sizeof(TLOGPALETTE) + (255 * sizeof(TPALETTEENTRY))); FillChar(pPal^, sizeof(TLOGPALETTE) + (255 * sizeof(TPALETTEENTRY)), #0); PPal^.palVersion := $300; PPal^.palNumEntries := 256; For i := 0 to (pPal^.PalNumEntries - 1) do. Begin. PPal^.palPalEntry[i].peRed := PBitmapInfo(pDibHeader)^.bmiColors[i].rgbRed; PPal^.palPalEntry[i].peGreen := PBitmapInfo(pDibHeader)^.bmiColors[i].rgbGreen; PPal^.palPalEntry[i].peBlue := PBitmapInfo(pDibHeader)^.bmiColors[i].rgbBlue; End; Pal := CreatePalette(pPal^); FreeMem(pPal, sizeof(TLOGPALETTE) + (255 * sizeof(TPALETTEENTRY))); OldPal := SelectPalette(Printer.Canvas.Handle, Pal, false); IsDcPalDevice := true. End; {send the bits to the printer}. StretchDiBits(Printer.Canvas.Handle, R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top, 0, 0, ABitmap.Width, ABitmap.Height, pBits, PBitmapInfo(pDibHeader)^, DIB_RGB_COLORS, SRCCOPY); {Just incase you printer drver is a palette device}. If isDcPalDevice = true then. Begin. SelectPalette(Printer.Canvas.Handle, oldPal, false); DeleteObject(Pal); End; {Clean up allocated memory}. GlobalUnlock(hBits); GlobalFree(hBits); GlobalUnlock(hDibHeader); GlobalFree(hDibHeader); End;
| Как изменить порт для принтера? |
Code: Uses. WinSpool; { Function SetPrinterToPort. Parameters : HPrinter: handle of printer to change, obtained from OpenPrinter. Port: port name to use, e.g. LPT1:, COM1:, FILE: Returns: The name of the previous port the printer was attached to. Description: Changes the port a printer is attached to using Win32 API functions. The changes made are NOT local to this process, they will affect all. Other processes that try to use this printer! It is recommended to set the. Port back to the old port returned by this function after. The end of the print job. Error Conditions: Will raise EWin32Error exceptions if SetPrinter or GetPrinter fail. Created: 21.10.99 by P. Below}. Function SetPrinterToPort(hPrinter: THandle; const port: string): string; Var. PInfo: PPrinterInfo2; BytesNeeded: DWORD; Begin. {Figure out how much memory we need for the data buffer. Note that GetPrinter is. Supposed to fail with a specific error code here. The amount of memory will. Be larger than Sizeof(TPrinterInfo2) since variable amounts of data are appended. To the record}. SetLastError(NO_ERROR); GetPrinter(hPrinter, 2, nil, 0, @bytesNeeded); If GetLastError <> ERROR_INSUFFICIENT_BUFFER then. RaiseLastWin32Error; PInfo := AllocMem(bytesNeeded); Try. If not GetPrinter(hPrinter, 2, pInfo, bytesNeeded, @bytesNeeded) then. RaiseLastWin32Error; With pInfo^ do. Begin. Result := pPortname; PPortname := @port[1]; End; If not SetPrinter(hPrinter, 2, pInfo, 0) then. RaiseLastWin32Error; Finally. FreeMem(pInfo); End; End; Function GetCurrentPrinterHandle: THandle; Var. Device, Driver, Port: array[0..255] of char; HDeviceMode: THandle; Begin. Printer.GetPrinter(Device, Driver, Port, hDeviceMode); If not OpenPrinter(@Device, Result, nil) then. RaiseLastWin32Error; End;
| Как перехватывать горячие клавиши в StringGrid? |
Следующий пример демонстрирует перехват сообщения CM_DIALOGCHAR на уровне формы. Это даст нам возможность реагировать на диалоговые комбинации клавишь только, если нажата клавиша Alt, не давая тем самым отработать стандартному обработчику.
Code: Type. TForm1 = class(TForm). Button1: TButton; StringGrid1: TStringGrid; Procedure FormCreate(Sender: TObject); Procedure Button1Click(Sender: TObject); Procedure StringGrid1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); Private. { Private declarations }. Procedure CMDialogChar(var Message: TCMDialogChar); Message CM_DIALOGCHAR; Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.FormCreate(Sender: TObject); Begin. Button1.Caption := 'E&xit'; End; Procedure TForm1.Button1Click(Sender: TObject); Begin. Application.Terminate; End; Procedure TForm1.StringGrid1KeyDown(Sender: TObject; Var Key: Word; Shift: TShiftState); Begin. ShowMessage('Grid keypress = ' + Char(Key)); Key := 0; End; Procedure TForm1.CMDialogChar(var Message: TCMDialogChar); Begin. If ssAlt in KeyDataToShiftState(Message.KeyData) then. Inherited; End;
| Возведение в степень. |
Code: Function Degree(const DegBasis, DegParam: Extended): Extended; Asm. // Вход: DegBasis, DegParam --> в стеке. // Выход: Result --> ST(0). // Примечание: // В случае некорректных данных в ST(0) возвращается DegParam. XOR EDX, EDX. FLD DegParam. FTST. FNSTSW AX. SAHF. JNZ @@DegParam_is_not_0. FFREE ST. FLD1. JMP @@exit. @@DegParam_is_not_0: JC @@DegParam_is_less_than_0. JMP @@cmp_DegBasis_to_0. @@DegParam_is_less_than_0: OR EDX, 1. @@cmp_DegBasis_to_0: FLD DegBasis. FTST. FNSTSW AX. SAHF. JNZ @@DegBasis_is_not_0. TEST EDX, 1. JZ @@DegParam_is_greater_than_0. FSTP ST. JMP @@exit. @@DegParam_is_greater_than_0: FSTP ST(1). JMP @@exit. @@DegBasis_is_not_0: TEST BYTE PTR DegBasis + 9, 80H. JZ @@DegBasis_is_greater_than_0. FABS. FXCH. OR EDX, 2. FLD ST. FRNDINT. FSUB ST, ST(1). FTST. FNSTSW AX. SAHF. FSTP ST. JZ @@DegParam_is_integer. FLD1. FDIV ST, ST(1). FABS. FLD1. FCOMP. FNSTSW AX. SAHF. JC @@1_div_Abs_DegParam_greater_or_equal_to_1. JZ @@1_div_Abs_DegParam_greater_or_equal_to_1. FSTP ST. FSTP ST(1). JMP @@exit. @@1_div_Abs_DegParam_greater_or_equal_to_1: FISTP QWORD PTR @@Int_64. TEST BYTE PTR @@Int_64, 1. JNZ @@continue. FSTP ST(1). JMP @@exit. @@DegParam_is_integer: FLD ST. FISTP QWORD PTR @@Int_64. TEST BYTE PTR @@Int_64, 1. JNZ @@continue. XOR EDX, EDX. @@continue: FXCH. @@DegBasis_is_greater_than_0: FYL2X. FLD ST. FRNDINT. FSUB ST(1), ST. FXCH ST(1). F2XM1. FLD1. FADD. FSCALE. FSTP ST(1). TEST EDX, 2. JZ @@exit. FCHS. JMP @@exit. @@Int_64: DQ 0. @@exit: FWAIT. End;
©Drkb::04088.
Автор ___ALex___
Это может звучать тривиально, но как мне возвести число в степень? Например, 2^12 = 4095.
На самом деле вопрос далеко не тривиальный. Проблема в том, что сам алгоритм функции далеко не прост. Функцией Power(X, N) (т.е. X^N) должны четко отслеживаться несколько возможных ситуаций:
X любое число, N = 0.
X = 1, N любое число.
X = 0 и N > 0.
X = 0 и N < 0.
X > 0.
X < 0 и N нечетное целое.
X < 0 и N целое.
X < 0 и N нецелое.
Посмотрите на следующую, абсолютно правильно работающую функцию (тем не менее она может быть и не самой эффективной!):
Code: Interface. Type. EPowerException = class(Exception). End; Implementation. Function Power(X, N: real): extended; Var. T: longint; R: real; IsInteger: boolean; Begin. If N = 0 then. Begin. Result := 1.0; Exit; End; If X = 1.0 then. Begin. Result := 1.0; Exit; End; If X = 0.0 then. Begin. If N > 0.0 then. Begin. Result := 0.0; Exit; End. Else. Raise EPowerException.Create('Результат - бесконечность'); End; If (X > 0) then. Try. Result := exp(N * ln(X)); Exit; Except. Raise. EPowerException.Create('Результат - переполнение или потеря значимости'); End; { X - отрицательный, но мы все еще можем вычислить результат, если n целое. }. { пытаемся получить целую часть n с использованием типа longint, вычисление }. { четности n не займет много времени }. Try. T := trunc(n); If (n - t) = 0 then. IsInteger := true. Else. IsInteger := False; Except. { Лишний бит может вызвать переполнение или потерю значимости }. R := int(n); If (n - r) = 0 then. Begin. IsInteger := true; If frac(r / 2) = 0.5 then. T := 1. Else. T := 2; End. Else. IsInteger := False; End; If isInteger then. Begin. {n целое}. If odd(t) then. {n нечетное}. Try. Result := -exp(N * ln(-X)); Exit; Except. Raise. EPowerException.Create('Результат - переполнение или потеря значимости'); End. Else. {n четное}. Try. Result := exp(N * ln(-X)); Exit; Except. Raise. EPowerException.Create('Результат - переполнение или потеря значимости'); End; End. Else. Raise EPowerException.Create('Результат невычисляем'); End;
| Как сделать анимацию минимизации формы? |
In FormShow:
Code: Var. RecS, RecL: TRect; Begin. RecS := Rect(Screen.Width, Screen.Height, Screen.Width, Screen.Height); RecL := ThisForm.BoundsRect; DrawAnimatedRects(GetDesktopWindow, IDANI_CAPTION, RecS, RecL); { ... } End;
| Как изменить шрифт и выравнивание в заголовке формы? |
Note: The formDeactivate never gets called so when the form isn't active, sometimes the FormPaint isn't called. If anything causes the form to be repainted while in inactive, it draws correctly.
Code: Unit Unit1; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; Type. TForm1 = class(TForm). Procedure FormPaint(Sender: TObject); Procedure FormResize(Sender: TObject); Procedure FormDeactivate(Sender: TObject); Procedure FormActivate(Sender: TObject); Private. Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.FormPaint(Sender: TObject); Var. LabelHeight, LabelWidth, LabelTop: Integer; Caption_height, border3d_y, button_width, border_thickness: Integer; MyCanvas: TCanvas; CaptionBarRect: TRect; Begin. CaptionBarRect := Rect(0, 0, 0, 0); MyCanvas := TCanvas.Create; MyCanvas.Handle := GetWindowDC(Form1.Handle); Border3d_y := GetSystemMetrics(SM_CYEDGE); Button_width := GetSystemMetrics(SM_CXSIZE); Border_thickness := GetSystemMetrics(SM_CYSIZEFRAME); Caption_height := GetSystemMetrics(SM_CYCAPTION); LabelWidth := Form1.Canvas.TextWidth(Form1.Caption); LabelHeight := Form1.Canvas.TextHeight(Form1.Caption); LabelTop := LabelHeight - (caption_height div 2); CaptionBarRect.Left := border_thickness + border3d_y + button_width; CaptionBarRect.Right := Form1.Width - (border_thickness + border3d_y). - (button_width * 4); CaptionBarRect.Top := border_thickness + border3d_y; CaptionBarRect.Bottom := caption_height; If Form1.Active then. MyCanvas.Brush.Color := clActiveCaption. Else. MyCanvas.Brush.Color := clInActiveCaption; MyCanvas.Brush.Style := bsSolid; MyCanvas.FillRect(CaptionBarRect); MyCanvas.Brush.Style := bsClear; MyCanvas.Font.Color := clCaptionText; MyCanvas.Font.Name := 'MS Sans Serif'; MyCanvas.Font.Style := MyCanvas.Font.Style + [fsBold]; DrawText(MyCanvas.Handle, PChar(' ' + Form1.Caption), Length(Form1.Caption) + 1, CaptionBarRect, DT_CENTER or DT_SINGLELINE or DT_VCENTER); MyCanvas.Free; End; Procedure TForm1.FormResize(Sender: TObject); Begin. Form1.Paint; End; Procedure TForm1.FormDeactivate(Sender: TObject); Begin. Form1.Paint; End; Procedure TForm1.FormActivate(Sender: TObject); Begin. Form1.Paint; End; End.
| Как сделать roll-up форму? |
Code: Unit testmain; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, Buttons, ShellAPI; Type. TForm1 = class(TForm). Procedure FormCreate(Sender: TObject); Private. { Private declarations }. FOldHeight: Integer; Procedure WMNCRButtonDown(var Msg: TWMNCRButtonDown); Message WM_NCRBUTTONDOWN; Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Procedure TForm1.FormCreate(Sender: TObject); Begin. FOldHeight := ClientHeight; End; Procedure TForm1.WMNCRButtonDown(var Msg: TWMNCRButtonDown); Var. I: Integer; Begin. If (Msg.HitTest = HTCAPTION) then. If (ClientHeight = 0) then. Begin. I := 0; While (I < FOldHeight) do. Begin. I := I + 40; If (I > FOldHeight) then. I := FOldHeight; ClientHeight := I; Application.ProcessMessages; End; End. Else. Begin. FOldHeight := ClientHeight; I := ClientHeight; While (I > 0) do. Begin. I := I - 40; If (I < 0) then. I := 0; ClientHeight := I; Application.ProcessMessages; End; End; End; End.
| Как сделать subform? |
Those programmers who use the Win API in their programs know that Win32 allows you to insert one dialog box into another one and you'll can deal with subdialog's controls as them were in parent dialog. The good example of it is PropertySheet. I don't know why Borland hided this ability from us and why didn't it insert 'subforming' ability in TForm control. Here I can tell how to use a form as control (subform) in other one and how to create subform controls. It will work in D2, D3 and may be D4 (unfortunatelly, I have not it and can't check). The next steps shows how to make subform component:
| Как создать форму в форме элипса? |
Code: Procedure TForm1.FormCreate(Sender: TObject); Var. Region: HRGN; Begin. Region := CreateEllipticRgn(0, 0, 300, 300); SetWindowRgn(Handle, Region, True); End;
| Как запретить перемещение формы? |
Code: Type. TyourForm = class(TForm). Private. { Private declarations }. Procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST; End; Procedure TyourForm.WMNCHitTest(var Message: TWMNCHitTest); Begin. Inherited; With Message do. If Result = HTCAPTION then. Result := HTNOWHERE; End;
| Как иммитировать появление формы как нового приложения? |
How i can create a form and this form stay in another icon in task bar ? (Looks like a new aplication).
| Как перетаскивать (Drag and Drop) выделенный текст между компонентами Memo? |
Данный способ позволяет не погружаясь глубоко в создание компонент осуществить операцию "drag and drop" выделенного текста.
Создайте новый компонент (TMyMemo), наследовав его от TMemo. И объявите его следующим образом:
Code: Type. TMyMemo = class(TMemo). Private. FLastSelStart : Integer; FLastSelLength : Integer; Procedure WMLButtonDown(var Message: TWMLButtonDown); Message WM_LBUTTONDOWN; Published. Property LastSelStart : Integer read FLastSelStart. Write FLastSelStart; Property LastSelLength : Integer read FLastSelLength. Write FLastSelLength; End; Добавьте обработчик WMLButtonDown: Procedure TMyMemo.WMLButtonDown(var Message: TWMLButtonDown); Var. Ch : Integer; Begin. If SelLength > 0 then begin. Ch := LoWord(Perform(EM_CHARFROMPOS,0, MakeLParam(Message.XPos,Message.YPos))); LastSelStart := SelStart; LastSelLength := SelLength; If (Ch >= SelStart) and (Ch <= SelStart+SelLength-1) then. BeginDrag(True). Else. Inherited; End. Else. Inherited; End;
Как сделать виртуальный диск?
Code: { ... } If DefineDosDevice(DDD_RAW_TARGET_PATH, 'P:', 'F:\Backup\Music\Modules') then. ShowMessage('Drive was created successfully'). Else. ShowMessage('Error creating drive'); { ... }
©Drkb::01378.
Взято с Delphi Knowledge Base: http://www.baltsoft.com/
| Как нарисовать метафайл? |
Code: Unit Metaform; Interface. Uses. SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls; Type. TForm1 = class(TForm). Panel1: TPanel; BitBtn1: TBitBtn; Image1: TImage; Procedure BitBtn1Click(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Type. TMetafileCanvas = class(TCanvas). Private. FClipboardHandle: THandle; FMetafileHandle: HMetafile; FRect: TRect; Protected. Procedure CreateHandle; override; Function GetMetafileHandle: HMetafile; Public. Constructor Create; Destructor Destroy; override; Property Rect: TRect read FRect write FRect; Property MetafileHandle: HMetafile read GetMetafileHandle; End; Constructor TMetafileCanvas.Create; Begin. Inherited Create; FClipboardHandle := GlobalAlloc(GMEM_SHARE or GMEM_ZEROINIT, SizeOf(TMetafilePict)); End; Destructor TMetafileCanvas.Destroy; Begin. DeleteMetafile(CloseMetafile(Handle)); If Bool(FClipboardHandle) then. GlobalFree(FClipboardHandle); If Bool(FMetafileHandle) then. DeleteMetafile(FMetafileHandle); Inherited Destroy; End; Procedure TMetafileCanvas.CreateHandle; Var. MetafileDC: HDC; Begin. { Create a metafile DC in memory }. MetafileDC := CreateMetaFile(nil); If Bool(MetafileDC) then. Begin. { Map the top,left corner of the displayed rectangle to the top,left of the. Device context. Leave a border of 10 logical units around the picture. }. With FRect do. SetWindowOrg(MetafileDC, Left - 10, Top - 10); { Set the extent of the picture with a border of 10 logical units.}. With FRect do. SetWindowExt(MetafileDC, Right - Left + 20, Bottom - Top + 20); { Play any valid metafile contents to it. }. If Bool(FMetafileHandle) then. Begin. PlayMetafile(MetafileDC, FMetafileHandle); End; End; Handle := MetafileDC; End; Function TMetafileCanvas.GetMetafileHandle: HMetafile; Var. MetafilePict: PMetafilePict; IC: HDC; ExtRect: TRect; Begin. If Bool(FMetafileHandle) then. DeleteMetafile(FMetafileHandle); FMetafileHandle := CloseMetafile(Handle); Handle := 0; { Prepair metafile for clipboard display. }. MetafilePict := GlobalLock(FClipboardHandle); MetafilePict^.mm := mm_AnIsoTropic; IC := CreateIC('DISPLAY', nil, nil, nil); SetMapMode(IC, mm_HiMetric); ExtRect := FRect; DPtoLP(IC, ExtRect, 2); DeleteDC(IC); MetafilePict^.xExt := ExtRect.Right - ExtRect.Left; MetafilePict^.yExt := ExtRect.Top - ExtRect.Bottom; MetafilePict^.HMF := FMetafileHandle; GlobalUnlock(FClipboardHandle); { I'm giving you this handle, but please do NOT eat it. }. Result := FClipboardHandle; End; Procedure TForm1.BitBtn1Click(Sender: TObject); Var. MetafileCanvas: TMetafileCanvas; Begin. MetafileCanvas := TMetafileCanvas.Create; MetafileCanvas.Rect := Rect(0, 0, 500, 500); MetafileCanvas.Ellipse(10, 10, 400, 400); Image1.Picture.Metafile.LoadFromClipboardFormat(cf_MetafilePict, MetafileCanvas.MetafileHandle, 0); MetafileCanvas.Free; End; End.
| Как начертить круг? |
Code: { ... } Implementation. {$R *.DFM}. Uses. Math; Procedure DrawCircle(CenterX, CenterY, Radius: Integer; Canvas: TCanvas; Color: TColor); Procedure PlotCircle(x, y, x1, y1: Integer); Begin. Canvas.Pixels[x + x1, y + y1] := Color; Canvas.Pixels[x - x1, y + y1] := Color; Canvas.Pixels[x + x1, y - y1] := Color; Canvas.Pixels[x - x1, y - y1] := Color; Canvas.Pixels[x + y1, y + x1] := Color; Canvas.Pixels[x - y1, y + x1] := Color; Canvas.Pixels[x + y1, y - x1] := Color; Canvas.Pixels[x - y1, y - x1] := Color; End; Var. X, y, r: Integer; X1, y1, p: Integer; Begin. X := CenterX; Y := CenterY; R := Radius; X1 := 0; Y1 := r; P := 3 - 2 * r; While (x1 < y1) do. Begin. Plotcircle(x, y, x1, y1); If (p < 0) then. P := p + 4 * x1 + 6. Else. Begin. P := p + 4 * (x1 - y1) + 10; Y1 := y1 - 1; End; X1 := x1 + 1; End; If (x1 = y1) then. Plotcircle(x, y, x1, y1); End;
Как сменить пароль для таблицы Paradox?
| Как сменить пароль для таблицы Paradox? |
Нет способа сделать это в пределах Delphi VCL. Кажется, это довольно серьезное упущение. Однако, есть возможность сделать это напрямую через Borland Database Engine через интерфейс предоставляемый модулями DBIPROCS и DBITYPES. Нужно использовать DbiDoRestructure.
©Drkb::02527.
Copyright © 1996 Epsylon Technologies.
Взято из FAQ Epsylon Technologies (095)-913-5608; (095)-913-2934; (095)-535-5349.
| Как определить установлена ли звуковая карта? |
Code: { ... } If WaveOutGetNumDevs > 0 then. ShowMessage('Wave-Device present'). Else. ShowMessage('No Wave-Device present'); { ... }
©Drkb::02262.
Code: Function IsSoundCardInstalled: Boolean; Type. SCFunc = function: UInt; stdcall; Var. LibInst: LongInt; EntryPoint: SCFunc; Begin. Result := False; LibInst := LoadLibrary(PChar('winmm.dll')); Try. If LibInst <> 0 then. Begin. EntryPoint := GetProcAddress(LibInst, 'waveOutGetNumDevs'); If (EntryPoint <> 0) then. Result := True; End; Finally. If (LibInst <> 0) then. FreeLibrary(LibInst); End; End;
| Как сделать отступ в Memo? |
С помощью API-функции SendMessage можно задать поля в Memo-компоненте. Если необходимо, например, сделать отступ в 20 пикселей слева то можно это сделать следующим образом:
| Как скрыть свойства объекта? |
В иерархии VCL в большинстве случаев существует уровень объектов-"предшественников" (TCustomXXXX), в которых многие свойства скрыты. Для унаследованных от таких "предшественников" объектов можно "открывать" на выбор те или иные свойства. А как можно скрыть свойства, которые объявлены в published-области от Object Insресtоr'а, но при этом оставить возможность доступа во время работы программы? Решение состоит в объявлении свойства "по новой" в public-области. В примере скрытым будет у объекта TMyControl свойство Height.
Code: TMyControl = class(TWinControl). Protected. Procedure SetHeight(Value: Integer); Function GetHeight: Integer; Public. Property Height: Integer read GetHeight write SetHeight; End; Procedure TMyControl.SetHeight(Value: Integer); Begin. Inherited Height := Value; End; Function TMyControl.GetHeight; Begin. Result := inherited Height; End;
| Работа с Photoshop. |
Code: Uses. ComObj, ActiveX, PhotoShopTypeLibrary_TLB; Var. PS: IPhotoShopApplication; Unknown: IUnknown; Begin. Result := GetActiveObject(CLASS_PhotoshopApplication, nil, Unknown); If (Result = MK_E_UNAVAILABLE) then. PS := CoPhotoshopApplication.Create. Else. Begin. { make sure no other error occurred }. OleCheck(Result); OleCheck(Unknown.QueryInterface(IPhotoShopApplication, PS)); End; PS.Visible := True; End;
| Как найти пароль к базе данных? |
I know there that there are many utilities out there costing $$ for removing the password of an access database. Here's how to implement it in Delphi.Please note that this method is not meant for a database with user-level security and work group information file. The idea is based on the file format of an access db.
The password is stored from location $42 and encrypted using simple xoring. The following function does decryption.
Code: Function GetPassword(filename: string): string; Var. Stream: TFilestream; Buffer: array[0..12] of char; Str: string; Begin. Try. Stream := TFileStream.Create(filename, fmOpenRead); Except. ShowMessage('Could not open the file.Make sure that the file is not in use.'); Exit; End; Stream.Seek($42, soFromBeginning); Stream.Read(buffer, 13); Stream.Destroy; Str := chr(Ord(buffer[0]) xor $86); Str := str + chr(Ord(buffer[1]) xor $FB); Str := str + chr(Ord(buffer[2]) xor $EC); Str := str + chr(Ord(buffer[3]) xor $37); Str := str + chr(Ord(buffer[4]) xor $5D); Str := str + chr(Ord(buffer[5]) xor $44); Str := str + chr(Ord(buffer[6]) xor $9C); Str := str + chr(Ord(buffer[7]) xor $FA); Str := str + chr(Ord(buffer[8]) xor $C6); Str := str + chr(Ord(buffer[9]) xor $5E); Str := str + chr(Ord(buffer[10]) xor $28); Str := str + chr(Ord(buffer[11]) xor $E6); Str := str + chr(Ord(buffer[12]) xor $13); Result := str; End;
| Печать содержимого DBGrid через QuickReport. |
Автор: Rafael Ribas Аgиi1у.
Частенько у пользователя возникает необходимость распечатать отчёт из базы данных. Естественно, что он начинает просить Вас добавить такую возможность в приложение. Как оказалось, при помощи TQuickRep данную задачу можно очень легко решить.
Итак, приступим. Для начала создайте новую форму, назвав её TGridReport, и поместите на неё TQuickRep. Переименуйте QuickRep в GridRep. Затем сделайте следующию процедуру, которая получает в качестве параметра DBGrid:
Code: Procedure TGridReport.Preview(Grid: TDBGrid); Var. I, CurrentLeft, CurrentTop : integer; BMark: TBookmark; Begin. GridRep.Dataset:=Grid.DataSource.DataSet; If not GridRep.Bands.HasColumnHeader then. GridRep.Bands.HasColumnHeader:=true; If not GridRep.Bands.HasDetail then. GridRep.Bands.HasDetail:=true; GridRep.Bands.ColumnHeaderBand.Height:=Abs(Grid.TitleFont.Height) + 10; GridRep.Bands.DetailBand.Height:=Abs(Grid.Font.Height) + 10; CurrentLeft := 12; CurrentTop := 6; {Запись, на которой пользователь останавливается в DBGrid}. BMark:=Grid.DataSource.DataSet.GetBookmark; {Запретим мерцание грида в процессе работы отчёта}. Grid.DataSource.DataSet.DisableControls; Try. For i:=0 to Grid.FieldCount - 1 do. Begin. If (CurrentLeft + Canvas.TextWidth(Grid.Columns[i].Title.Caption)) > (GridRep.Bands.ColumnHeaderBand.Width) then. Begin. CurrentLeft := 12; CurrentTop := CurrentTop + Canvas.TextHeight('A') + 6; GridRep.Bands.ColumnHeaderBand.Height := GridRep.Bands.ColumnHeaderBand.Height + (Canvas.TextHeight('A') + 10); GridRep.Bands.DetailBand.Height := GridRep.Bands.DetailBand.Height + (Canvas.TextHeight('A') + 10); End; {Создадим заголовок отчёта при помощи QRLabels}. With TQRLabel.Create(GridRep.Bands.ColumnHeaderBand) do. Begin. Parent := GridRep.Bands.ColumnHeaderBand; Color := GridRep.Bands.ColumnHeaderBand.Color; Left := CurrentLeft; Top := CurrentTop; Caption:=Grid.Columns[i].Title.Caption; End; {Создадим тело отчёта при помощи QRDBText}. With TQRDbText.Create(GridRep.Bands.DetailBand) do. Begin. Parent := GridRep.Bands.DetailBand; Color := GridRep.Bands.DetailBand.Color; Left := CurrentLeft; Top := CurrentTop; Alignment:=Grid.Columns[i].Alignment; AutoSize:=false; AutoStretch:=true; Width:=Grid.Columns[i].Width; Dataset:=GridRep.Dataset; DataField:=Grid.Fields[i].FieldName; CurrentLeft:=CurrentLeft + (Grid.Columns[i].Width) + 15; End; End; LblPage.Left := bdTitle.Width - lblPage.Width - 10; LblDate.Left := bdTitle.Width - lblDate.Width - 10; {Далее вызовем метод предварительного просмотра из QuickRep}. GridRep.PreviewModal; {либо, если желаете, то PreviewModal}. Finally. With Grid.DataSource.DataSet do. Begin. GotoBookmark(BMark); FreeBookmark(BMark); EnableControls; End; End; End;
Borland Delphi 7 - миграция в сторону .Net.
Автор: Виталий Чужа.
Qui non proficit, deficit. Теряет тот, кто не идет вперед.
Delphi 7 Studio позиционируется как первый независимый инструмент для облегчения миграции к платформе .Net.
Итак, перед нами очередная версия знаменитого продукта фирмы Borland - Delphi 7 Studio, о выходе которого было сообщено в пресс-релизе компании от 6 августа 2002 года. Новая версия Delphi позиционируется как первый независимый инструмент для облегчения миграции к платформе .Net и, будучи доступна с лета 2002 года, поставляется в четырех редакциях: Architect, Enterprise, Professional и Personal. В шестой версии, как вы помните, отсутствовала редакция Architect, да и само понятие (студии). Также нужно заметить, что, хотя Delphi несколько <потяжелела> и изменился перечень поддерживаемых операционных систем, системные требования Delphi 6 и Delphi 7 версий Enterprise, в основном, остались прежними.
Для версии Architect, которая является самой полной и дорогой (99) редакцией, требования к ПК выше - для полной установки ей требуется 540 Мб пространства на жестком диске. Цены на <стандартные> версии, по сравнению с Delphi 6, не изменились: Enterprise стоит 99, Professional - 9 .
Какие же новые возможности были включены в Delphi 7 Studio? Рассмотрим их по порядку.
Среда разработки.
Изменения, затронувшие IDE, коснулись палитры компонент, достройщика кода, отладчика и настроек редактора кода. В палитре компонент появились следующие закладки: новая версия закладки System только для CLX-приложений, закладки Indy Intercepts и Indy I/O Handlers, содержащие компоненты с поддержкой протоколов интернет с открытым исходным кодом (редакции Professional и Enterprise). Для разработки веб-приложений - новые закладки IW Standard, IW Data, IW Client Side, и IW Control с компонентами IntraWeb, а также новая закладка Rave с компонентами генерации отчетов. Кроме того, если закладка содержит компоненты, для доступа к которым необходима горизонтальная прокрутка, то теперь добраться до них можно и с помощью ниспадающего меню.
Достройщик кода (code completion) теперь работает быстрее и позволяет перейти к объявлениям элементов через их список путем нажатия клавиши <Ctrl> и клика мышью на любом идентификаторе в списке. Появился новый достройщик HTML-кода, который автоматически отображает нужные HTML-элементы и атрибуты в редакторе кода (для редакций Professional и Enterprise). Для удобства работы через меню Tools р Editor Options р Code Insight можно настроить цвета, используемые достройщиком кода. Также необходимо отметить возможность написания собственных менеджеров завершения кода.
Окно просмотра сообщений отладчика (Watch List) обзавелось множеством закладок для облегчения процесса отлова ошибок. Каждую закладку можно настроить - отображать ее или спрятать. Диалоговое окно Run Parameters теперь имеет новую настройку: рабочий каталог (Working Directory), указав который, можно настроить каталог, используемый для отладки.
Из Project Manager можно компилировать лишь часть проектов в группе, щелкнув правой кнопкой по проекту и выбрав пункты меню Make All from Here или Build All from Here, а в окне просмотра сообщений (Message view) появилось множество закладок для отображения разных типов сообщений (Build, Search, и т. д.) В окне, доступном через меню View р Component List, можно выбирать несколько компонент с помощью клавиши <Ctrl>. Окно настроек редактора кода, доступное через меню Tools р Editor Options р Source Options, позволяет устанавливать различные настройки для языков Pascal, C++, C#, HTML, XML, контролировать отображение знаков пробела и табуляции, редактировать шаблоны кода. Окно, доступное через меню Tools р Editor Options р Color, имеет две новые настройки: цвет символа (Foreground Color) и цвет фона (Background Color) - вместо цветовой сетки для настройки цвета в редакторе кода.
Веб-технологии.
В седьмую версию Delphi вошел набор компонент IntraWeb от AtoZed Software, который может быть использован для написания приложений веб-сервера с использованием стандартного инструментария разработки форм. IntraWeb также годится для разработки страниц Web Broker и WebSnap приложений. Полная версия IntraWeb входит в состав редакции Delphi Enterprise. Delphi также поддерживает Apache 2 - как целевой тип для Web Broker, WebSnap и SOAP. В то же время Borland не рекомендует использовать Win-CGI для приложений веб-сервера или веб-сервисов. Вместо этого рекомендуется использовать CGI, ISAPI/NSAPI, или Apache.
Веб-сервисы.
Появился новый UDDI (Universal Description, Discovery, and Integration) браузер у волшебника импорта WSDL, который позволяет сканировать реестр UDDI для поиска веб-сервиса и импорта адреса WSDL документа. Сам реестр UDDI является базой данных, в которой регистрируются компании, поставляющие веб-сервисы. Новые классы и интерфейсы позволяют читать или вставлять заголовки в SOAP-конверты, пересылающие сообщения между клиентом и сервером. Приложения веб-сервисов (как клиентов, так и серверов) теперь в состоянии обрабатывать вложения. Вложения (наследники класса TSOAPAttachment) отсылаются вместе с закодированными сообщениями SOAP как часть составной формы. Когда приложение получает сообщение с вложением, оно сохраняет вложение во временном файле, доступном вашей программе.
Новые события, обрабатываемые компонентом THTTPReqResp, позволят вам перехватывать сообщения HTTP до их отсылки и отслеживать ход получения или отсылки длинных сообщений. В классе THTTPSoapPascalInvoker доступны сообщения, которые позволят написать код, исполняемый до или после того, как объект класса осуществит вызов запрашиваемого метода. Интерфейс IOPConvert получил новое свойство Encoding, что позволяет указать набор символов для использования в сообщениях, передаваемых между клиентом поставщиком веб-сервиса.
Базы данных.
Обновлены драйвера dbExpress для Informix SE, Oracle 9i, DB2 7.2, InterBase 6.5 и MySQL 3.23.49. Новый драйвер поставляется и для MS SQL Server 2000. А вот SQL Links Borland не рекомендует использовать, более того - работа над усовершенствованием SQL Links прекращается. Сообщается также, что эта технология не будет поставляться вместе с Delphi после 2002 года. В виде замены рекомендовано использовать dbExpress для доступа к базам данных SQL серверов.
Прекращена также поддержка CORBA-соединений компонентами DataSnap.
Библиотека компонент.
Приложения, написанные с использованием VCL, теперь включают компоненты, поддерживающие библиотеку элементов управления Windows шестой версии. Поэтому приложение будет автоматически использовать новые элементы управления Windows в ОС Windows XP, если найдет подходящий файл-манифест.
Новый модуль DBClientActns включает три новых компонента для работы с клиентскими наборами данных: TClientDataSetApply, TclientDataSetUndo и TClientDataSetRevert.
Закладка dbExpress включает новый компонент TSimpleDataSet для использования с простыми, двухуровневыми приложениями баз данных (TSimpleDataSet заменил собой TSQLClientDataSet). Обновлена закладка Dialogs на палитре компонент - добавлен TPageSetupDialog для представления стандартного диалогового окна Windows настройки параметров страницы. На закладку Additional добавлены компоненты TXPColorMap, TstandardColorMap и TtwilightColorMap для окраски меню и панелей инструментов.
У VCL-версии компонента TCustomForm добавились два новых свойства: ScreenSnap и SnapBuffer, контролирующие <прилипание> окна к краям экрана. Компонент TCustomComboBoxEx получил новое свойство AutoCompleteOptions, которое позволяет откликаться на ввод пользователя.
Библиотека времени выполнения.
Модуль Classes содержит новый класс-исключение EFileStreamError. Классы EFCreateError и EFOpenError являются наследниками этого класса. Поскольку конструктор этого класса принимает имя файла как параметр, то в сообщении об исключительной ситуации можно видеть имя файла, в котором она произошла.
Класс TStrings также обладет теперь двумя новыми свойствами: ValueFromIndex и NameValueSeparator.
В модуле StrUtils также произошли определенные изменения, касающиеся работы с многобайтными наборами символов. Ранее функции LeftStr, RightStr и MidStr принимали параметр типа AnsiString и возвращали значение того же типа, не поддерживая многобайтные наборы символов. Теперь каждая из этих функций заменена парой перегруженных функций, одна из которых работает с типом AnsiString, а другая - с типом WideString. Также в модуль добавлена новая функция для поиска в строке, именуемая PosEx.
В модуле SysUtils функции, работающие над форматированием и разбором чисел, валют и значений типа дата и время, заменены на безопасные при работе с нитями.
Модуль VarCmplx содержит новые функции VarComplexLog2, VarComplexLog10, VarComplexLogN, VarComplexTimesImaginary и VarComplexTimesReal.
В модуль Variants добавлены функции VarIsError и VarAsError. Исключение EVariantError теперь является родительским классом для более тщательно разработанных классов-исключений, которые используются в коде с использованием переменных типа variant.
Компилятор.
Компилятор Delphi поддерживает три дополнительных предупреждения: Unsafe_Type, Unsafe_Code и Unsafe_Cast. Эти предупреждения по умолчанию отключены, однако их можно задействовать с помощью директив компилятора {$WARN UNSAFE_CODE ON}, команды компилятора командной строки (dcc32 -W+UNSAFE_CODE), или посредством среды разработки, воспользовавшись меню Project р Options р Compiler Messages. Эта особенность должна помочь вам портировать код в окружение управляемого выполнения платформы Microsoft .NET. В этом окружении значит, что действия, выполняемые программой, не могут быть проверены во время анализа, осуществляемого Just In Time (JIT) компилятором. Такой код представляется небезопасным. Примером такого кода могут послужить операции с указателями.
Поддержка Rave Reports (для редакции Professional и Enterprise).
Вместе с Delphi теперь поставляется набор компонент для генерации отчетов Rave Reports фирмы Nevrona. Полное его название - Rave 4.0 Borland Edition (BE). Включает 19 компонентов, содержащих более 500 методов, свойств и событий. Компоненты компилируются в ехе-файл приложения и не требуют для работы никаких дополнительных файлов. В генератор отчетов включена поддержка графики, выравнивания и переноса текста, точное позиционирование страницы, конфигурирование принтера, управление шрифтами, полнофункциональный предпросмотр. Rave 4.0 BE справляется и с отчетами вида master-detail, письмами, счетами и т. д. (рис. 1).
Поддержка ModelMaker (для редакции Professional и Enterprise).
ModelMaker призван помочь упростить дизайн, разработку и поддержку классов и интерфейсов. ModelMaker также включает инструменты для создания диаграмм в стиле UML (Unified Modeling Language), которые могут быть использованы для создания и модификации исходного кода проекта.
Отмечу, что версия Delphi Enterprise включает полноценную версию ModelMaker, а Delphi Professional - лишь 30-дневную пробную версию, хотя функциональность обоих версий одинакова.
Заключение.
Несмотря на множество нововведений в этой версии Borland Delphi, можно отметить, как минимум, три важные тенденции.
Первая - сворачивание собственных технологий, таких как Borland Database Engine (BDE) и SQL Links. Кажется вероятным отказ от набора компонент Quick Reports, успешно поставлявшихся с несколькими последними версиями Delphi.
Вторая - ставшая уже традиционной поддержка написания приложений для Linux.
Третья - на мой взгляд, более важная - постепенный отход ведущих средств разработки от платформы Win32 и их миграция в сторону платформы .Net. И хотя, как сказано в пресс-релизе Borland, за одну ночь новая платформа не изменит технологию разработки и распространения приложений, однако платформа .Net - следующая, эволюционная ступень Windows, приход которой неизбежен. Принимая во внимание тот факт, что разработчики в основной своей массе люди инертные, а также то, что на изучение новых технологий необходимо довольно много времени и средств, Borland сделала ставку на необходимость эволюционного развития Delphi и, на мой взгляд, не ошиблась. Включение в эту версию подсказок и предупреждений компилятора, касающихся совместимости с Microsoft .Net, возможности импорта узлов (assembly) .Net в Delphi и экспорта COM объектов в приложения .Net, подтверждает сказанное выше. Полноценная же поддержка этой многообещающей платформы наверняка будет реализована в следующей версии Delphi - Delphi .Net, которую и будем с нетерпением ждать.
Взято с сайта http://delphi.h5.ru.
©Drkb::04634.
| .Net глазами дельфийца. Использование Delphi в .Net. |
.Net глазами дельфийца. Использование Delphi в .Net.
Для программистов, рассматривающих вопрос перехода на новую систему разработки, один из главных вопросов – как в новой системе можно будет использовать старые наработки.
В настоящей статье рассмотрены некоторые простые способы использования наработок Delphi (автор использовал версию 6) в .Net. Естественно, полностью портировать Delphi в .Net под силу только разработчикам Borland (или аналогичным серьезным людям, владеющим к тому же полными исходниками Delphi), поэтому здесь мы не будем рассматривать клиентские (визуальные) приложения, использующие VCL. Тем не менее некоторый объем функционального кода, реализованного в Delphi, может быть использован и в .Net (пример такого кода – вычислительные модули).
Итак, для переноса Delphi-кода в .Net можно использовать следующие способы:
- переписывание кода;
- использование в .Net COM-объектов, разработанных в Delphi;
- использование в .Net библиотек DLL, разработанных в Delphi;
- хакерские способы.
Переписывание кода.
Этот способ, несмотря на его прямолинейность, в некоторых случаях может оказаться наиболее рациональным и даже упростить результирующий код.
Техника переноса алгоритмов на другие языки знакома многим программистам. Более того, существуют автоматические трансляторы типа Pas2C, которые входной паскалевский код преобразуют в C/C++-код (естественно, кое-что приходится править потом вручную, но основа получается вполне приемлемая).
Не останавливаясь подробно на очевидных моментах ручной трансляции кода, хотелось бы напомнить следующее:
- поскольку в C# нет процедур и функций вне классов, для реализации глобальных подпрограмм целесообразно использовать специальный(-ые) класс(ы) со статическими методами;
- поскольку в C# нет глобальных переменных, для их реализации целесообразно использовать специальный(-ые) класс(ы) со статическими полями;
- для реализации конструкций, эквивалентных записям Delphi, можно использовать как классы, так и структуры (struct) – решение зависит от объема данных, которые, возможно, будут копироваться при передаче в качестве параметров и пр.;
- поскольку в C# объекты удаляются автоматически, необходимо тщательно проанализировать последовательность создания и удаления объектов, особенно если в Delphi-объектах в деструкторе выполнялось освобождение ресурсов – в таких случаях оптимальным вариантом будет или введение соответствующих методов «закрытия», или реализация интерфейса IDisposable.
Использование в .Net COM-объектов, разработанных в Delphi.
Поскольку стандарт OLE четко определяет протокол обмена и форматы данных, то никаких особенностей при разработке COM-объектов в Delphi нет (не считая того, что нежелательно использовать имена методов, совпадающие с методами класса Object из C#: Equals, GetHashCode, GetType, ReferenceEquals, ToString).
Если же исходный код реализован, например, в виде набора классов Delphi, то можно написать в Delphi очень простой COM-объект (inproc-server), который будет выполнять функции фасада (см. например Шаблоны проектирования на www.dotsite.spb.ru ).
Кстати, во многих случаях введение «фасада» позволяет даже упростить систему со стороны основного кода.
Использование в .Net библиотек DLL, разработанных в Delphi.
Т.к. в Delphi позволяет разрабатывать динамические библиотеки DLL, то можно существующий Delphi-код «упаковать» в библиотеку. Этот способ заставляет выполнить несколько бОльшую работу в C#, чем при использовании COM-объектов, хотя в некоторых случаях вполне может быть использован из соображений, например, быстродействия системы.
Вызов функций DLL из C# достаточно неплохо описан в документации .Net. Ниже приводится пример использования DLL, разработанной в Delphi.
Заголовки DLL-функций в Delphi:
Code: // Процедура без параметров. Procedure Proc1; stdcall; // Процедура с целочисленными параметрами. Procedure Proc2(A, B: integer); stdcall; // Процедура с вещественными параметрами. Procedure Proc3(A, B: double); stdcall; // Процедура с логическими параметрами. Procedure Proc4(A, B: boolean); stdcall; // Процедура с параметрами типа дата/время. Procedure Proc5(A, B: TDateTime); stdcall; // Процедура со строковыми параметрами. Procedure Proc6(P1, P2: PChar); stdcall;
Русификация Kylix.
В этой статье я решил рассказать тебе, как можно заставить Kylix уважать русский язык. Для этого нужно полностью русифицировать Linux. Это не так уж и сложно и мы сейчас полностью опишем этот процесс.
Для Kylix желательно использовать кодовую страницу KOI-8r, которая является практически стандартом русского языка в Linux. Для того, чтобы Kylix мог понимать русские шрифты, нужно сделать следующее:
1. Открыть файл /etc/sysconfig/i18n и добавить в него следующие недостающие строки:
LANG=ru.
LANGUAGE=ru_RU.KOI8-R:ru #опционально.
LC_CTYPE=ru_RU.KOI8-R.
LC_NUMERIC=ru_RU.KOI8-R.
LC_TIME=ru_RU.KOI8-R.
LC_COLLATE=ru_RU.KOI8-R.
LC_MONETARY=ru_RU.KOI8-R.
LC_MESSAGES=ru_RU.KOI8-R.
LC_ALL=ru_RU.KOI8-R.
SYSFONT=UniCyr_8x16.
SYSFONTACM=koi8-r.
Если какие-то из этих строк уже есть в файле, то нужно отредактировать существующие.
В системе уже должны быть установлены русские шрифты. В последних дистрибутивах почти всегда идут шрифты Cronyx Cyrillic, которых достаточно для нормальной работы Kylix.
И последнее, что надо сделать - настрокить переключение клавиатуры в файле /etc/X11/XF86Config. Там нужно отредактировать следующие строки:
Option "XkbLayout" "ru(winkeys)".
Option "XkbOptions" "grp:ctrl_shift_toggle".
Последняя строка показывает как будет переключатся раскладка. В данном случае будет использоваться сочетание клавишь Ctrl+Shift.
Вот и всё!!! Желательно ещё и перегрузить Linux, чтобы он заговорил по русски.
Взято с сайта http://delphi.h5.ru.
©Drkb::04620.
| Как экспортировать содержимое DBGrid в Excel или ClipBoard? |
Пример dbgrid (DBGrid1) имеет всплывающее меню, которое позволяет две опции "Send to Excel" и "Copy".
// ЗАМЕЧАНИЕ: этот метод должен включать COMObj, Excel97 units.
// ОБНОВЛЕНИЕ: если Вы используете Delphi 4, то замените xlWBatWorkSheet на 1 (один).
Code: //----------------------------------------------------------- // если toExcel = false, то экспортируем содержимое dbgrid в Clipboard. // если toExcel = true, то экспортируем содержимое dbgrid в Microsoft Excel. Procedure ExportDBGrid(toExcel: Boolean); Var. Bm: TBookmark; Col, row: Integer; Sline: String; Mem: TMemo; ExcelApp: Variant; Begin. Screen.Cursor := crHourglass; DBGrid1.DataSource.DataSet.DisableControls; Bm := DBGrid1.DataSource.DataSet.GetBookmark; DBGrid1.DataSource.DataSet.First; // создаём объект Excel. If toExcel then. Begin. ExcelApp := CreateOleObject('Excel.Application'); ExcelApp.WorkBooks.Add(xlWBatWorkSheet); ExcelApp.WorkBooks[1].WorkSheets[1].Name := 'Grid Data'; End; // Сперва отправляем данные в memo. // работает быстрее, чем отправлять их напрямую в Excel. Mem := TMemo.Create(Self); Mem.Visible := false; Mem.Parent := MainForm; Mem.Clear; Sline := ''; // добавляем информацию для имён колонок. For col := 0 to DBGrid1.FieldCount-1 do. Sline := sline + DBGrid1.Fields[col].DisplayLabel + #9; Mem.Lines.Add(sline); // получаем данные из memo. For row := 0 to DBGrid1.DataSource.DataSet.RecordCount-1 do. Begin. Sline := ''; For col := 0 to DBGrid1.FieldCount-1 do. Sline := sline + DBGrid1.Fields[col].AsString + #9; Mem.Lines.Add(sline); DBGrid1.DataSource.DataSet.Next; End; // копируем данные в clipboard. Mem.SelectAll; Mem.CopyToClipboard; // если необходимо, то отправляем их в Excel. // если нет, то они уже в буфере обмена. If toExcel then. Begin. ExcelApp.Workbooks[1].WorkSheets['Grid Data'].Paste; ExcelApp.Visible := true; End; FreeAndNil(ExcelApp); DBGrid1.DataSource.DataSet.GotoBookmark(bm); DBGrid1.DataSource.DataSet.FreeBookmark(bm); DBGrid1.DataSource.DataSet.EnableControls; Screen.Cursor := crDefault; End;
| Как создать компонент во время выполнения приложения? |
При создании визуальных контролов в runtime, важным моментом является назначение родительских свойств и использование метода SetBounds, чтобы этот контрол стал видимы.
Code: Type. TForm1 = class(TForm). Protected. MyLabel: TLabel; Procedure LabelClick(Sender: TObject); Procedure CreateControl; End; Procedure TForm1.LabelClick(Sender: TObject); Begin. (Sender as Label).Caption := ... End; Procedure TForm1.CreateControl; Var. ALeft, ATop, AWidth, AHeight: Integer; Begin. ALeft := 10; ATop := 10; AWidth := 50; AHeight := 13; MyLabel := TLabel.Create(Self); MyLabel.Parent := Self; MyLabel.Name:='LabelName'; MyLabel.SetBounds(ALeft, ATop, AWidth, AHeight); MyLabel.OnClick := LabelClick; End;
| Изучаем ассемблер в Delphi. |
Автор: Ian Hodger.
Vit - Печатается в DRKB с сокращениями.
Основное предназначение этой статьи, заполнить пробелы в оригинальной документации по Borland Delphi Developer, при этом весь программный код, а так же теория, полность совместимы со всеми версиями Delphi.
Использование Ассемблера в Борландовком Delphi.
Перед тем, как начать, хотелось бы определиться с уровнем знаний, необходимых для нормального усвоения данного материала. Необходимо быть знакомым со встроенными средствами отладки в Delphi. Так же необходимо иметь представление о таких терминах как тип реализации (instantiation), null pointer и распределение памяти. Если в чём-то из вышеупомянутого Вы сомневаетесь, то постарайтесь быть очень внимательны и осторожны при воплощении данного материала на практике. Кроме того, будет обсуждаться только 32-битный код, так что понадобится компилятор не ниже Delphi 2.0.
Зачем использовать Ассемблер?
На мой взгляд, Object Pascal, это инструмент, позволяющий генерировать быстрый и эффективный код, однако использование ассемблера в некоторых случаях позволяет решать некоторые задачи более эффективно. За всю работу с Delphi, я пришёл к выводу, что использование низкоуровневого кода необходимо в двух случая.
1. Обработка большого количества данных. Nb. В данный случай не входит ситуация, когда используется язык запроса данных.
2. В высокоскоростных подпрограммах работы с дисплеем. Nb. Имеется ввиду использование простых процедур на чистом паскале, но никак не внешних библиотек и DirectX.
В конце статьи мы рассмотрим примеры, которые явно отражают значимость этих критериев, а так же не только когда и где использовать ассемблерные вставки, но и как включать такой код в Delphi.
Что такое Ассемблер?
Надеюсь, что Все читатели этой статьи имеют как минимум поверхностное представление о работе процессора. Грубо говоря, это калькулятор с большим объёмом памяти. Память, это не более чем упорядоченная последовательнось двоичных цифр. Каждая такая цифра является байтом. Каждый байт может содержать в себе значение от 0 до 255, а так же имеет свой уникальный адрес, при помощи которого процессор находит нужные значения в памяти. Процессор так же имеет набор регистров (это можно расценить как глобальные переменные). Например eax,ebx,ecx и edx, это универсальные 32-битные регистры. Это значит, что самое большое число, которое мы можем записать в регистр eax, это 2 в степени 32 минус 1, или 4294967295.
Как мы уже выяснили, процессор манипулирует значениями регистров. Машинный код операции прибавления 10 к значению регистра eax будет выглядеть следующим образом.
05/0a/00/00/00.
Однако, такая запись абсолютно не читабельна и, как следствие, не пригодна при отладке программы. Так вот Ассемблер, это простое представление машинных команд в более удобном виде. Теперь давайте посмотрим, как будет выглядеть прибавление 10 к eax в ассемблерном представлении:
Code: Add eax,10 {a := a + 10}.
А вот так выглядит вычитаение значения ebx из eax.
Code: Sub eax,ebx {a := a - b }.
Чтобы сохранить значние, можно просто поместить его в другой регистр.
Code: Mov eax,ecx {a := c }.
Или даже лучше, сохранить значение по определённому адресу в памяти.
Code: Mov [1536],eax {сохраняет значение eax по адресу 1536}.
Что из себя представляет TWebBrowser?
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Веббраузер это Microsoft's Internet Explorer в виде ActiveX контрола.
Его можно импортировать в Delphi IDE и размещать на форме на равне с другими компонентами. Поэтому, чтобы превратить Ваше приложение в браузер, достаточно воспользоваться всей мощью IE.
©Drkb::03444.
Где можно найти документацию на WebBrowser?
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Вопрос: Где можно найти документацию на WebBrowser? Ответ: Можно заглянуть на сайт Microsoft в раздел WebBrowser overview, а так же на страницу WebBrowser object.
©Drkb::03445.
| Как производить Печать? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Есть два способа вывода на печать. Первый пример работает в IE 4.x и выше,
В то время как второй пример расчитан на IE 3.x:
Code: Var. VaIn, vaOut: OleVariant; ... WebBrowser.ControlInterface.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, vaIn, vaOut); либо. Procedure TForm1.PrintIE; Var. CmdTarget : IOleCommandTarget; VaIn, vaOut: OleVariant; Begin. If WebBrowser1.Document < > nil then. Try. WebBrowser1.Document.QueryInterface(IOleCommandTarget, CmdTarget); If CmdTarget < > nil then. Try. CmdTarget.Exec( PGuid(nil), OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, vaIn, vaOut); Finally. CmdTarget._Release; End; Except. // Ничего. End; End;
| Как вызвать команды Find, Option или View Source? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Вот пример вызова диалога.
Code: Const. HTMLID_FIND = 1; HTMLID_VIEWSOURCE = 2; HTMLID_OPTIONS = 3; ... Procedure TForm1.FindIE; Const. CGID_WebBrowser: TGUID = '{ED016940-BD5B-11cf-BA4E-00C04FD70816}'; Var. CmdTarget : IOleCommandTarget; VaIn, vaOut: OleVariant; PtrGUID: PGUID; Begin. New(PtrGUID); PtrGUID^ := CGID_WebBrowser; If WebBrowser1.Document < > nil then. Try. WebBrowser1.Document.QueryInterface(IOleCommandTarget, CmdTarget); If CmdTarget <> nil then. Try. CmdTarget.Exec( PtrGUID, HTMLID_FIND, 0, vaIn, vaOut); Finally. CmdTarget._Release; End; Except. // Ничего. End; Dispose(PtrGUID); End;
| Как запретить всплывающее меню при нажатии правой кнопки мыши? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Вам необходимо включить интерфейс IDocHostUIHandler.
Для этого Вам понадобятся два файла: ieConst.pas и IEDocHostUIHandler.pas.
В методе ShowContextMenu интерфейса IDocHostUIHandler,
Необходимо изменить возвращаемое значение с E_NOTIMPL на S_OK.
После этого меню перестанет реагировать на правое нажатие кнопки мыши.
Добавьте два модуля, упомянутые выше в секцию Uses и добавьте следующий код:
Code: ... var. Form1: TForm1; FDocHostUIHandler: TDocHostUIHandler; ... Implementation. ... Procedure TForm1.FormCreate(Sender: TObject); Begin. FDocHostUIHandler := TDocHostUIHandler.Create; End; Procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); Begin. FDocHostUIHandler.Free; End; Procedure TForm1.WebBrowser1NavigateComplete2(Sender: TObject; PDisp: IDispatch; var URL: OleVariant); Var. Hr: HResult; CustDoc: ICustomDoc; Begin. Hr := WebBrowser1.Document.QueryInterface(ICustomDoc, CustDoc); If hr = S_OK then CustDoc.SetUIHandler(FDocHostUIHandler); End;
| Как загрузить строковые данные в WebBrowser не прибегая к открытию файла? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Загрузите строку массив Variant, а затем запишите в документ (Document):
Code: ... Var. V: Variant; HTMLDocument: IHTMLDocument2; Begin. HTMLDocument := WebBrowser1.Document as IHTMLDocument2; V := VarArrayCreate([0, 0], varVariant); V[0] := HTMLString; // Это Ваша HTML строка. HTMLDocument.Write(PSafeArray(TVarData(v).VArray)); HTMLDocument.Close; ... End; ...
| Как загрузить потоковые(stream) данные в WebBrowser не прибегая к открытию файла? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Code: Function TForm1.LoadFromStream(const AStream: TStream): HRESULT; Begin. AStream.seek(0, 0); Result := (WebBrowser1.Document as IPersistStreamInit).Load(TStreamAdapter.Create(AStream)); End;
Перечислители.
Объекты- перечислители обеспечивают поиск любых объектов ADO, которые имеют доступ к источникам данных. При этом другие перечислители также видны в данном перечислителе.
Первичный поиск источников данных осуществляется в провайдере ADO. Перечислители могут отбирать только источники данных конкретных типов, поэтому провайдер обеспечивает доступ к конкретному типу хранилища данных.
В составе ADO имеется системный корневой перечислитель, который выполняет начальный поиск других перечислителей и источников данных. Его можно использовать, зная его идентификатор класса CLSID_OLEDB_ENUMERATOR.
В Delphi GUID глобального перечислителя содержится в файле \Delphi7\Source \Vcl\OleDB.pas.
CLSID_OLEDB_ENrjMERATOR: TGUID= '{C8B522DO-5CF3-11CE-ADE5-OOAA0044773D}.
Функции перечислителя содержатся в интерфейсе isourcesRowset. Метод.
Function GetSourcesRowset(const punkOuter: lUnknown; const riid: TGUID; cPropertySets: UINT; rgProperties: PDBPropSetArray; out ppSourcesRowset: lUnknown): HResult; stdcall;
Возвращает ссылку на объект набора рядов (см. выше), содержащий сведения о найденных источниках данных или перечислителях.
| Шаблоны в Object Pascal. |
Шаблоны в Object Pascal.
( перевод одноименной статьи с сайта community.borland.com ).
Наверное каждый Delphi программист хоть раз общался с программистом C++ и объяснял насколько.
Delphi мощнее и удобнее. Но в некоторый момент, программист C++ заявляет примерно следующее.
"OK, но Delphi использует Pascal, а значит не поддерживает множественное наследование и шаблоны,
Поэтому он не так хорош как C++.".
Насчет множественного наследования можно легко заявить, что Delphi имеет интерфейсы, которые.
Прекрасно справляются со своей задачей, но вот насчет шаблонов Вам придётся согласится, так как.
Object Pascal не поддерживает их.
Давайте посмотрим на эту проблему по-внимательней.
Шаблоны позволяют делать универсальные контейнеры такие как списки, стеки, очереди, и т.д.
Если Вы хотите осуществить что-то подобное в Delphi, то у Вас есть два пути:
Использовать контейнер TList, который содержит указатели. В этом случае Вам придётся всё.
Время делать явное приведение типов.
Сделать подкласс контейнера TCollection или TObjectList, и убрать все методы, зависящие от типов.
Каждый раз, когда Вы захотите использовать новый тип данных.
Третий вариант, это сделать модуль с универсальным классом контейнера, и каждый раз, когда нужно.
Использовать новый тип данных, нам прийдётся в редакторе искать и вносить исправления. Было бы.
Здорово, если всю эту работу за Вас делал компилятор.... вот этим мы сейчас и займёмся!
Например, возьмём классы TCollection и TCollectionItem. Когда Вы объявляете нового потомка TCollectionItem.
, то так же Вы наследуете новый класс от TOwnedCollection и переопределяете большинство методов, чтобы.
Их можно было вызывать с новыми типами.
Давайте посмотрим, как создать универсальную коллекцию шаблонов класса:
Шаг 1: Создайте новый текстовый файл (не юнитовский) с именем TemplateCollectionInterface.pas:
Code: _COLLECTION_ = class (TOwnedCollection). Protected. Function GetItem (const aIndex : Integer) : _COLLECTION_ITEM_; Procedure SetItem (const aIndex : Integer; Const aValue : _COLLECTION_ITEM_); Public. Constructor Create (const aOwner : TComponent); Function Add : _COLLECTION_ITEM_; Function FindItemID (const aID : Integer) : _COLLECTION_ITEM_; Function Insert (const aIndex : Integer) : _COLLECTION_ITEM_; Property Items [const aIndex : Integer] : _COLLECTION_ITEM_ read GetItem write SetItem; End;
| Как использовать протокол about? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Code: Procedure TForm1.LoadHTMLString(sHTML: String); Var. Flags, TargetFrameName, PostData, Headers: OleVariant; Begin. WebBrowser1.Navigate('about:' + sHTML, Flags, TargetFrameName, PostData, Headers). End;
| Как использовать протокол res? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Протокол " res:" позволяет просмотреть HTML файл, сохранённый как ресурс.
Более подробная информация доступна на Microsoft site:
Code: Procedure TForm1.LoadHTMLResource; Var. Flags, TargetFrameName, PostData, Headers: OleVariant; Begin. WebBrowser1.Navigate('res://' + Application.ExeName + '/myhtml', Flags, TargetFrameName, PostData, Headers). End;
Как получить полный исходник HTML?
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
В IE5, можно получить исходник используя свойство outerHTML тэгов.
HTML. В IE4 или IE3, Вам понадобится записать документ в файл, а затем.
Загрузить файл в TMemo, TStrings, и т.д.
Code: Var. HTMLDocument: IHTMLDocument2; PersistFile: IPersistFile; Begin. ... HTMLDocument := WebBrowser1.Document as IHTMLDocument2; PersistFile := HTMLDocument as IPersistFile; PersistFile.Save(StringToOleStr('test.htm'), True); While HTMLDocument.readyState < > 'complete' do. Application.ProcessMessages; ... End;
Автор: Ron Loewy Обратите внимание: Вам понадобится импортировать библиотеку.
MSHTML и добавить MSHTML_TLB как ActiveX, в секцию Uses.
©Drkb::03487.
Как получить POST данные?
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Если данные передаются в формате 'animal=cat& color=brown' и т.д.,
То попробуйте использовать следующий код:
Code: Procedure TDBModule.Navigate(stURL, stPostData: String; var wbWebBrowser: TWebBrowser); Var. VWebAddr, vPostData, vFlags, vFrame, vHeaders: OleVariant; ILoop: Integer; Begin. {Are we posting data to this Url?}. If Length(stPostData)> 0 then. Begin. {Require this header information if there is stPostData.}. VHeaders:= 'Content-Type: application/x-www-form-urlencoded'+ #10#13#0; {Set the variant type for the vPostData.}. VPostData:= VarArrayCreate([0, Length(stPostData)], varByte); For iLoop := 0 to Length(stPostData)- 1 do // Iterate. Begin. VPostData[iLoop]:= Ord(stPostData[iLoop+ 1]); End; // for. {Final terminating Character.}. VPostData[Length(stPostData)]:= 0; {Set the type of Variant, cast}. TVarData(vPostData).vType:= varArray; End; {And the other stuff.}. VWebAddr:= stURL; {Make the call Rex.}. WbWebBrowser.Navigate2(vWebAddr, vFlags, vFrame, vPostData, vHeaders); End; {End of Navigate procedure.}.
©Drkb::03448.
Автор: Craig Foley Ответ: А это другой способ:
Code: Procedure TForm1.SubmitPostForm; Var. StrPostData: string; Data: Pointer; URL, Flags, TargetFrameName, PostData, Headers: OleVariant; Begin. { < !-- submit this html form: --> < form method=" post" action=" http://127.0.0.1/cgi-bin/register.pl" > < input type=" text" name=" FIRSTNAME" value=" Hans" > < input type=" text" name=" LASTNAME" value=" Gulo" > < input type=" text" name=" NOTE" value=" thats it" > < input type=" submit" > < /form> } StrPostData := 'FIRSTNAME=Hans& LASTNAME=Gulo& NOTE=thats+it'; PostData := VarArrayCreate([0, Length(strPostData) - 1], varByte); Data := VarArrayLock(PostData); Try. Move(strPostData[1], Data^, Length(strPostData)); Finally. VarArrayUnlock(PostData); End; URL := 'http://127.0.0.1/cgi-bin/register.pl'; Flags := EmptyParam; TargetFrameName := EmptyParam; Headers := EmptyParam; // TWebBrowse. // эти заголовки соответствующими зна?ениями. WebBrowser1.Navigate2(URL, Flags, TargetFrameName, PostData, Headers); End;
©Drkb::03449.
Автор: Hans Gulo.
В BeforeNavigate2, если посмотреть на PostData, увидем строку типа "????????", а как нормальную получить ?
Code: Function VariantArrayToStream(VarArray: OleVariant): TStream; Var. PLocked: Pointer; Begin. Result:= TMemoryStream.Create; If VarIsEmpty(VarArray) or VarIsNull(VarArray) then Exit; Result.Size := VarArrayHighBound(VarArray, 1) - VarArrayLowBound(VarArray, 1) + 1; PLocked := VarArrayLock(VarArray); Try. Result.Write(pLocked^, Result.Size); Finally. VarArrayUnlock(VarArray); Result.Position := 0; End; End; Procedure TForm1.WebBrowser1BeforeNavigate2(Sender: TObject; Const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool); Var. Stream: TStream; Begin. Stream:= VariantArrayToStream(PostData); Try. Memo1.Lines.LoadFromStream(stream); Finally. Stream.Free; End; End;
©Drkb::03450.
Автор Alex.
Взято из http://forum.sources.ru.
| Как сделать WebBrowser плоским вместо 3D? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Следующий пример устанавливает borderStyle:
Code: Procedure TForm1.WBDocumentComplete(Sender: TObject; Const pDisp: IDispatch; var URL: OleVariant); Var. Doc : IHTMLDocument2; Element : IHTMLElement; Begin. Doc := IHTMLDocument2(TWebBrowser(Sender).Document); If Doc = nil then Exit; Element := Doc.body; If Element = nil then Exit; Case Make_Flat of. TRUE : Element.style.borderStyle := 'none'; FALSE : Element.style.borderStyle := ''; End; End;
| Как сохранить веб страничку в bitmap? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. ViewObject: IViewObject; SourceDrawRect: TRect; Begin. If EmbeddedWB1.Document < > nil then. Try. EmbeddedWB1.Document.QueryInterface(IViewObject, ViewObject); If ViewObject < > nil then. Try. SourceDrawRect := Rect(0, 0, Image1.Width, Image1.Height); ViewObject.Draw(DVASPECT_CONTENT, 1, nil, nil, Self.Handle, Image1.Canvas.Handle, @sourceDrawRect, nil, nil, 0); Finally. ViewObject._Release; End; Except. End; End;
| Как сохранить веб страничку в jpg? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Code: Procedure generateJPEGfromBrowser(browser: iWebBrowser2; jpegFQFilename: String; SrcHeight: Integer; srcWidth: Integer; tarHeight: Integer; tarWidth: Integer); Var. SourceDrawRect : TRect; TargetDrawRect: TRect; SourceBitmap: TBitmap; TargetBitmap: TBitmap; Jpeg: TJPEGImage; ViewObject: IViewObject; Begin. SourceBitmap := TBitmap.Create ; TargetBitmap := TBitmap.Create ; Jpeg := TJPEGImage.Create ; Try. Try. SourceDrawRect := Rect(0,0, srcWidth , srcHeight ); SourceBitmap.Width := srcWidth ; SourceBitmap.Height := srcHeight ; viewObject := browser as IViewObject; if viewObject = nil then. Exit; OleCheck(viewObject.Draw(DVASPECT_CONTENT, 1, nil, nil, self.Handle, SourceBitmap.Canvas.Handle, @sourceDrawRect, nil, nil, 0)); // Изменяем размер исходного битмапа для коне?ного битмапа. TargetDrawRect := Rect(0,0, tarWidth, tarHeight); TargetBitmap.Height := tarHeight; TargetBitmap.Width := tarWidth; TargetBitmap.Canvas.StretchDraw(targetDrawRect, sourceBitmap); // Созда?м JPEG из Bitmap и сохраняем его. Jpeg.Assign(targetBitmap) ; MakeFileWriteable(jpegFQFilename); Jpeg.SaveToFile (jpegFQFilename); Finally. Jpeg.free; SourceBitmap.free ; TargetBitmap.free; End; Except. // Обработка ошибок. End; End;
Что такое DOM?
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Document Object Model это платформенно независимый интерфейс,
Позволяющий программам и скриптам динамически обновлять и.
Изменять содержимое, структуру и стиль документов. Вопрос: Где можно почитать документацию по DOM? Ответ: Обзор материалов по DOM на W3C site , а так же FAQ.
Не забудьте заглянуть на Document object на сайте Microsoft.
©Drkb::03527.
| Как работать со всеми фреймами, отображёнными в данный момент в WebBrowser? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Данный пример показывает как определить в каких фреймах разрешена команда 'copy':
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. I: integer; Begin. For i := 0 to (WebBrowser1.OleObject.Document.frames.Length - 1) do. If WebBrowser1.OleObject.Document.frames.item(i).document.queryCommandEnabled('Copy') then. ShowMessage('copy command is enabled for frame no.' + IntToStr(i)); End;
| Как работать со всеми ячейками < TABLE> ? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Пример показывает как добавить содержимое каждой ячейки в TMemo:
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. I, j: integer; OvTable: OleVariant; Begin. // Я использовал первую таблицу на странице в ка?естве примера. OvTable := WebBrowser1.OleObject.Document.all.tags('TABLE').item(0); for i := 0 to (ovTable.Rows.Length - 1) do. Begin. For j := 0 to (ovTable.Rows.Item(i).Cells.Length - 1) do. Begin. Memo1.Lines.Add(ovTable.Rows.Item(i).Cells.Item(j).InnerText; End; End; End;
| Кобинации клавиш Ctrl-C, Ctrl-O, и т.д. не срабатывают. В чём проблема? |
Взято из FAQ:http://blackman.km.ru/myfaq/cont4.phtml.
Перевод материала с сайта members.home.com/hfournier/webbrowser.htm.
Это не ошибка. Информацию по данному вопросу можно найти на сайте.
Microsoft KnowledgeBase статья Q168777.
Приведённый ниже код, устраняет данную проблему:
Code: ... var. Form1: TForm1; FOleInPlaceActiveObject: IOleInPlaceActiveObject; SaveMessageHandler: TMessageEvent; ... Implementation. ... Procedure TForm1.FormActivate(Sender: TObject); Begin. SaveMessageHandler := Application.OnMessage; Application.OnMessage := MyMessageHandler; End; Procedure TForm1.FormDeactivate(Sender: TObject); Begin. Application.OnMessage := SaveMessageHandler; End; Procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); Begin. Application.OnMessage := SaveMessageHandler; FOleInPlaceActiveObject := nil; End; Procedure TForm1.MyMessageHandler(var Msg: TMsg; var Handled: Boolean); Var. IOIPAO: IOleInPlaceActiveObject; Dispatch: IDispatch; Begin. { exit if we don't get back a webbrowser object }. If WebBrowser = nil then. Begin. Handled := False; Exit; End; Handled:=(IsDialogMessage(WebBrowser.Handle, Msg) = True); if (Handled) and (not WebBrowser.Busy) then. Begin. If FOleInPlaceActiveObject = nil then. Begin. Dispatch := WebBrowser.Application; If Dispatch < > nil then. Begin. Dispatch.QueryInterface(IOleInPlaceActiveObject, iOIPAO); If iOIPAO < > nil then. FOleInPlaceActiveObject := iOIPAO; End; End; if FOleInPlaceActiveObject < > nil then. If ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP)) and. ((Msg.wParam = VK_BACK) or (Msg.wParam = VK_LEFT) or (Msg.wParam = VK_RIGHT)) then. //nothing - do not pass on Backspace, Left or Right arrows. Else. FOleInPlaceActiveObject.TranslateAccelerator(Msg); End; End;
Как скрыть своё приложение?
Если Вы хотет, чтобы Ваша программа не появлялась на панели задач, но форма была видна, то воспользуйтесь следующей командой:
Code: ShowWindow (Application.handle, SW_HIDE);
Данная команда в основном используется в приложениях, которае активируются через иконку в трее.
©Drkb::00408.
Взято из http://forum.sources.ru.
| Как приложение оставить свёрнутым в иконку? |
Для этого необходимо обработать сообщение WMQUERYOPEN. Однако обработчик сообщения необходимо поместить в секции private - т.е. в объявлении TForm.
Code: Procedure WMQueryOpen(var Msg: TWMQueryOpen); message WM_QUERYOPEN; Реализация будет выглядеть следующим образом: Procedure WMQueryOpen(var Msg: TWMQueryOpen); Begin. Msg.Result := 0; End;
| Рождение, жизнь и гибель формы. |
Перевод одноимённой статьи с сайта delphi.about.com ).
В Windows основной элемент пользовательского интерфейса - форма. В Delphi каждый проект имеет по крайней мере одно окно - главное окно приложения. Все окна в Delphi основаны на объекте TForm. В данной статье мы рассмотрим основные события учавствующие в "жизни формы".
Форма.
Формы имеют свои свойства, события и методы, при помощи которых Вы можете управлять видом и поведением формы. Форма, это обычный компонент Delphi, но в отличие от других, её нет на панели компонентов. Обычно форма создаётся при создании нового проекта (File | New Application). Вновь созданная форма будет главной формой приложения.
Дополнительные формы в проекте создаются через File | New Form. Так же существуют и другие способы создания форм, но здесь мы не будем рассматривать их...
Как и любой другой компонент (объект) форма имеет свои методы и реагирует на события. Давайте рассмотрим некоторые из этих событий...
Рождение.
OnCreate -> OnShow -> OnActivate-> OnPaint -> OnResize -> OnPaint ...
OnCreate.
Событие OnCreate возникает при создании TForm и только один раз. При создании формы (у каторой свойство Visible установлено в True), события произойдут в следующем порядке: OnCreate, OnShow, OnActivate, OnPaint.
В обработчике события OnCreate можно сделать какие-либо инициализационные действия, однако, любые объекты созданные в OnCreate будут уничтожены в событии OnDestroy.
OnShow.
Это событие генерируется, когда форма станет видимой. OnShow вызывается сразу перед тем, как форма станет видимой. Это событие случается, если установить свойство формы Visible в True, либо при вызове методов Show или ShowModal.
OnActivate.
Это событие генерируется, когда форма становится активной, тоесть когда форма получает фокус ввода. Это событие можно использовать для того, чтобы сменить элемент формы который должен получить фокус.
OnPaint, OnResize.
Эти события вызываются каждый раз, когда форма изначально создаётся. При этом OnPaint вызывается каждый раз, когда какому-нибудь элементу формы необходимо перерисоваться (это событие можно использовать, если необходимо при этом рисовать на форме что-то особенное).
Жизнь.
Когда форма создана и все её элементы ждут своих событий, чтобы обрабатывать их, жизнь формы продолжается до тех пор, пока кто-нибудь не нажмёт крестик в верхнем правом углу формы!
Уничтожение.
При уничтожении формы, события генерируются в следующем порядке:
... OnCloseQuery -> OnClose-> OnDeactivate -> OnHide -> OnDestroy.
OnCloseQuery.
Если мы попытаемся закрыть форму при помощи метода Close либо другим доступным способом (Alt+F4 либо через системное меню), то сгенерируется событие OnCloseQuery. Таким образом, это событие можно использовать, чтобы предотвратить закрытие формы. Обычно, событие OnCloseQuery используется для того, чтобы спросить пользователя - уверен ли он (возможно в приложении остались несохранённые данные).
Procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
Begin.
If MessageDlg('Really close this window?', mtConfirmation, [mbOk, mbCancel], 0) = mrCancel then.
CanClose := False;
End;
Обработчик события OnCloseQuery содержит переменную CanClose, которая определяет, можно ли форме закрыться. Изначальное значение этой переменной True. Однако в обработчике OnCloseQuery можно установить возвращаемое значение CloseQuery в False, чтобы прервать выполнение метода Close.
OnClose.
Если OnCloseQuery вернул CanClose=True (что указывает на то, что форма должна быть закрыта), то будет будет сгенерировано событие OnClose.
Событие OnClose даёт последний шанс, чтобы предотвратить закрытие формы. Обработчик OnClose имеет параметр Action со следующими четырьмя возможными значениями:
CaNone. Форме не разрешено закрыться. Всё равно, что мы установим CanClose в False в OnCloseQuery.
CaHide. Вместо закрытия, форма будет скрыта.
CaFree. Форма будет закрыта, и занятые ей ресурсы будут освобождены.
CaMinimize. Вместо закрытия, форма будет минимизирована. Это значение устанавливается поумолчанию у дочерних форм MDI.
Замечание: Когда пользователь шутдаунит Windows, то будет вызвано OnCloseQuery, а не OnClose. Если Вы не хотите, чтобы Windows завершила свою работу, то поместите свой код в обработчик события OnCloseQuery, хотя CanClose=False не сделает, того, что надо здесь.
OnDestroy.
После того, как метод OnClose будет обработан и форма будет закрыта, то будет вызвано событие OnDestroy. В OnCreate обычно делаются действия, противоположные тем, которые проделывались в OnCreate, то есть уничтожение созданных объектов и освобождение выделенной памяти.
Естевственно, что когда главная форма проекта будет закрыто, то приложение будет завершено.
| Прозрачность в Delphi 6. |
Перевод одноимённой статьи с сайта delphi.about.com ).
В Delphi 6 разработчикам Windows-приложений доступна одна из замечательных возможностей создавать (полу)прозрачные формы (окна). В Delphi 6 класс TForm поддерживает формы со слоями, которые имеют свойства AlphaBlend, AlphaBlendValue, TransparentColor, и TransparentColorValue.
Прозрачность в форме означает то, что пользователь может видить то, что находится позати формы.
Чтобы подготовить форму к прозрачности, Вам потребуется установить свойство AlphaBlend в True. Если AlphaBlend установлено в True, то свойство AlphaBlendValue указывает степень прозрачности. Это свойство позволяет задать значения от 0 до 255. 0 указывает на полную прозрачность окна, в то время как 255 указывает на непрозрачное окно.
Так же возможно устанавливать свойства AlphaBlend и AlphaBlendValue во время разработки (или во время выполнения приложения) при помощи Object Inspector.
Возможно, Вы подумаете, что такая возможность в Delphi, может Вам пригодиться довольно редко, однако прозрачностью можно довольно эффективно привлекать внимание пользователей Вашей программы:
Code: Procedure TAboutBox.FormClose. (Sender: TObject; var Action: TCloseAction); Var. I, cavb : 0..255; Begin. If AlphaBlend=False then. Begin. AlphaBlendValue:=255; AlphaBlend:=True; End; Cavb:=AlphaBlendValue; For i := cavb downto 0 do. Begin. AlphaBlendValue := i; Application.ProcessMessages; End. End;
Липкие окошки.
Из одноимённой статьи с сайта delphi.about.com.
В статье рассматривается приём создания обработчиков сообщений, которые позволяют форме при перетаскивании "прилипать" к краям экранной области.
Конечно же в Win API такой возможности не предусмотрено, поэтому мы воспользуемся сообщениями Windows. Как нам извесно, Delphi обрабатывает сообщения через события, генерируя его в тот момент, когда Windows посылает сообщений приложению. Однако некоторые сообщения не доходят до нас. Например, при изменении размеров формы, генерируется событие OnResize, соотвествующее сообщению WM_SIZE, но при перетаскивании формы никакой реакции не происходит. Конечно же форма может получить это сообщение, но изначально никаких действий для данного сообщения не предусмотрено.
Итак, при перемещении, окну посылается сообщение WM_MOVING. Обрабатывая данной сообщение, приложение может отслеживать размер и расположение перетаскиваемого квадрата и, при необходимости, изменять их.
Так же существует сообщение WM_WINDOWPOSCHANGING, которое посылается окну, в случае, если его размер, расположение или место в Z порядке собираются измениться, как результат вызова функции SetWindowPos либо другой функции управления окном.
Чаще всего с сообщением передаются дополнительные параметры, которые сообщают нам необходимую информацию. Например, сообщение WM_MOVE, указывающее на то, что форма изменила своё местоположение, так же передаёт в параметре LPARAM новые координаты X и Y.
Сообщение WM_WINDOWPOSCHANGING передаёт нам ТОЛЬКО один параметр - указатель на структуру WindowPos, которая содержит информацию о новом размере и местоположении окна. Вот как выглядит структура WindowPos:
Code: TWindowPos = packed record. Hwnd: HWND; {Identifies the window.}. HwndInsertAfter: HWND; {Window above this one}. X: Integer; {Left edge of the window}. Y: Integer; {Right edge of the window}. Cx: Integer; {Window width}. Cy: Integer; {Window height}. Flags: UINT; {Window-positioning options.}. End;
Наша задача проста: нам необходима, чтобы форма прилипла к краю экрана, если она находится на определённом расстоянии от окна (допустим 20 пикселей).
Пример.
К новой форме добавьте Label, один контрол Edit и четыре Check boxes. Измените имя контрола Edit на edStickAt. Измените имена чекбоксов на chkLeft, chkTop, и т.д... Для установки количества пикселей используем edStickAt, который будет использоваться для определения необходимого расстояния до края экрана достаточного для приклеивания формы.
Нас интересует только одно сообщение WM_WINDOWPOSCHANGING. Обработчик для данного сообщения будет объявлен в секции private. Ниже приведён полный код этого процедуры "прилипания" вместе с комментариями. Обратите внимание, что Вы можете предотвратить "прилипание" формы к определённому краю, путё снятия нужной галочки.
Для получения рабочей области декстопа (минус панель задач, панель Microsoft и т.д.), используем SystemParametersInfo, первый параметр которой SPI_GETWORKAREA.
Code: ... Private. Procedure WMWINDOWPOSCHANGING. (Var Msg: TWMWINDOWPOSCHANGING); Message WM_WINDOWPOSCHANGING; ... Procedure TfrMain.WMWINDOWPOSCHANGING. (var Msg: TWMWINDOWPOSCHANGING); Const. Docked: Boolean = FALSE; Var. RWorkArea: TRect; StickAt : Word; Begin. StickAt := StrToInt(edStickAt.Text); SystemParametersInfo. (SPI_GETWORKAREA, 0, @rWorkArea, 0); With Msg.WindowPos^ do begin. If chkLeft.Checked then. If x <= rWorkArea.Left + StickAt then begin. X := rWorkArea.Left; Docked := TRUE; End; If chkRight.Checked then. If x + cx >= rWorkArea.Right - StickAt then begin. X := rWorkArea.Right - cx; Docked := TRUE; End; If chkTop.Checked then. If y <= rWorkArea.Top + StickAt then begin. Y := rWorkArea.Top; Docked := TRUE; End; If chkBottom.Checked then. If y + cy >= rWorkArea.Bottom - StickAt then begin. Y := rWorkArea.Bottom - cy; Docked := TRUE; End; If Docked then begin. With rWorkArea do begin. // не должна вылезать за пределы экрана. If x < Left then x := Left; If x + cx > Right then x := Right - cx; If y < Top then y := Top; If y + cy > Bottom then y := Bottom - cy; End; {ширина rWorkArea}. End; {}. End; {с Msg.WindowPos^}. Inherited; End; End.
Теперь достаточно запустить проект и перетащить форму к любому краю экрана.
©Drkb::00508.
Комментарии:
Автор: Nashev.
А так короче... И, ИМХО, лучше:
Code: Procedure TCustomGlueForm.WMWindowPosChanging1(var Msg: TWMWindowPosChanging); Var. WorkArea: TRect; StickAt : Word; Begin. StickAt := 10; SystemParametersInfo(SPI_GETWORKAREA, 0, @WorkArea, 0); With WorkArea, Msg.WindowPos^ do. Begin. // Сдвигаем границы для сравнения с левой и верхней сторонами. Right:=Right-cx; Bottom:=Bottom-cy; If abs(Left - x) <= StickAt then x := Left; If abs(Right - x) <= StickAt then x := Right; If abs(Top - y) <= StickAt then y := Top; If abs(Bottom - y) <= StickAt then y := Bottom; End; Inherited; End;
В проекте осталось 2 глюка:
1) Если у формы, к которой прицепили другую форму за правую/нижнюю границы попробовать переместить эти границы, прицепленная форма останется на месте но все равно будет прикрепленной.
2) Иногда 3 формы прикрепляются друг к другу, и иначе, как воспользовавшись 1-ым глюком, их не расцепить.
Состав проекта:
Сам проект, uCustomGlueForm - форма с добавленной липкостью 3 формы - пустышки, наследники TCustomGlueForm.
Для использования сделанного в своих проектах надо добавить в проект, и свои формы создавать, наследуя от него, например, через мастер "File/New...".
В принципе, если липкость нужна без прилипания (а это уже работает без глюков) можно выкинуть все методы, кроме.
Procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging);message WM_WINDOWPOSCHANGING;
И все переменные, а в самом WMWindowPosChanging удалить все упоминания этих переменных.
©Drkb::00509.
Взято из http://forum.sources.ru.
| Как сделать прозрачное окно родными средствами Windows2000? |
В Windows2000 есть для этого ф-я SetLayeredWindowAttributes, вот пример её использования:
Code: Unit Win2k; Interface. Uses Graphics, Windows; Function SetLayeredWindowAttributes( Hwnd : HWND; // handle to the layered window. CrKey : TColor; // specifies the color key. BAlpha : byte; // value for the blend function. DwFlags : DWORD // action. ): BOOL; stdcall; Function SetLayeredWindowAttributes; external 'user32.dll'; Implementation. End. Program WinLayer; Uses. Windows, SysUtils, Win2k in 'Win2k.pas'; Const. WS_EX_LAYERED= $80000; LWA_COLORKEY = 1; LWA_ALPHA = 2; Var. Hndl : THandle; Transp : Byte; Begin. Writeln('Windows2000 Layer <- build by AK ->'); Writeln(' Usage: WINLAYER.EXE [window name] [Transp (0-255)]'); Writeln(' Example: WINLAYER "Calculator" 200'); Writeln; If ParamCount <> 2 then exit; Hndl := FindWindow(nil, PChar(ParamStr(1))); Transp := StrToIntDef(ParamStr(2), 128); If SetWindowLong(Hndl, GWL_EXSTYLE, GetWindowLong(Hndl, GWL_EXSTYLE) or WS_EX_LAYERED) = 0 then. Writeln('Error !'); If not SetLayeredWindowAttributes(Hndl, 0, Transp, LWA_ALPHA) then. // ^^^ степень прозрачности. // 0 - полная прозрачность. // 255 - полная непрозрачность. Writeln('Error !'); End.
| Как скрыть кнопку [х] в заголовке окна? |
Автор: Fernando Silva.
Пример показывает, как при инициализации формы происходит поиск нашего окна, а затем вычисление местоположения нужной нам кнопки в заголовке окна.
Code: Procedure TForm1.FormCreate(Sender: TObject); Var. HwndHandle: THANDLE; HMenuHandle: HMENU; IPos: Integer; Begin. HwndHandle := FindWindow(nil, PChar(Caption)); If (hwndHandle <> 0) then. Begin. HMenuHandle := GetSystemMenu(hwndHandle, FALSE); If (hMenuHandle <> 0) then. Begin. DeleteMenu(hMenuHandle, SC_CLOSE, MF_BYCOMMAND); IPos := GetMenuItemCount(hMenuHandle); Dec(iPos); { Надо быть уверенным, что нет ошибки т.к. -1 указывает на ошибку }. If iPos > -1 then. DeleteMenu(hMenuHandle, iPos, MF_BYPOSITION); End; End; End;
Объекты соединения с источниками данных.
Внутренний механизм ADO, обеспечивающий соединение с хранилищем данных, использует два типа объектов. Это объекты-источники данных и объекты-сессии.
Объект-источник данных обеспечивает представление информации о требуемом реальном источнике данных и подключение к нему.
Для ввода сведений о хранилище данных используется интерфейс iDBProperties. Для успешного подключения необходимо задать обязательные сведения. Вероятно, для любого хранилища данных будет актуальной информация об его имени, пользователе и пароле. Однако каждый тип хранилища имеет собственные уникальные настройки. Для получения списка всех обязательных параметров соединения с данным хранилищем можно воспользоваться методом.
Function GetPropertylnfo(cPropertylDSets: UINT; rgPropertylDSets: PDBPropIDSetArray; var pcPropertylnfoSets: UINT; out prgPropertylnfoSets: PDBPropInfoSet; ppDescBuffer: PPOleStr): HResult; stdcall;
Который возвращает заполненную структуру DBPROPINFO.
PDBPropInfo = ^TDBPropInfo;
DBPROPINFO = packed record.
PwszDescription: PWideChar;
DwPropertylD: DBPROPID;
DwFlags: DBPROPFLAGS;
VtType: Word;
VValues: OleVariant;
End;
TDBPropInfo = DBPROPINFO;
Для каждого обязательного параметра в элементе dwFlags устанавливается значение DBPROPFLAGS_REQUIRED.
Для инициализации соединения необходимо использовать метод.
Function Initialize: HResult; stdcall;
Интерфейса iDBinitiaiize объекта-источника данных.
| Как сделать, чтобы форма закрывалась при нажатии Esc? |
Для начала необходимо установить свойство формы KeyPreview в True. А потом уже можно отлавливать "Esc":
Code: Procedure TForm1.FormCreate(Sender: TObject); Begin. Form1.KeyPreview := True; End; Procedure TForm1.FormKeyPress. (Sender: TObject; var Key: Char); Begin. If key = #27 then Close; End;
Bitmap.PixelFormat - подробности.
Общее.
Новое в Delphi 3 свойство scanline допускает быстрый доступ к отдельным пикселям, но необходимо указать с каким Bitmap.PixelFormat вы работаете, прежде чем сможете иметь доступ к пикселям.
Возможные PixelFormats включают:
PfDevice.
Pf1bit - посмотреть подробности.
Pf4bit.
Pf8bit - посмотреть подробности.
Pf15bit.
Pf16bit.
Pf24bit - посмотреть подробности.
Pf32bit.
©Drkb::03770.
Взято с http://delphiworld.narod.ru.
| Bitmap.PixelFormat:=pf24bit; |
Для pf24bit-изображений необходимо определить:
Code: CONST. PixelCountMax = 32768; TYPE. PRGBArray = ^TRGBArray; TRGBArray = ARRAY[0..PixelCountMax-1] OF TRGBTriple;
| Bitmap.PixelFormat:=pf8bit; |
Доступ к такому формату изображения легко получить, используя TByteArray (определен в SysUtils.PAS):
Code: PByteArray = ^TByteArray; TByteArray = array[0..32767] of Byte;
| Bitmap.PixelFormat:=pf1bit; |
Доступ к pf8bit-изображениям осуществляется достаточно легко, с тех пор, как они стали использовать один байт на пиксель. Но вы можете сохранить много памяти, если вам необходим единственный бит на пиксель (как, например, с различными масками) в случае pf1bit-изображения.
Как и в случае с pf8bit-изображениями, используйте TByteArray для доступа к pf1bit-ным линиям чередования (Scanlines). Но для доступа к отдельным пикселям вам понадобиться работать с битами отдельного байта. Так, ширина линии чередования равна Bitmap.Width DIV 8 байт.
Нижеприведенный код показывает как можно создать шаблон 1-битного изображения: черный, белый, полоски, "g", "стрелка" и случайный -- опция "инвертировано" также доступна. (Надеюсь, технологию вы освоете без труда.).
Создайте форму с Image1: для TImage я использую одно изображение Image1 размером 256x256 и свойством Stretch := TRUE, чтобы отдельные пиксели было легко разглядеть. Кнопки Black, White и Stripes имеют свойство tags, c соответствующими значениями 0, 255, и 85 ($55 = 01010101 в двоичной системе исчисления), вызывающие при нажатии обработчик события ButtonStripesClick.
Кнопки "g" и "arrow" имеют собственные обработчики событий, позволяющие корректно распечатать тестовые изображения на принтере HP Laserjet.
"Random" случайным образом устанавливает биты в 1-битном изображении.
"Invert" меняет нули на единички и наоборот.
Code: // Пример того, как использовать Bitmap.Scanline для PixelFormat=pf1Bit. // По просьбе Mino Ballone из Италии. // // Авторское право (C) 1997, Earl F. Glynn, Overland Park, KS. // Все права защищены. // Может свободно использоваться для некоммерческих целей. Unit ScreenSingleBit; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; Type. TForm1 = class(TForm). Image1: TImage; ButtonBlack: TButton; ButtonWhite: TButton; ButtonStripes: TButton; ButtonG: TButton; ButtonArrow: TButton; ButtonRandom: TButton; ButtonInvert: TButton; Procedure ButtonStripesClick(Sender: TObject); Procedure ButtonGClick(Sender: TObject); Procedure FormCreate(Sender: TObject); Procedure FormDestroy(Sender: TObject); Procedure ButtonRandomClick(Sender: TObject); Procedure ButtonInvertClick(Sender: TObject); Procedure ButtonArrowClick(Sender: TObject); Private. Bitmap: TBitmap; { Private declarations }. Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Const. BitsPerPixel = 8; Procedure TForm1.ButtonStripesClick(Sender: TObject); Var. I: INTEGER; J: INTEGER; Row: pByteArray; Value: BYTE; Begin. Value := (Sender as TButton).Tag; // Value = $00 = 00000000 в двоичном исчислении для черного. // Value = $FF = 11111111 в двоичном исчислении для белого. // Value = $55 = 01010101 в двоичном исчислении для черных и белых полос. For j := 0 to Bitmap.Height - 1 do. Begin. Row := pByteArray(Bitmap.Scanline[j]); For i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do. Begin. Row[i] := Value. End. End; Image1.Picture.Graphic := Bitmap. End; Procedure TForm1.ButtonGClick(Sender: TObject); Const. {Изображение "g" было адаптировано для печати на принтере. LaserJet IIP в соответствии с техническим руководством}. G: array[0..31, 0..3] of BYTE = { 0}(($00, $FC, $0F, $C0), {00000000 11111100 00001111 11000000}. { 1}($07, $FF, $1F, $E0), {00000111 11111111 00011111 11100000}. { 2}($0F, $FF, $9F, $C0), {00001111 11111111 10011111 11000000}. { 3}($3F, $D7, $DE, $00), {00111111 11010111 11011110 00000000}. { 4}($3E, $01, $FE, $00), {00111110 00000001 11111110 00000000}. { 5}($7C, $00, $7E, $00), {01111100 00000000 01111110 00000000}. { 6}($78, $00, $7E, $00), {01111000 00000000 01111110 00000000}. { 7}($F0, $00, $3E, $00), {11110000 00000000 00111110 00000000}. { 8}($F0, $00, $3E, $00), {11110000 00000000 00111110 00000000}. { 9}($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}. {10}($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}. {11}($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}. {12}($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}. {13}($F0, $00, $3E, $00), {11110000 00000000 00111110 00000000}. {14}($78, $00, $3E, $00), {01111000 00000000 00111110 00000000}. {15}($78, $00, $3E, $00), {01111000 00000000 00111110 00000000}. {16}($78, $00, $7E, $00), {01111000 00000000 01111110 00000000}. {17}($3C, $00, $FE, $00), {00111100 00000000 11111110 00000000}. {18}($1F, $D7, $DE, $00), {00011111 11010111 11011110 00000000}. {19}($0F, $FF, $5E, $00), {00001111 11111111 10011110 00000000}. {20}($07, $FF, $1E, $00), {00000111 11111111 00011110 00000000}. {21}($00, $A8, $1E, $00), {00000000 10101000 00011110 00000000}. {22}($00, $00, $1E, $00), {00000000 00000000 00011110 00000000}. {23}($00, $00, $1E, $00), {00000000 00000000 00011110 00000000}. {24}($00, $00, $1E, $00), {00000000 00000000 00011110 00000000}. {25}($00, $00, $3E, $00), {00000000 00000000 00111110 00000000}. {26}($00, $00, $3C, $00), {00000000 00000000 00111100 00000000}. {27}($00, $00, $7C, $00), {00000000 00000000 01111100 00000000}. {28}($00, $01, $F8, $00), {00000000 00000001 11111000 00000000}. {29}($01, $FF, $F0, $00), {00000001 11111111 11110000 00000000}. {30}($03, $FF, $E0, $00), {00000011 11111111 11100000 00000000}. {31}($01, $FF, $80, $00)); {00000001 11111111 10000000 00000000}. Var. I: INTEGER; J: INTEGER; Row: pByteArray; Begin. For j := 0 to Bitmap.Height - 1 do. Begin. Row := pByteArray(Bitmap.Scanline[j]); For i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do. Begin. Row[i] := G[j, i]. End. End; Image1.Picture.Graphic := Bitmap. End; Procedure TForm1.ButtonArrowClick(Sender: TObject); Const. {Изображение "стрелка" было адаптировано для печати на принтере. LaserJet IIP в соответствии с техническим руководством}. Arrow: array[0..31, 0..3] of BYTE = { 0}(($00, $00, $80, $00), {00000000 00000000 10000000 00000000}. { 1}($00, $00, $C0, $00), {00000000 00000000 11000000 00000000}. { 2}($00, $00, $E0, $00), {00000000 00000000 11100000 00000000}. { 3}($00, $00, $F0, $00), {00000000 00000000 11110000 00000000}. { 4}($00, $00, $F8, $00), {00000000 00000000 11111000 00000000}. { 5}($00, $00, $FC, $00), {00000000 00000000 11111100 00000000}. { 6}($00, $00, $FE, $00), {00000000 00000000 11111110 00000000}. { 7}($00, $00, $FF, $00), {00000000 00000000 11111111 00000000}. { 8}($00, $00, $FF, $80), {00000000 00000000 11111111 10000000}. { 9}($FF, $FF, $FF, $C0), {11111111 11111111 11111111 11000000}. {10}($FF, $FF, $FF, $E0), {11111111 11111111 11111111 11100000}. {11}($FF, $FF, $FF, $F0), {11111111 11111111 11111111 11110000}. {12}($FF, $FF, $FF, $F8), {11111111 11111111 11111111 11111000}. {13}($FF, $FF, $FF, $FC), {11111111 11111111 11111111 11111100}. {14}($FF, $FF, $FF, $FE), {11111111 11111111 11111111 11111110}. {15}($FF, $FF, $FF, $FF), {11111111 11111111 11111111 11111111}. {16}($FF, $FF, $FF, $FF), {11111111 11111111 11111111 11111111}. {17}($FF, $FF, $FF, $FE), {11111111 11111111 11111111 11111110}. {18}($FF, $FF, $FF, $FC), {11111111 11111111 11111111 11111100}. {19}($FF, $FF, $FF, $F8), {11111111 11111111 11111111 11111000}. {20}($FF, $FF, $FF, $F0), {11111111 11111111 11111111 11110000}. {21}($FF, $FF, $FF, $E0), {11111111 11111111 11111111 11100000}. {22}($FF, $FF, $FF, $C0), {11111111 11111111 11111111 11000000}. {23}($00, $00, $FF, $80), {00000000 00000000 11111111 10000000}. {24}($00, $00, $FF, $00), {00000000 00000000 11111111 00000000}. {25}($00, $00, $FE, $00), {00000000 00000000 11111110 00000000}. {26}($00, $00, $FC, $00), {00000000 00000000 11111100 00000000}. {27}($00, $00, $F8, $00), {00000000 00000000 11111000 00000000}. {28}($00, $00, $F0, $00), {00000000 00000000 11110000 00000000}. {29}($00, $00, $E0, $00), {00000000 00000000 11100000 00000000}. {30}($00, $00, $C0, $00), {00000000 00000000 11000000 00000000}. {31}($00, $00, $80, $00)); {00000000 00000000 10000000 00000000}. Var. I: INTEGER; J: INTEGER; Row: pByteArray; Begin. For j := 0 to Bitmap.Height - 1 do. Begin. Row := pByteArray(Bitmap.Scanline[j]); For i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do. Begin. Row[i] := arrow[j, i]. End. End; Image1.Picture.Graphic := Bitmap. End; Procedure TForm1.FormCreate(Sender: TObject); Begin. Bitmap := TBitmap.Create; With Bitmap do. Begin. Width := 32; Height := 32; PixelFormat := pf1bit. End; Image1.Picture.Graphic := Bitmap. End; Procedure TForm1.FormDestroy(Sender: TObject); Begin. Bitmap.Free. End; Procedure TForm1.ButtonRandomClick(Sender: TObject); Var. I: INTEGER; J: INTEGER; Row: pByteArray; Begin. For j := 0 to Bitmap.Height - 1 do. Begin. Row := pByteArray(Bitmap.Scanline[j]); For i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do. Begin. Row[i] := Random(256). End. End; Image1.Picture.Graphic := Bitmap. End; Procedure TForm1.ButtonInvertClick(Sender: TObject); Var. I: INTEGER; J: INTEGER; Row: pByteArray; Begin. For j := 0 to Bitmap.Height - 1 do. Begin. Row := pByteArray(Bitmap.Scanline[j]); For i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do. Begin. Row[i] := not Row[i]. End. End; Image1.Picture.Graphic := Bitmap. End; End.
| Как писать консольные приложения в Delphi? |
Автор: Alex G. Fedorov.
Все настоящие программисты делятся на три категории: на тех, кто пишет программы, завершающиеся по нажатию F10, Alt-F4, Alt-X. Все остальные принципы деления надуманны.
Статья представляет собой изучение создания консольного приложения в Delphi. Прежде чем начать вникать в подробности, необходимо уточнить, что консольные приложения это особый вид Windows приложений - с одной стороны он имеет полный доступ к функциям Win API, с другой - не имеет графического интерфейса и выполняется в текстовом режиме.
Простая консольная программа.
На момент написания статьи (1997г.), в Delphi не было возможности автоматически создавать консольные приложения (возможно на сегодняшний день этот недостаток устранён), поэтому мы создадим пустой файл и поместим в него следующий код:
Code: Program ConPrg; {$APPTYPE CONSOLE}. Begin. End.
Сравнительный анализ технологий CORBA и COM.
В последние 2-3 года резко возрос интерес к так называемым распределенным системам. Под распределенными системами обычно понимают программные комплексы, составные части которых функционируют на разных компьютерах в сети. Эти части взаимодействуют друг с другом, используя ту или иную технологию различного уровня - от непосредственного использования сокетов TCP/IP до технологий с высоким уровнем абстракции, таких, как RMI или CORBA.
Рост популярности распределенных систем вызван существенным ужесточением требований, предъявляемых заказчиком к современным программным продуктам.
Пожалуй, важнейшими из этих требований являются следующие:
· Обеспечение масштабируемости систем, т.е. способности эффективно обслуживать как малое, так и очень большое количество клиентов одновременно.
· Надежность создаваемых приложений. Программный комплекс должен быть устойчив не только к ошибкам пользователей (это определяется главным образом качеством клиентских приложений), но и к сбоям в системе коммуникаций. Надежность подразумевает использование транзакций, т.е. гарантированного перехода системы в процессе функционирования из одного устойчивого и достоверного состояния в другое.
· Возможность непрерывной работы в течение длительного времени (так называемый режим 24x7, т.е. режим круглосуточной работы в течение недель и месяцев).
· Высокий уровень безопасности системы, под которой понимается не только контроль доступности тех или иных ресурсов системы и защищенность информации на всех этапах функционирования, но и отслеживание выполняемых действий с высокой степенью достоверности.
· Высокая скорость разработки приложений и простота их сопровождения и модификации с использованием программистов средней квалификации.
· Оказалось, что обеспечить соответствие этим требованиям, используя традиционные технологии - а именно, двухзвенные системы клиент-сервер, в которых в качестве серверов выступают системы управления базами данных, почти невозможно.
· Заметим, что далеко не для всех приложений вышеперечисленные требования являются существенными. Легко представить распределенную систему, которая функционирует на небольшом (до 100) количестве компьютеров, работающих в локальной сети, где нет проблем с перезапуском одного или двух-трех серверов в случае необходимости, а главными задачами, для решения которых используются распределенные технологии, являются задачи использования функциональности нескольких стандартных серверов приложений (например, текстовых процессоров, броузеров и электронных таблиц) в интересах клиентских задач. Необходимо иметь в виду, что термин распределенные системы относится к огромному числу задач самого разного класса.
Данный обзор содержит сравнительный анализ двух наиболее популярных и комплексных систем создания распределенных приложений, а именно, CORBA консорциума OMG и COM (DCOM, COM+) фирмы Microsoft. Этот обзор предназначен главным образом для менеджеров проектов, руководителей информационных служб и др., т.е. для тех, кому приходится принимать ответственные, стратегические решения. Вследствие этого, в нем будут отсутствовать чисто технические аспекты обоих технологий, которые интересны только для разработчиков.
Кроме того, при написании обзора не ставилась задача сделать окончательный вывод типа ... таким образом, CORBA (COM), бесспорно, лучше, чем COM (CORBA). Связано это не с модной в наше время политкорректностью или отсутствием у автора своей точки зрения по этому вопросу. Дело даже не в том, что существуют определенные трудности с формализацией такого сравнения - я мог бы представить результаты сравнительных анализов, в которых используются численные оценки (баллы), выставленные экспертами, весовые коэффициенты и прочее, что придает отчету серьезный и весомый вид. Дело в том, что COM и CORBA можно сравнивать только с учетом определенных допущений. Отказ от таких допущений легко позволяет получить какой угодно результат. Вот эти допущения:
· CORBA, в отличие от СОМ'а, является концепцией, а не ее реализацией. Когда мы говорим COM, то понимаем под этим скорее набор конкретных средств - элементов операционной системы, библиотек, утилит и т.п., являющихся составной частью того, что называется Microsoft Windows. Под термином CORBA понимается именно сложная и развитая концепция, сформулированная на уровне специального языка описаний - IDL. Реализации же этой концепции могут сильно отличаться друг от друга по различным критериям, наиболее важным в том или другом случае. Inprise/Corel VisiBroker и Application Server, BEA WebLogic, Iona Orbix, Oracle Application Server и картриджи Oracle, IBM BOSS - все эти продукты используют те или иные возможности CORBA. Технология Sun Enterpise JavaBeans создана поверх CORBA и использует такие ее возможности, как удаленный вызов объектов, службу имен, управление транзакциями. Реализация EJB фирмы Inprise связано с CORBA еще более тесным образом. Мы будем сравнивать COM и CORBA именно как концепции создания распределенных систем, а не как ту или иную их реализацию.
· При работе с реальным проектом необходимо учитывать область применения той или иной технологии. COM (как цельное и комплексное решение) способен функционировать только под Windows NT/Windows2000. Рассуждения о переносе COM на другие операционные системы в настоящий момент носят скорее рекламный характер. Ни компонентная модель COM (т.е. ActiveX), ни монитор транзакций (Microsoft Transaction Server, MTS) не способны работать нигде, кроме Windows, и сама Microsoft не проявляет никакой активности в этом напрвлении (вопросами переноса тех или иных элементов COM на другие платформы занимается консорциум Open Group). CORBA является многоплатформенным решением просто по определению.
· Помимо чисто технологических аспектов, при принятии решения необходимо оценить затраты на закупку необходимого программного обеспечения, его сопровождения и обучение персонала. COM (в отличие от CORBA) официально является бесплатной технологией. Вы получаете все необходимое, просто приобретя, например, Windows NT Server. (Кстати, сам факт конкуренции дорогостоящей технологии с аналогичной, но бесплатной, многое говорит об их технических возможностях).
· Наличие готовых серверов приложений, пригодных для решения вашей задачи. Если Вы можете решить свои проблемы, используя функциональность Microsoft Office, то ничего лучше COM вы, естественно, не найдете.
· Таким образом, главной задачей настоящего обзора является попытка помочь руководителю того или иного уровня принять квалифицированное решение в каждом конкретном случае. Поскольку и CORBA, и COM позиционируются соответственно OMG и Microsoft как универсальные технологии создания распределенных систем, мы будем оценивать и сравнивать их именно с этой точки зрения. Предполагается, что для проекта используется платформа Windows (в противном случае нечего рассматривать COM) и имеется достаточно средств для закупки основных стандартных частей той или иной реализации (иначе обсуждение CORBA теряет смысл).
Концептуальный фундамент технологии.
COM.
Технология создавалась фирмой Microsoft как средство взаимодействия приложений (в том числе составных частей операционной системы) Windows, функционирующих на одном компьютере, с последующим развитием для использования в пределах локальной сети. Главная задача на момент создания - обеспечение технологии Object Linking and Embedding (OLE 1.0). Характерно, что обмен данными между приложениями (Dynamic Data Exchange, DDE) первоначально строился не по COM-технологии, а с использованием механизма сообщений (messages). Развитие технологии идет по мере добавления новых возможностей. Как универсальная технология взаимодействия приложений COM начал использоваться с OLE 2.0 (1991). Концепция технологии неразрывно связана с ее реализацией. Появление новых возможностей - это просто появление новых библиотек, функций API и утилит Windows. Общий знаменатель технологии - двоичная структура объекта, хотя в настоящий момент существует язык описания структуры объекта - Interface definition Language (IDL).
CORBA.
Технология создавалась консорциумом OMG как универсальная технология создания распределенных систем в гетерогенных средах. OMG представляет собой некоммерческую организацию, являющуюся содружеством разработчиков программного обеспечения и его потребителей, объединивших свои усилия для создания спецификаций этой технологии. В настоящий момент в OMG состоит более 800 членов, включая всех сколько-нибудь серьезных производителей программного обеспечения (и даже c недавнего времени Microsoft). Первая спецификация CORBA появилась в 1991 г. Новые возможности официально считаются добавленными в CORBA в момент утверждения соответствующей спецификации. Как правило, в разработке спецификации участвуют крупнейшие специалисты в данной области. Разработка реализации - задача конкретной фирмы. Обычно от утверждения спецификации до появления высококачественной реализации проходит довольно много времени - иногда несколько лет. Общий знаменатель технологии - объявления на языке IDL, который является сердцем CORBA с момента ее появления. (Существуют три различных языка описаний с одним и тем же названием - OSF IDL, Microsoft IDL и OMG IDL).
Выводы.
Технология CORBA носит существенно более общий и универсальный характер, чем COM, что заложено в ее фундаменте. Опережение разработки спецификаций (по сравнению с реализациями) позволяет добиться более связной, целостной и гармоничной системы. С другой стороны, при разработке реального проекта нужно предварительно убедиться, что высококачественная реализация того или иного сервиса CORBA уже доступна (источниками проблем могут служить, например, Persistence Service и Security Service).
Комплексность системы.
COM.
COM содержит все необходимое, что нужно для построения распределенной системы: технологию удаленного вызова методов (как статических, так и динамических), базы данных серверных объектов (библиотеки типов), которые могут быть импортированы для анализа структуры серверов COM, универсальный протокол обмена между клиентами и серверами, спецификации так называемых составных документов (ActiveDoc), объектный монитор транзакций (MTS), компонентную модель (ActiveX) и др. Все составные части прекрасно соответствуют друг другу в рамках модели COM. Уникальной возможностью COM является универсальная технология доступа к базам данных - OLE DB/ADO.
CORBA.
В настоящий момент CORBA не имеет своей собственной компонентной модели; работа над ней началась в 1998 г. и еще не завершена. Это главный серьезный недостаток. Правда, он несколько компенсируется наличием основанной на CORBA компонентной моделью Enterprise JavaBeans, так что программисты на Java находятся в привилегированном положении. Все остальное, что присутствует в COM, имеется и в CORBA, и даже более того - за исключением универсальной технологии доступа к БД. Опять-таки, Java-программисты имеют преимущество и здесь - за счет наличия общей для Java технологии доступа к данным JDBC.
Выводы.
В настоящий момент COM более законченная система, но на более низком уровне и при существенно большем количестве ограничений, определяемых самой концепцией системы.
Используемые языки программирования.
COM.
Потенциально COM могут поддерживать самые различные языки программирования - все решает фирма Microsoft. Добавление некоторых расширений или экспертов (wizard) в систему разработки позволит использовать для работы с COM любой язык программирования. В настоящий момент наиболее широко используются Visual Basic, C++ и Delphi. Серьезные проблемы возникли при использования языка, на который возлагались особые надежды - с Java. Microsoft добилась прекрасного взаимодействия Java с COM, но достигнуто это было путем отказа от переносимости таких Java-программ на другие виртуальные машины Java. Не случайно продукт фирмы Microsoft - J++ - не содержит в названии Java. Вообще, уровень стандартизации для COM достаточно слаб. Это не обязательно нужно рассматривать как недостаток - в конце концов, язык C лет пятнадцать прекрасно обходился без формального стандарта.
CORBA.
Под стандартом применительно к CORBA понимается то, что официально утверждено консорциумом OMG. Надо сказать, что это очень высокий уровень легитимности, так как авторитет OMG в компьютерном мире чрезвычайно высок. В настоящий момент стандартизовано отображение языка IDL на 6 языков программирования - Ada, C, C++, Cobol, Java и Smalltalk. Существуют также отображения на Pascal (точнее, Delphi), Perl, Python и еще десяток языков.
Наиболее используемыми языками в настоящий момент являются Java (вследствие прекрасного взаимодействия Java-технологий, особенно JDBC, RMI, JNDI и EJB, с CORBA), и C++ - как самый эффективный, мощный и распространенный язык компьютерной индустрии.
Выводы.
Обе технологии не испытывают особых проблем с точки зрения взаимодействия с языками программирования. Некоторые преимущества имеет CORBA - за счет более строгой стандартизации и более богатого выбора доступных средств разработки.
Уровень абстракции.
COM.
COM реализует высокий уровень абстракции - все вопросы низкого уровня, такие, как взаимодействия с операционной системой или сетевыми средствами, "спрятаны" от прикладного программиста.
CORBA.
CORBA обеспечивает даже несколько более высокий уровень за счет базировании технологии исключительно на языке описания IDL с последующим отображением таких спецификаций на конкретный язык программирования, а также некоторых возможностей, например, автоматического (т.е. прозрачного для программиста) распространения контекста транзакций.
Выводы.
Обе технологии реализуют примерно одинаковый и достаточно высокий уровень абстракций.
Поддержка компонентной модели.
COM.
Компонентная модель Microsoft, базирующаяся на COM-технологии, в основе имеет двоичную структуру объектов. Это не вызывает никаких проблем при ориентации на одну платформу и операционную систему. Безусловным достоинством такой модели является простота создания компонентов с использованием различных языков программирования. С другой стороны, такая компонентная модель неизбежно связана с определенными ограничениями и недостатками. Одним из самых серьезных недостатков является то, что ее нельзя, строго говоря, назвать объектно-ориентированной.
CORBA.
Как уже говорилось, CORBA в настоящее время не имеет своей компонентной модели. Пусть это не имеет практического значения для Java-программистов, но в общем случае эта та область, где OMG (и фирмам-производителям программного обеспечения) еще предстоит серьезно поработать.
Выводы.
Это та область, где COM пока имеет существенные преимущества по сравнению с CORBA. C другой стороны, при разработке реальных проектов использование на стороне сервера компонентов Enterprise JavaBeans, построенных поверх инфраструктуры CORBA, предоставляет разработчику значительные преимущества по сравнению с компонентами ActiveX.
Универсальный протокол обмена.
COM.
Передача данных между клиентом и сервером основана на наборе типов, которые называются OLE Automation types. В принципе, схема маршалинга (в том числе типы передаваемых данных) определяется парой стандартных интерфейсов COM. Реализовав по-своему некоторые из методов этих интерфейсов, можно определить свою схему маршалинга. Впрочем, это нетривиальная задача, и обычно разработчики используют уже готовую стандартную реализацию. Упаковка данных и их передача могут быть реализованы поверх различных сетевых протоколов.
CORBA.
CORBA значительно более строго и формально подходит к механизмам обмена и передаче данных. Определен протокол CORBA (General Inter-ORB Protocol, GIOP) - и его реализация на базе протокола TCP/IP (Internet Inter-ORB Protocol, IIOP). CORBA способна передавать данные различных типов, включая структуры (struct) и объединения (union), в том числе содержащие рекурсивные определения. Предусмотрена система описания и контроля типов - как на уровне IDL-деклараций, так и динамическая. Для каждого языка используется свое отображение данных IDL. В версии 2.3 появился новый, заимствованный из RMI механизм передачи объектов CORBA - так называемая "передача по значению (objects-by-value)". В предыдущих версиях CORBA при вызове удаленных методов объекты можно было передавать только по ссылке.
Выводы.
CORBA предоставляет значительно больше возможностей, и эти возможности строго формализованы - в противном случае было бы невозможно обеспечить совместную работу различных средств от различных производителей программного обеспечения.
Поддержка со стороны различных производителей и открытость.
COM.
В настоящий момент официальным "хранителем стандарта" технологии COM является консорциум Open Group, хотя "главным игроком на этом поле" является, конечно же, Microsoft. Поддержкой технологии COM занимаются многие небольшие фирмы - некоторые из них создают компоненты ActiveX, некоторые - как Software AG - занимаются переносом фрагментов COM на другие платформы, некоторые - как Borland - создают RAD-инструменты, основанные в том числе и на COM. В любом случае, только Microsoft решает, какая часть технологии и в каком виде попадает из лабораторий Microsoft в открытые спецификации.
CORBA.
Ситуация с CORBA совершенно иная. CORBA является результатом совместных усилий огромного числа фирм, среди которых Sun, Oracle, IBM, Netscape/AOL, DEC/Compaq, JavaSoft, Borland/Visigenic (в настоящий момент в связи со слиянием Inprise и Corel принято решение о восстановлении имени Visigenic), BEA, Iona и многие другие. Можно сказать, что все производители программного обеспечения, которое должно функционировать на различных платформах и под управлением различных операционных систем, выбрали CORBA как стандартную инфраструктуру создания программных продуктов. Естественно, все спецификации CORBA являются полностью открытыми. В лагере сторонников CORBA просматривается четкая тенденция к тесному взаимодействию и некоторой унификации используемых технологий (в качестве примера можно привести отказ Sun и ORACLE от создания собственного ORB и лицензирование Visigenic VisiBroker).
Выводы.
COM и CORBA имеют совершенно разный уровень открытости и поддержки со стороны ведущих фирм в компьютерной индустрии.
Развитость сервисной части.
COM.
COM предоставляет минимальный набор совершенно необходимых средств - кодогенераторы c IDL, утилиты управления доступом (dcomcnfg) и др. Как правило, разработчики пользуются теми или иными инструментами, встроенными в конкретные средства разработки (примером может служит утилита работы с библиотеками типов или эксперт создания ActiveX-объектов в Borland Delphi/C++ Builder).
CORBA.
CORBA имеет очень развитую сервисную часть; например, только для поиска серверных объектов по различным критериям можно использовать 4 различных сервиса CORBA. Кроме того, OMG стремится к максимальной стандартизации вспомогательных возможностей CORBA.
Выводы.
CORBA предоставляет разработчикам существенно большие возможности, чем COM, в области сервисов и вспомогательных средств. С другой стороны, COM-программисты обычно не испытывают какого-либо дискомфорта из-за их недостатка. Вследствие ограниченности области применения COM объективно нет необходимости в создании таких же развитых и универсальных средств, как это совершенно необходимо для CORBA.
Самодокументирование системы.
COM.
COM предусиатривает возможность создания локальной базы данных, хранящей информацию о COM-объектах, их интерфейсах, методах и т.д. для сервера приложений COM. Такая база данных называется библиотекой типов (type library). Использование библиотек типов не является обязательным для OLE, хотя это необходимо в технологии ActiveX. Для получения информации из type library пользователь должен явно импортировать ее, для чего необходимо выбрать соответствующую запись реестра Windows на конкретном компьютере.
CORBA.
CORBA имеет аналог библиотеки типов COM - это так называемый Репозитарий Интерфейсов (Interface Repository). Чтобы оценить принципиальную разницу в подходе CORBA по сравнению с COM, достаточно сказать, что Репозитарий Интерфейсов CORBA сам представляет из себя объект CORBA со всеми вытекающими из этого возможностями. Помимо Репозитария Интерфейсов, CORBA использует Репозитарий Реализаций (Implementation Repository), который представляет из себя базу данных, содержащую информацию о серверах приложений CORBA.
Выводы.
Средства интроспекции (самодокументрования) CORBA значительно более развиты, мощны, гибки и универсальны, чем аналогичные средства COM. CORBA-программисты получают дополнительные преимущества по сравнению со своими коллегами, работающими с COM, при использовании Java (Java-технологии очень тесно связаны с CORBA). Связано это с тем, что Java предусматривает свой уровень интроспекции, дополнительный по отношению к CORBA.
Технология и описание проекта.
COM.
Обычно объявления на языке IDL при работе с COM не являются существенной частью спецификации проекта, так как IDL-спецификации играют вспомогательную роль в COM-технологии вследствие ее базирования на двоичном стандарте объектов. Кроме того, язык Microsoft IDL не очень развит с точки зрения объявления типов данных, из которых строится программа.
CORBA.
Проект с использованием CORBA всегда начинается с написания IDL-спецификаций (особый случай - использование Java, когда в принципе возможно генерировать стандартный код CORBA непосредственно на базе классов Java, хотя вряд ли это разумно для больших проектов.). Эти IDL-спецификации прекрасно отражают суть выполняемых действий с точки зрения функционирования распределенной системы. Синтаксис OMG IDL очень похож на синтаксис С++ (включая те же самые директивы препроцессора). Таким образом, IDL-спецификации могут с успехом использоваться как часть документации проекта.
Выводы.
Описания OMG IDL более достоверны и существенно более наглядны, чем описания Microsoft IDL, в роли существенной части спецификации проекта.
Виды объектов.
COM.
Объекты COM всегда рассматривались (и продолжают рассматриваться) как серверные объекты, которые просто реализуют тот или иной набор методов. Различные объекты, реализующие один и тот же интерфейс и созданные с помощью обращения к одной и той же фабрике классов, не отличаются друг от друга. Объектная ссылка, которую получает клиент, является указателем на интерфейс и не содержит информации о конкретном объекте. Другими словами, COM оперирует объектами, не имеющими состояния. В общем случае, если клиент, используя одну и ту же объектную ссылку, в цикле вызвал десять раз один и тот же метод, он не может быть уверен, что он обратился к одному, а не к двум, пяти или десяти различным объектам. Естественно, объекты без состояния не имеет смысла хранить дольше, чем существует сервер приложений, в котором они были созданы. Такие объекты применительно к распределенным системам называются временными (transient). COM-объект - это конкретная переменная C++, Visual Basic или Pascal, находящаяся в оперативной памяти и существующая, пока работает создавший ее сервер приложений. Он может быть создана по запросу клиента или заранее (например, с помощью MTS). При работе с COM сопоставить с объектом какое-либо состояние - задача прикладного программиста. Это можно сделать, используя определенный режим создания объектов (выбрав модель управления потоками), хранить состояние объекта на стороне клиента (если это возможно) или использовать так называемые моникеры, которые можно рассматривать как обобщение понятия ключа записи в базе данных. Каждый из этих способов имеет очень существенные ограничения.
CORBA.
Понятие "объекта" в CORBA принципиально отличается от своего COM-аналога. Объект CORBA не является переменной языка программирования и в общем случае время его существования не связано со временем работы серверных или клиентских приложений. СОRВА-объект не занимает никаких ресурсов компьютера - оперативной памяти, сетевых ресурсов и т.п. Эти ресурсы занимает только так называемый "сервант" (servant), который является "инкарнацией" одного или нескольких CORBA-объектов. Именно сервант является переменной языка программирования. Пока не существует сервант, сопоставленный с конкретным объектом CORBA, этот объект не может обслуживать вызовы клиентов, но, тем не менее, он существует. Результатом создания объекта (при этом совершенно не обязательно при этом создается и сопоставляется с этим объектом соответствующий сервант!) является так называемая "объектная ссылка" CORBA. Объектная ссылка сопоставлена с этим, и только с этим объектом, и это сопоставление остается корректным в течение всего срока существования CORBA-объекта (может быть, в течение нескольких лет). Объектная ссылка CORBA правильно интерпретируется ОRВ'ами от любого производителя программного обеспечения. После уничтожения CORBA-объекта все объектные ссылки на него навсегда теряют смысл. С помощью объектной ссылки клиент вызывает методы объекта, при этом инкарнациями этого объекта могут быть различные серванты (не более одного одновременно), которые физически могут находиться даже на различных компьютерах.
Выводы.
Объект COM может рассматриваться как достаточно примитивный частный случай объекта CORBA - как по своим возможностям, так и с точки зрения его цикла жизни. COM и CORBA предлагают совершенно несопоставимые возможности по созданию и управлению объектами, что жизненно важно для создания надежных и масштабируемых приложений.
Способы взаимодействия.
COM.
COM поддерживает как статический, так и динамический способ взимодействия клиента и сервера. Под динамическим способом понимается подход, когда проверка, реализует ли сервер нужный метод, а также все необходимые действия по выполнению удаленного вызова выполняются на этапе работы клиентского приложения, а не этапе его компиляции. При статическом вызове эти действия выполняются именно на этапе компиляции. Разумеется, статический способ предпочтительнее во всех отношениях (когда он возможен вообще). Для его использования сервер COM должен создать и экспортировать библиотеку типов, которая затем импортируется клиентом и используется как часть клиентского процесса.
CORBA.
CORBA также поддерживает статический и динамический способ организации удаленных вызовов.
Выводы.
Обе технологии предлагают примерно одинаковые возможности в этом плане. CORBA имеет определенные преимущества при использовании динамических вызовов за счет более развитых средств получения информации о серверах (репозитарии интерфейсов).
Производительность.
COM.
COM демонстрирует очень высокую производительность. Читатель, интересующийся этим вопросом, найдет большое количество очень интересной информации в прекрасной книге R. Orfali и D. Harkey "Client/server Programming with Java and CORBA", second edition, Wiley, 1998. Разумеется, производительность существенно зависит от того, какой способ - статический или динамический - вы используете.
CORBA.
Для корректного сравнения CORBA и COM с точки зрения производительности необходимо составить целую систему тестов. Кроме того, необходимо учесть влияние использования того или иного языка программирования. На основе информации, приводимой Orfali и Harkey, а также результатов небольшого сравнительного тестирования, проведенного самим автором обзора (использовался Borland C++ Builder 4.0 и VisiBroker 3.3 для C++), можно сказать, что CORBA демонстрирует даже несколько более высокую производительность. Еще раз повторимся: производительность очень сильно зависит от количества и типов аргументов методов (не забывайте, что их нужно упаковать и передать по сети, а затем распаковать), от выбранной модели управления потоками, от используемых языков программирования (клиент и сервер при этом не обязательно должны быть написаны на одном языке), от конкретной реализации CORBA и многих других факторов.
Выводы.
И COM, и CORBA демонстрируют примерно одинаковую (и очень высокую) производительность. Для CORBA говорить о конкретных цифрах можно только для конкретной реализации. В качестве примера приведем следующий факт: Inprise/Visigenic Visibroker прозрачным для разработчика образом работает по-разному в зависимости от того, находятся ли клиентский и серверный объект в одном адресном пространстве, в разных адресных пространствах, но на одном компьютере, или на разных компьютерах. Производительность при этом может отличается на порядок.
Масштабируемость.
COM.
Проблемы обеспечения масштабируемости не были заложены в фундамент технологии, если не считать ориентацию на использование только объектов без состояния. Существенным препятствием для создания масштабируемых приложения является очень жесткая связь между клиентом и сервером (объект, т.е. совокупность ресурсов на сервере, не может быть удален, пока клиент явно не укажет, что этот объект больше не нужен). В реальных проектах необходимо управлять состоянием объектов, и это затрудняет создание масштабируемых приложений, так как это обязанность не COM, а программиста. Сильной стороной COM является гибкая модель управления потоками. Основным инструментом, повышающим уровень масштабируемости COM-систем, является MTS.
CORBA.
В отличие от COM, CORBA с самого начала рассматривалась как технология создания масштабируемых систем. Разделение собственно объектов CORBA и их сервантов, схемы соответствия между ними, характеристики объектных адаптеров, модели управления потоками и соединениями, схемы активации серверов приложений, универсальные решения по сохранению состояния объектов, автоматическое управление контекстом транзакций и безопасности - все это очень способствует решению данной проблемы.
Выводы.
Масштабируемость системы во многом зависит от качества разработки проекта, продуманности принимаемых решений и квалификации менеджеров проекта и разработчиков. При сравнении технологий можно говорить о предпосылках, способствующих (или, наоборот, препятствующих) достижению нужных требований. При прочих равных условиях CORBA имеет громадные преимущества по сравнению с COM.
Устойчивость к сбоям.
COM.
Устойчивость к сбоям COM-систем находится на невысоком уровне, в том числе из-за уже упомянутой излишне жесткой привязки клиентов и серверов. Основным средством обеспечения устойчивости к сбоям (оно же средство управления нагрузкой серверов) является диспетчер, который позволяет перенаправлять вызовы клиента на различные сервера приложений COM. Не слишком содействует отказоустойчивости системы и необходимость выполнения "вручную" большого количества действий по управлению транзакциями.
CORBA.
CORBA имеет несколько более высокий уровень устойчивости к сбоям за счет большей изоляции клиентов и серверов, автоматического сохранения состояния объектов, более мощной и продуманной схемы управления транзакциями (включая автоматический откат транзакций по тайм-ауту), а также автоматической привязки объектной ссылки и конкретного объекта CORBA.
Выводы.
Проблема обеспечения устойчивости к сбоям, так же как и проблемы обеспечения масштабируемости, не рассматривались как первоочередные при разработке концепции COM. С CORBA ситуация обстоит во многом лучше, но проблемы остаются и здесь. Обе технологии не имеют (или почти не имеют) стандарных средств обеспечения устойчивости к сбоям. Такие компоненты, как VisiBroker Smart Agents, не являются стандартным средством CORBA (хотя они и способны решить многие проблемы при работе с реальными проектами.).
Управление транзакциями.
COM.
Монитором транзакции в COM является MTS. Сервер приложений COM должен быть написан в специальном стиле для того, чтобы иметь возможность взаимодействовать с MTS (такой сервер приложений должен быть реализован в виде DLL). MTS позволяет достаточно гибко управлять режимами выполнения транзакций в системе и поддерживает двухфазное завершение транзакций. Одним из существенных недостатков схемы управления транзакциями COM является необходимость явной передачи контекста транзакции в качестве аргумента при вызове удаленных методов. Такая схема не является ни эффективной, ни гарантирующей от ошибок (особенно при вовлечении в транзакцию большого количества объектов).
CORBA.
Управление транзакциями берет на себя так называемый Сервис Управления Транзакциями CORBA (Object Transaction Service, OTS). Он является существенно более гибкой, продуманной и формализованной системой, чем MTS, и содержит все необходимое в рамках CORBA-модели. Сервер приложений CORBA и Сервис транзакций запускаются и работают независимо друг от друга. Важной особенностью CORBA является тесное взаимодействие OTS и ORB, что обеспечивает автоматическое распространение контекста транзакций в многопоточной распределенной среде. Спецификация CORBA предусматривает (необязательную) поддержку вложенных транзакций.
Выводы.
На уровне спецификаций Сервис транзакций CORBA имеет определенные преимущества перед MTS. На практике для реализации этих преимуществ нужно предпринять определенные действия. Особенно это касается двухфазного подтверждения транзакций при работе с гетерогенными базами данных. Например, для реализации такой схемы при работе с Java необходимо иметь специальные JDBC-драйвера, которые, насколько мне известно, в настоящий момент не слишком доступны для широкого круга баз данных. В этом плане COM имеет серьезные преимущества за счет взаимодействия MTS со стандартной технологией доступа к базам данных OLE DB/ADO.
Обеспечение безопасности.
COM.
В настоящий момент система безопасности COM базируется на системе безопасности Windows NT/Windows 2000; кроме того, предусмотрена защита данных при их передаче с использованием Socket Security Layer (SSL). Отдельная проблема - обеспечение безопасности при передаче компонентов ActiveX с использованием протокола HTTP. Здесь используется система электронных подписей, лицензий и т.п. - говоря упрощенно, клиент выполняет код компонента, который пришел с "правильного" сервера.
CORBA.
С CORBA дела обстоят сложнее - главным образом, в силу того, что ставилась задача создать универсальную систему безопасности, которая могла бы использовать все основные существующие в этой области технологии. Работа над Сервисом Безопасности (Security Service) продолжалась в течение 2 лет, и ее спецификация была принята в 1996 г. Она содержит около 250 страниц. Она позволяет обеспечить уровень безопасности B2 (уровень, близкий к высшему уровню защиты, который используется в государственных учреждениях). Предусмотрена идентификация пользователя, списки прав доступа к ресурсам, система аудита и многое другое. Особенно приятно, что разработчик не должен явно взаимодействовать с этим сервисом - это задача для ORB. Основная нагрузка возложена на системных администраторов. Все это прекрасно, но существует одна небольшая проблема - где взять полномасштабную, высококачественную реализацию этого сервиса? Такие реализации существуют (Gradient, Concept-5), но их использование ограниченно за пределами США. Сервис безопасности от Borland/Visigenic в этом году еще не появится (хотя работа над ним идет).
Выводы.
В настоящий момент для реальных проектов для обеих технологий используются сходные решения в области обеспечения безопасности (идентификация на уровне операционной системы и кодирование информации с помощью SSL). Естественно, возможны варианты. Потенциально CORBA предоставляет существенно большие возможности - проблемы здесь организационного, а не концептуального плана.
Взаимодействие с Internet.
COM.
Основой взаимодействия через Internet при работе с COM являются расширения возможностей протокола HTTP, выполненные Microsoft. Броузеры Microsoft (Internet Explorer 3 и выше) позволяют выполнять код ActiveX-компонентов, полученных с Web-серверов. Кроме того, URL доступны при использовании COM - с ними могут работать моникеры.
CORBA.
Спецификации CORBA не оговаривают использование Internet в качестве особого случая. Интеграция CORBA и Internet выполняется естественным образом - за счет использования протокола IIOP, построенного поверх TCP/IP. URL-имена могут быть использованы в качестве имен для Службы Именования CORBA. На практике производители программного обеспечения предоставляют расширения CORBA, упрощающие работу с Internet (VisiBroker URL Naming Service) или решающие те или иные проблемы - например, "обход" ограничений, накладываемых на апплеты Java, используемых в качестве CORBA-клиентов (например, Borland/Visigenic GateKeeper).
Выводы.
CORBA (особенно при использования Java) без каких-либо проблем может быть интегрирована с Internet. Взаимодействие COM и Internet основано на использовании ActiveX и требует использования только броузеров, поддерживающих тег <Object> Microsoft. Косвенным образом проблемы совместной работы COM и Internet могут возникнуть из-за несовместимости виртуальной машины Java Microsoft с другими виртуальными машинами.
Скорость разработки систем.
COM.
Скорость разработки COM-систем может быть очень высокой за счет интенсивного использования компонентной модели ActiveX, а также универсальных подходов, таких, как OLE DB. Не составляет особого труда создание Internet-приложений с броузером Microsoft в качестве клиентского приложения.
CORBA.
Скорость разработки CORBA-систем сильно зависит от используемой технологии. Наверное, максимально эффективным способом создания распределенных систем в настоящий момент является использование Java-технологий, основанных на CORBA - Enterprise JavaBeans и так называемых Application Sеrvеr'ов, например, BEA WebLogic и Inprise Application Server. Использование этих технологий позволяет чрезвычайно быстро создавать высокоэффективные, масштабируемые, транзакционные сервера приложений. Клиентская часть таких систем может быть написана на любом языке программирования, поддерживающим CORBA.
Выводы.
При прочих равных условиях CORBA позволяет создавать распределенные системы быстрее, чем COM, за счет большей функциональности middleware и, соответственно, меньшей нагрузки на прикладного разработчика.
Простота использования.
COM.
COM очень прост для простых небольших приложений и чрезвычайно сложен как инструмент создания комплексных систем. Он содержит большое количество "узких" мест - недостаточно гибкую стандартную схему маршалинга, отсутствие состояния объектов, низкая устойчивость к сбоям. Технология не является объектно-ориентированной в классическом смысле этого слова, что в общем случае не способствует простоте ее использования. Достоинством технологии является комплексность и универсальность подходов в рамках COM-модели.
CORBA.
Сложность CORBA заключается в ее огромных возможностях. Программисту необходимо знать большое количество интерфейсов из различных сервисов CORBA, правильно использовать возможности объектных адаптеров и многое другое. Поскольку CORBA использует различные схемы отображения IDL на разные языки программирования, то программисту в общем случае надо знать их особенности для 2-3 наиболее широко используемых языков - в первую очередь, C++ и Java.
Выводы.
Объективно CORBA сложнее за счет того, что она предназначена для решения существенно более сложных задач, чем COM. При разработке реальных проектов нужно иметь в виду, что распределение "интеллектуальной" нагрузки среди участников разработки для COM и CORBA несколько отличается: в случае COM требуются более квалифицированные (но более узко специализированные) программисты, а для CORBA можно задействовать программистов среднего уровня, но чрезвычайно важно иметь квалифицированных архитектора проекта и руководителей групп программистов.
Взаимодействие с другими технологиями.
COM.
COM является достаточно замкнутой и "самодостаточной" системой. В последнее время Microsoft тесно взаимодействует с OMG на базе создания спецификации моста "COM-CORBA". Вследствие существенных различий в возможностях, не представляет труда имитировать поведение COM-объекта как CORBA-объекта, но не наоборот.
CORBA.
CORBA как технология в настоящий момент (до создания спецификаций, а затем и реализаций своей компонентной модели) является скорее инфраструктурой для создания распределенных систем. Не удивительно, что в этом качестве она активно взаимодействует с другими технологиями - в первую очередь с RMI и Enterprise JavaBeans. CORBA очень тесно - на уровне протокола ESIOP - взаимодействует с широко используемой, но морально устаревшей технологией DCE.
Выводы.
CORBA является существенно более открытой, универсальной и гибкой системой, чем COM. И COM, и CORBA способны тесно и эффективно взаимодействовать со стандартными средствами обеспечения безопасности.
Общие выводы.
Несмотря на внешнюю похожесть, что вызвано общностью решаемых задач, между COM и CORBA, пожалуй, больше различий, чем сходства. В большинстве случаев либо нецелесообразно использовать CORBA (для небольших и простых проектов под Windows просто по причине относительно высоких затрат на приобретение программного обеспечения, лицензий и пр.), либо практически невозможно использовать COM (для сложных, масштабируемых, высоконадежных проектов или просто при работе в гетерогенных средах, а не только в Windows). Windows-приложения, ориентированные на взаимодействие с Microsoft Office, всегда будут использовать COM; проекты с использованием Java и любых Java-технологий (кроме Microsoft J++), как говорится, "сам бог велел" строить на основе CORBA. Во многих случаях выбор технологии диктует выбор той или иной части проекта: если вы планируете работать, например, с ORACLE 8i, то, безусловно, гораздо лучше ориентироваться на CORBA. Область, где эти технологии реально конкурируют, на мой взгляд, очень невелика.
Как нетрудно заметить, автор настоящего обзора является сторонником CORBA, чего и желает всем своим читателям.
За дополнительной информацией обращайтесь в Interface Ltd.
©Drkb::04354.
Взято с http://delphiworld.narod.ru.
| Аналог функции С memcmp. |
Автор: Dennis Passmore.
Я создал следующие две функции, существенно повышающие произвотельность в приложениях, активно работающих с данными. Вам нужно всего-лишь обеспечить контроль типов и границ допустимого диапазона, все остальное они сделают с любым типом данных лучше нас :-) .
Code: Function Keys_are_Equal(var OldRec, NewRec; KeyLn : word): boolean; assembler; Asm. PUSH DS. MOV AL,01. CLD. LES DI,NewRec. LDS SI,OldRec. MOV CX,KeyLn. CLI. REPE CMPSB. STI. JZ @1. XOR AL,AL. @1: POP DS. End; Function First_Key_is_Less(var NewRec, OldRec; Keyln : word): boolean; assembler; Asm. PUSH DS. MOV AL,01. CLD. LES DI,NewRec. LDS SI,OldRec. MOV CX,KeyLn. CLI. REPE CMPSB. STI. JZ @5. JGE @6. @5: XOR AL,AL. @6: POP DS. End;
| Переключение консольного приложения в полный экран. |
Code: { There is no documented way to make a console application fullscreen. The following code works for both NT and Win9x. For win NT I used the undocumented SetConsoleDisplayMode and. GetConsoleDisplayMode functions. } { Function GetConsoleDisplayMode(var lpdwMode: DWORD): BOOL; stdcall; External 'kernel32.dll'; // lpdwMode: address of variable for current value of display mode. } Function NT_GetConsoleDisplayMode(var lpdwMode: DWORD): Boolean; Type. TGetConsoleDisplayMode = function(var lpdwMode: DWORD): BOOL; Stdcall; Var. HKernel: THandle; GetConsoleDisplayMode: TGetConsoleDisplayMode; Begin. Result := False; HKernel := GetModuleHandle('kernel32.dll'); If (hKernel > 0) then. Begin @GetConsoleDisplayMode := GetProcAddress(hKernel, 'GetConsoleDisplayMode'); If Assigned(GetConsoleDisplayMode) then. Begin. Result := GetConsoleDisplayMode(lpdwMode); End; End; End; { Function SetConsoleDisplayMode(hOut: THandle; // standard output handle. DwNewMode: DWORD; // specifies the display mode. Var lpdwOldMode: DWORD // address of variable for previous value of display mode. ): BOOL; stdcall; external 'kernel32.dll'; } Function NT_SetConsoleDisplayMode(hOut: THandle; dwNewMode: DWORD; Var lpdwOldMode: DWORD): Boolean; Type. TSetConsoleDisplayMode = function(hOut: THandle; dwNewMode: DWORD; Var lpdwOldMode: DWORD): BOOL; Stdcall; Var. HKernel: THandle; SetConsoleDisplayMode: TSetConsoleDisplayMode; Begin. Result := False; HKernel := GetModuleHandle('kernel32.dll'); If (hKernel > 0) then. Begin @SetConsoleDisplayMode := GetProcAddress(hKernel, 'SetConsoleDisplayMode'); If Assigned(SetConsoleDisplayMode) then. Begin. Result := SetConsoleDisplayMode(hOut, dwNewMode, lpdwOldMode); End; End; End; Function GetConsoleWindow: THandle; Var. S: AnsiString; C: Char; Begin. Result := 0; Setlength(S, MAX_PATH + 1); If GetConsoleTitle(PChar(S), MAX_PATH) <> 0 then. Begin. C := S[1]; S[1] := '$'; SetConsoleTitle(PChar(S)); Result := FindWindow(nil, PChar(S)); S[1] := C; SetConsoleTitle(PChar(S)); End; End; Function SetConsoleFullScreen(bFullScreen: Boolean): Boolean; Const. MAGIC_CONSOLE_TOGGLE = 57359; Var. DwOldMode: DWORD; DwNewMode: DWORD; HOut: THandle; HConsole: THandle; Begin. If Win32Platform = VER_PLATFORM_WIN32_NT then. Begin. DwNewMode := Ord(bFullScreen); NT_GetConsoleDisplayMode(dwOldMode); HOut := GetStdHandle(STD_OUTPUT_HANDLE); Result := NT_SetConsoleDisplayMode(hOut, dwNewMode, dwOldMode); End. Else. Begin. HConsole := GetConsoleWindow; Result := hConsole <> 0; If Result then. Begin. If bFullScreen then. Begin. SendMessage(GetConsoleWindow, WM_COMMAND, MAGIC_CONSOLE_TOGGLE, 0); End. Else. Begin. // Better solution than keybd_event under Win9X ? Keybd_event(VK_MENU, MapVirtualKey(VK_MENU, 0), 0, 0); Keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, 0), 0, 0); Keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, 0), KEYEVENTF_KEYUP, 0); Keybd_event(VK_MENU, MapVirtualKey(VK_MENU, 0), KEYEVENTF_KEYUP, 0); End; End; End; End; Procedure TForm1.Button1Click(Sender: TObject); Var. S: string; Begin. AllocConsole; Try. SetConsoleFullScreen(True); Write('Hi, you are in full screen mode now. Type something [Return]: '); Readln(s); SetConsoleFullScreen(False); // ShowMessage(Format('You typed: "%s"', [s])); Finally. FreeConsole; End; End;
| Изменяем заголовок окна. |
Автор: Christian Cristofori.
В примере показывается, как изменять заголовок окна (видимый в списке задач при переключении между приложениями) при минимизации окна в иконку.
Сперва необходимо определить сообщение поумолчанию:
Code: Const. DefMsgNorm = 'MyApp version 1.0'; DefMsgIcon = 'MyApp. (Use F12 to turn of)';
| Как консольное приложение может узнать, что Винды завершаются? |
Автор: Nomadic.
Все процессы получают сигналы CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT и CTRL_SHUTDOWN_EVENT. А делается это (грубо говоря :) так:
Code: BOOL Ctrl_Handler( DWORD Ctrl ). { If( (Ctrl == CTRL_SHUTDOWN_EVENT) || (Ctrl == CTRL_LOGOFF_EVENT) ). { // Вау! Юзер обламывает! } Else. { // Тут что-от другое можно творить. А можно и не творить :-). } Return TRUE; }
Что такое MIDAS (multi-tired distributed application service)?
MIDAS - multi-tired distributed application service suite- это технология Borland для создания многоуровневых приложений баз данных. Применение данной архитектуры позволяет быстро разрабатывать простые в сопровождении и установке, надежные, распределенные БД. Трехуровневое приложение баз данных содержит несколько компонентов (слоев):
А) Слой БД. Хранит данные. Выполняет функции хранения информации, обеспечения целостности и непротиворечивости данных. Пример -локальные (dBase, Paradox) и серверные БД (Oracle, Sybase, MS SQL), текстовые файлы и т.д.
Б) Слой бизнес логики (сервер приложений) - это программа, обеспечивающая доступ клиентов к информации. На этом слое вводится понятие сервиса, как некоей услуги, поставляемой клиенту (например, получение данных об остатке денег на счете, как частный случай из реляционной БД). В этом слое реализуются правила и алгоритмы обработки информации, отражающие поведение реального моделируемого объекта (бизнес правила). Например, проверка остатка денег на не отрицательность, перевод денег со счета на счет.
В) Презентационный слой (тонкий клиент). Задача этого слоя, используя сервисы слоя бизнес логики, предоставлять пользователям запрошенную информацию в форме удобной и приятной во всех отношениях. Может быть выполнен в виде традиционного exe файла или в качестве тонкого клиента можно использовать Web броузер.
Применение данной схемы позволяет создать клиентское приложение, которое практически не требует настройки и сопровождения, вся логика работы с БД сосредоточена в среднем слое (сервере приложений). Соответственно при доработке алгоритмов доступа к БД необходимо лишь переустановить сервер приложений. MIDAS предназначен для обеспечения связи между слоем бизнес логики и презентационным слоем. Он позволяет организовать взаимодействие тонкого клиента с сервером приложений. При этом сервер приложений взаимодействует с реляционной БД (чаще всего данные хранятся именно в этой форме) как и обычные приложения работы с БД, разработанные в Delphi. Тонкий клиент для конечного пользователя ничем не отличается от обычного (толстого) клиента БД. Разница в том, что толстый клиент через BDE, ADO, компоненты прямого доступа к серверам БД и другие библиотеки работает с БД, а тонкий клиент взаимодействует с сервером приложений, используя MIDAS. Сервер приложений скрывает от клиента детали доступа и обработки БД. На компьютере с тонким клиентом не нужно устанавливать и настраивать BDE, ADO, клиентскую часть сервера БД. Необходимо лишь иметь небольшие по объему dll, которые легко переносить вместе с exe файлом тонкого клиента. В качестве тонкого клиента может использоваться и Web браузер. Разработка пользовательского интерфейса тоже мало чем отличается от обычного клиента. Особенности - необходимость размещения и настройки одного из компонентов доступа к серверу приложений (DCOMConnection, SocketConnection, WebConnection, CorbaConnection) и вместо Table, Query, StoredProc использовать компонент ТС1iеntDаtаSеt. Сервер приложений, как правило, имеет весьма несложный пользовательский интерфейс (чаще одну главную форму). Да он ему и не нужен. Сердце сервера приложений - это удаленный модуль данных (Remote Data Module). В зависимости от протокола связи TRemoteDataModule, MTSDataModule, TCORBADataModule. Внутри удаленного модуля данных расположены невизуальные компоненты доступа к данным. Каждому компоненту, который должен быть доступен тонкому клиенту необходимо сопоставить компонент - TDataSetProvider. Кроме того, для автоматического запуска серверов приложений по запросу клиента и реализации стратегии безопасности доступа бывает необходимо иметь дополнительную программу SCM (service control manager).
Создание трехуровневого приложения.
Итак, создадим наше первое приложение для просмотра таблицы animals.dbf БД DBDEMOS. Как указывалось выше нам необходимо разработать сервер приложений и тонкого клиента (слой БД - это dbf файл).
Создание сервера приложений.
Создадим новое приложение, используя пункт меню File / New Application. Созданная при этом форма Form1 будет главной формой сервера приложений. Далее создадим удаленный модуль данных, в него будут помещаться невизуальные компоненты доступа к данным и в нем сосредотачивается вся функциональность сервера приложений. Отличие его от обычного модуля данных в том, что удаленный модуль данных обеспечивает возможность тонким клиентам получать данные с сервера приложений. Выберем пункт меню File / New и в появившемся диалоге перейдем к закладке Miltitier.
Выберем значок Remote Data Module и нажмем кнопку ОК (Замечание в случае использования CORBA необходимо выбирать CORBA Data Module). После этого на экране появляется диалог RemoteDataModuleWizard. Заполним поле CoClass Name базовым именем нашего удаленного модуля MyRDM. Обратите внимание, что к указанному имени будет добавлена буква T в наименовании класса и I в наименовании интерфейса для управления удаленным модулем. Остальные поля оставьте как есть (мы пишем простейшего сервера). Нажмем кнопку OK. В проект будет добавлен модуль Unit2, содержащий удаленный модуль данных. С закладки DataAccess добавим компонент TTable и свяжем его с таблицей animals.dbf алиаса DBDEMOS и активируем (свойства DatabaseName установим в DBDEMOS, TableName в animals.d, Active в true. Напомню, что порядок установки свойств важен). Теперь для того, чтобы сделать данную таблицу доступной тонкому клиенту необходим еще один компонент TDataSetProvider. Он расположен на закладке Midas. Чтобы связать DataSetProvider с TTable (как впрочем и с другими компонентами доступа к данным) установим свойство DataSet первого равным Table1. Результат наших усилий можно увидеть на рис.3. Осталось запустить сервер приложений на выполнение. Во-первых, чтобы увидеть наши труды, а во-вторых, чтобы при первом запуске сервер приложений произвел необходимые действия по своей регистрации в реестре.
Удаленный модуль данных.
В принципе сервер приложений готов и работоспособен (без написания кода !!!), однако внесем последний штрих - счетчик подключений клиентов. Для этого на главную форму поместим два компонента TLabel. Свойство Caption одного установим в "Количество подключений", а второго в "0". Вид главной формы представлен на рис.4.
В обработчики события OnCreate удаленного модуля инкрементируем значение счетчика.
With Form1.Label2 do.
Caption := IntToStr(StrToInt(Caption) + 1);
А в OnDestroy того же компонента декрементируем.
With Form1.Label2 do.
Caption := IntToStr(StrToInt(Caption) - 1);
Создание тонкого клиента.
Теперь настало время взяться за написание клиента. Создадим новое приложение, использующее в качестве протокола связи с сервером сокеты. Выбрав File / New Application в меню, на главную форму установим компонент TSocketConnection с закладки Midas. Данный компонент обеспечивает взаимодействие с сервером приложений. Далее нам необходимо запустить программу Borland Socket Server (расположена в Delphi/Bin/scktsrvr.exe). Данная программа обрабатывает запросы клиентов, передает их серверу приложений и запускает его, если раньше он не был запущен. Программы, выполняющие данные функции называют SCM (service control manager). Запуск Borland Socket Server необходим лишь в случае использования протокола сокетов. При запуске Borland Socket Server помещает свой значок в панель задач.
Настроим SocketConnection1. Значение свойства ServerName выберем из выпадающего списка и установим равным Project1.MyRDM, таким образом мы указываем к какому серверу приложений присоединяться. Затем необходимо указать на какой машине находится данный сервер. Для этого надо установить либо свойство Address в значение IP адреса или выбрать из списка значений свойства Host имя Вашего PC. Наконец, установим свойство Connected в true. О-о-пс, если все было выполнено верно, запустится сервер приложений и его счетчик клиентов будет установлен в 1. Для доступа к данным в тонком клиенте вместо TTable, TQuery используется TClientDataset. Разместим на главной форме его и мы, взяв с закладки Midas. Значение свойства RemoteServer установим в SocketConnection1, ProviderName в DataSetProvider1 (вспомним, что разместили его в удаленном модуле данных сервера приложений). Внимание, порядок установки этих свойств важен. Активизируем TClientDataSet, установив свойство Active в true. Далее порядок проектирования не отличается от разработки обычного приложения БД. Т.е. размещаем на форме DataSource, DBGrid, DBNavigator.Набор свойств, которые нужно установить приведен в таблице 1.
Таблица 1.
DataSource1.
DataSet ClientDataSet1.
DBGrid1, DBNavigator.
DataSource DataSource1.
Если все выполнено правильно, то в сетке просмотра появятся данные. Вид главной формы приведен на рис.5. Ну вот, теперь можно запустить клиента. При этом на запущенном сервере приложений счетчик увеличится до2. Можно запустить еще несколько экземпляров клиентов, загнуть пальцы и сделать умное лицо. :))).
Какую из технологий (протоколов) распределенных вычислений лучше использовать с MIDAS ?
Протокол обеспечивает механизм вызова сервера приложений и соединения с ним клиента.
На данный момент Delphi поддерживает четыре протокола организации распределенных вычислений:
DCOM - технология Microsoft для создания и использования удаленных объектов автоматизации.
Достоинства:
А) Встроена в Windows 98,NT,2000. Может быть установлен в Windows 95, как дополнительная опция. Это не требует запуска дополнительных приложений на стороне сервера для управления подключением клиентов.
Б) Реализован автоматический запуск сервера приложений при вызове его клиентом и автоматическое закрытие при отсутствии клиентов.
Недостатки:
А) Существуют проблемы с работой DCOM в сетях без контроллера домена NT. Поэтому использовать эту технологию в полной мере можно лишь при наличии в сети сервера с ОС Windows NT, 2000.
Б) В DCOM нет встроенных средств обеспечения прозрачности положения сервера приложений (location transparency), т.е другими словами всегда нужно указывать на каком компьютере расположен сервер приложений. Данное ограничение можно сгладить применением SimpleObjectBroker.
В) Использовать DCOM можно лишь на платформе Windows.
Sockets - самый простой из протоколов организации распределенных вычислений. В его основе лежит использование сокетов TCP/IP.
Достоинства:
А) Требует минимум установленных компонентов ОС.
Б) Может функционировать в любой сети Windows (c выходом Kylix надеюсь и в Linux), использующей TCP/IP.
Недостатки:
А) Требует постоянно запущенного на сервере менеджера подключений клиентов (ScktSrvr.exe).
Б) Отсутствие механизмов обеспечения безопасности, другими словами все серверы приложений данной машины могут быть использованы с любого клиента, имеющего доступ по TCP/IP.
WebConnection - использует HTTP.
Достоинства:
А) Позволяет организовать вызов объекта с машины находящейся за пределами сегмента сети, защищенного файрволом.
Недостатки:
А) Требует установки на стороне клиента Wininet.dll (поставляется с IE 3 и выше).
Б) Web сервер IIS 4 (и выше ) или Netscape enterprise 3.6 (и выше).
CORBA - использует соответствующую спецификацию OMG. Проще говоря CORBA - независимый от ОС стандарт взаимодействия объектов в распределенной системе.
Достоинства:
А) Независимость от ОС.
Б) Наличие встроенного механизма обеспечения прозрачности положения сервера приложений. Т.е не нужно волноваться за то, где запущен сервер приложений, он будет автоматически найден.
В) Возможность выбора между автоматическим запуском сервера приложений при подключении клиента или запуска "вручную".
Недостатки:
А) Требуется установка дополнительного программного обеспечения (брокера объектных запросов - хотя бы на одной машине в сети).
Б) Более сложная установка и настройка (по сравнению с DCOM) сервера приложений при автоматическом запуске.
В) Использование в Delphi COM для реализации CORBA.
Таким образом можно дать следующие рекомендации по выбору протокола;
Если Вы новичок в MIDAS, то для изучения лучше всего используйте DCOM, установив сервер приложений и клиента на одной машине.
Если Ваша БД буде использоваться только с ОС Windows в сетях с контроллером домена Windows NT,2000 - используйте DCOM.
Если Вы не уверены, в том что в сети будет контроллер домена, то используйте либо CORBA, либо сокеты.
В случае, если необходимо обеспечить запуск серверов приложений на любой машине в сети и конфигурация сети может меняться (или заранее неизвестны имена машин, где будут функционировать сервера приложений) лучше использовать CORBA.
Если Ваши сервера приложений должны быть доступны не только в локальной сети, но и "снаружи" - WebConnection подойдет лучше всего.
Когда Вы не хотите забивать себе голову (а надо бы) DCOM-ми, CORBA-ми, stab-ми, skeleton-ми, правами доступа итд SocketConnection поможет Вам быстро слабать многослойную БД.
©Drkb::04331.
Взято с http://delphiworld.narod.ru.
Что такое реестр?
Реестр - это системная база данных. Получить доступ к ней можно написав в командной строке ("ПУСК > "Выполнить") слово "RegEdit" - при этом запуститься программа для редактирования реестра. Окно этой программы поделено на две части. В левой (более узкой панели) показана древовидная структура ключей. Ключ - это раздел, отвечающий за какие-либо установки. Сами установки называются параметрами, находящимися в правой панели. Каждый параметр имеет своё имя, значение и тип. Параметры бывают строкового типа, двоичного и типа DWORD. Их очень много, но их назначение зависит от того, в каком ключе находится той или иной параметр. Ключи делятся между шестью основными разделами:
HKEY_CLASSES_ROOT - Содержит информацию об OLE, операциях перетаскивания (drag-and-drop - с англ. перетащить-и-отпустить) и ярлыках. В данном разделе можно так же указать программы, запускаемые при активизации файлов определённого типа. Данный раздел является псевдонимом для ветви HKEY_LOCAL_MACHINE\Software\Classes.
HKEY_CURRENT_USER - Содержит индивидуальные установки для каждого пользователя, зарегистрированного в системе. Данный раздел является псевдонимом для ветви HKEY_USERS.
HKEY_LOCAL_MACHINE - Содержит аппаратные и программные установки, необходимые для функционирования оборудования и программ. Данный раздел так же хранит конфигурацию Windows.
HKEY_USERS - Содержит установки пользователей и соответствующие конфигурационные данные, такие как цвет окна, расположение элементов на рабочем столе, обои, заставки.
HKEY_CURRENT_CONFIG - Содержит информацию о текущем аппаратном профиле. Если вы не используете аппаратные профили, данный раздел содержит установки Windows по умолчанию.
HKEY_DYN_DATA - В отличие от других разделов, которые хранят статистические данные (неизменяющиеся во время сеанса), данный раздел содержит указатели на динамические данные (постоянно изменяющиеся во время работы компьютера). Windows использует данный раздел для отслеживания профилей оборудования plug-and-play, статистики по производительности и драйверов виртуальных устройств VxD.
Все данные системного реестра заключаются в двух файлах, находящихся в директории Windows - это System.dat и User.dat.
©Drkb::02266.
Взято с http://delphiworld.narod.ru.
Что такое сокет?
Существует мировой стандарт структуры протоколов связи - семиуровневая OSI (Open Systems Interface - интерфейс открытых систем). На каждом из уровней этой структуры решается свой объем задач своими методами. Сокеты находятся на так назывемом транспортном уровне - ниже находится сетевой протокол IP, выше - специализированные протоколы сеансового уровня, ориентированные на решение конкретных задач - это всем известные FTP, SMTP, etc.
Если смотреть по сути, сокет - это модель одного конца сетевого соединения, со всеми присущими ему свойствами, и, естественно - возможностью получать и передавать данные. По содержанию - это прикладной программный интерфейс, входящий в состав многих ОС. В семействе Windows - начиная с версии 3.11, и носит название WinSock. Прототипы функций WinSock API находятся в файле winsock.pas. В Delphi есть полноценная инкапсуляция клиентского и серверного сокетов, представленная компонентами TClientSocket и TServerSocket, находящимися на закладке Internet.
Сокеты не обязательно базируются на протоколе TCP/IP, они могут также базироваться на IPX/SPX, etc.
Также Вам следует ознакомиться со списком зарезервированных номеров портов.
Механизм взаимодействия сокетов таков. С одной из двух сторон запускается серверный сокет, который сразу после запуска находится в режиме прослушивания (listening), точнее - ожидания запросов от клиентов. После получения запроса от клиента устанавливается связь, и создается новый экземпляр серверного сокета.
Так как работа с сокетами, это, по сути - операции ввода/вывода, которые бывают синхронными или асинхронными, то и тип работы сокета обладает бывает синхронным или асинхронным. Компоненты TClientSock и TServerSock поддерживают оба режима работы.
Дополнение от Анатолия Подгорецкого:
Когда говорят СОКЕТ то часто не представляют, что это такое. Можно говорить об моделях, об реализациях и так далее. о есть одно простое толкование, применимое для протокола IP. Как известно для взаимодействия между машинами по протоколу IP используются адреса и порты.
Первое на текущий момент представляют из себя 32-x битный адрес, наиболее часто его представляют в символьной форме mmm.nnn.ppp.qqq (адрес разбитый на четыре октета по одному байту в октете и разделеный точками) .
Второе - это номер порта в диапазоне от нуля до 65535.
Так вот эта пара и есть сокет (гнездо в в котором расположены адрес и порт).
В процессе обмена как правило используются два сокета - сокет отправителя и сокет получателя.
Апример при обращении к моему серверу на HTTP порт сокет будет выглядеть так: 194.106.118.30:80, а ответ будет поступать на mmm.nnn.ppp.qqq:xxx.
©Drkb::03544.
Взято с http://delphiworld.narod.ru.
| Что такое сообщения Windows? |
Потребности программиста на Delphi практически полностью удовлетворяются возможностями работы с событиями, предоставляемыми VCL. Но при создании серьёзных нестандартных приложений и особенно при разработке компонентов Delphi вам, безусловно потребуется непосредственно обрабатывать сообщения Windows, после чего генерировать события, соответствующие этим сообщениям.
Что же такое сообщение? Сообщение - это извещение о некотором имевшем место событии, посылаемое системой Windows в адрес приложения. Любые действия пользователя - щелчок мышью, изменение размеров окна приложения, нажатие клавиши на клавиатуре - вынуждают Windows отправить приложению сообщение, извещающее о том, что же произошло в системе. Сообщение представляет собой определённую запись, объявленную в модуле Windows так:
Code: Type. TMsg = packed record. Hwnd: HWND; // Дескриптор окна-получателя. Message: UINT; // Идентификатор сообщения. WParam: WPARAM; // 32 Бита дополнительной информации. LParam: LPARAM; // Ещё 32 бита дополнительной информации. Time: DWORD; // Время создания сообщения. Pt: TPoint; // Положение указателя мыши в момент создания сообщения. End;
| Как работать с реестром средствами API? |
Создать подраздел в реестре:
RegCreateKey (Key:HKey; SubKey: PChar; var Result: HKey): Longint;
Key - указывает на "корневой" раздел реестра, в Delphi1 доступен только один - HKEY_CLASSES_ROOT, а в Delphi3 - все.
SubKey - имя раздела - строится по принципу пути к файлу в DOS (пример subkey1\subkey2\ ...). Если такой раздел уже существует, то он открывается.
В любом случае при успешном вызове Result содержит Handle на раздел.
Об успешности вызова судят по возвращаемому значению, если ERROR_SUCCESS, то успешно, если иное - ошибка.
Открыть подраздел:
RegOpenKey(Key: HKey; SubKey: PChar; var Result: HKey): Longint;
Раздел Key.
Подраздел SubKey.
Возвращает Handle на подраздел в переменной Result. Если раздела с таким именем нет, то он не создается.
Возврат - код ошибки или ERROR_SUCCESS, если успешно.
Закрывает раздел:
RegCloseKey(Key: HKey): Longint;
Закрывает раздел, на который ссылается Key.
Возврат - код ошибки или ERROR_SUCCESS, если успешно.
Удалить подраздел:
RegDeleteKey(Key: HKey; SubKey: PChar): Longint;
Удалить подраздел Key\SubKey.
Возврат - код ошибки или ERROR_SUCCESS, если нет ошибок.
Получить имена всех подразделов раздела Key:
RegEnumKey(Key:HKey; index: Longint; Buffer: PChar; cb: Longint): Longint;
Key - Handle на открытый или созданный раздел.
Buffer - указатель на буфер.
Cb - размер буфера.
Index - индекс, должен быть равен 0 при первом вызове RegEnumKey. Типичное использование - в цикле While, где index увеличивается до тех пор, пока очередной вызов RegEnumKey не завершится ошибкой.
Возвращает текстовую строку, связанную с ключом Key\SubKey:
RegQueryValue(Key: HKey; SubKey: PChar; Value: PChar; var cb: Longint): Longint;
Ключ\подключ Key\SubKey.
Value - буфер для строки.
Cb - размер, на входе - размер буфера, на выходе - длина возвращаемой строки.
Возврат - код ошибки.
Задать новое значение ключу Key\SubKey:
RegSetValue(Key: HKey; SubKey: PChar; ValType: Longint; Value: PChar; cb: Longint): Longint;
Ключ\подключ Key\SubKey.
ValType - тип задаваемой переменной,
Value - буфер для переменной.
Cb - размер буфера. В Windows 3.1 допустимо только Value=REG_SZ.
Возврат - код ошибки или ERROR_SUCCESS, если нет ошибок.
Удаляет значение lpValueName находящееся в ключе hKey:
RegDeleteValue(HKEY hKey, LPCTSTR lpValueName);
HKey - ключ. hKey должен был быть открыт с доступом KEY_SET_VALUE процедурой RegOpenKey.
LpValueName - значение, находящееся в ключе hKey.
Возвращает ERROR_SUCCESS если успешно.
Выдает список значений у ключа hKey:
LONG RegEnumValue( HKEY hKey, DWORD dwIndex, LPTSTR lpValueName, LPDWORD lpcbValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
HKey - ключ.
DwIndex - этот параметр должен быть 0 при первом вызове, а далее по анологии с RegEnumKey (т.е. можно использовать в цикле),
LpValueName - буфер для названия значения.
LpcbValueName - размер lpValueName.
LpReserved должно быть всегда 0.
LpType - буфер для названия типа (int).
LpData - буфер для данных.
LpcbData-размер для lpData.
Примечание:
При каждой новом вызове функции после предыдущего нужно заново переназначить lpcbValueName.
LpcbValueName = sizeof(lpValueName).
Примеры:
Code: { Создаем список всех подразделов указанного раздела }. Procedure TForm1.Button1Click(Sender: TObject); Var. MyKey: HKey; { Handle для работы с разделом }. Buffer: array[0 .. 1000] of char; { Буфер }. Err, { Код ошибки }. Index: longint; { Индекс подраздела }. Begin. Err := RegOpenKey(HKEY_CLASSES_ROOT, 'DelphiUnit', MyKey); { Открыли раздел }. If Err <> ERROR_SUCCESS then. Begin. MessageDlg('Нет такого раздела !!', mtError, [mbOk], 0); Exit; End; Index := 0; {Определили имя первого подраздела }. Err := RegEnumKey(MyKey, index, Buffer, Sizeof(Buffer)); While err = ERROR_SUCCESS do { Цикл, пока есть подразделы }. Begin. Memo1.lines.add(StrPas(Buffer)); { Добавим имя подраздела в список }. Inc(index); { Увеличим номер подраздела }. Err := RegEnumKey(MyKey, index, Buffer, Sizeof(Buffer)); { Запрос }. End; RegCloseKey(MyKey); { Закрыли подраздел }. End;
| Как активизировать предыдущий экземпляр вашей программы? |
Если внутренняя переменная hPrevInst не равна нулю, то она содержит дескриптор предыдущего запущенного экземпляра вашей программы. Вы просто находите открытое окно по его дескриптору и, при необходимости, выводите на передний план. Весь код расположен в файле .DPR file, НЕ в модуле. Строки, которые вам необходимо добавить к вашему .DPR-файлу, в приведенном ниже примере помечены {*}.
Code: Program Once; Uses. {*} WinTypes, WinProcs, SysUtils, Forms, Onceu in 'ONCEU.PAS' {Form1}; {$R *.RES}. {*}TYPE. {*} PHWND = ^HWnd; {*} FUNCTION EnumWndProc(H : hWnd; P : PHWnd) : Integer; Export; {*} VAR ClassName : ARRAY[0..30] OF Char; {*} BEGIN. {*} {Если это окно принадлежит предшествующему экземпляру...}. {*} IF GetWindowWord(H, GWW_HINSTANCE) = hPrevInst THEN. {*} BEGIN. {*} {... проверяем КАКОЕ это окно.}. {*} GetClassName(H, ClassName, 30); {*} {Если это главное окно приложения...}. {*} IF StrIComp(ClassName, 'TApplication') = 0 THEN. {*} BEGIN. {*} {... ищем}. {*}{*} P^ := H; {*} EnumWndProc := 0; {*} END; {*} END; {*} END; {*} PROCEDURE CheckPrevInst; {*} VAR PrevWnd : hWnd; {*} BEGIN. {*} IF hPrevInst <> 0 THEN. {*} {Предыдущий экземпляр запущен}. {*} BEGIN. {*} PrevWnd := 0; {*} EnumWindows(@EnumWndProc, LongInt(@PrevWnd)); {*} {Ищем дескриптор окна предыдущего}. {*} {экземпляра и активизируем его}. {*} IF PrevWnd <> 0 THEN. {*} IF IsIconic(PrevWnd) THEN. {*} ShowWindow(PrevWnd, SW_SHOWNORMAL). {*} ELSE BringWindowToTop(PrevWnd); {*} Halt; {*} END; {*} END; Begin. {*} CheckPrevInst; Application.Title := 'Once'; Application.CreateForm(TForm1, Form1); Application.Run; End.
| Как добавить событие OnMouseLeave? |
Все потомки TComponent могут посылать сообщения CM_MOUSEENTER и CM_MOUSELEAVE во время вхождения и покидания курсора мыши области компонента. Если вам необходимо, чтобы ваши компоненты обладали реакцией на эти события, необходио написать для них соответствующие обработчики.
Code: Procedure CMMouseEnter(var msg:TMessage); message CM_MOUSEENTER; Procedure CMMouseLeave(var msg: TMessage); message CM_MOUSELEAVE; .. .. .. Procedure MyComponent.CMMouseEnter(var msg:TMessage); Begin. Inherited; {действия на вход мыши в область компонента}. End; Procedure MyComponent.CMMouseLeave(var msg: TMessage); Begin. Inherited; {действия на покидание мыши области компонента}. End;
| Как программно добавить принтер? |
Чтобы программно добавить принтер, необходимо воспользоваться API функцией AddPrinter, которая имеет три параметра:
· Имя принтера
· Уровень печати
· Описание принтера
Следующий пример является надстройкой для этой функции. Для этого необходимо знать Имя принтера, которое будет отображаться в Проводнике, имя порта, к которому подключён принтер (т.е. LPT1:), имя драйвера (прийдётся посмотреть вручную) и имя процессора печати (который обычно "winprint").
Code: Unit unit_AddPrinter; Interface. Function AddAPrinter(PrinterName, PortName, DriverName, PrintProcessor: string): boolean; Implementation. Uses. SysUtils, WinSpool, Windows; Function AddAPrinter(PrinterName, PortName, DriverName, PrintProcessor: string): boolean; Var. PName: PChar; Level: DWORD; PPrinter: PPrinterInfo2; Begin. PName := nil; Level := 2; New(pPrinter); PPrinter^.pServerName := nil; PPrinter^.pShareName := nil; PPrinter^.pComment := nil; PPrinter^.pLocation := nil; PPrinter^.pDevMode := nil; PPrinter^.pSepFile := nil; PPrinter^.pDatatype := nil; PPrinter^.pParameters := nil; PPrinter^.pSecurityDescriptor := nil; PPrinter^.Attributes := 0; PPrinter^.Priority := 0; PPrinter^.DefaultPriority := 0; PPrinter^.StartTime := 0; PPrinter^.UntilTime := 0; PPrinter^.Status := 0; PPrinter^.cJobs := 0; PPrinter^.AveragePPM :=0; PPrinter^.pPrinterName := PCHAR(PrinterName); PPrinter^.pPortName := PCHAR(PortName); PPrinter^.pDriverName := PCHAR(DriverName); PPrinter^.pPrintProcessor := PCHAR(PrintProcessor); If AddPrinter(pName, Level, pPrinter) <> 0 then. Result := true. Else. Begin. // ShowMessage(inttostr(GetlastError)); Result := false; End; End; End.
| Выводим цветной текст на форме под любым углом. |
Автор: Lutfi Baran.
Пример демонстрирует вывод теста случайным образом на форме под определённым углом. Добавляем в форму компонент TButton и в событие OnClick следующий код:
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. Logfont: TLogFont; Font: Thandle; Count: integer; Begin. LogFont.lfheight := 20; Logfont.lfwidth := 20; Logfont.lfweight := 750; LogFont.lfEscapement := -200; Logfont.lfcharset := 1; Logfont.lfoutprecision := out_tt_precis; Logfont.lfquality := draft_quality; Logfont.lfpitchandfamily := FF_Modern; Font := createfontindirect(logfont); Selectobject(Form1.canvas.handle, font); SetTextColor(Form1.canvas.handle, rgb(0, 0, 200)); SetBKmode(Form1.canvas.handle, transparent); For count := 1 to 100 do. Begin. Canvas.textout(Random(form1.width), Random(form1.height), 'Hello'); SetTextColor(form1.canvas.handle, rgb(Random(255), Random(255), Random. (255))); End; Deleteobject(font); End;
| Как узнать перечень таблиц базы и количество записей в них? |
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. SL: TStrings; Index: Integer; Begin. SL := TStringList.Create; Try. ADOConnection1.GetTableNames(SL, False); For index := 0 to (SL.Count - 1) do begin. Table1.Insert; Table1.FieldByName('Name').AsString := SL[index]; ADOTable1.TableName := SL[index]; ADOTable1.Open; Table1.FieldByName('Records').AsInteger := ADOTable1.RecordCount; Table1.Post; End; Finally. SL.Free; ADOTable1.Close; End; End;
| Анимация без DirectX. |
Автор: http://sunsb.dax.ru.
При попытке изобразить некую анимацию использую только средства TCanvas, на экране получается черте-чего. Все мельтешит, дергается, одним словом - не годится.
Для получения " гладкой" ( не мельтешащей ) анимация в программах не использующих DirectX, я обычно использую следующую технику.
Узким местом в процессе является момент изменения картинки на экране, поэтому рисование нужно проводить на невидимом для пользователя канвасе, и только подготовив там обновляемые участки выводить их на видимый экран.
Для того, чтобы стереть кртинку в том месте где ее уже нет, нужно помнить позицию в которой она была выведена в прошлый раз. Обзовем эту позицию Old: TRect, текущую позицию запомним в New: TRect.
TRect я использую, на сучай если размер отображаемой картинки может изменяться.
Стандартным подходом является написание двух процедур - Hide и Show, одна из которых прячет картинку в старой позиции, выводя участок фона поверх нее, а вторая выводит в новой позиции.
Такой вариант не проходит и приводит к мерцанию изображения.
Я предлагаю оставить процедуру Hide в покое, и пользоваться ей только если картинку нужно совсем убрать с экрана.
Процедура Show будет выполнять обе нужные функции. Для обновления экрана нам нужно погасить картинку в старой позиции и показать в новой.
Тут возможны два варианта.
Первый - старый и новый прямоугольники пересекаются. В этом случае мы создаем временный TBItmap - tmp с размером их объединения, заполняем его требуемым участком фона, и рисуем на нем картинку. После такой подготовки выводим tmp в нужной позиции экрана.
Второй - старый и новый прямоугольники не пересекаются. В этом случае мы просто копируем прямоугольник old с невидимой копии фона на экран ( процедура Hide ), и рисуем нужную картинку в прямоугольнике new.
При таком подходе мы избегаем двойной перерисовки экрана, что исключает мерцание.
Ниже программа которая все это делает.
Code: Var wsrf: TPaintBox; // видимый экран. Var ssrf: TBitmap; // скрытый неизменяемый фон. Var bmp : TBitmap; // картинка для анимации. Var tmp : TBitmap; // временное хранилище. Function hasIntersect( const A,B : TRect): boolean; Var R: trect; // пересекаются ли прямоугольники. Begin. Result := false; R.Left := max( A.Left, B.Left ); R.Right := min( A.Right, B.Right ); If R.Left > = R.Right then exit; R.Top := max( A.Top, B.Top ); R.Bottom:= min( A.Bottom, B.Bottom ); If R.Top > = R.Bottom then exit; Result := true; End; Function Union( A, B: TRect ):TRect; Begin // результат - объединение. If EmptyRect( A ) then result := B. Else if EmptyRect( B ) then result := A. Else begin. Result.Left := min( A.Left, B.Left ); Result.Top := min( A.Top, B.Top ); Result.Right := max( A.Right, B.Right ); Result.Bottom:= max( A.Bottom, B.Bottom ); End; End; Procedure TOneTooth.Hide; Begin. Tmp.Width := bmp.Width; Tmp.Height:= bmp.Height; Tmp.Canvas.CopyRect( bmpRect(tmp), ssrf.Canvas, old ); Wsrf.Canvas.Draw( old.Left, old.Top, tmp ); End; Procedure TOneTooth.Show; Var R, R1 : TRect; Begin. Now.Right := now.Left + bmp.Width ; //корректировка now на случай. Now.Bottom := now.Top + bmp.Height; //изменения размеров bmp. If hasIntersect( old, now ) then begin. R := Union( old, now ); Tmp.Width := R.Right-R.Left; Tmp.Height:= R.Bottom-R.Top; Tmp.Canvas.CopyRect( bmpRect(tmp), ssrf.Canvas, R ); // фон. Tmp.Canvas.Draw( now.left-r.left, now.Top-r.top, bmp ). // фон + картинка. End else begin. Hide; Tmp.Canvas.CopyRect( bmpRect(bmp), ssrf.Canvas, now ); // фон. Tmp.Canvas.Draw( 0, 0, bmp ); // фон + картинка. R:=now; End; Wsrf.Canvas.Draw( R.Left, R.Top, tmp ); Old := now; End;
| ANSI ---> ASCII. |
Code: {преобразование Ansi to Ascii}. Function AnToAs(s: String) : String; Var i,kod : Integer; Begin. Result:=s; For i:=1 to length(s) do. Begin. Kod:=Ord(s[i]); If kod=13 then Result[i]:=' '; If ( kod>=192) and ( kod=239) then. Result[i]:=Chr(kod-64); If ( kod>=240) and ( kod=255) then. Result[i]:=Chr(kod-16); If kod=168 then Result[i]:=Chr(240); If kod=184 then Result[i]:=Chr(241); End; End;
Как выключить, перезагрузить или завершить Windows?
Code: Function MyExitWindows(RebootParam: Longword): Boolean; Var. TTokenHd: THandle; TTokenPvg: TTokenPrivileges; CbtpPrevious: DWORD; RTTokenPvg: TTokenPrivileges; PcbtpPreviousRequired: DWORD; TpResult: Boolean; Const. SE_SHUTDOWN_NAME = 'SeShutdownPrivilege'; Begin. If Win32Platform = VER_PLATFORM_WIN32_NT then. Begin. TpResult := OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, TTokenHd); If tpResult then. Begin. TpResult := LookupPrivilegeValue(nil, SE_SHUTDOWN_NAME, TTokenPvg.Privileges[0].Luid); TTokenPvg.PrivilegeCount := 1; TTokenPvg.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; CbtpPrevious := SizeOf(rTTokenPvg); PcbtpPreviousRequired := 0; If tpResult then. Windows.AdjustTokenPrivileges(TTokenHd, False, TTokenPvg, CbtpPrevious, RTTokenPvg, PcbtpPreviousRequired); End; End; Result := ExitWindowsEx(RebootParam, 0); End; // Example to shutdown Windows: Procedure TForm1.Button1Click(Sender: TObject); Begin. MyExitWindows(EWX_POWEROFF or EWX_FORCE); End; // Example to reboot Windows: Procedure TForm1.Button1Click(Sender: TObject); Begin. MyExitWindows(EWX_REBOOT or EWX_FORCE); End;
{2. Console Shutdown Demo}.
Code: Program Shutdown; {$APPTYPE CONSOLE}. Uses. SysUtils, Windows; // Shutdown Program. // (c) 2000 NeuralAbyss Software. // www.neuralabyss.com. Var. Logoff: Boolean = False; Reboot: Boolean = False; Warn: Boolean = False; DownQuick: Boolean = False; CancelShutdown: Boolean = False; PowerOff: Boolean = False; TimeDelay: Integer = 0; Function HasParam(Opt: Char): Boolean; Var. X: Integer; Begin. Result := False; For x := 1 to ParamCount do. If (ParamStr(x) = '-' + opt) or (ParamStr(x) = '/' + opt) then Result := True; End; Function GetErrorstring: string; Var. Lz: Cardinal; Err: array[0..512] of Char; Begin. Lz := GetLastError; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, lz, 0, @err, 512, nil); Result := string(err); End; Procedure DoShutdown; Var. Rl, flgs: Cardinal; HToken: Cardinal; Tkp: TOKEN_PRIVILEGES; Begin. Flgs := 0; If downQuick then flgs := flgs or EWX_FORCE; If not reboot then flgs := flgs or EWX_SHUTDOWN; If reboot then flgs := flgs or EWX_REBOOT; If poweroff and (not reboot) then flgs := flgs or EWX_POWEROFF; If logoff then flgs := (flgs and (not (EWX_REBOOT or EWX_SHUTDOWN or EWX_POWEROFF))) or. EWX_LOGOFF; If Win32Platform = VER_PLATFORM_WIN32_NT then. Begin. If not OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, HToken) then. Writeln('Cannot open process token. [' + GetErrorstring + ']'). Else. Begin. If LookupPrivilegeValue(nil, 'SeShutdownPrivilege', tkp.Privileges[0].Luid) then. Begin. Tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; Tkp.PrivilegeCount := 1; AdjustTokenPrivileges(hToken, False, tkp, 0, nil, rl); If GetLastError <> ERROR_SUCCESS then. Writeln('Error adjusting process privileges.'); End. Else. Writeln('Cannot find privilege value. [' + GetErrorstring + ']'); End; { if CancelShutdown then. If AbortSystemShutdown(nil) = False then. Writeln(\'Cannot abort. [\' + GetErrorstring + \']\'). Else. Writeln(\'Cancelled.\'). Else. Begin. If InitiateSystemShutdown(nil, nil, timeDelay, downQuick, Reboot) = False then. Writeln(\'Cannot go down. [\' + GetErrorstring + \']\'). Else. Writeln(\'Shutting down!\'); End; } End; // else begin. ExitWindowsEx(flgs, 0); // end; End; Begin. Writeln('Shutdown v0.3 for Win32 (similar to the Linux version)'); Writeln('(c) 2000 NeuralAbyss Software. All Rights Reserved.'); If HasParam('?') or (ParamCount = 0) then. Begin. Writeln('Usage: shutdown [-akrhfnc] [-t secs]'); Writeln(' -k: do not really shutdown, only warn.'); Writeln(' -r: reboot after shutdown.'); Writeln(' -h: halt after shutdown.'); Writeln(' -p: power off after shutdown'); Writeln(' -l: log off only'); Writeln(' -n: kill apps that do not want to die.'); Writeln(' -c: cancel a running shutdown.'); End. Else. Begin. If HasParam('k') then warn := True; If HasParam('r') then reboot := True; If HasParam('h') and reboot then. Begin. Writeln('Error: Cannot specify -r and -h parameters together!'); Exit; End; If HasParam('h') then reboot := False; If HasParam('n') then downQuick := True; If HasParam('c') then cancelShutdown := True; If HasParam('p') then powerOff := True; If HasParam('l') then logoff := True; DoShutdown; End; End.
©Drkb::01670.
Взято с http://delphiworld.narod.ru.
Для выполнения перезагрузки/выключения предназначены функции ExitWindows/ExitWindowsEx.
ExitWindows:
Описание:
Function ExitWindows(Reserved: DWord; ReturnCode: Word): Bool;
Инициирует стандартную процедуру завершения работы с Windows. Все прикладные задачи должны подтвердить завершение работы Windows. Вызывает функцию 4CH прерывания 21H DOS.
Параметры:
Reserved: Установлен в нуль.
ReturnCode: Значение, передаваемое в DOS (в регистре AL).
Возвращаемое значение:
Нуль, если одна или несколько задач отказываются завершить работу.
Примеры использования:
ExitWindows(EWX_LOGOFF,0); - завершение сеанса.
ExitWindows(EWX_SHUTDOWN,0); - выключение компьютера.
ExitWindows(EWX_REBOOT,0); - перезагрузка.
Флаги EWX_FORCE, EWX_POWEROFF и EWX_FORCEIFHUNG могут комбинироваться к нужному действию.
ExitWindowsEx:
Функция ExitWindowsEx() представляет собой расширенный вариант ExitWindows().
Описание:
BOOL ExitWindowsEx( UINT uFlags, DWORD dwReserved, );
Функция ExitWindowsEx перезагружает(restart) или выключает систему (shutdown), а также может завершить сессию для текущего пользователя(log off).
Параметры:
UFlags -- флаг завершения работы, может принимать следущие значения:
EWX_LOGOFF завершает сессию текущего пользователя.
EWX_POWEROFF выключает питание компьютера(компьютер должен поддерживать данную функцию).
EWX_REBOOT перезагружает систему.
EWX_SHUTDOWN завершает работу комьпьютера до того места, где он может быть безопасно выключен: сброшенны все файловые буферы на диск, завершает работу всех процессов.
DwReserved --Зарезирвированно для последующих нужд, параметр игнорируется.
Возвращаемое значение:
Не ноль если всё прошло успешно.
Пример использования:
ExitWindowsEx(EWX_SHUTDOWN,0);
Остальные примеры смотри в описании первой функции.
Вышеописанные примеры действительны только для w9x/Me.
Дело в том, что, чтобы выполнить функциию в NT ОС, нужно получить права на выполнение этой функции. Сделать это можно через AdjustTokenPriviligies.
С помощью нижеприведённой функции можно получить любую привелегию, в т.ч. и привеленгию SeShutdownPrivilege, которая нужна для разрешения функции ExitWindows(Ex).
Code: Function SetPrivilege(aPrivilegeName: String; aEnabled: Boolean ): Boolean; Var TPPrev, TP: TTokenPrivileges; Token: THandle; DwRetLen: DWord; Begin. Result:=False; OpenProcessToken(GetCurrentProcess,TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, @Token ); TP.PrivilegeCount:=1; IF LookupPrivilegeValue(nil,PChar(aPrivilegeName),TP.Privileges[0].LUID )) then. Begin. IF aEnabled then TP.Privileges[0].Attributes:=SE_PRIVILEGE_ENABLED. Else TP.Privileges[0].Attributes:=0; DwRetLen:= 0; Result:=AdjustTokenPrivileges(Token,False,TP,SizeOf(TPPrev),TPPrev,dwRetLen); End; CloseHandle(Token); End;
Пример использования для среды NT:
SetPrivilege('SeShutdownPrivilege',True);
ExitWindowsEx(EWX_SHUTDOWN,0);
©Drkb::01671.
Автор: Song.
Взято из http://forum.sources.ru.
| Как скопировать содержимое строки в буфер обмена? |
Code: Procedure CopyStringToClipboard(s: string); Var. Hg: THandle; P: PChar; Begin. Hg:=GlobalAlloc(GMEM_DDESHARE or GMEM_MOVEABLE, Length(S)+1); P:=GlobalLock(hg); StrPCopy(P, s); GlobalUnlock(hg); OpenClipboard(Application.Handle); SetClipboardData(CF_TEXT, hg); CloseClipboard; GlobalFree(hg); End;
| Использование функций перечисления WinAPI. |
Для получения информации о множественных объектах Windows (окнах, принтерах, шрифтах, настройках экрана и так далее - всего несколько десятков вариантов) используются функции, начинающиеся с Enum. Эти функции работают по принципу, аналогичному итератору TCollection.FirstThat, то есть они вызывают функцию, переданную им в качестве параметра для каждого перечисляемого объекта, передавая ей в параметрах данные объекта и, в последнем параметре, указатель на пользовательские данные, переданный функции EnumXXX. Перечисление продолжается до тех пор, пока не будут перечислены все объекты. Немедленно прекратить перечисление можно, возвратив False. Ниже приведен пример заполнения списка ListBox1 данными по всем окнам Windows в виде " класс - заголовок" по нажатию кнопки Button1.
Code: Function AddWinInfo(WinHandle: HWnd; List: TStringList): Boolean; Stdcall; Var. WinCaption,WinClass: array[0..255] of Char; Begin. Result:=True; GetClassName(WinHandle,WinClass,SizeOf(WinClass)); GetWindowText(WinHandle,WinCaption,SizeOf(WinCaption)); List.Add(WinClass+' - '+WinCaption); End; Procedure TfrmMain.Button1Click(Sender: TObject); Begin. With ListBox1,Items do. Begin. Clear; EnumWindows(@AddWinInfo,LParam(Items)); End; End;
ADO+Delphi5: постоянные ошибки BOF... или EOF...
Обновлённый MDAC конфликтует с старыми компонентами VCL.
Путь разрешения проблемы:
Идём на сайт Борланда, скачиваем SP1 для Delphi5 + специальный ADO Patch.
Сам ADO Patch здесь:
Ftp://ftpc.inprise.com/pub/delphi/devsupport/updates/adoexpress/d5adoupdate2.exe.
Service Pack здесь:
Enterprise: ftp://ftpc.borland.com/pub/delphi/devsupport/updates/delphi5/D5EntUpdate.exe.
Professional: ftp://ftpc.borland.com/pub/delphi/devsupport/updates/delphi5/D5ProUpdate.exe.
Standard: ftp://ftpc.borland.com/pub/delphi/devsupport/updates/delphi5/D5StdUpdate.exe.
©Drkb::02661.
Автор: Vit (www.delphist.com, www.drkb.ru, www.unihighlighter.com, www.nevzorov.org).
| Добавляем дополнительную кнопку в заголовок формы. |
Автор: Vimil Saju.
Чтобы добавить дополнительную кнопку, нам прийдётся создать обработчики для следующих событий:
WM_NCPAINT;//вызывается, когда перерисовывается не клиентская область формы.
WM_NCACTIVATE; вызывается, когда заголовок формы становится активныи.
WM_NCLBUTTONDOWN; вызывается, когда кнопка мыши нажимается на не клиентской области.
WM_NCMOUSEMOVE; вызывается, когда курсор мыши передвигается по не клиентской области.
WM_MOUSEMOVE;вызывается, когда курсор мыши передвигается по клиентской области.
WM_LBUTTONUP; вызывается, когда кнопка мыши отпушена в клиентской области.
WM_NCLBUTTONUP; вызывается, когда кнопка мыши отпушена в не клиентской области.
WM_NCLBUTTONDBLCLK; вызывается при двойном щелчке мышкой в не клиентской области.
Приведённый ниже код модифицирован, чтобы избавиться от нежелательного мерцания кнопки.
Будем использовать следующие переменные:
H1(Thandle) : хэндл контекста устройства всего окна, включая не клиентскую область.
Pressed(boolean): индикатор, показывающий, нажата кнопка или нет.
Focuslost(boolean): индикатор, показывающий, находится ли фокус на кнопке или нет.
Rec(Trect): размер кнопки.
Code: Type. TForm1 = class(TForm). Procedure FormPaint(Sender: TObject); Procedure FormResize(Sender: TObject); Procedure FormCreate(Sender: TObject); Private. { Private declarations }. Public. Procedure WMNCPAINT(var msg:tmessage);message WM_NCPAINT; Procedure WMNCACTIVATE(var msg:tmessage);message WM_NCACTIVATE; Procedure WMNCMOUSEDOWN(var msg:tmessage);message WM_NCLBUTTONDOWN; Procedure WMNCMOUSEMOVE(var msg:tmessage);message WM_NCMOUSEMOVE; Procedure WMMOVE(var msg:tmessage);message WM_MOUSEMOVE; Procedure WMLBUTTONUP(var msg:tmessage);message WM_LBUTTONUP; Procedure WMNCMOUSEUP(var msg:tmessage);message WM_NCLBUTTONUP; Procedure WNCLBUTTONDBLCLICK(var msg:tmessage);message WM_NCLBUTTONDBLCLK; End; Var. Form1: TForm1; H1:thandle; Pressed:boolean; Focuslost:boolean; Rec:trect; Implementation. {$R *.DFM}. Procedure tform1.WMLBUTTONUP(var msg:tmessage); Begin. Pressed:=false; Invalidaterect(form1.handle,@rec,true); Inherited; End; Procedure tform1.WMMOVE(var msg:tmessage); Var tmp:boolean. Begin. Tmp:=focuslost; Focuslost:=true; If tmp<>focuslost then. Invalidaterect(form1.handle,@rec,true); Inherited; End; Procedure tform1.WMNCMOUSEMOVE(var msg:tmessage); Var. Pt1:tpoint; Tmp:boolean; Begin. Tmp:=focuslost; Pt1.x:=msg.LParamLo-form1.left; Pt1.y:=msg.LParamHi-form1.top; If not(ptinrect(rec,pt1)) then. Focuslost:=true. Else. Focuslost:=false; If tmp<>focuslost then. Invalidaterect(form1.handle,@rec,true); End; Procedure tform1.WNCLBUTTONDBLCLICK(var msg:tmessage); Var pt1:tpoint; Begin. Pt1.x:=msg.LParamLo-form1.left; Pt1.y:=msg.LParamHi-form1.top; If not(ptinrect(rec,pt1)) then. Inherited; End; Procedure tform1.WMNCMOUSEUP(var msg:tmessage); Var pt1:tpoint; Begin. Pt1.x:=msg.LParamLo-form1.left; Pt1.y:=msg.LParamHi-form1.top; If (ptinrect(rec,pt1)) and (focuslost=false) then. Begin. Pressed:=false; { Enter your code here when the button is clicked. } Invalidaterect(form1.handle,@rec,true); End. Else. Begin. Pressed:=false; Focuslost:=true; Inherited; End; End; Procedure tform1.WMNCMOUSEDOWN(var msg:tmessage); Var pt1:tpoint; Begin. Pt1.x:=msg.LParamLo-form1.left; Pt1.y:=msg.LParamHi-form1.top; If ptinrect(rec,pt1) then. Begin. Pressed:=true; Invalidaterect(form1.handle,@rec,true); End. Else. Begin. Form1.paint; Inherited; End; End; Procedure tform1.WMNCACTIVATE(var msg:tmessage); Begin. Invalidaterect(form1.handle,@rec,true); Inherited; End; Procedure tform1.WMNCPAINT(var msg:tmessage); Begin. Invalidaterect(form1.handle,@rec,true); Inherited; End; Procedure TForm1.FormPaint(Sender: TObject); Begin. H1:=getwindowdc(form1.handle); Rec.left:=form1.width-75; Rec.top:=6; Rec.right:=form1.width-60; Rec.bottom:=20; Selectobject(h1,getstockobject(ltgray_BRUSH)); Rectangle(h1,rec.left,rec.top,rec.right,rec.bottom); If (pressed=false) or (focuslost=true) then. Drawedge(h1,rec,EDGE_RAISED,BF_RECT). Else if (pressed=true) and (focuslost=false) then. Drawedge(h1,rec,EDGE_SUNKEN,BF_RECT); Releasedc(form1.handle,h1); End; Procedure TForm1.FormResize(Sender: TObject); Begin. Form1.paint; End; Procedure TForm1.FormCreate(Sender: TObject); Begin. Rec.left:=0; Rec.top:=0; Rec.bottom:=0; Rec.right:=0; End;
Комментарии специалистов:
Дата: 25 Августа 2000г.
Автор: NeNashev nashev@mail.ru.
InvalidateRect на событие Resize ничего не даёт. Но даже без него.
Кнопка всё равно моргает при Resize формы... Надо ещё где-то убрать.
Для рисования кнопок на заголовке окна лучше пользоваться.
DrawFrameControl а не DrawEdge... Так и с не серыми настройками.
Интерфейса всё правильно будет. Да и проще так.
Названия функций, констант и т.п лучше писать так, как они в описаниях.
Даются, а не подряд маленькими буквами. Особенно для публикации. Так.
Оно и читается по большей части лучше, и в С такая привычка Вам не.
Помешает...
Сравнивать логическое значение с логической константой чтоб получить.
Логическое значение глупо, так как логическое значение у Вас уже есть.
Тоесь вместо.
If (pressed=true) and (focuslost=false).
Лучше писать.
If Pressed and not FocusLost.
Для конструирования прямоугольников и точек из координат есть две.
Простые функции Rect и Point.
В общем Ваша процедура FormPaint может выглядеть так:
Code: Procedure TMainForm.FormPaint(Sender: TObject); Var h1:THandle; Begin. H1:=GetWindowDC(MainForm.Handle); Rec:=Rect(MainForm.Width-75,6,MainForm.Width-60,20); If Pressed and not FocusLost then. DrawFrameControl(h1, rec, DFC_BUTTON, DFCS_BUTTONPUSH or DFCS_PUSHED). Else. DrawFrameControl(h1, rec, DFC_BUTTON, DFCS_BUTTONPUSH); ReleaseDC(MainForm.Handle,h1); End;
| Оптимизация программы для работы в фоновом режиме. |
Я пишу программу в Delphi, которая каждый час должна проверять размер файла. Это также предполагает, что в случае неактивности приложения оно должно работать сторожевым псом в фоновом режиме win 95 и NT. Как мне сделать это...?? Вот некоторый исходный код, который должен делать то, что вы хотите. Я его только что создал и еще не успел проверить, но что-то подобное я уже делал, так что это должно работать. Код допускает одно предположение, о котором вы должны отдавать себе отчет. Оно заключается в том, что приложение должно запускатьтся одновременно с Windows (может быть из группы автозапуска), так как код использует GetTickCount, возвращающий в миллисекундах время с момента старта системы, это необходимо для ежечасной инициализац ии кода выполнения задачи. По-моему это то, что вам нужно. Величина, возвращаемая GetTickCount имеет тип DWORD, но Delphi ее хранит как LongInt, поэтому большие значения могут иметь отрицательную величину (после примерно 25 дней). Данный эффект в алгоритм е проверки наступления часа неопределен (я действительно не считал это). Аналогично, значение будет повторяться в цикле каждые 49.7 дней и может появиться другой эффект, когда раз в 49.7 дней в одном реальном часе алгоритм сработает дважды. Надеюсь это ни как не скажется на вашей задаче. Во всяком случае разве это не то, что вы хотели? Успехов!
Code: Program Project1; Uses Messages, Windows; {$R *.RES}. Function KeepRunning: Boolean; Var. Msg: TMsg; Begin. Result := True; While PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do. Begin. If (Msg.Message = WM_QUIT) then. Result := False; DispatchMessage(Msg); End; End; Function OnTheHour: Boolean; Begin. { Это действительно проверяется в течение одной секунды }. { (или меньше) для проверки наступления нового часа, }. { когда нам необходимо запустить нашу задачу на выполнение }. Result := (GetTickCount mod. (1 {hr} * 60 {min} * 60 {sec} * 1000 {msec}) < 1000); End; Const. Filetocheck = 'c:\somedir\somefile.ext'; Magicsize = 1000000; Var. F: file; Size: longint; Begin. { проверка наступления нового часа с момента запуска системы }. While keeprunning do. Begin. { проверяем наступление часа }. If onthehour then. Begin. { открываем файл с размером записи 1 байт }. { и проверяем его размер }. Assignfile(f, filetocheck); Reset(f, 1); Size := filesize(f); Closefile(f); { теперь проверяем изменение размера файла }. If (size >= MAGICSIZE) then. Begin. { Предпринимаем какие-то действия }. End; { Теперь "сидим" в этом участке кода }. { и ожидаем очередного часа, здесь можно }. { предусмотреть выход из программы или иное действие }. While (KeepRunning and OnTheHour) do. {ничего}; End; End; End.
| Взлом Windows-приложений. |
Введение.
Для начала я научу вас пользоваться W32Dasm. Я не хочу вам давать детальную помощь, как делать краки, но я могу научить вас самим добывать себе умения и навыки взлома. Когда вы используете W32Dasm, знайте, что он не даст вам серийные номера или коды, он лишь покажет путь, где находится место, где можно эти номера вводить. То, что я делаю каждый день при взломе программ, будет описано в этом справочнике, шаг за шагом.
Инструменты.
Из инструментов взлома вам нужно следующее:
W32Dasm 8.5 или боолее позднюю версию,
Hacker's View 5.24,
Norton Commander (я позднее объясню, почему я его использую).
Turbo Pascal 7.0.
TASM и TLINK 3.0.
Как кракнуть Quick View Plus 4.0.
Запустите ORDER32.EXE.
Кликните на $49 Single User License (вы можете кликнуть и на $59), затем ACCEPT, потом UNLOCK BY PHONE.
Введите любой код для получения сообщения об ошибке (вы должны записать это сообщение), потом выйдите из программы, кликнув на CANCEL.
Запустите Norton Comander, перейдите в директорию QVP.
Скопируйте ORDER32.EXE в ORDER32.EXX (для сохранности), а затем скопируйте ORDER32.EXE в 1.EXE (для использования в W32Dasm).
Запустите W32Dasm и раздессимблируйте 1.EXE.
После этого, кликните на STRING DATA REFERENCE, найдите там сообщение "You have entered an incorrect code.Please check your entry" (вы должны помнить,что это было сообщение об ошибке) и дважды щелкните мышью по нему.
Закройте SDR окно. Вы должны увидеть сообщение:
* Possible reference to String Resource ID=00041: "You have entered...
:004049F8 6A29 push 00000029.
:004049FA FF353CCE4000 push dword ptr [0040CE3C].
ОК, теперь вы должны найти последнее сравнение типа CMP,JNE, JE,TEST и т.д. перед сообщением об ошибке. Нажимайте стрелку "вверх", пока не найдете:
:004049CD 755A jne 00404A29.
* Possible reference to String Resource ID=00032: "You must select...
:004049CF 6A20 push 00000020.
...
...
* Possible reference to String Resource ID=00040: "Unlock Error".
Теперь вы знаете, куда идет скачок при введении неправильного кода. Теперь можно посмотреть, что произойдет, если "jne" на "je". Убедитесь, что зеленая полоска находится на надписи:
004049CD 755A jne 00404A29, вы должны увидеть Offset address внизу на статусной строке типа @Offset 00003DCDh Это место, где вы можете внести изменения в ORDER32.EXE.
Перейдите обратно в Norton Commander, запустите HIEW ORDER32.EXE, нажмите F4 для выбора режима декодирования (Decode Mode), нажмите F5 и введите 3DCD. Вы должны увидеть следующее :
00003DCD: 755A jne 000003E29.
00003DCF: 6A20 push 020.
00003DD1: FF15 call w,[di].
Это то место, где вы можете изменить байты, нажмите F3, введите 74, нажмите F9 для обновления ORDER32.EXE. Выйдите из HIEW.
Запустите ORDER32.EXE, введите любой код. Ура ! Мы сломали QVP 4.0 ! Но ! Что будет, если ввести настоящий серийный номер ? Появляется сообщение об ошибке ! Что это ?
Снова запустиите HIEW ORDER32.EXE, нажмите F4, выберите Decode, нажмите F5 и введите 3DCD. Нажмите F3, введите EB, нажмите F9. Вы прямо "прыгнете" на Unlocked диалог.
Как кракнуть Hex WorkShop 2.51.
Запустите HWORKS32.EXE.
Кликните на HELP, About HEX Wo..
Введите любой код, чтобы получить сообщение об ошибке (вы должны записать это сообщение) и выйдите из программы.
Запустите Norton Commander, перейдите в директорию HWS.
Скопируйте файл HWORKS32.EXE в HWORKS32.EXX (для сохранности) и скопируйте файл HWORKS32.EXE в 1.EXE (для использования в W32Dasm).
Запустите W32Dasm и "разберите" 1.EXE.
После этого, нажмите мышью на FIND TEXT, введите "You have entered an" (вы должны помнить, что это сообщение об ошибочно введенном серийном номере) и найдите соответствующую строку (вы не сможете сделать это в SDR-окне !).
Вы должны увидеть следующую строку:
Name: DialogID_0075, # of Controls=003, Caption: "Registration Unsucce..
001-ControlID:FFFF, Control Class:""Control Text:"You have entered an..
002-ControlID:FFFF, Control Class:""Control Text:"Please confirm you..
Ок, теперь вы знаете, что ControlID будет использоваться, когда вы введете неверный код. Кликните FIND TEXT, введите "dialogid_0075" и вы найдете:
* Possible reference to DialogID_0075.
:0041E233 6A75 push 00000075.
:0041E235 8D8D10FFFFFF lea ecx, dword ptr [ebp+FF10].
Теперь вы должны поискать последнюю ссылку, типа CMP, JNE, JE и пр. перед диалогом об ошибке. Нажимайте клавишу "вверх", пока не найдете :
:0041E145 837DEC00 cmp dword ptr [ebp-14], 00000000.
:0041E149 0F8479000000 je 0041E1C8.
:0041E14F 8B8DFCFEFFFF mov ecx, dword ptr [ebp+FEFC].
Теперь вам нужно посмотреть, что произойдет, если "je" заменить на "jne". Убедитесь, что зеленая полоска установлена на строке.
:0041E149 0F8479000000 je 0041E1C8.
Вы должны на нижней статусной строке увидеть оффсетный адрес, типа:
@Offset0001D549h.
Это то место, где вы сможете кракнуть HWORKS32.EXE.
Перейдите обратно в Norton Commander, запустите HIEW HWORKS32.EXE, нажмите F4 для выбора режима декодирования (Decode Mode), нажмите F5 и введите ID549. Вы должны увидеть следующее :
0001D549: 0F847900 je 00001D5C6 ---------- (1).
0001D54D: 0000 add [bx][si],al.
0001D54F: 8B8DFCFE mov cx,[di][0FEFC].
Это то место, где вы сможете изменить несколько байтов, нажмите F3, введите 0F85, нажмите F9 для обновления файла HWORKS32.EXE. Выйдите из HIEW.
Запустите HWORKS32.EXE и введите любой код, работает ? НЕТ !?!??!?! Хе-хе-хе... Не волнуйтесь ! Снова перейдите в Нортон. Скопируйте HWORKS32.EXX в HWORKS32.EXE (теперь вы видите, почему я делаю копию файла с расширением ЕХХ для сохранности). Теперь перейдите в W32Dasm, вы должны перейти туда, где только что были (на 0041У145).
Нажмите F3 для очередного поиска "DialogID_0075", вы должны найти:
* Possible reference to DialogID_0075.
:00430ADD 6A75 push 00000075.
:00430ADF 8D8D10FFFFFF lea ecx, dword ptr [ebp+FF10].
Ok, теперь вы теперь можете посмотреть на последние ссылки, типа CMP, JNE, JE и т.д. перед диалогом об ошибке. Нажимайте стрелку вверх, пока не найдете :
:004309EF 837DEC00 cmp dword ptr [ebp-14], 00000000.
:004309F3 0F8479000000 je 00430A72.
:004309F9 8B8DFCFEFFFF mov ecx, dword ptr [ebp+FEFC].
Теперь вы можете посмотреть, что произойдет, если "je" заменить на 'jne". (это должно сработать). Переместите полоску на:
004309F3 0F8479000000 je 00430A72.
На статусной строке внизу экрана вы должны следующее:
@Offset0002FDF3h (оффсетный адрес).
Это то место, где вы сможете кракнуть HWORKS32.EXE.
Перейдите в Norton Commander, запустите HIEW HWORKS32.EXE, нажмите F4 для выбора Decode Mode (ASM), нажмите F5 и введите 2FDF3. Вы должны увидеть:
0002FDF3: 0F847900 je 00001D5C6 ---------- (1).
0002FDF7: 0000 add [bx][si],al.
0002FDF9: 8B8DFCFE mov cx,[di][0FEFC].
Это то место, где вы сможете изменить несколько байтов, нажмите F3, введите 0F85, нажмите F9 для обновления файла HWORKS32.EXE. Выйдите из HIEW.
Запустите снова HWORKS32.EXE и введите любой код. Работает ? Виола !!! Поздравляю !!! Вы крякнули HEX WorkShop 2.51 !
Как сделать собственный патч.
Здесь напечатан исходный код на Паскале :
Code: Uses Crt; Const. A: array[1..1] of record {<-------- 1 byte to be patched}. A: Longint; B: Byte; End = ((A: $3DCD; B: $EB)); {<--------------- offset "3DCD" and byte "EB" to be changed}. Var. Ch: Char; I: Byte; F: file; FN: file of byte; Size: longint; Begin. Writeln('TKC Little Patch'); Writeln('Crack for QVP 4.0 by TKC/PC 97'); Assign(F, 'ORDER32.EXE'); {<-------------- filename to be patched}. {$I-}Reset(F, 1); {$I+}. If IOResult <> 0 then. Begin. Writeln('File not found!'); Halt(1); End; For I := 1 to 1 do {<---------------------- 1 byte to be patched}. Begin. Seek(F, A[I].A); Ch := Char(A[I].B); Blockwrite(F, Ch, 1); End; Writeln('File successfully patched!'); End.
| Управление приложением через Telnet. |
Итак, начнем с главного - почему для удаленного администрирования своей программы следует использовать именно Telnet? Ответ на этот вопрос достаточно прост:
Утилита Telnet есть на любом компьютере с операционной системой Windows, UNIX, AIX и т.п., поэтому ее не требуется писать или устанавливать.
Telnet является штатным средством удаленного администрирования.
Telnet подразумевает текстовый обмен, поэтому его очень легко поддерживать в своей программе.
Возможностей текстового терминала как правило достаточно для управления программой, ее настройки и администрирования.
Рассмотрим немного теории. Утилиту Telnet легче всего запустить через Start->Run (Пуск -> Выполнить). После запуска необходимо произвести соединение с удаленным хостом, для чего выполняется используется меню "Connect->Remote System". При этом выводится меню соединения, в котором необходимо указать три параметра: хост, порт и тип терминала. В качестве хоста указывается имя удаленного компьютера (или его IP адрес), порт можно задать двумя путями - выбором/вводом символического имени (например, telnet), или вводом номера порта. Мы будем пользоваться вторым путем, т.е. будем использовать нестандартные номера портов. Тип терминала оставим vt100.
Утилита Telnet поддерживает параметры командой строки:
Telnet [remote_host] [port].
Где.
Remote_host представляет собой имя или IP адрес удаленной машины.
Port номер порта. Если соединение идет по стандартному порту, то этот параметр опускается.
Пример:
Telnet zaitsevov или telnet zaitsevov 5000.
Протокол Telnet очень прост - сначала устанавливается TCP/IP соединение с удаленной машиной. Затем, когда пользователь вводит символ, происходит его передача удаленному хосту. Для простоты будем называть его сервером.
Далее возможно два режима работы - с локальным эхом или без локального эха (режим по умолчанию). Если работа ведется с локальным эхом, то каждый вводимый пользователем символ немедленно отображается на экране. При работе без локального эха сервер обязан создавать эхо, дублирую принимаемые данные клиенту. Это позволят тестировать канал (каждый символ проходит по кругу) и организовывать ввод данных без эха (например, для ввода пароля). Мои примеры ориентированы на работу без локального эха.
При приеме любой информации от сервера утилита Telnet немедленно отображает его на экране. Это позволяет серверу организовывать эхо и выводить любую информацию в текстовом виде. При этом поддерживатся некоторые управляющие коды, например, код "забой", стирающий один символ.
Итак, приступим к разработке приложения. Создадим пустой проект и поместим на форму компонент ServerSocket1 типа TServerSocket. Зададим ему порт, например 5000. Напоминаю, что:
Номер порта должен быть нестандартным, чтобы не пересекаться с другими программами. При этом желательно считывать его из INI файла, что даст возможность настройки при необходимости.
Свойство Active должно быть false и устанавливаться в true при запуске программы. Иначе приложение свалится при попытке запуска второй копии или при отсутствии сети. Установку Active := true следует делать в блоке try ... except.
Итак, в обработчике OnCreate формы пишем:
Code: Begin. Try. ServerSocket1.Active := true; Except. ShowMessage('Ошибки при активации ServerSocket'); End; End;
Арифметика указателей.
Сначала короткое объяснение арифметике указателя. Когда вы имеете дело с динамическими страницами памяти, то все, что вы имеете - это указатели на начала блоков памяти. Вы хотите просмотреть всю строку памяти, чтобы понять какие функции необходимы для работы с данными, хранящимися в памяти? Это возможно путем изменения места в памяти, на которое указывает указатель. Это называется арифметикой указателя.
Основополагающая идея при занятиях арифметикой с указателем - указатель должен быть увеличен на значение корректного приращения. (Корректное приращение определяется размером объекта, на который показывает указатель. Например, char = 1 байт; integer = 2 байта; double = 8 байт и т.д.) Функции Inc() и Dec() изменяют значение корректного приращения. (Компилятор знает правильный размер объекта.).
Если вы осуществляете динамическое распределение памяти, то делать это можно примерно так:
Code: Uses WinCRT; Procedure TForm1.Button1Click(Sender: TObject); Var. MyArray: array[0..30] of char; B: ^char; I: integer; Begin. StrCopy(MyArray, 'Дельфи - рулез фарева!'); {помещаем что-то в память для организации указателя}. B := @MyArray; { назначаем указатель на текущую позицию памяти }. For i := StrLen(MyArray) downto 0 do. Begin. Write(b^); { пишем символ в текущую позицию указателя. }. Inc(b); { перемещаем указатель на следующий байт памяти }. End; End;
Нижеследующий код демонстрирует работу функций Inc() и Dec(), увеличивающих или уменьшающих указатель на размер соответствующего типа:
Code: Var. P1, P2: ^LongInt; L: LongInt; Begin. P1 := @L; { назначаем оба указателя на одно и то же место }. P2 := @L; Inc(P2); { Увеличиваем один }. { Здесь мы получаем разницу между смещениями двух. Указателей. Поскольку первоначально они указывали на одно. И то же место памяти, то результатом данного вызова. Будет разница между двумя указателями после вызова Inc(). }. L := Ofs(P2^) - Ofs(P1^); { L = 4; т.е. sizeof(longInt) }. End;
Вы можете изменить тип объекта, на который указывает P1 и P2, на какой-то другой и убедиться, что (SizeOf(P1^)) всегда возвращает величину корректного приращения (проще сказать, что это размер объекта - В.О.).
©Drkb::00256.
Взято с http://delphiworld.narod.ru.
Описание протокола ARP (Address Resolution Protocol).
Для определения локального адреса по IP-адресу используется протокол разрешения адреса Address Resolution Protocol, ARP. Протокол ARP работает различным образом в зависимости от того, какой протокол канального уровня работает в данной сети - протокол локальной сети (Ethernet, Token Ring, FDDI) с возможностью широковещательного доступа одновременно ко всем узлам сети, или же протокол глобальной сети (X.25, frame relay), как правило не поддерживающий широковещательный доступ. Существует также протокол, решающий обратную задачу - нахождение IP-адреса по известному локальному адресу. Он называется реверсивный ARP - RARP (Reverse Address Resolution Protocol) и используется при старте бездисковых станций, не знающих в начальный момент своего IP-адреса, но знающих адрес своего сетевого адаптера.
В локальных сетях протокол ARP использует широковещательные кадры протокола канального уровня для поиска в сети узла с заданным IP-адресом.
Узел, которому нужно выполнить отображение IP-адреса на локальный адрес, формирует ARP запрос, вкладывает его в кадр протокола канального уровня, указывая в нем известный IP-адрес, и рассылает запрос широковещательно. Все узлы локальной сети получают ARP запрос и сравнивают указанный там IP-адрес с собственным. В случае их совпадения узел формирует ARP-ответ, в котором указывает свой IP-адрес и свой локальный адрес и отправляет его уже направленно, так как в ARP запросе отправитель указывает свой локальный адрес. ARP-запросы и ответы используют один и тот же формат пакета. Так как локальные адреса могут в различных типах сетей иметь различную длину, то формат пакета протокола ARP зависит от типа сети.
В поле типа сети для сетей Ethernet указывается значение 1. Поле типа протокола позволяет использовать пакеты ARP не только для протокола IP, но и для других сетевых протоколов. Для IP значение этого поля равно 080016.
Длина локального адреса для протокола Ethernet равна 6 байтам, а длина IP-адреса - 4 байтам. В поле операции для ARP запросов указывается значение 1 для протокола ARP и 2 для протокола RARP.
Узел, отправляющий ARP-запрос, заполняет в пакете все поля, кроме поля искомого локального адреса (для RARP-запроса не указывается искомый IP-адрес). Значение этого поля заполняется узлом, опознавшим свой IP-адрес.
В глобальных сетях администратору сети чаще всего приходится вручную формировать ARP-таблицы, в которых он задает, например, соответствие IP-адреса адресу узла сети X.25, который имеет смысл локального адреса. В последнее время наметилась тенденция автоматизации работы протокола ARP и в глобальных сетях. Для этой цели среди всех маршрутизаторов, подключенных к какой-либо глобальной сети, выделяется специальный маршрутизатор, который ведет ARP-таблицу для всех остальных узлов и маршрутизаторов этой сети. При таком централизованном подходе для всех узлов и маршрутизаторов вручную нужно задать только IP-адрес и локальный адрес выделенного маршрутизатора. Затем каждый узел и маршрутизатор регистрирует свои адреса в выделенном маршрутизаторе, а при необходимости установления соответствия между IP-адресом и локальным адресом узел обращается к выделенному маршрутизатору с запросом и автоматически получает ответ без участия администратора.
©Drkb::03385.
Взято с http://delphiworld.narod.ru.
| Пример массива констант (Array of Const). |
"Array of const" это массив переменных, декларированных как константы. Непосредственно они представлены структурой TVarRec. Скобки просто ограничивают массив. Массив констант дает вам возможность передавать процедуре переменное количество параметров type-safe (безопасным) способом. Вот пример:
Code: Type. TVarRec = record. Data: record case Integer of. 0: (L: LongInt); 1: (B: Boolean); 2: (C: Char); 3: (E: ^Extended); 4: (S: ^string); 5: (P: Pointer); 6: (X: PChar); 7: (O: TObject); End; Tag: Byte; Stuff: array[0..2] of Byte; End; Function PtrToStr(P: Pointer): string; Const. HexChar: array[0..15] of Char = '0123456789ABCDEF'; Function HexByte(B: Byte): string; Begin. Result := HexChar[B shr 4] + HexChar[B and 15]; End; Function HexWord(W: Word): string; Begin. Result := HexByte(Hi(W)) + HexByte(Lo(W)); End; Begin. Result := HexWord(HiWord(LongInt(P))) + ':' + HexWord(LoWord(LongInt(P))); End; Procedure Display(X: array of const); Var. I: Integer; Begin. For I := 0 to High(X) do. With TVarRec(X[I]), Data do. Begin. Case Tag of. 0: ShowMessage('Integer: ' + IntToStr(L)); 1: if B then. ShowMessage('Boolean: True'). Else. ShowMessage('Boolean: False'); 2: ShowMessage('Char: ' + C); 3: ShowMessage('Float: ' + FloatToStr(E^)); 4: ShowMessage('String: ' + S^); 5: ShowMessage('Pointer: ' + PtrToStr(P)); 6: ShowMessage('PChar: ' + StrPas(X)); 7: ShowMessage('Object: ' + O.ClassName); End; End; End; Procedure TForm1.Button1Click(Sender: TObject); Var. P: array[0..5] of Char; Begin. P := 'Привет'#0; Display([-12345678, True, 'A', 1.2345, 'ABC', Ptr($1234, $5678), P, Form1]); End;
ASCII драйвер для CSV-файлов.
Использование драйвера ASCII для файлов с разделительной запятой.
Delphi (и BDE) имеют способность использовать ASCII файлы для хранения таблиц. Драйвер ASCII имеет возможность транслировать значения данных ASCII-поля фиксированной длины или файла с разделительной запятой в поля и величины, которые могут отображаться компонентом TTable. Трансляция ASCII файла целиком зависит от сопровождающего файла схемы (Schema File). Файл схемы для файла ASCII данных определяет различные атрибуты, необходимые для преобразования данных ASCII файла в значения отдельных полей. Определения полей для файла с ASCII полями фиксированной длины достаточно простая задача, необходимо знать позиции всех полей, для всех строк они одинаковы. Для файлов с разделительной запятой данный процесс чуть более усложнен из-за того, что не все данные в таком файле во всех строках имеют одинаковую длину. Данный совет как раз и концентрируется на описании этой трудной темы, связанной с чтением данных из файлов с разделительной запятой, имеющих варьируемую длину поля.
Файл схемы.
Файл схемы для файла данных ASCII содержит информацию, которая определяет оба типа файла (версии с разделительной запятой и полем с фиксированной длиной), а также определяет поля, которые представлены значениями данных в каждой строке файла данных ASCII. (Все поля файла схемы нечуствительны к регистру, поэтому написание "ascii" равнозначно написанию "ASCII".) Для того, чтобы файл схемы был признан в качестве такового, он должен иметь то же имя, что и файл данных ASCII, для которого он содержит схему, но иметь расширение .SCH (SCHema - схема). Атрибуты описания файла:
File name: Располагаемый в квадратных скобках, данный атрибут определяет.
Имя файла ASCII данных (с расширением имени файла,
Которое должно быть .TXT).
Filetype: Определяет, имеет ли файл ASCII данных структуру файла с.
Полями фиксированной длины (используется атрибут FIXED) или.
Файлом с разделительной запятой (со значениями данных, которые.
Потенциально могут изменять длину (используется атрибут VARYING).
Delimiter: Определяет символ, которым "окантуривают" значения данных типа.
String (обычно двойные кавычки, десятичный ASCII код 34).
Separator: Определяет символ, который используется для разделения отдельных.
Значений данных (обычно запятая). Данный символ должен быть.
Видимым символом, т.е. не может быть пробелом (десятичный ASCII.
Код 32).
CharSet: Определяет драйвер языка (используется атрибут ASCII).
Расположенные ниже атрибуты файла являются определениями поля, задающими правила для каждой строки файла данных ASCII. Данные определения служат источником информации для Delphi и BDE, первоначально необходимой для создания виртуального поля в памяти, в свою очередь служащее для хранения значений данных; тип данных виртуального поля определяется после чтения и трансляции данных из ASCII файла, определения размера и применения атрибутов. Различные атрибуты, определяющие поле файла данных ASCII:
Field: Имя виртуального поля (всегда будет "Field"), сопровождаемое.
Целым числом, определяющим порядковый номер поля относительно.
Других полей в файле данных ASCII. Например, первое поле -
Field1, второе Field2, и т.д..
Field name: Определяет выводимое имя поля, отображаемое в виде.
Заголовка колонки в TDBGrid. Соглашения имен для.
Таблиц ASCII такие же, как и для таблиц Paradox.
Field type: Определяет, какой тип данных BDE должен использоваться при.
Трансляции значений данных каждого поля и сообщает.
Delphi тип виртуального поля, которое необходимо создать.
Используйте определение Для значений типа.
----------------------- ----------------------------
CHAR Символ.
FLOAT 64-битное число с плавающей точкой.
NUMBER 16-битное целое.
BOOL Boolean (T или F).
LONGINT 32-битное длинное целое.
DATE Поле Date.
TIME Поле Time.
TIMESTAMP Поле Date + Time.
(Фактически формат для значений данных даты и времени.
Будет определяться текущими настройками конфигурации BDE,
Страница с закладкой Date.).
Data value length: Максимальная длина значения данных соответствующего поля.
Данный атрибут определяет длину виртуального поля,
Создаваемое Delphi для получения считываемых значений из.
ASCII-файла.
Number of decimals: Приложение к полю типа FLOAT; определяет количество цифр.
Справа от десятичной точки; необходимо для включения в.
Определение виртуального поля.
Offset: Отступ от начала строки, позиция начала данных описываемого.
Поля; задается для всех строк файла.
Например, приведенное ниже определение поля относится к первому полю таблицы ASCII. Данная строка определяет значения данных типа String с именем "Text", максимальная длина значения данных составляет три символа (и в Delphi компонентах для работы с базами данных, типа TDBGrid, поле будет отображаться только тремя символами), десятичный порядок (значение данных типа String никогда не сможет иметь десятичные значения, тем более после запятой), и смещение относительно нулевой позиции (поскольку описываемая область первая, то она сама начинается с нулевой позиции, перед ней не находится ни одно поле).
Field1=Text,Char,3,00,00.
Вот пример файла схемы с тремя полями, первое поле имеет тип String, второе и третье тип Date. Данный файл схемы должен содержаться в файле с именем DATES.SCH и обеспечивать определения полей для файла данных ASCII с именем DATES.TXT.
[DATES].
Filetype=VARYING.
Delimiter=".
Separator=,
CharSet=ascii.
Field1=Text,Char,3,00,00.
Field2=First Contact,Date,10,00,03.
Field3=Second,Date,10,00,13.
Данная схема определяет поле с разделительной запятой, где все данные могут быть отнесены к типу String, значения полей "окантурены" двойными кавычками и отдельные значения полей разделены запятой (за исключением любых запятых, которые могут находится между разделительными запятыми, внутри отдельных значений полей типа String). Первое поле типа character имеет длину три символа, без определения десятичного порядка и с нулевым отступом от начала строки. Второе поле данных имеет длину 10, без определения десятичного порядка и отступ, равный трем. Третье поле данных имеет длину 10, без определения десятичного порядка и отступ, равный 13.
Для чтения файлов ASCII с разделительной запятой, параметры длины и отступа для определения поля относятся не к значениям данных в файлах ASCII (что явно противоположно для файлов с полями фиксированной длиной), а к виртуальным полям, определяемым в приложении, в котором будет размещены считываемые данные. Параметр длины должен отражать максимальную длину значений данных для каждого поля, не считая огриничительных кавычек и разделительной запятой. Это наиболее трудно при оценке значений данных типа String, поскольку фактическая длина значений данных может быть существенно различаться для каждой строки в файле данных ASCII. Параметр отступа для каждого поля не будет являться позицией значений данных в файле ASCII (что относится к файлам с полями фиксированной длины), а представляет собой совокупную длину всех предыдущих полей (и снова, определение полей в памяти, не значения данных в файле ASCII).
Вот файл данных с именем DATES.TXT, который соответствует описанному выше файлу схемы:
"A",08/01/1995,08/11/19955.
"BB",08/02/1995,08/12/1995.
"CCC",08/03/1995,08/13/1995.
Максимальная длина фактических значений данных в первом поле составляет три символа ("CCC"). Поскольку это первое поле и предшествующих полей не существует, отступ для данного поля равен нулю. Длина первого поля (3) используется в качестве отступа для второго поля. Длина второго поля, значение date, равно 10 и отражает максимальную длину значения данных этого поля. Совокупная длина первого и второго полей используется в качестве значения отступа для третьего поля (3 + 10 = 13).
Только когда соответствующая длина значения данных ASCII файла или длина каждого поля добавляется к длине предыдущих полей, вычисляется значение отступа и получается позиция очередного поля, только тогда данный процесс правильно считает данные. Если из-за неправильных установочных параметров в файле схемы данные транслируются неверно, то в большинстве типов полей могут возникнуть неблагоприятные эффекты типа обрезания строк, или интерпретирование цифр как нулей. Обычно в таком случае данные выводятся, но ошибки не возникает. Тем не менее, значения определенного формата в процессе трансляции в подходящий тип данных могут вызвать ошибку, если считываемые символы не соответствуют символам, например, в типе date. В контексте вышесказанного, ошибка с типом dаtе может возникнуть и-за того, что при неправильном определении в значения данных могут попасть данные другого, соседнего поля. При таком стечение обстоятельств трансляция данных прерывается, и в файле схемы требуется установка правильной длины поля и его отступа.
©Drkb::02963.
Взято с http://delphiworld.narod.ru.
Использование BDE приложений в Peer-To-Peer сети.
Сетью Peer-To-Peer (сеть, где каждая машина действует как клиент и как сервер) может быть одна из следующих сетей, включая другие сетевые платформы, совместимые с ними:
Windows 95.
Windows NT.
Lantastic.
Netware Lite.
BDE автоматически обнаруживает таблицы на сетевом диске, но он не может их определить на dedicated сервере или server/client. Dedicated-сервера уведомляют приложение клиента о том, что файл был изменен или заблокирован. Данная функциональность отсутствует в Peer-To-Peer (не-dedicated) сетях. Для ее включения в сетях Peer-To-Peer, установите "LOCAL SHARE" в TRUE в BDE Configuration Utility на странице System. Это должно быть сделано на всех клиентах BDE, которые имеют доступ к таблицам в сетях, указанных выше. В случае файловых серверов Novell данное требование не является необходимым.
Если используемые таблицы - таблицы Paradox, они также должны использовать каталог с сетевым контролем. Данный каталог должен находиться в сети для всех используемых клиентских приложений. Хорошим стилем считается использование отдельного каталога для приложения, сети и таблиц. Поясним примером:
<Каталог общего доступа>
|
|--- <Каталог таблиц>
|--- <Каталог Exe-файлов>
|--- <Сетевой каталог>
Существуют две различных среды BDE, которые необходимо принимать во внимание:
Использование только 32-битных приложений BDE.
Использование только 32-битных приложений BDE совместно с 16-битными.
Установка только для 32-битных приложений.
32-битное BDE полностью поддерживает соглашение об путях UNC вместе с длинными именами файлов. Рекомендуется использование соглашения UNC для всех сетевых соединений BDE. UNC позволяет обойтись без подключения (mapped) сетевых дисков. Это позволяет иметь доступ к таблицам и сетевым каталогам без необходимости заставлять пользователя подключать сетевые диски. UNC имеет следующий синтаксис:
\<Имя сервера>\<Имя каталога общего доступа>\<Путь к каталогу>+<Имя файла>
Вот простой пример стандартного псевдонима (alias) BDE с использованием UNC:
Псевдоним: MyUNCAlias.
Тип: STANDARD.
Путь: \\FooServer\FooShare\Sharedir\Tables.
Драйвер по умолчанию: Paradox.
Сетевой каталог может быть установлен и таким способом:
Драйвер: Paradox.
Сетевой каталог: \\FooServer\FooShare\Sharedir\NetDir.
Сетевой каталог может быть установлен во время выполнения приложения с помощью session.netfiledir (Delphi) или DbiSetProp (C++ / Delphi).
Если по какой-либо причине UNC не может использоваться в 32-битных приложениях, следуйте за следующими инструкциями для среды с 32-битными и 16-битными приложениями BDE.
Установка для 16-битных и 32-битных приложений BDE.
Поскольку 16-битное Windows API не поддерживает UNC, ее не поддерживает и 16-битное BDE. Для того, чтобы позволить приложениям иметь общий доступ к таблицам, все клиенты должны подключить один и тот же каталог на сервере. Если сервер также используется и в качестве клиента, то все другие клиенты должны подключить его корневой каталог диска. Логический диск при этом у клиентов может быть разным. Вот несколько примеров с работающими и неработающими настройками:
Клиент1:
Путь: X:\Каталог общего доступа\Таблицы.
Клиент2:
Путь: X:\Каталог общего доступа\Таблицы.
Работоспособно.
Клиент1: (Также машина с таблицами):
Путь: X:\Каталог общего доступа\Таблицы.
Клиент2:
Путь: X:\Каталог общего доступа\Таблицы.
Работоспособно.
Клиент1: (Также машина с таблицами):
Путь: C:\Каталог общего доступа\Таблицы.
Клиент2:
Путь: X:\Каталог общего доступа\Таблицы.
КлиентЗ:
Путь: R:\Каталог общего доступа\Таблицы.
Работоспособно.
Клиент1:
Путь: X:\Каталог общего доступа\Таблицы.
Клиент2:
Путь: X:\Таблицы (Где "X:\Таблицы" реально -
"X:\Каталог общего доступа\Таблицы", но имеющий.
Общий доступ в "Каталог общего доступа").
Неработоспособно. BDE должен иметь возможность.
Иметь доступ к файлу Network Control (управление.
Сетью).
Итог (установки для сетей Peer-To-Peer):
16- и/или 32-битные приложения:
В BDE Configuration Utility установите "LOCAL SHARE" в TRUE.
Не используйте UNC-имена.
Не используйте таблицы с длинными именами файлов.
Убедитесь в том, что все клиенты подключены к одному и тому же каталогу на сервере.
Только 32-битные приложения:
В BDE Configuration Utility установите "LOCAL SHARE" в TRUE.
Для получения доступа к сетевому каталогу и каталогу с таблицами используйте UNC-имена.
При невыполнении описанных выше шагов пользователи могут блокировать таблицы с получением следующей ошибки:
"Directory is controlled by other .NET file.".
(Каталог управляется другим .NET-файлом).
"File: PDOXUSRS.LCK" ("Файл: PDOXUSRS.LCK").
"Directory: " (Каталог: ).
ИЛИ.
"Multiple .NET files in use.".
(Используются несколько .NET-файлов.).
"File: PDOXUSRS.LCK".
(Файл: PDOXUSRS.LCK).
©Drkb::02612.
Взято с http://delphiworld.narod.ru.
Протокол блокировки BDE.
Данная информация будет полезна каждому, кто решил разрабатывать приложения для работы с базами данных с использованием Delphi и BDE.
Таблицы, типы полей и поддерживаемые характеристики.
Каждый следующий выпуск Paradox, начиная с версии 2.0, содержал улучшения структуры таблицы. Все типы таблиц Paradox, начиная с Paradox 1.0 и заканчивая Paradox 3.5, совместимые друг с другом.
Paradox 4.0 добавляет новый тип данных к формату таблиц: Binary Large Objects (бинарные большие объекты), обычно известные как ВLОb'ы, и новые типы вторичных индексов. Paradox 4.0 поддерживает два типа BLOb-полей: Memo и BLOb. Paradox старее версии 4.0 и Engine до версии 3.0 не могут читать, писать и создавать этот новый табличный формат. При попытке чтения или записи таблиц типа Paradox 4.0 более ранней версией Paradox, вы получите ошибку о защите таблицы паролем.
Paradox 5.0 добавляет несколько новых типов данных к формату таблиц: Long Integer, Time, TimeStamp, Logical, Autoincrement, BCD, Bytes. Paradox 7.0 добавляет наследуемый вторичный индекс. Создание или любое изменение таблицы переводит ее формат на новый уровень, включающий все вышеописанные характеристики. По умолчанию создаваемая с использованием Database Desktop или BDE (Borland Database Engine) таблица имеет тип Paradox 4.0. Данный тип, заданный по умолчанию, может быть изменен с помощью утилиты BDE configuration utility или Database desktop configuration utility, и ему может быть присвоен тип Paradox 3, 4, 5 или 7 для BDE.
Paradox 4.0 может читать, писать и создавать таблицы типа Paradox, совместимые с таблицами версий от Paradox 1.0 до Paradox 4.0. Так, таблица, созданая в Paradox 1.0, совместима с Paradox 4.0. Таблица, созданная в Engine 1.0 или 2.0, может быть прочитана и записана в Paradox 4.0.
Paradox и Engine не изменяет тип таблицы при чтении или записи. Тип таблицы изменяется только при ее реструктуризации.
Протоколы блокировки Paradox.
Есть два различных протокола блокировки Paradox: протокол, введенный в Paradox 2.0 и протокол, введенный в Paradox 4.0. Эти два протокола не совместимы друг с другом. Протокол блокировки не оказывает влияния на тип таблицы, с которым может работать программа. Существуют несколько программ, также поддерживающих протоколы блокировки; тем не менее, эти программы в отдельный момент времени могут поддерживать только один протокол. Здесь мы рассматриваем только протокол блокировки версии 4.0.
Протокол блокировки Database Desktop/ Paradox 4.0.
Протокол блокировки Paradox 4.0 - единственный протокол, доступный для Paradox 4.0 и IDAPI Engine. Обозначение "Paradox 4.0 locking protocol" представляет данный стиль блокировки.
Блокировки каталога.
Paradox 4.0 располагает файл блокировки, PDOXUSRS.LCK, в каждом каталоге, в котором доступны таблицы. Файл блокировки регулирует доступ к файлам, расположенным в каталоге. Файл блокировки ссылается на PDOXUSRS.NET, поэтому все пользователи должны подключать данные по одному и тому же пути. При этом в каталоге также располагается эксклюзивный файл PARADOX.LCK. Это делается для того, чтобы предохранить те версии Paradox или Engine, которые используют старую блокировочную систему, от неумышленного получения доступа к таблицам.
Рабочие каталоги и каталоги общего доступа.
Когда Paradox или Database Desktop необходимо получить доступ к таблицам, расположенным в каталоге, то в этом каталоге они размещают "общий" файл PDOXUSRS.LCK и "эксклюзивный" файл PARADOX.LCK. Этим способом они "метят" каталог для того, чтобы другие пользователи Paradox 4.0 также могли иметь доступ к таблицам, расположенным в данном каталоге. Эксклюзивный файл PARADOX.LCK устанавливается в этом каталоге для обеспечения работы несовместимого протокола блокировки, и, таким образом, для уменьшения риска при постинге данных. В Раrаdох'е этот каталог известен как рабочий, "Working" каталог.
Частные/эксклюзивные каталоги.
Для Paradox и Database Desktop также необходим каталог, где они могли бы сохранять временные файлы, например, результаты запроса. При запуске Paradox или Paradox Runtime, они также размещают в каталоге "эксклюзивные" файлы PDOXUSRS.LCK и PARADOX.LCK, определяя данный каталог как место для хранения временных файлов. Это обозначает, что другие пользователи Paradox не смогут получить доступ к таблицам в этом каталоге. В Раrаdох'е этот каталог известен как частный, "Private" каталог.
Блокировка таблицы.
Paradox 4.0 размещает каждую табличную блокировку в блокирующем файле PDOXUSRS.LCK, располагаемом в каталоге с таблицами. Теперь нет необходимости в использовании отдельного блокирующего файла для каждой таблицы, как это было в предыдущих версиях. Например, если три пользователя просматривают таблицу CUSTOMER.DB и один пользователь реструктуризирует таблицу ORDERS.DB, то файл PDOXUSRS.LCK будет иметь общую блокировку, указывающую на каждого из тех трех пользователей, просматривающих таблицу CUSTOMER.DB, и эксклюзивную блокировку на ORDERS.DB для пользователя, реструктуризирующего таблицу.
Протокол блокировки параллельности Paradox 4.0 (Locking Protocol Concurrency).
В многопользовательской среде протокол блокировки Paradox 4.0 поддерживает параллелизм, т.е. одновременное использование приложений, через файл PDOXUSRS.NET. Все пользователи, которые хотят иметь общий доступ к таблицам Paradox, должны иметь один и тот же путь к файлу PDOXUSRS.NET, но при этом логическая буква сетевого диска может отличаться. Для того, чтобы предотвратить доступ к файлам, расположенным в каталоге, предыдущим версиям, Paradox размещает PDOXUSRS.LCK и эксклюзивный файл PARADOX.LCK в каждом каталоге, где имеются доступные таблицы. Каждый пользователь, который хочет дать общий доступ к таблице в этом каталоге, должен подключить этот каталог с одним и тем же путем, с использованием одного логического сетевого диска и пути. Затем Paradox разместит всю информацию о блокировках для этой таблице в файле PDOXUSRS.LCK, уменьшая этим количество необходимых файлов.
Сетевой управляющий файл (Network Control File).
Сетевой управляющий файл Paradox, PDOXUSRS.NET, служит в качестве контрольной точки для всех блокирующих файлов, создаваемых Paradox. Net-файл содержит список пользователей, в настоящий момент использующих BDE, вместе со списком используемых ими таблиц. Каждый блокирующий файл ссылается на сетевой управляющий файл и содержит информацию о блокировках таблицы и пользователях, заблокировавших эти таблицы, поэтому все пользователи должны иметь один и тот же путь к сетевому управляющему файлу, но при этом логическая буква сетевого диска может отличаться.
Например, если вы используете том DATA на сервере SERVER_1, и сетевой управляющий файл расположен в каталоге \PDOXDATA, то все пользователи должны использовать путь \\SERVER_1\DATA:\PDOXDATA, тем не менее, любой пользователь может при этом использовать свою логическую букву сетевого диска. Если в вашей сети не пользуют тома, DATA должен быть корневым каталогом SERVER_1.
Если вы подключаете \\SERVER_1\DATA в корень диска P, то каждая система Paradox должна определять расположение PARADOX.NET как P:\PDOXDATA\. Тем не менее, другие пользователи могут подключить \\SERVER_1\DATA к корневому каталогу O и установить O:\PDOXDATA\ как местоположение сетевого управляющего файла.
Конфигурирование 16-битного Database Engine / IDAPI.CFG.
Файл конфигурации Database Engine хранит специфическую сетевую информацию, список псевдонимов баз дынных и другую информацию. Вы можете конфигурировать IDAPI с помощью программы конфигурации Database Engine, BDECFG.EXE, и устанавливать с помощью нее месторасположение сетевого управляющего файла. Также возможно добавление, удаление и изменение псевдонимов баз данных (включая информацию об используемом драйвере и типе псевдонима), каким способом IDAPI осуществляет общий доступ к локальным таблицам для программ, использующих протокол блокировки Paradox 4.0, а также некоторые особенности относительно таблиц и способа отображения данных.
Локальные 16-битные установки.
Файл WIN.INI содержит путь к файлу IDAPI.CFG, "рабочему" ("Working") каталогу Database Desktop и "частному" ("Private") каталогу Database Desktop. Для изменения этих значений необходимо загрузить файл WIN.INI в любой текстовый редактор и отредактировать его. Путь к файлу IDAPI.CFG описан в группе [IDAPI] как CONFIGFILE=<полный диск, путь и имя файла> или CONFIGFILE01=<полный диск, путь и имя файла>.
Месторасположение "рабочего" ("Working") и "частного" ("Private") каталога Database Desktop описано в группе [DBD] соответственно как WORKDIR=<полный диск и каталог> и PRIVDIR=<полный диск и каталог>.
Конфигурирование 32-битного Database Engine / IDAPI32.CFG.
Конфигурационный файл BDE хранит ту же информацию, что и конфигурационный файл Database Engine. Для конфигурирования IDAPI32.CFG используется утилита BDE Configuration, BDECFG32.EXE. Вдобавок к этому, вы можете сохранять информацию в регистрах, или сразу, и в регистрах, и в IDAPI32.CFG.
Локальные 32-битные установки.
В регистрах содержится путь к IDAPI32.CFG, к "рабочему" ("Working") и частному ("Private") каталогу. Месторасположение файла IDAPI32.CFG хранится в ключе HKEY_LOCAL_MACHINE\Software\Borland\Database Engine. Значение CONFIGFILE01 содержит данные типа <полный диск, путь и имя файла>.
Месторасположение каталогов BDE "Working" и "Private" хранится соответственно в ключах HKEY_CURRENT_USER\Software\Borland\DBD\7.0\Configuration\WorkDir и HKEY_CURRENT_USER\Software\Borland\DBD\7.0\Configuration\PrivDir. По умолчанию, данные для каждого каталога хранятся в виде <Полный диск и каталог>.
Доступ к таблицам Paradox.
BDE сначала пытается получить доступ к файлу PDOXUSRS.NET. Если файл PDOXUSRS.NET не найден, Paradox создает новый файл PDOXUSRS.NET и продолжает процедуру запуска. Если файл PDOXUSRS.NET присутствует, но владелец этого net-файла использует другой путь, т.е. подключил сервер иначе, возникает исключительная ситуация "Multiple net files in use" (Используются несколько net-файлов) и BDE прекращает свою работу. После того, как сеть успешно открыла эксклюзивную блокировку, PARADOX.LCK размещается во временном, частном каталоге. При невозможности установки блокировки, BDE прекращает свою работу. Причина неудачи может заключаться в том, что какой-то пользователь имеет в этом каталоге эксклюзивную блокировку, или же файлы блокировки используют различные net-файлы. После того, как каталог будет защищен от частного использования, общий файл PARADOX.LCK будет расположен в рабочем каталоге, и на этом процесс инициализации будет завершен.
©Drkb::02503.
Взято с http://delphiworld.narod.ru.
| Обратные вызовы BDE32 для получения статуса операций. |
Дополнительная документация, описывающая вызовы функций BDE, находится в файле BDE32.HLP (расположенном в каталоге, где установлен 32-битный IDAPI).
При создании функций обратного вызова BDE, BDE будет осуществлять "обратный вызов" функций вашего приложения, позволяя тем самым извещать ваше приложение о происходящих событиях, а в некоторых случаях передавать информацию обратно BDE.
BDE определяет несколько возвращаемых типов, которые могут быть установлены для обратного вызова: состояние больших пакетных операций.
Запросы для передачи информации вызывающему оператору.
Данный совет подробно описывает обратный вызов типа cbGENPROGRESS, позволяющий изменять полоску прогресса в соответствии с состоянием операции.
Чтобы это сделать, необходимо сперва вызвать функцию DbiGetCallBack(), возвращающую дескриптор обратного вызова, который мог быть уже установлен (с этими параметрами), и сохранить информацию в структуре данных. Затем установить свой обратный вызов, заменяя им любой установленный до этого.
При установке вашего обратного вызова вам понадобится передавать BDE указатель на структуру данных, содержащую информацию о предыдущем установленном обратном вызове, после чего, при выполнении вашей функции обратного вызова, вы можете воспользоваться оригинальным обратным вызовом (если он установлен).
BDE каждый раз возвращает вашему приложению сообщение, содержащее количество обработанных записей, или же процентное соотношение обработанных записей, также передаваемое в виде целого числа. Ваш код должен учитывать эту ситуацию. Если процентное поле в структуре обратного вызова больше чем -1, можно сделать вывод что передан процент и можно сразу обновить линейку прогресса. Если же это поле меньше нуля, обратный вызов получил текстовое сообщение, помещенное в поле szTMsg и содержащее количество обработанных записей. В этом случае вам понадобится осуществить грамматический разбор текстового сообщения, преобразовать остальные строки в целое, затем вычислить текущий процент обработанных записей, и только после этого изменить линейку прогресса.
Наконец, после осуществления операции с данными, вам необходимо "отрегистрировать" ваш обратный вызов, и вновь установить предыдущую функцию обратного вызова (если она существует).
Для следующего примера необходимо создать форму и расположить на ней две таблицы, компонент ProgressBar и кнопку.
Code: Unit Testbc1; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, DBGrids, DB, DBTables, ComCtrls; Type. TForm1 = class(TForm). Table1: TTable; BatchMove1: TBatchMove; Table2: TTable; Button1: TButton; ProgressBar1: TProgressBar; Procedure Button1Click(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. Uses Bde; {Здесь расположены Dbi Types и Procs}. {$R *.DFM}. {тип структуры данных для сохранения информации о предыдущем обратном вызове}. Type. TDbiCbInfo = record. EcbType: CBType; IClientData: longint; DataBuffLn: word; DataBuff: pCBPROGRESSDesc; DbiCbFn: pointer; End; Type. PDbiCbInfo = ^TDbiCbInfo; {Наша функция обратного вызова}. Function DbiCbFn(ecbType: CBType; IClientData: Longint; CbInfo: pointer): CBRType stdcall; Var. S: string; Begin. {Проверяем, является ли тип обратного вызова тем, который мы ожидаем}. If ecbType = cbGENPROGRESS then. Begin. {если iPercentDone меньше нуля, извлекаем число}. {обработанных записей из параметра szMsg}. If pCBPROGRESSDesc(cbInfo).iPercentDone < 0 then. Begin. S := pCBPROGRESSDesc(cbInfo).szMsg; Delete(s, 1, Pos(': ', s) + 1); {Вычислям процент выполненного и изменяем линейку прогресса}. Form1.ProgressBar1.Position := Round((StrToInt(s) / Form1.Table1.RecordCount) * 100); End. Else. Begin. {Устанавливаем линейку прогресса}. Form1.ProgressBar1.Position := PCBPROGRESSDesc(cbInfo).iPercentDone; End; End; {существовал ли предыдущий зарегистрированный обратный вызов?}. {если так - осуществляем вызов и возвращаемся}. If PDbiCbInfo(iClientData)^.DbiCbFn <> nil then. DbiCbFn := PfDBICallBack(PDbiCbInfo(iClientData)^.DbiCbFn). (ecbType, PDbiCbInfo(iClientData)^.iClientData, CbInfo). Else. DbiCbFn := cbrCONTINUE; End; Procedure TForm1.Button1Click(Sender: TObject); Var. CbDataBuff: CBPROGRESSDesc; {Структура DBi}. {структура данных должна хранить информацию о предыдущем обратном вызове}. OldDbiCbInfo: TDbiCbInfo; Begin. {Убедимся в том, что перемещаемая таблица открыта}. Table1.Open; {Убедимся в том, что таблица-приемник закрыта}. Table2.Close; {получаем информацию о любом установленном обратном вызове}. DbiGetCallBack(Table2.Handle, CbGENPROGRESS, @OldDbiCbInfo.iClientData, @OldDbiCbInfo.DataBuffLn, @OldDbiCbInfo.DataBuff, PfDBICallBack(OldDbiCbInfo.DbiCbFn)); {регистрируем наш обратный вызов}. DbiRegisterCallBack(Table2.Handle, CbGENPROGRESS, Longint(@OldDbiCbInfo), SizeOf(cbDataBuff), @cbDataBuff, @DbiCbFn); Form1.ProgressBar1.Position := 0; BatchMove1.Execute; {если предыдущий обратный вызов существовал - вновь устанавливаем его,}. {в противном случае "отрегистрируем" наш обратный вызов}. If OldDbiCbInfo.DbiCbFn <> nil then. DbiRegisterCallBack(Table2.Handle, CbGENPROGRESS, OldDbiCbInfo.iClientData, OldDbiCbInfo.DataBuffLn, OldDbiCbInfo.DataBuff, OldDbiCbInfo.DbiCbFn). Else. DbiRegisterCallBack(Table2.Handle, CbGENPROGRESS, Longint(@OldDbiCbInfo), SizeOf(cbDataBuff), @cbDataBuff, Nil); {Показываем наш успех!}. Table2.Open; End; End.
| Как установить минимальный размер окна? |
Необходимо объявить обработчик события для WM_GETMINMAXINFO:
Code: ... Private. {©Drkb v.3(2007): www.drkb.ru}. Procedure WMGetMinMaxInfo(var Message : TWMGetMinMaxInfo); Message WM_GETMINMAXINFO; А вот как выглядит сам обработчик: Procedure TForm1.WMGetMinMaxInfo(var Message : TWMGetMinMaxInfo); Begin. Message.MinMaxInfo^.ptMinTrackSize := Point(Width, Height); Message.MinMaxInfo^.ptMaxTrackSize := Point(Width, Height); End;
| BMP ---> DIB. |
Если файл хранится в формате BMP, как мне преобразовать его в DIB и как затем отобразить?
Это не тривиально, но помочь нам смогут функции GetDIBSizes и GetDIB из модуля GRAPHICS.PAS. Приведу две процедуры: одну для создания DIB из TBitmap и вторую для его освобождения:
Code: { Преобразование TBitmap в DIB }. Procedure BitmapToDIB(Bitmap: TBitmap; Var BitmapInfo: PBitmapInfo; Var InfoSize: integer; Var Bits: pointer; Var BitsSize: longint); Begin. BitmapInfo := nil; InfoSize := 0; Bits := nil; BitsSize := 0; If not Bitmap.Empty then. Try. GetDIBSizes(Bitmap.Handle, InfoSize, BitsSize); GetMem(BitmapInfo, InfoSize); Bits := GlobalAllocPtr(GMEM_MOVEABLE, BitsSize); If Bits = nil then. Raise. EOutOfMemory.Create('Не хватает памяти для пикселей изображения'); If not GetDIB(Bitmap.Handle, Bitmap.Palette, BitmapInfo^, Bits^) then. Raise Exception.Create('Не могу создать DIB'); Except. If BitmapInfo <> nil then. FreeMem(BitmapInfo, InfoSize); If Bits <> nil then. GlobalFreePtr(Bits); BitmapInfo := nil; Bits := nil; Raise; End; End; { используйте FreeDIB для освобождения информации об изображении и битовых указателей }. Procedure FreeDIB(BitmapInfo: PBitmapInfo; InfoSize: integer; Bits: pointer; BitsSize: longint); Begin. If BitmapInfo <> nil then. FreeMem(BitmapInfo, InfoSize); If Bits <> nil then. GlobalFreePtr(Bits); End;
| Вычисление интеграла. |
Вычисление интеграла с заданной точностью алгоритмом Симпсона.
Code: // (c) Copydown 2002, all left reserved. http://world.fpm.kubsu.ru. {$APPTYPE CONSOLE}. {$F+} {разрешение передачи функций, как параметров}. Type FunctionType = function(x: real): real; {интегрируемая функция}. Function f(x: real): real; begin f := x end; {интегрирование от a до b функции f с точностью e}. Function IntegralSimpson(a, b: real; f: FunctionType; e: real): real; Var. H, x, s, s1, s2, s3, sign: real; Begin. If (a = b) then. Begin. IntegralSimpson := 0; exit. End; If (a > b) then. Begin. X := a; a := b; b := x; sign := -1. End. Else sign:=1; H := b - a; s := f(a) + f(b); s2 := s; Repeat. S3 := s2; h := h/2; s1 := 0; x := a + h; Repeat. S1 := s1 + 2*f(x); x := x + 2*h; Until (not(x < b)); S := s + s1; s2 := (s + s1)*h/3; x := abs(s3 - s2)/15. Until (not(x > e)); IntegralSimpson := s2*sign; End; Begin. {вывод результата интегрирования от 0 до 1 функции f с точностью 0.001}. Writeln(IntegralSimpson(0, 1, f, 0.001)); Writeln; writeln('Press Enter'); readln; End.
Вызов c-шной функции с переменным числом параметров.
Автор: Владимир Переплетчик.
Комментарий к статье по поводу wsprintf.
Сама по себе статья вызывает мало интереса, кроме того, что поднята интересная проблема - вызов с-шной функции с переменным числом параметров. В ответах с использованием массивов вообще, IMHO, ошибка - на стек попадет адрес массива, а в с это совсем не то. Но решение проблемы существует, правда надо ручками повозиться со стеком. Приводимая ниже функция на скорую руку переделывается из работающей в реальном проекте похожего буфера с-паскаль, но там функция в dll имеет тип вызова cdecl и другие обязательные параметры, в связи с чем возможны "опечатки".
Code: // Пишем функцию-переходник, маскируя с-шные "..." паскалевским. // array of const. Function sprintf(out, fmt: Pchar; args: array of const): Integer; Var. I: Integer; BufPtr: Pchar; S: string; Buf: array[0..1024] of char; Begin. BufPtr := buf; // Формируем буфер параметров. Можно, конечно, и прямо на стеке, // но головной боли слишком много - проще так. For I := low(Par) to High(Par) do. Case Par[I].VType of. VtInteger: // Здесь все просто - 4 байта на стек. Begin. Integer(Pointer(BufPtr)^) := Par[I].VInteger; Inc(BufPtr, 4); End; VtExtended: // Здесь хуже - слова надо местами поменять :-(( Begin. Integer(Pointer(BufPtr)^) := Integer(Pointer(Pchar(Par[I].VExtended) + 4)^); Inc(BufPtr, 4); Integer(Pointer(BufPtr)^) := Integer(Pointer(Par[I].VExtended)^); Inc(BufPtr, 4); End; VtPChar: // Здесь тоже все хорошо - 4 байта. Begin. Pointer(Pointer(BufPtr)^) := Par[I].VPchar; Inc(BufPtr, 4); End; VtString, vtAnsiString: // А здесь во избежание чудес надо. // копию строки снять. Begin. If Par[I].VType = vtString then. S := Par[I].VString^ Else. S := string(Par[I].VAnsiString); Pointer(Pointer(BufPtr)^ := StrPCopy(StrAlloc(Length(S) + 1), S); Inc(BufPtr, 4); End; End; // Поддержку других типов доделывать самостоятельно, // вооружившись толковым пособием по с и ассемблеру. I := (BufPtr - buf) div 4; // Сколько раз на стек слово положить. Asm. Push dword ptr [out]. Push dword ptr [fmt]. Mov ecx, dword ptr [i]. Mov eax, dword ptr [buf] // stdcall - параметры в прямом. // порядке. @@1: Push dword ptr [eax]. Add eax, 4. Loop @@1. Call [wsprintf]. Mov dword ptr [Result], eax // Сохранить результат. Mov eax, dword ptr [i] // Привести в порядок стек. Shl eax, 2. Add eax, 8. Add esp, eax. End; // Почистить строки. For I := low(Par) to High(Par) do. Case Par[I].VType of. VtInteger: Inc(BufPtr, 4); VtExtended: Inc(BufPtr, 8); VtPChar: Inc(BufPtr, 4); VtString, vtAnsiString: Begin. StrDispose(PChar(PPointer(BufPtr)^)); Inc(BufPtr, 4); End; End; End;
В таком виде методика уже имеет смысл. Изменения при типах вызова cdecl / pascal понятны.
©Drkb::00292.
Взято с http://delphiworld.narod.ru.
| Вызов Delphi DLL из MS Visual C++ |
Во-первых, Вам необходимо объявить все экспортируемые в Delphi DLL функции с ключевыми словами export; stdcall;
Во-вторых, файл заголовка VC++ должен объявить все функции как тип __declspec(dllexport) __stdcall (применяйте двойное подчеркивание в секции объявления прототипа функции extern "C" { ... }. (вместо этого можно также использовать __declspec(dllimport)...). Для примера:
Code: Extern "C" { Int __declspec(dllexport) __stdcall plusone(int); }.
В-третьих, в VC++ компилятор настраивается на "украшающее" имена функций __stcall, так что Ваша Delphi DLL соответственно должна экспортировать эти функции. Для этого необходимо модифицировать файл Delphi 2.0 .DPR для Вашего DLL, модифицируя имена всех функций, прописанных в разделе экспорта. Для примера, если Вы экспортируете функцию function plusone (intval : Integer), Вам необходимо включить следующую строку в раздел экспорта .DPR-файла:
Code: Plusone name 'plusone@4'
Число, следующее за символом @, является общей длиной в байтах всех функциональных аргументов. Самый простой путь для обнаружения неправильных значений - попытаться слинковать Вашу VC++ программу и посмотреть на наличие возможной ошибки компоновщика "unresolved external".
И, наконец, Вы можете легко создать библиотеку импорта, используя утилиту LIB из поставки VC++. Для этого необходимо вручную (!!) создать .DEF-файл для Вашей DLL с секцией экспорта, перечисляющей имена и/или порядковые номера всех экспортируемых DLL функций. Формат .DEF-файла очень прост:
Code: Library MYLIB. Description 'Моя собственная DLL' Exports. Plusone@4.
Затем запускаете LIB из командной строки DOS/Win95, и в качестве параметра подставляете имя .DEF-файла. Например, LIB /DEF:MYDLL.DEF. Наконец, через диалог Build|Settings|Linker Вы информируете VC++ о полученном .LIB-файле.
Вот пример кода:
Code: *******MYDLLMU.PAS. Unit MyDLLMU; Interface. Function plusone(val : Integer) : Integer; export; stdcall; Procedure ChangeString(AString : PChar); export; stdcall; Implementation. Uses. Dialogs, SysUtils; Function plusone(val : Integer) : Integer; Begin. Result := val + 1; End; Procedure ChangeString(AString : PChar); Begin. If AString = 'Здравствуй' then. StrPCopy(AString, 'Мир'); End; End.
MYDLL.DPR.
Code: Library mydll; { Существенное замечание об управлении памятью в DLL: Если DLL экспортирует функции со. Строковыми параметрами или возвращающие строковые значения, модуль ShareMem надо. Указывать в разделе Uses библиотеки и проекта первым. Это касается любых строк, Передаваемых как в DLL, так и из нее, даже если они размещаются внутри записей или. Объектов. Модуль ShareMem служит интерфейсом менеджера разделяемой памяти. DELPHIMM.DLL, который должен разворачиваться одновременно с данной DLL. Чтобы избежать. Применения DELPHIMM.DLL, строковую информацию можно передавать с помощью параметров. Типа PChar или ShortString. }. Uses. SysUtils, Classes, MyDLLMU in 'MyDLLMU.pas'; Exports. Plusone name 'plusone@4', ChangeString name 'ChangeString@4'; Begin. End.
| Ловим баги или почему программы допускают недопустимые операции. |
Ошибки - неизбежное зло программирования. Видимо пока трудно даже представить средство с помощью которого можно избавится от них. Человеку, которые выдумает это чудодейственное лекарство, благодарные потомки-программисты, несомненно, воздвигнут памятник. Пока же остается лишь заниматься обычным делом: ловлей багов.
"Нарушение Доступа" - фраза, которую пользователи видят, когда приложение делает попытки обратиться к памяти, которая не обозначена для их использования - и как следствие происходит сбой в работе программы:
Access violation at address <HEX_value>
In module <Application.Exe>.
Read of address <HEX_value_2>
Ситуация при которой Windows давала бы полную свободу программам - записывай данные куда хочешь, скорее всего бы привела к разноголосице программ и полной потери управления над компьютером. Но этого не происходит - Windows стоит на страже "границ памяти" и отслеживает недопустимые операции. Если сама она справиться с ними не в силах - происходит запуск утилиты Dr. Watson, которая записывает данные о возникшей ошибки, а сама программа закрывается.
Известно что, при программирование, особенно крупных программных продуктов, уследить за всеми процессами в коде невозможно, да и нет необходимости. Использование сторонних компонентов и библиотек только усложняет дело. Именно поэтому программисты Delphi, порой и сталкиваются со "своенравными" программами, которые то и дело норовят "сбросить пользователя". Итак, давайте рассмотрим некоторые вопросы, связанные с корректной средой программирования, так и непосредственно проблем написания кода, которые ведут к возникновению ошибок типа "ошибка доступа" (AVS) и очертим наиболее известные пути их исправления.
Мы можем поделить AVS, с которыми сталкиваются при разработке в Delphi на два основных типах: ошибки при выполнения и некорректная разработка проекта, что вызывает ошибки при работе программы.
Ошибки возникают при старте и закрытии Delphi или формировании проекта. Причиной могут являться сбои в "железе" компьютера.
Эти ошибки могут быть вызваны различными источниками, включая систему BIOS, операционную систему или аппаратные подпрограммы драйверов. Некоторые видео-, звуковые или сетевые платы могут фактически вызывать подобного рода ошибки в Delphi. Для решения подобных аппаратных проблем можно предпринять последовательность неких "стандартных" ходов:
Проверить, что не имеется никаких конфликтов между установленными устройствами, устранить обнаруженные конфликты;
Попробовать слегка уменьшить "аппетиты" видеодрайвера - поставить меньшее разрешение;
В случае если у вас двухпроцесорная система обеспечить равное изменение шага для каждого процессора;
И в конце концов просто попытаться заменить драйвера на более свежие.
Но помимо чисто железных проблем - большую головную боль могут вызвать ошибки в работе программного обеспечения. Особенно это касается непосредственно операционной системы. Зачастую Windows терпит крах спонтанно. Вот рекомендации которые помогут вам создать более устойчивую среду программирования:
Хотя Windows 9X популярная система, разработку лучше проводить в Windows NT или Windows 2000 - это более устойчивые операционные системы. Естественно при переходе на них придется отказаться от некоторых благ семейства Windows 95/98/Me - в частности не все программы адоптированы для Windows NT/2000. Зато вы получите более надежную и стабильную систему.
Не забывайте о том, как важно всегда иметь под рукой свежие версии компонентов для Delphi и дополнительных библиотек. В отличие от Windows создатели данных пакетов стараются от версии к версии уменьшать количество ошибок.
Следите за тем, что бы устанавливаемые компоненты были предназначены непосредственно для вашей версии Delphi. Попробуйте деинсталлировать чужеродные компоненты один за другим (или пакет за пакетом) пока проблема не будет устранена.
Контролируйте все программные продукты установленные на вашей машине и деинсталлируйте те из них, которые сбоят. Фаворитами AV среди них являются шароварные утилиты и программы и бета версии программных продуктов.
Все вышеперечисленное в основном не касалось самого процесса программирования и в малой степени зависит от разработчика. Теперь же обратимся к теме, как не допустить при разработки программного продукта ситуации при которой, он сам будет являться причиной ошибки.
Вы могли бы рассмотреть компилирование вашего приложения с директивой {$D}, данная директива компилятора может создавать файлы карты (файлы с расширением map, которые можно найти в том же каталоге, что и файлы проекта), которые могут послужить большой справкой в локализации источника подобных ошибок. Для лучшего "контроля" за своим приложением, компилируйте его с директивой {$D}. Таким образом, вы заставите Delphi генерировать информацию для отладки, которая может послужить подспорьем при выявление возникающих ошибок.
Следующая позиция в Project Options - Linker & Compiler позволяет вам, определить все для последующей отладки. Лучше всего, если помимо самого выполняемого кода будет доступна и отладочная информация - это поможет при поиске ошибок. Отладочная информация увеличивает размер файла и занимает дополнительную память при компилировании программ, но непосредственно на размер или быстродействие выполняемой программы не влияет. Включение опций отладочной информации и файла карты дают детальную информацию только, если вы компилируете программу с директивой {$D+}.
Эта информация состоит из таблицы номеров строк для каждой процедуры, которая отображает адреса объектных кодов в номера строк исходного текста. Директива $D обычно используется совместно с другой директивой - $L, что позволяет или запрещает генерацию информации о локальных символах для отладки.
Таким образом вы без труда сможете найти точный адрес той подпрограммы, которая была ответственна за ошибку. Одна из наиболее общих причин ошибок выполнения - использование объекта, который еще не был создан. Если второй адрес при выдачи ошибки - FFFFFFF (или 0000000) Вы можете почти утверждать, что было обращение к объекту, который еще не был создан. Например, вызов метода формы, которая не была создана.
Code: Procedure TfrMain.OnCreate(Sender: TObject); Var. BadForm: TBadForm; Begin. BadForm.Refresh; // причина ошибки. End;
| Как изменить цвет TButton? |
Code: { You cannot change the color of a standard TButton, Since the windows button control always paints itself with the. Button color defined in the control panel. But you can derive derive a new component from TButton and handle. The and drawing behaviour there. } Unit ColorButton; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls; Type. TDrawButtonEvent = procedure(Control: TWinControl; Rect: TRect; State: TOwnerDrawState) of object; TColorButton = class(TButton). Private. FCanvas: TCanvas; IsFocused: Boolean; FOnDrawButton: TDrawButtonEvent; Protected. Procedure CreateParams(var Params: TCreateParams); override; Procedure SetButtonStyle(ADefault: Boolean); override; Procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED; Procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED; Procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM; Procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM; Procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK; Procedure DrawButton(Rect: TRect; State: UINT); Public. Constructor Create(AOwner: TComponent); override; Destructor Destroy; override; Property Canvas: TCanvas read FCanvas; Published. Property OnDrawButton: TDrawButtonEvent read FOnDrawButton write FOnDrawButton; Property Color; End; Procedure Register; Implementation. Procedure Register; Begin. RegisterComponents('Samples', [TColorButton]); End; Constructor TColorButton.Create(AOwner: TComponent); Begin. Inherited Create(AOwner); FCanvas := TCanvas.Create; End; Destructor TColorButton.Destroy; Begin. Inherited Destroy; FCanvas.Free; End; Procedure TColorButton.CreateParams(var Params: TCreateParams); Begin. Inherited CreateParams(Params); With Params do Style := Style or BS_OWNERDRAW; End; Procedure TColorButton.SetButtonStyle(ADefault: Boolean); Begin. If ADefault <> IsFocused then. Begin. IsFocused := ADefault; Refresh; End; End; Procedure TColorButton.CNMeasureItem(var Message: TWMMeasureItem); Begin. With Message.MeasureItemStruct^ do. Begin. ItemWidth := Width; ItemHeight := Height; End; End; Procedure TColorButton.CNDrawItem(var Message: TWMDrawItem); Var. SaveIndex: Integer; Begin. With Message.DrawItemStruct^ do. Begin. SaveIndex := SaveDC(hDC); FCanvas.Lock; Try. FCanvas.Handle := hDC; FCanvas.Font := Font; FCanvas.Brush := Brush; DrawButton(rcItem, itemState); Finally. FCanvas.Handle := 0; FCanvas.Unlock; RestoreDC(hDC, SaveIndex); End; End; Message.Result := 1; End; Procedure TColorButton.CMEnabledChanged(var Message: TMessage); Begin. Inherited; Invalidate; End; Procedure TColorButton.CMFontChanged(var Message: TMessage); Begin. Inherited; Invalidate; End; Procedure TColorButton.WMLButtonDblClk(var Message: TWMLButtonDblClk); Begin. Perform(WM_LBUTTONDOWN, Message.Keys, Longint(Message.Pos)); End; Procedure TColorButton.DrawButton(Rect: TRect; State: UINT); Var. Flags, OldMode: Longint; IsDown, IsDefault, IsDisabled: Boolean; OldColor: TColor; OrgRect: TRect; Begin. OrgRect := Rect; Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT; IsDown := State and ODS_SELECTED <> 0; IsDefault := State and ODS_FOCUS <> 0; IsDisabled := State and ODS_DISABLED <> 0; If IsDown then Flags := Flags or DFCS_PUSHED; If IsDisabled then Flags := Flags or DFCS_INACTIVE; If IsFocused or IsDefault then. Begin. FCanvas.Pen.Color := clWindowFrame; FCanvas.Pen.Width := 1; FCanvas.Brush.Style := bsClear; FCanvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom); InflateRect(Rect, - 1, - 1); End; If IsDown then. Begin. FCanvas.Pen.Color := clBtnShadow; FCanvas.Pen.Width := 1; FCanvas.Brush.Color := clBtnFace; FCanvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom); InflateRect(Rect, - 1, - 1); End. Else. DrawFrameControl(FCanvas.Handle, Rect, DFC_BUTTON, Flags); If IsDown then OffsetRect(Rect, 1, 1); OldColor := FCanvas.Brush.Color; FCanvas.Brush.Color := Color; FCanvas.FillRect(Rect); FCanvas.Brush.Color := OldColor; OldMode := SetBkMode(FCanvas.Handle, TRANSPARENT); FCanvas.Font.Color := clBtnText; If IsDisabled then. DrawState(FCanvas.Handle, FCanvas.Brush.Handle, nil, Integer(Caption), 0, ((Rect.Right - Rect.Left) - FCanvas.TextWidth(Caption)) div 2, ((Rect.Bottom - Rect.Top) - FCanvas.TextHeight(Caption)) div 2, 0, 0, DST_TEXT or DSS_DISABLED). Else. DrawText(FCanvas.Handle, PChar(Caption), - 1, Rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER); SetBkMode(FCanvas.Handle, OldMode); If Assigned(FOnDrawButton) then. FOnDrawButton(Self, Rect, TOwnerDrawState(LongRec(State).Lo)); If IsFocused and IsDefault then. Begin. Rect := OrgRect; InflateRect(Rect, - 4, - 4); FCanvas.Pen.Color := clWindowFrame; FCanvas.Brush.Color := clBtnFace; DrawFocusRect(FCanvas.Handle, Rect); End; End; End.
| Как поменять иконку и строку в заголовке консольного окна? |
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. H: HWND; AIcon: TIcon; Begin. AllocConsole; SetConsoleTitle(PChar('Console Title')); Sleep(0); H := FindWindow(nil, PChar('Console Title')); AIcon := TIcon.Create; ImageList1.GetIcon(0, AIcon); SendMessage(h, WM_SETICON, 1, AIcon.Handle); AIcon.Free; End;
| Как поменять приоритет моего приложения? |
Code: Procedure TForm1.Button1Click(Sender: TObject); Var. ProcessID: DWORD; ProcessHandle: THandle; ThreadHandle: THandle; Begin. ProcessID := GetCurrentProcessID; ProcessHandle := OpenProcess(PROCESS_SET_INFORMATION, False, ProcessID); SetPriorityClass(ProcessHandle, REALTIME_PRIORITY_CLASS); ThreadHandle := GetCurrentThread; SetThreadPriority(ThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); End;
| Изменить громкость. |
Эта программа увеличивает громкость выбранного канала на 1000:
Code: Uses MMSystem; Procedure TForm1.Button1Click(Sender: TObject); Var. Vol: longint; LVol, RVol: integer; Begin. AuxGetVolume(ListBox1.ItemIndex, @Vol); LVol := Vol shr 16; If LVol < MaxWord - 1000 then. LVol := LVol + 1000. Else. LVol := MaxWord; RVol := (Vol shl 16) shr 16; If RVol < MaxWord - 1000 then. RVol := RVol + 1000. Else. RVol := MaxWord; AuxSetVolume(ListBox1.ItemIndex, LVol shl 16 + RVol); End; Procedure TForm1.FormCreate(Sender: TObject); Var. I: integer; Cap: TAuxCaps; Begin. For i := 0 to auxGetNumDevs - 1 do. Begin. AuxGetDevCaps(i, Addr(cap), SizeOf(cap)); ListBox1.Items.Add(cap.szPname). End; End;
©Drkb::03610.
Code: Procedure SetVolume(X: Word); Var. IErr: Integer; I: integer; A: TAuxCaps; Begin. For i := 0 to auxGetNumDevs do. Begin. AuxGetDevCaps(i, Addr(a), SizeOf(a)); If a.wTechnology = AUXCAPS_CDAUDIO then. Break; End; // Устанавливаем одинаковую громкость для левого и правого каналов. // VOLUME := LEFT*$10000 + RIGHT*1. IErr := auxSetVolume(i, (X * $10001)); If (iErr‹›0) then. ShowMessage('No audio devices are available!'); End; Function GetVolume: Word; Var. IErr: Integer; I: integer; A: TAuxCaps; Vol: word; Begin. For i := 0 to auxGetNumDevs do. Begin. AuxGetDevCaps(i, Addr(a), SizeOf(a)); If a.wTechnology = AUXCAPS_CDAUDIO then. Break; End; IErr := auxGetVolume(i, addr(vol)); GetVolume := vol; If (iErr‹›0) then. ShowMessage('No audio devices are available!'); End;
©Drkb::03611.
Code: Unit Volumes; Interface. Uses. Windows, Messages, Classes, ExtCtrls, ComCtrls, MMSystem; Const. CDVolume = 0; WaveVolume = 1; MidiVolume = 2; Type. TVolumeControl = class(TComponent). Private. FDevices : array[0..2] of Integer; FTrackBars : array[0..2] of TTrackBar; FTimer : TTimer; Function GetInterval: Integer; Procedure SetInterval(AInterval: Integer); Function GetVolume(AIndex: Integer): Byte; Procedure SetVolume(AIndex: Integer; aVolume: Byte); Procedure InitVolume; Procedure SetTrackBar(AIndex: Integer; ATrackBar: TTrackBar); { Private declarations }. Procedure Update(Sender: TObject); Procedure Changed(Sender: TObject); Protected. { Protected declarations }. Procedure Notification(AComponent: TComponent; AOperation: TOperation); override; Public. { Public declarations }. Constructor Create(AOwner: TComponent); override; Destructor Destroy; override; Published. { Published declarations }. Property Interval: Integer read GetInterval write SetInterval default. 500; Property CDVolume: Byte index 0 read GetVolume write SetVolume stored. False; Property CDTrackBar: TTrackBar index 0 read FTrackBars[0] write. SetTrackBar; Property WaveVolume: Byte index 1 read GetVolume write SetVolume. Stored False; Property WaveTrackBar: TTrackBar index 1 read FTrackBars[1] write. SetTrackBar; Property MidiVolume: Byte index 2 read GetVolume write SetVolume. Stored False; Property MidiTrackBar: TTrackBar index 2 read FTrackBars[2] write. SetTrackBar; End; Procedure Register; Implementation. Procedure Register; Begin. RegisterComponents('Any', [TVolumeControl]); End; Type. TVolumeRec = record. Case Integer of. 0: (LongVolume: Longint); 1: (LeftVolume, RightVolume : Word); End; Function TVolumeControl.GetInterval: Integer; Begin. Result := FTimer.Interval; End; Procedure TVolumeControl.SetInterval(AInterval: Integer); Begin. FTimer.Interval := AInterval; End; Function TVolumeControl.GetVolume(AIndex: Integer): Byte; Var Vol: TVolumeRec; Begin. Vol.LongVolume := 0; If FDevices[AIndex] < > -1 then. Case AIndex of. 0: auxGetVolume(FDevices[AIndex], @Vol.LongVolume); 1: waveOutGetVolume(FDevices[AIndex], @Vol.LongVolume); 2: midiOutGetVolume(FDevices[AIndex], @Vol.LongVolume); End; Result := (Vol.LeftVolume + Vol.RightVolume) shr 9; End; Procedure TVolumeControl.SetVolume(aIndex: Integer; aVolume: Byte); Var Vol: TVolumeRec; Begin. If FDevices[AIndex] < > -1 then. Begin. Vol.LeftVolume := aVolume shl 8; Vol.RightVolume := Vol.LeftVolume; Case AIndex of. 0: auxSetVolume(FDevices[AIndex], Vol.LongVolume); 1: waveOutSetVolume(FDevices[AIndex], Vol.LongVolume); 2: midiOutSetVolume(FDevices[AIndex], Vol.LongVolume); End; End; End; Procedure TVolumeControl.SetTrackBar(AIndex: Integer; ATrackBar: TTrackBar); Begin. If ATrackBar < > FTrackBars[AIndex] then. Begin. FTrackBars[AIndex] := ATrackBar; Update(Self); End; End; AOperation: TOperation); Var I: Integer; Begin. Inherited Notification(AComponent, AOperation); If (AOperation = opRemove) then. For I := 0 to 2 do if (AComponent = FTrackBars[I]). Then FTrackBars[I] := Nil; End; Procedure TVolumeControl.Update(Sender: TObject); Var I: Integer; Begin. For I := 0 to 2 do. If Assigned(FTrackBars[I]) then. With FTrackBars[I] do. Begin. Min := 0; Max := 255; If Orientation = trVertical. Then Position := 255 - GetVolume(I). Else Position := GetVolume(I); OnChange := Self.Changed; End; End; Constructor TVolumeControl.Create(AOwner: TComponent); Begin. Inherited Create(AOwner); FTimer := TTimer.Create(Self); FTimer.OnTimer := Update; FTimer.Interval := 500; InitVolume; End; Destructor TVolumeControl.Destroy; Var I: Integer; Begin. FTimer.Free; For I := 0 to 2 do. If Assigned(FTrackBars[I]) then. FTrackBars[I].OnChange := Nil; Inherited Destroy; End; Procedure TVolumeControl.Changed(Sender: TObject); Var I: Integer; Begin. For I := 0 to 2 do. If Sender = FTrackBars[I] then. With FTrackBars[I] do. Begin. If Orientation = trVertical. Then SetVolume(I, 255 - Position). Else SetVolume(I, Position); End; End; Procedure TVolumeControl.InitVolume; Var AuxCaps : TAuxCaps; WaveOutCaps : TWaveOutCaps; MidiOutCaps : TMidiOutCaps; I,J : Integer; Begin. FDevices[0] := -1; For I := 0 to auxGetNumDevs - 1 do. Begin. AuxGetDevCaps(I, @AuxCaps, SizeOf(AuxCaps)); If (AuxCaps.dwSupport and AUXCAPS_VOLUME) < > 0 then. Begin. FTimer.Enabled := True; FDevices[0] := I; Break; End; End; FDevices[1] := -1; For I := 0 to waveOutGetNumDevs - 1 do. Begin. WaveOutGetDevCaps(I, @WaveOutCaps, SizeOf(WaveOutCaps)); If (WaveOutCaps.dwSupport and WAVECAPS_VOLUME) < > 0 then. Begin. FTimer.Enabled := True; FDevices[1] := I; Break; End; End; FDevices[2] := -1; For I := 0 to midiOutGetNumDevs - 1 do. Begin. MidiOutGetDevCaps(I, @MidiOutCaps, SizeOf(MidiOutCaps)); If (MidiOutCaps.dwSupport and MIDICAPS_VOLUME) < > 0 then. Begin. FTimer.Enabled := True; FDevices[2] := I; Break; End; End; End; End.
©Drkb::03612.
Выставь на форму 2 тракбара и двигай их. Если у тебя звучит музыка, ты должен услышать изменения громкости правого и левого каналов.
Code: Procedure TForm1.TrackBar1Change(Sender: TObject); Var. S: dword; A,b: word; H: hWnd; Begin. A:=trackbar1.position; B:=trackbar2.position; S:=(a shl 16) or b; WaveOutSetVolume(h,s); End;
| Как создавать не квадратные формы и контролы? |
Всё, что нам нужно, это HRGN и дескриптор (handle) элемента управления. SetWindowRgn имеет три параметра: дескриптор окна, которое будем менять, дескритор региона и булевый (boolean) параметр, который указывает - перерисовывать или нет после изменения. Как только у нас есть дескриптор и регион, то можно вызвать SetWindowRgn(Handle, Region, True) и вуаля!
Здесь приведён пример использования функции BitmapToRgn (описанной в примере Как создать регион(HRNG) по маске).
Заметьте, что Вы не должны освобождать регион при помощи DeleteObject, так как после вызова SetWindowRgn владельцем региона становится операционная система.
Code: Var. MaskBmp: TBitmap; Begin. MaskBmp := TBitmap.Create; Try. MaskBmp.LoadFromFile('FormShape.bmp'); Height := MaskBmp.Height; Width := MaskBmp.Width; // ОС владеет регионом, после вызова SetWindowRgn. SetWindowRgn(Self.Handle, BitmapToRgn(MaskBmp), True); Finally. MaskBmp.Free; End; End;
| Иерархия классов. |
Следующий модуль строит дерево классов.
Code: Unit InfoForm; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, StdCtrls, ExtCtrls, Buttons, Clipbrd, Comctrls, Db, Dbcgrids, Dbctrls, Dbgrids, Dblookup, Dbtables, Dialogs, Filectrl, Grids, Mask, Menus, Mplayer, Oleconst, Olectnrs, Olectrls, Outline, Tabnotbk, Tabs, IniFiles, Printers, Registry, DsgnIntf, Provider, BdeProv, DBClient, ComObj, ActiveX, DDEMan, IBCtrls, Math, Nsapi, Isapi, ScktComp, Axctrls, Calendar, CgiApp, checklst, ColorGrd, ComServ, syncobjs, httpapp, dbweb, DirOutln, Gauges, DsIntf, ToolIntf, EditINtf, ExptIntf, VirtIntf, Istreams, isapiapp, dblogdlg, masks, ExtDlgs, Spin; Type. TForm1 = class(TForm). ListBox1: TListBox; Label1: TLabel; Edit1: TEdit; Label2: TLabel; Panel1: TPanel; TreeView1: TTreeView; ProgressBar1: TProgressBar; Button1: TButton; Button2: TButton; Procedure Button1Click(Sender: TObject); Procedure TreeView1Change(Sender: TObject; Node: TTreeNode); Procedure Button2Click(Sender: TObject); Private. Function AddClass (NewClass: TClass): TTreeNode; Function GetNode (BaseClass: TClass): TTreeNode; Public. { Public declarations }. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Type. TClassArray = array [1..498] of TClass; Const. ClassArray: TClassArray = ( EAbort, EAccessViolation, EAssertionFailed, EBitsError, EClassNotFound, EComponentError, EControlC, EConvertError, EDatabaseError, EDateTimeError, EDBClient, EDBEditError, EDBEngineError, EDivByZero, EExternalException, EFCreateError, EFilerError, EFOpenError, EIBError, EInOutError, EIntError, EIntfCastError, EIntOverflow, EInvalidArgument, EInvalidCast, EInvalidContainer, EInvalidGraphic, EInvalidGraphicOperation, EInvalidGridOperation, EInvalidImage, EInvalidInsert, EInvalidOp, EInvalidOperation, EInvalidPointer, EListError, EMathError, EMCIDeviceError, EMenuError, EMethodNotFound, ENoResultSet, EOleCtrlError, EOleError, EOleException, EOleException, EOleSysError, EOutlineError, EOutOfMemory, EOutOfResources, EOverflow, EPackageError, EParserError, EPrinter, EPrivilege, EPropertyError, EPropReadOnly, EPropWriteOnly, ERangeError, EReadError, EReconcileError, ERegistryException, EResNotFound, ESocketError, EStackOverflow, EStreamError, EStringListError, EThread, ETreeViewError, EUnderflow, EUpdateError, EVariantError, EWin32Error, EWriteError, Exception, EZeroDivide, OutlineError, TActiveForm, TActiveFormControl, TActiveFormFactory, TActiveXControl, TActiveXControlFactory, TActiveXPropertyPage, TActiveXPropertyPageFactory, TAdapterNotifier, TAggregatedObject, TAnimate, TApplication, TAutoIncField, TAutoIntfObject, TAutoObject, TAutoObjectFactory, TBatchMove, TBCDField, TBDECallback, TBDEDataSet, TBevel, TBinaryField, TBitBtn, TBitmap, TBitmapImage, TBits, TBlobField, TBlobStream, TBookmarkList, TBooleanField, TBoolProperty, TBrush, TButton, TButtonControl, TBytesField, TCalendar, TCanvas, TCaptionProperty, TCGIApplication, TCGIRequest, TCGIResponse, TChangeLink, TCharProperty, TCheckBox, TCheckConstraint, TCheckConstraints, TCheckListBox, TClassProperty, TClientDataSet, TClientSocket, TClientWinSocket, TClipboard, TCollection, TCollectionItem, TColorDialog, TColorGrid, TColorProperty, TColumn, TColumnTitle, TComboBox, TComboButton, TComClassManager, TCommonDialog, TCommonDialog, TComObject, TComObjectFactory, TComponent, TComponentEditor, TComponentList, TComponentNameProperty, TComponentProperty, TComServer, TComServerObject, TComServerObject, TConnectionPoint, TConnectionPoints, TContainedObject, TControl, TControlCanvas, TControlScrollBar, TConversion, TCoolBand, TCoolBands, TCoolBar, TCriticalSection, TCurrencyField, TCursorProperty, TCustomAdapter, TCustomAdapter, TCustomCheckBox, TCustomComboBox, TCustomControl, TCustomDBGrid, TCustomEdit, TCustomForm, TCustomGrid, TCustomGroupBox, TCustomHotKey, TCustomImageList, TCustomLabel, TCustomListBox, TCustomListView, TCustomMaskEdit, TCustomMemo, TCustomMemoryStream, TCustomModule, TCustomOutline, TCustomPageProducer, TCustomPanel, TCustomProvider, TCustomRadioGroup, TCustomRemoteServer, TCustomRemoteServer, TCustomRichEdit, TCustomServerSocket, TCustomSocket, TCustomStaticText, TCustomTabControl, TCustomTreeView, TCustomUpDown, TCustomWebDispatcher, TCustomWinSocket, TDatabase, TDataLink, TDataModule, TDataSet, TDataSetDesigner, TDataSetTableProducer, TDataSetUpdateObject, TDataSetUpdateObject, TDataSource, TDataSourceLink, TDateField, TDateProperty, TDateTimeColors, TDateTimeField, TDateTimePicker, TDBCheckBox, TDBComboBox, TDBCtrlGrid, TDBCtrlGridLink, TDBCtrlPanel, TDBDataSet, TDBEdit, TDBError, TDBGrid, TDBGridColumns, TDBImage, TDBListBox, TDBLookupCombo, TDBLookupComboBox, TDBLookupControl, TDBLookupList, TDBLookupListBox, TDBMemo, TDBNavigator, TDBRadioGroup, TDBRichEdit, TDBText, TDdeClientConv, TDdeClientItem, TDdeMgr, TDdeServerConv, TDdeServerItem, TDefaultEditor, TDesigner, TDirectoryListBox, TDirectoryOutline, TDragControlObject, TDragObject, TDrawGrid, TDriveComboBox, TDSTableProducer, TDSTableProducerEditor, TEdit, TEnumPropDesc, TEnumProperty, TEvent, TEventDispatch, TField, TFieldDataLink, TFieldDef, TFieldDefs, TFileListBox, TFiler, TFileStream, TFilterComboBox, TFindDialog, TFloatField, TFloatProperty, TFont, TFontAdapter, TFontCharsetProperty, TFontDialog, TFontNameProperty, TFontProperty, TForm, TFormDesigner, TGauge, TGraphic, TGraphicControl, TGraphicField, TGraphicsObject, TGridDataLink, TGroupBox, THandleObject, THandleStream, THeader, THeaderControl, THeaderSection, THeaderSections, THintWindow, THotKey, THTMLTableAttributes, THTMLTableCellAttributes, THTMLTableColumn, THTMLTableColumns, THTMLTableElementAttributes, THTMLTableHeaderAttributes, THTMLTableRowAttributes, THTMLTagAttributes, THTTPDataLink, TIBComponent, TIBEventAlerter, TIComponentInterface, TIcon, TIconImage, TIconOptions, TIEditorInterface, TIEditReader, TIEditView, TIEditWriter, TIExpert, TIFileStream, TIFormInterface, TImage, TImageList, TIMainMenuIntf, TIMemoryStream, TImeNameProperty, TIMenuItemIntf, TIModuleCreator, TIModuleInterface, TIModuleNotifier, TIndexDef, TIndexDefs, TIndexFiles, TIniFile, TInplaceEdit, TIntegerField, TIntegerProperty, TInterface, TInterfacedObject, TIProjectCreator, TIResourceEntry, TIResourceFile, TISAPIApplication, TISAPIRequest, TISAPIResponse, TIStream, TIStreamAdapter, TIToolServices, TIVCLStreamAdapter, TLabel, TList, TListBox, TListColumn, TListColumns, TListColumns, TListItem, TListItems, TListSourceLink, TListView, TLoginDialog, TLookupList, TMainMenu, TMask, TMaskEdit, TMediaPlayer, TMemo, TMemoField, TMemoryStream, TMenu, TMenuItem, TMetafile, TMetafileCanvas, TMetafileImage, TMethodProperty, TModalResultProperty, TMPFilenameProperty, TNavButton, TNavButton, TNavDataLink, TNotebook, TNumericField, TObject, TOleContainer, TOleControl, TOleForm, TOleGraphic, TOleStream, TOpenDialog, TOpenPictureDialog, TOrdinalProperty, TOutline, TOutlineNode, TPage, TPageControl, TPageProducer, TPaintBox, TPaintControl, TPanel, TParaAttributes, TParam, TParamList, TParams, TParser, TPen, TPersistent, TPicture, TPictureAdapter, TPopupDataList, TPopupGrid, TPopupMenu, TPrintDialog, TPrinter, TPrinterSetupDialog, TProgressBar, TPropertyEditor, TPropertyPage, TProvider, TProviderObject, TQuery, TQueryTableProducer, TRadioButton, TRadioGroup, TReader, TRegIniFile, TRegistry, TRemoteServer, TReplaceDialog, TResourceStream, TRichEdit, TSaveDialog, TSavePictureDialog, TScreen, TScrollBox, TScroller, TScrollingWinControl, TServerAcceptThread, TServerClientThread, TServerClientWinSocket, TServerSocket, TServerWinSocket, Dbtables.TSession, TSessionList, TSetElementProperty, TSetProperty, TShape, TSharedImage, TShortCutProperty, TSimpleEvent, TSmallintField, TSpeedButton, TSpinButton, TSpinEdit, TSplitter, TStaticText, TStatusBar, TStatusBar, TStatusPanel, TStatusPanels, TStoredProc, TStream, TStringField, TStringGrid, TStringGrid, TStringGridStrings, TStringList, TStringProperty, TStrings, TStringsAdapter, TStringStream, TSynchroObject, TTabbedNotebook, TTabControl, TTable, TTabList, TTabOrderProperty, TTabPage, TTabSet, TTabSheet, TTextAttributes, TThread, TThreadList, TTimeField, TTimeProperty, TTimer, TTimerSpeedButton, TToolBar, TToolButton, TTrackBar, TTreeNode, TTreeNodes, TTreeView, TTypedComObject, TTypedComObjectFactory, TUpdateSQL, TUpDown, TVarBytesField, TVirtualStream, TWebActionItem, TWebActionItems, TWebApplication, TWebDispatcher, TWebModule, TWebRequest, TWebResponse, TWinCGIRequest, TWinCGIResponse, TWinControl, TWinSocketStream, TWordField, TWriter. ); Function TForm1.AddClass (NewClass: TClass): TTreeNode; Var. ParentNode: TTreeNode; Begin. // if the class is not there... Result := GetNode (NewClass); If Result = nil then. Begin. // look for the parent (eventually adding it). ParentNode := AddClass (NewClass.ClassParent); // add the new class. Result := TreeView1.Items.AddChildObject ( ParentNode, NewClass.ClassName, Pointer (NewClass)); End; End; Function TForm1.GetNode (BaseClass: TClass): TTreeNode; Var. Node1: TTreeNode; Begin. Result := nil; // not found. // find the node in the tree. Node1 := TreeView1.Items.GetFirstNode; While Node1 <> nil do. Begin. If Node1.Text = BaseClass.ClassName then. Begin. Result := Node1; Exit; End; Node1 := Node1.GetNext; Forms.Application.ProcessMessages; End; (* slower loop... For I := 0 to TreeView1.Items.Count - 1 do. Begin. If TreeView1.Items [I].Text = BaseClass.ClassName then. Begin. Result := TreeView1.Items [I]; Exit; End; Application.ProcessMessages; End;*). End; Procedure TForm1.Button1Click(Sender: TObject); Var. I: Integer; Begin. // don't restart this loop. Button1.Enabled := False; // add the root class. TreeView1.Items.AddObject (nil, 'TObject', Pointer (TObject)); // add each class to the tree. ProgressBar1.Min := Low (ClassArray); ProgressBar1.Max := High (ClassArray); For I := Low (ClassArray) to High (ClassArray) do. Begin. AddClass (ClassArray [I]); ProgressBar1.Position := I; End; Beep; ShowMessage ('Tree Completed'); Button2.Enabled := True; Button1.Enabled := False; End; Procedure TForm1.TreeView1Change ( Sender: TObject; Node: TTreeNode); Var. MyClass: TClass; Begin. MyClass := TClass (Node.Data); Edit1.Text := Format ('Name: %s - Size: %d bytes', [MyClass.ClassName, MyClass.InstanceSize]); With Listbox1.Items do. Begin. Clear; While MyClass.ClassParent <> nil do. Begin. MyClass := MyClass.ClassParent; Add (MyClass.ClassName); End; // while. End; // with. End; Procedure TForm1.Button2Click(Sender: TObject); Begin. Screen.Cursor := crHourglass; TreeView1.SortType := stText; Screen.Cursor := crDefault; Button2.Enabled := False; End; End.
| Как удалить/восстановить файлы из корзины? |
Code: Program del; Uses. ShellApi; //function SHFileOperation(const lpFileOp: TSHFileOpStruct): Integer; stdcall; Var. T: TSHFileOpStruct; P: string; Begin. P := 'C:\Windows\System\EL_CONTROL.CPL'; With T do. Begin. Wnd := 0; WFunc := FO_DELETE; PFrom := Pchar(P); FFlags := FOF_ALLOWUNDO. End; SHFileOperation(T); End.
Восстановление.
Есть некоторые причуды, и Вы должны помнить о следующем:
Дайте полный путь для каждого файла. Не доверяйте текущей директории, даже если Вы ее изменили непосредственно перед вызовом функции. Функция WinAPI SHFileOperation не достаточно "умная" для использования текущей директории при отсутствии информации о предыдущей директории (для осуществления функции восстановления). Так, даже если используете флаг FOF_ALLOWUNDO, это не восстановит удаленные файлы из корзины, поскольку функция ничего не знает о предыдущем месторасположении файлов, и, таким образом, не сможет их восстановить файлы из корзины в их оригинальное месторасположение. Она просто удалит файлы из текущей директории.
Microsoft скорректировала документацию о члене pFrom. Новая редакция сообщает о подробностях работы в пакетном режиме: необходимо разделить имя каждого файла символом NULL (#0) и добавить к концу списка двойной символ NULL. Терминатор из двух символов NULL необходим в любом случае: работаете вы с одним файлом, или же используете пакетный режим. Иногда это работает и без терминатора, но чаще нет. Это связано с тем, что функции при работе с памятью считывает данные из памяти, располагающейся до терминатора, а поскольку длина строки может не совпадать с распределенной памятью, то данные, находящиеся после терминатора, просто не обрабатываются.
Пример правильного кодирования:
Code: Var. FileList: string; FOS: TShFileOpStruct; Begin. FileList := 'c:\delete.me'#0'c:\windows\temp.$$$'#0#0; { если Вы используете имена файлов в строковых переменных: }. FileList := Filename1 + #0 + Filename2 + #0#0; FOS.pFrom := PChar(FileList); // бла бла бла. End;
| Требуется нажать в другом приложении пару кнопок? |
Требуется нажать в "другом" приложении пару кнопок (button). (кнопки не имеют hotkeys). Ищу окно так (Дельфи):
Code: If FindWindow(nil, 'Advanced Dialer')<> 0 then. ShowMessage('OK');
| Копируем русский текст в буфер обмена в Windows2000. |
У меня Windows NT/2000. Когда копирую текст на русском языке, скажем, из TMemo в Ворд 97/2000, то получаю в результате каракули. Эти каракули исправляются, если перед копированием насильно переключить клавиатуру пользователя на русский язык. Но если у него нет этой клавиатуры, или если лучше не переключать ее, то как можно сообщить системе, что мы будем копировать РУССКИЙ текст. На форме создается невидимый TRichEdit (я обозвал его TRE в коде). Далее текст копируется в клипборд как обычно, после чего вызывается следующая процедура.
Code: Procedure TMainForm.ConvertClipboard; Begin. TRE.SelectAll; TRE.ClearSelection; TRE.Lines.Add(Clipboard.AsText); TRE.SelectAll; TRE.Font.Name := 'Times New Roman'; //тут нужный шрифт. TRE.Font.Size := 12; // тут нужный размер. // или просто берите TRE.Font := MainMemo.Font; TRE.SelAttributes.Charset := RUSSIAN_CHARSET; TRE.CopyToClipboard; End.
| Работа с коллекциями - сохранение и загрузка. |
Code: Unit DelphiPt; Interface. Uses. Classes, Graphics; Type. TDDHPoint = class (TCollectionItem). Private. FX, fY: Integer; Public. Text: string; Procedure WriteText (Writer: TWriter); Procedure ReadText (Reader: TReader); Procedure DefineProperties (Filer: TFiler); override; Procedure Paint (Canvas: TCanvas); Procedure Assign (Pt: TPersistent); override; Published. Property X: Integer read fX write fX; Property Y: Integer read fY write fY; End; TWrapper = class (TComponent). Private. FColl: TCollection; Published. Property MyColl: TCollection read FColl write FColl; Public. Constructor Create (Owner: TComponent); override; Destructor Destroy; override; End; Implementation. // TWrapper constructor and destructor. Constructor TWrapper.Create (Owner: TComponent); Begin. Inherited Create (Owner); FColl := TCollection.Create (TDDHPoint); End; Destructor TWrapper.Destroy; Begin. FColl.Clear; FColl.Free; Inherited Destroy; End; // class TDDHPoint methods. Procedure TDDHPoint.WriteText (Writer: TWriter); Begin. Writer.WriteString (Text); End; Procedure TDDHPoint.ReadText (Reader: TReader); Begin. Text := Reader.ReadString; End; Procedure TDDHPoint.DefineProperties (Filer: TFiler); Begin. Filer.DefineProperty ( 'Text', ReadText, WriteText, (Text <> '')); End; Procedure TDDHPoint.Paint (Canvas: TCanvas); Begin. Canvas.Ellipse (fX - 3, fY - 3, fX + 3, fY + 3); Canvas.TextOut (fX + 5, fY + 5, Text); End; Procedure TDDHPoint.Assign (Pt: TPersistent); Begin. If Pt is TDDHPoint then. Begin. Fx := TDDHPoint (Pt).fX; FY := TDDHPoint (Pt).fY; Text := TDDHPoint (Pt).Text; End. Else. // raise an exception. Inherited Assign (pt); End; //initialization. //RegisterClass (TWrapper); End.
| COM. Агрегация и нотификация вообще и для Delphi в частности. |
Автор: Виталий Маматов.
Специально для Королевства Delphi.
Вступление.
К написанию данной статьи меня подтолкнула моя недавняя пробежка по вопросам относительно COM. Значительное их число сводилось к непониманию принципов организации агрегирования и нотификации. И, дабы лишний раз не утруждать себя напрасно, было решено, дать такой развёрнутый ответ с примером и сразу для всех.
Про литературу.
Самой полезной книжкой по технологии COM для меня стала неброская книжонка А.Кобирниченко "Visual Studio 6. Искусство программирования". Для примера два одинаковых понятия из тоже хорошей книжки Елмановой и Трепалина "Delphi 4 технология COM" но несколько путаной:
Apartment:
Елманова:
"Можно вызывать методы объекта только из того потока, где объект был создан. При этом одновременно можно создать несколько отличающихся объектов в разных потоках, но каждый объект обязан вызываться только из того потока, где был создан".
Кобирниченко:
"Каждый объект выполняется в своём потоке. Потоков может быть несколько, но всю синхронизацию берёт на себя сама библиотека. Объект, выполняющийся в одном потоке, ничего не знает о других потоках, поэтому может не заботится о многопоточном доступе к своим методам.".
Теоретические экскурсы в данной статье, в основном, основаны на книге Кобирниченко.
Часть первая: Теория.
Агрегация.
При агрегировании внешний объект не реализует сам интерфейсы имеющиеся у внутреннего. Вместо этого он передаёт своему клиенту указатель указатель непосредственно на интерфейс внутреннего. Клиент напрямую общается с внутренним объектом. Взамен появляются требования к внутреннему объекту связанные с реализацией IUnknown внутреннего объекта. Все вызовы IUnknown внутреннего объекта должны делегироваться методам IUnknown внешнего объекта.
Нотификация.
Помимо методов реализующих входящие интерфейсы вызываемые клиентом, Объект может объявить исходящие интерфейсы, реализация которых возложена на самого клиента.
Исходящие интерфейсы являются расширением принципа уведомления, реализованного в составных документах всё уведомление в которых построено на IAdviseSink с ограниченным набором событий. Установление соединения на основе этого интерфейса требует всего одного вызова IOleObject::SetAdise.
При использовании точек соединения нужно четыре вызова: QueryInterface для получения IConnectionPointContainer, затем FindConnectionPoint для получения нужной точки соединения, затем Advise для передачи указателя на IUnknown исходящего интерфейса и, наконец, QueryInterface со стороны клиента для получения самого исходящего интерфейса. Вся эта деятельность, особенно в случае DCOM, может занять значительное время. Собственно по этому сама Microsoft рекомендует организовывать уведомление на основе собственных интерфейсов, похожих на IadviseSink, а не на основе точек соединения.
После такого введения, я думаю, вы уже готовы взять в руки инструмент Исследователя - IDE Delphi. В нашем случае ;).
Часть вторая: Махровая практика.
Агрегирование:
После тщательных поисков по Дельфийскому хелпу в данной предметной области, мною было обнаружено следующее: "TAggregatedObject is used as part of an aggregate that has a single controlling Iunknown" И приписка: "Note: For more information about aggregation, controlling objects, and interfaces, see the Inside OLE, second edition, by Kraig Brockschmidt" Ну, второе нам сейчас ни к чему, а вот с первым следует ознакомиться поближе.
Итак, вот он:
Code: TAggregatedObject = class. Private. FController: Pointer; Function GetController: IUnknown; Protected. { IUnknown }. Function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; Function _AddRef: Integer; stdcall; Function _Release: Integer; stdcall; Public. Constructor Create(Controller: IUnknown); Property Controller: IUnknown read GetController; End; Constructor TAggregatedObject.Create(Controller: IUnknown); Begin. FController := Pointer(Controller); End; Function TAggregatedObject.QueryInterface(const IID: TGUID; out Obj): HResult; Begin. Result := IUnknown(FController).QueryInterface(IID, Obj); End;
Посылать и считывать данные с COM порта, а также менять параметры (биты данных, четность).
Ниже представлен класс для работы с COM-портом. Протестирован в Windows 95. Класс выдернут из контекста, так что не ручаюсь в компиляции с первого раза, однако все функции работы с COM очевидны.
Code: Unit Unit1; Interface. Uses. Windows; Type. TComPort = class. Private. HFile: THandle; Public. Constructor Create; Destructor Destroy; override; Function InitCom(BaudRate, PortNo: Integer; Parity: Char; CommTimeOuts: TCommTimeouts): Boolean; Procedure CloseCom; Function ReceiveCom(var Buffer; Size: DWORD): Integer; Function SendCom(var Buffer; Size: DWORD): Integer; Function ClearInputCom: Boolean; End; Implementation. Uses. SysUtils; Constructor TComPort.Create; Begin. Inherited; CloseCom; End; Destructor TComPort.Destroy; Begin. CloseCom; Inherited; End; Function TComPort.InitCom(BaudRate, PortNo: Integer; Parity: Char; CommTimeOuts: TCommTimeouts): Boolean; Var. FileName: string; DCB: TDCB; PortParam: string; Begin. Result := FALSE; FileName := 'Com' + IntToStr(PortNo); {имя файла}. HFile := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); If hFile = INVALID_HANDLE_VALUE then. Exit; //установка требуемых параметров. GetCommState(hFile, DCB); //чтение текущих параметров порта. PortParam := 'baud=' + IntToStr(BaudRate) + ' parity=' + Parity + ' data=8 stop=1 ' + 'octs=off'; If BuildCommDCB(PChar(PortParam), DCB) then. Begin. Result := SetCommState(hFile, DCB) and. SetCommTimeouts(hFile, CommTimeOuts); End; If not result then. CloseCom; End; Procedure TComPort.CloseCom; Begin. If hFile < > INVALID_HANDLE_VALUE then. CloseHandle(hFile); HFile := INVALID_HANDLE_VALUE; End; Function TComPort.ReceiveCom(var Buffer; Size: DWORD): Integer; Var. Received: DWORD; Begin. If hFile = INVALID_HANDLE_VALUE then. Raise Exception.Create('Не открыта запись в Com порт'); If ReadFile(hFile, Buffer, Size, Received, nil) then. Begin. Result := Received; End. Else. Raise Exception.Create('Ошибка приема данных: ' + IntToStr(GetLastError)); End; Function TComPort.SendCom(var Buffer; Size: DWORD): Integer; Var. Sended: DWORD; Begin. If hFile = INVALID_HANDLE_VALUE then. Raise Exception.Create('Не открыта запись в Com порт'); If WriteFile(hFile, Buffer, Size, Sended, nil) then. Begin. Result := Sended; End. Else. Raise Exception.Create('Ошибка передачи данных: ' + IntToStr(GetLastError)); End; Function TComPort.ClearInputCom: Boolean; Begin. If hFile = INVALID_HANDLE_VALUE then. Raise Exception.Create('Не открыта запись в Com порт'); Result := PurgeComm(hFile, PURGE_RXCLEAR); End; End.
©Drkb::01349.
Взято с http://delphiworld.narod.ru.
| Как обрабатывать ошибки в COM-объектах. |
Code: TCustomBasePlugObject = class(TAutoObject, IUnknown, IDispatch). ... Protected. Function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): {$IFDEF _D4_}HResult{$ELSE}Integer{$ENDIF}; override; ... Function TCustomBasePlugObject.SafeCallException; Var. ExMsg: string; Begin. Result := inherited SafeCallException(ExceptObject, ExceptAddr); Try. If ExceptObject is EAbort then. Exit; ExMsg := 'Exception: PlugObject="' if ExceptObject is Exception then. Begin. ExMsg := ExMsg + #13' Message: '#13' ' + Exception(ExceptObject).Message + #13' Module:' + GetModuleFileName + #13' Adress:' + Format('%p', [ExceptAddr]); If (ExceptObject is EOleSysError) and. (EOleSysError(ExceptObject).ErrorCode < 0) then. ExMsg := ExMsg + #13' OleSysError.ErrorCode = '+IntToStr(EOleSysError(ExceptObject).ErrorCode); End; ToLog(ExMsg); Except. End; End;
Пример программирования com портов.
Code: Unit TestRosh; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; Type. TForm1 = class(TForm). Panel1: TPanel; Label1: TLabel; PortCombo: TComboBox; Label2: TLabel; BaudCombo: TComboBox; Label3: TLabel; ByteSizeCombo: TComboBox; Label4: TLabel; ParityCombo: TComboBox; Label5: TLabel; StopBitsCombo: TComboBox; Label6: TLabel; Memo1: TMemo; Edit1: TEdit; Button1: TButton; Memo2: TMemo; Edit2: TEdit; Label7: TLabel; Button2: TButton; Label8: TLabel; Edit3: TEdit; Procedure Button1Click(Sender: TObject); Procedure Memo2Change(Sender: TObject); Procedure Memo1Change(Sender: TObject); Procedure FormDestroy(Sender: TObject); Procedure Button2Click(Sender: TObject); Procedure PortComboChange(Sender: TObject); Procedure FormShow(Sender: TObject); Procedure Memo1DblClick(Sender: TObject); End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Uses. Registry; Var. HPort: THandle; Procedure TForm1.Memo1Change(Sender: TObject); Var. I: Integer; Begin. Edit1.Text := ''; For i := 1 to Length(Memo1.Text) do. Edit1.Text := Edit1.Text + Format('%x', [Ord(Memo1.Text[i])]) + ' ' End; Procedure TForm1.Memo2Change(Sender: TObject); Var. I: Integer; Begin. Edit2.Text := ''; For i := 1 to Length(Memo2.Text) do. Edit2.Text := Edit2.Text + Format('%x', [Ord(Memo2.Text[i])]) + ' ' End; Procedure TForm1.Button1Click(Sender: TObject); Var. S, D: array[0..127] of Char; Actual_bytes: Integer; DCB: TDCB; Begin. FillChar(S, 128, #0); FillChar(D, 128, #0); DCB.DCBlength := SizeOf(DCB); If not GetCommState(hPort, DCB) then. Begin. ShowMessage('Can not get port state: ' + IntToStr(GetLastError)); Exit; End; Try. DCB.BaudRate := StrToInt(BaudCombo.Text); Except. BaudCombo.Text := IntToStr(DCB.BaudRate); End; Try. DCB.ByteSize := StrToInt(ByteSizeCombo.Text); Except. ByteSizeCombo.Text := IntToStr(DCB.ByteSize); End; If ParityCombo.ItemIndex > -1 then. DCB.Parity := ParityCombo.ItemIndex. Else. ParityCombo.ItemIndex := DCB.Parity; If StopBitsCombo.ItemIndex > -1 then. DCB.StopBits := StopBitsCombo.ItemIndex. Else. StopBitsCombo.ItemIndex := DCB.StopBits; If not SetCommState(hPort, DCB) then. Begin. ShowMessage('Can not set new port settings: ' + IntToStr(GetLastError)); Exit; End; PurgeComm(hPort, PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR); StrPCopy(S, Memo1.Text); If not WriteFile(hPort, S, StrLen(S), actual_bytes, nil) then. Begin. ShowMessage('Can not write to port: ' + IntToStr(GetLastError)); Exit; End; If not ReadFile(hPort, D, StrToInt(Edit3.Text), actual_bytes, nil) then. ShowMessage('Can not read from port: ' + IntToStr(GetLastError)). Else. ShowMessage('Read ' + IntToStr(actual_bytes) + ' bytes'); Memo2.Text := D; End; Procedure TForm1.FormDestroy(Sender: TObject); Begin. With TRegistry.Create do. Begin. OpenKey('Shkila', True); WriteString('Port', PortCombo.Text); WriteString('Baud Rate', BaudCombo.Text); WriteString('Byte Size', ByteSizeCombo.Text); WriteString('Parity', IntToStr(ParityCombo.ItemIndex)); WriteString('Stop Bits', IntToStr(StopBitsCombo.ItemIndex)); Destroy; End; If not CloseHandle(hPort) then. Begin. ShowMessage('Can not close port: ' + IntToStr(GetLastError)); Exit; End; End; Procedure TForm1.Button2Click(Sender: TObject); Begin. HPort := CreateFile(PChar(PortCombo.Text), GENERIC_READ + GENERIC_WRITE, 0, Nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); If hPort = INVALID_HANDLE_VALUE then. ShowMessage('Can not open ' + PortCombo.Text + ': ' + IntToStr(GetLastError)). Else. Button2.Hide; End; Procedure TForm1.PortComboChange(Sender: TObject); Begin. FormDestroy(Sender); Button2.Show; End; Procedure TForm1.FormShow(Sender: TObject); Begin. With TRegistry.Create do. Begin. OpenKey('Shkila', True); PortCombo.Text := ReadString('Port'); BaudCombo.Text := ReadString('Baud Rate'); ByteSizeCombo.Text := ReadString('Byte Size'); ParityCombo.ItemIndex := StrToInt(ReadString('Parity')); StopBitsCombo.ItemIndex := StrToInt(ReadString('Stop Bits')); Destroy; End; End; Procedure TForm1.Memo1DblClick(Sender: TObject); Begin. Memo1.Lines.Clear; Memo2.Lines.Clear; Edit1.Text := ''; Edit2.Text := ''; End; End.
©Drkb::01350.
Взято с http://delphiworld.narod.ru.
| Как поместить курсор мышки в нужное место на форме? |
Code: Uses. Windows; Procedure PlaceMyMouse(Sender: TForm; X, Y: word); Var. MyPoint: TPoint; Begin. MyPoint := Sender.ClientToScreen(Point(X, Y)); SetCursorPos(MyPoint.X, MyPoint.Y); End;
| Компиляция ресурсов. |
Автор: Ralph Friedman.
У меня имеется приблизительно 36 маленьких растровых изображений, которые я хочу сохранить в файле и затем прилинковать его к exe. Как мне поместить их в res-файл?
Самый простой путь - создать файл с именем "BITMAPS.RC" и поместить в него список ваших .BMP-файлов:
BMAP1 BITMAP BMAP1.BMP.
BMAP2 BITMAP BMAP2.BMP.
CLOCK BITMAP CLOCK.BMP.
DBLCK BITMAP DBLCK.BMP.
DELOK BITMAP DELOK.BMP.
LUPE BITMAP LUPE.BMP.
OK BITMAP OK.BMP.
TIMEEDIT BITMAP TIMEEDIT.BMP.
Затем загрузите Resource Workshop (RW) и выберите пункт меню File|Project Open. В выпадающем списке "File Type" (тип файла) выберите RC-Resource Script и откройте файл, который вы только что создали. После того, как RW загрузит ваш файл, выберите пункт меню File|Project save as. Выберите объект RES-Resource из выпадающего списка "File Type" (тип файла). В поле редактирования "New File name" задайте имя нового файла, скажем, BITMAPS.RES. Нажмите OK. Теперь у вас есть файл ресурса. В вашем модуле Delphi добавьте после строки {$R *.RES} строку {$R BITMAPS.RES}. После компиляции вы получите exe-файл с скомпилированными ресурсами. Для получения доступа к ресурсам во время выполнения программы нужно сделать следующее:
Code: MyImage.Picture.Bitmap.Handle := LoadBitmap(HInstance, 'TIMEEDIT');
Помещение компонентов в DBGrid.
Данный совет и сопутствующий код показывает как просто поместить любой компонент в ячейку сетки данных. Компонент в данном контексте может означать любой видимый элемент управления - от простого combobox до сложного диалогового окна. Методы, описанные ниже, применимы практически к любому визуальному компоненту. Если Вы можете поместить его на форму, то, вероятно, сможете поместить и в ячейку DBGrid.
Здесь нет новых идей, фактически основная технология работы заключается в имитации-трансплантации внешних компонентов в DBGrid. Идея в том, чтобы получить контроль над табличной сеткой. Практически DBGrid состоит из набора компонентов TDBEdit. Вводя данные в ячейку, вы работаете непосредственно с TDBEdit. Остальные без фокуса ячейки в данный момент реально являются статичной картинкой. В данном совете Вы узнаете как поместить в сфокусированную ячейку другой, отличный от TDBEdit, визуальный компонент.
КОМПОНЕНТ #1 - TDBLOOKUPCOMBO.
Вам нужна форма с компонентом DBGrid на ней. Создайте новый проект и поместите на основную форму DBGrid.
Далее поместите на форму TTable, установите псевдоним (Alias) в DBDEMOS, TableName в GRIDDATA.DB и присвойте свойству Active значение True. Поместите DataSource и сошлитесь в свойстве DataSet на Table1. Вернитесь к DBGrid и укажите в свойстве DataSource компонент DataSource1. Данные из GRIDDATA.DB должные появиться в табличной сетке...
Первый элемент, который мы собираемся поместить в DBGrid - TDBLookupCombo, т.к. нам нужна вторая таблица для поиска. Поместите второй TTable на форму. Установите псевдоним (Alias) в DBDEMOS, TableName в CUSTOMER.DB и присвойте свойству Active значение True. Поместите второй DataSource и сошлитесь в свойстве DataSet на Table2.
Теперь нужно поместить компонент TDBLookupCombo из палитры Data Controls на любое место формы - это не имеет никакого значения, т.к. он обычно будет невидим или будет нами имплантирован в табличную сетку. Установите свойства компонента LookuoCombo следующим образом:
DataSource DataSource1.
DataField CustNo.
LookupSource DataSource2.
LookupField CustNo.
LookupDisplay CustNo {Вы можете изменить это на Company позже,
Но сейчас пусть это будет CustNo).
Пока мы только настроили компоненты. Теперь давайте создадим некоторый код.
Первое, что Вам необходимо - сделать так, чтобы DBLookupCombo, который Вы поместили на форму, во время запуска приложения оставался невидимым. Для этого выберите Form1 в инспекторе объектов, перейдите на закладку Events (события) и дважды щелкните на событии onCreate. Delphi немедленно сгенерит и отобразит скелет кода будущего обработчика события onCreate:
Code: Procedure TForm1.FormCreate(Sender: TObject); Begin. End;
Присвойте свойству Visible значение False в LookupCombo следующим образом:
Code: Procedure TForm1.FormCreate(Sender: TObject); Begin. DBLookupCombo1.Visible := False; End;
Наверняка многим стало интересно, почему я не воспользовался инспектором объектов для изменения свойств компонента. Действительно, можно было бы и так. Лично я таким способом инициализирую компоненты, чьи свойства могут изменяться во время работы приложения. Я изменил статическое свойство, которое не отображается во время проектирования (если воспользоваться инспектором объктов). Я думаю это делает код легче для понимания.
Теперь нам необходимо "прикрутить" компонент к нашей табличной сетке. Наша задача - автоматически отобразить DBLookupCombo в ячейке во время получения ею фокуса (или перемещении курсора). Для этого необходимо написать код для обработчиков двух событий: OnDrawDataCell и OnColExit. Первым делом обработаем событие OnDrawDataCell. Дважды щелкните на строчке OnDrawDataCell в инспекторе объектов и введите следующий код:
Code: Procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState); Begin. If (gdFocused in State) then. Begin. If (Field.FieldName = DBLookupCombo1.DataField) then. Begin. DBLookupCombo1.Left := Rect.Left + DBGrid1.Left; DBLookupCombo1.Top := Rect.Top + DBGrid1.top; DBLookupCombo1.Width := Rect.Right - Rect.Left; { DBLookupCombo1.Height := Rect.Bottom - Rect.Top; }. DBLookupCombo1.Visible := True; End; End; End;
Причины чрезмерного использования конструкций begin/end скоро станут понятны. В коде "говорится", что если параметр State имеет значение gdFocused, то данная ячейка имеет фокус (в любой момент времени только одна ячейка в табличной сетке может иметь фокус). Далее: если это выделенная ячейка и ячейка имеет тоже имя поля как и поле данных DBLookupCombo, DBLookupCombo необходимо поместить над этой ячейкой и сделать его видимым. Обратите внимание на определение позиции DBLookupCombo: она определяется относительно формы, а не ячейки. Так, например, положение левой стороны LookupCombo должно учитывать положение сетки (DBGrid1.Left) плюс положение соответствующей ячейки относительно сетки (Rect.Left).
Также обратите внимание на то, что определение высоты LookupCombo в коде закомментарено. Причина в том, что LookupCombo имеет минимальную высоту. Вы просто не сможете сделать ее меньше. Минимальная высота LookupCombo больше высоты ячейки. Если Вы раскомментарили строку, касающуюся высоты LookupCombo, Ваш код изменит размер компонента и Delphi немедленно его перерисует. Это вызовет неприятное моргание компонента. Бороться с этим невозможно. Позвольте, чтобы LookupCombo был немного больше, чем ячейка. Это выглядит немного странным, но это работает.
Теперь ради шутки запустите программу. Заработала? Сразу после запуска переместите курсор на одну из ячеек табличной сетки. Вы ожидали чего-то большего? Да! Мы только в середине пути. Теперь нам нужно спрятать LookupCombo при покидании курсором колонки. Напишем обработчик события onColExit. Это должно выглядеть примерно так:
Code: Procedure TForm1.DBGrid1ColExit(Sender: TObject); Begin. If DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField then. DBLookupCombo1.Visible := false; End;
Код использует свойство TDBGrids SelectedField для ассоциации имени поля ячейки (FieldName) с нашим LookupCombo. Код "говорит": "Если ячейка была в колонке с DBLookupCombo (имя поля ячейки совпадает с именем поля DBLookupCombo), его необходимо сделать невидимым".
Теперь снова запустите приложение. Чувствуете эффект?
Теперь вроде бы все правильно, но мы забыли об одной вещи. Попробуйте ввести новое значение в одно из LookupCombo. Проблема в том, что нажатие клавиши обрабатывает DBGrid, а не LookupCombo. Чтобы исправить это, нам нужно написать для табличной сетки обработчик события onKeyPress. Это должно выглядеть примерно так:
Code: Procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char); Begin. If (key <> chr(9)) then. Begin. If (DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField) then. Begin. DBLookupCombo1.SetFocus; SendMessage(DBLookupCombo1.Handle, WM_Char, word(Key), 0); End; End; End;
В данном коде "говорится": если нажатая клавиша не является клавишей Tab (Chr(9)) и текущее поле в табличной сетке LookupCombo, тогда установите фокус на LookupCombo и передайте сообщение с кодом нажатой клавиши LookupCombo. Здесь я воспользовался WIN API функцией. Вам не нужно знать как это работает, достаточно того, что это просто работает.
Небольшое пояснение я все же дам. Для того, чтобы функция Window SendMessage послала сообщение "куда надо", ей в качестве параметра необходим дескриптор ("адрес") нужного компонента. Используйте свойство компонента Handle. Затем нужно сообщить компоненту что мы от него хотим. В нашем случае это Windows-сообщение WM_CHAR, извещающее LookupCombo о том, что ему посылается символ. Наконец, мы передаем ему сам символ нажатой клавиши - word(Key). Word(key) - приведение к типу word параметра Key события нажатия клавиши. Все достаточно просто, правда? Все, что Вам действительно необходимо сделать - заменить имя DBLookupCombo1 нашего вымышленного компонента на имя реального компонента, который будет участвовать в "модернизации" табличной сетки. Более подробную информацию о функции SendMessage Вы можете почерпнуть из электронной справки, поставляемой вместе с Delphi.
Запустите снова Ваше приложение и попробуйте что-нибудь ввести. Это работает! Экспериментируя, Вы увидите что с помощью клавиши Tab Вы можете перейти из режима редактирования в режим перемещения курсора и наоборот.
Теперь перейдите к инспектору объектов и измнените у компонента DBLookupCombo свойство LookupDIsplay на Company. Снова запустите. Это то, что Вы ожидали?
КОМПОНЕНТ #2 - TDBCOMBO.
Здесь я не собираюсь обсуждать технологию имплантации DBCombo, так как она практически не отличается от той, что была показана выше. Все написанное в пункте #1 имеет силу и здесь. Вот пошагово разработанный код для вашего компонента.
Code: Procedure TForm1.FormCreate(Sender: TObject); Begin. DBLookupCombo1.Visible := False; DBComboBox1.Visible := False; End; Procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState); Begin. If (gdFocused in State) then. Begin. If (Field.FieldName = DBLookupCombo1.DataField) then. Begin. DBLookupCombo1.Left := Rect.Left + DBGrid1.Left; DBLookupCombo1.Top := Rect.Top + DBGrid1.top; DBLookupCombo1.Width := Rect.Right - Rect.Left; DBLookupCombo1.Visible := True; End. Else if (Field.FieldName = DBComboBox1.DataField) then. Begin. DBComboBox1.Left := Rect.Left + DBGrid1.Left; DBComboBox1.Top := Rect.Top + DBGrid1.top; DBComboBox1.Width := Rect.Right - Rect.Left; DBComboBox1.Visible := True; End. End; End; Procedure TForm1.DBGrid1ColExit(Sender: TObject); Begin. If DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField then. DBLookupCombo1.Visible := false. Else if DBGrid1.SelectedField.FieldName = DBComboBox1.DataField then. DBComboBox1.Visible := false; End; Procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char); Begin. If (key <> chr(9)) then. Begin. If (DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField) then. Begin. DBLookupCombo1.SetFocus; SendMessage(DBLookupCombo1.Handle, WM_Char, word(Key), 0); End. Else if (DBGrid1.SelectedField.FieldName = DBComboBox1.DataField) then. Begin. DBComboBox1.SetFocus; SendMessage(DBComboBox1.Handle, WM_Char, word(Key), 0); End; End; End;
КОМПОНЕНТ #3 - TDBCHECKBOX.
Технология работы с компонентом DBCheckBox более интересна. В этом случае нам необходимо дать понять пользователю о наличие компонента DBCheckBox в ячейках без фокуса. Вы можете вставлять статическое изображение компонента или динамически изменять изображение в зависимости от логического состояния элемента управления. Я выбрал второе. Я создал два BMP-файла - включенный (TRUE.BMP) и выключенный (FALSE.BMP) DBCheckBox. Поместите два компонента TImage на форму, присвойте им имена ImageTrue и ImageFalse и назначьте соответствующие BMP-файлы в свойстве Picture. Да, чуть не забыл: Вам также необходимо поместить на форму два компонента DBCheckbox. Установите набор данных обоих компонентов в DataSource1 и присвойстве свойству Color значение clWindow. Для начала создадим для формы обработчик события onCreate:
Code: Procedure TForm1.FormCreate(Sender: TObject); Begin. DBLookupCombo1.Visible := False; DBCheckBox1.Visible := False; DBComboBox1.Visible := False; ImageTrue.Visible := False; ImageFalse.Visible := False; End;
Теперь нам нужен обработчик события onDrawDataCell чтобы делать что-то с ячейками, не имеющими фокуса. Здесь подойдет следующий код:
Code: Procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState); Begin. If (gdFocused in State) then. Begin. If (Field.FieldName = DBLookupCombo1.DataField) then. Begin. // ...СМОТРИ ВЫШЕ. End. Else if (Field.FieldName = DBCheckBox1.DataField) then. Begin. DBCheckBox1.Left := Rect.Left + DBGrid1.Left + 1; DBCheckBox1.Top := Rect.Top + DBGrid1.top + 1; DBCheckBox1.Width := Rect.Right - Rect.Left { - 1}; DBCheckBox1.Height := Rect.Bottom - Rect.Top { - 1}; DBCheckBox1.Visible := True; End. Else if (Field.FieldName = DBComboBox1.DataField) then. Begin. // ...СМОТРИ ВЫШЕ. End. End. Else {в этом месте помещаем статическое изображение компонента}. Begin. If (Field.FieldName = DBCheckBox1.DataField) then. Begin. If TableGridDataCheckBox.AsBoolean then. DBGrid1.Canvas.Draw(Rect.Left, Rect.Top, ImageTrue.Picture.Bitmap). Else. DBGrid1.Canvas.Draw(Rect.Left, Rect.Top, ImageFalse.Picture.Bitmap). End. End; End;
Самое интересное место - последний участок кода. Он выполняется в случае, когда состояние не равно gdFocused и сам CheckBox находится в колонке. В нем осуществляется проверка данных поля: если они равны True, то выводится рисунок TRUE.BMP, в противном случае - FALSE.BMP. Предварительно я создал два изображения, представляющие собой "слепок" двух логических состояния компонента, теперь будет очень трудно обнаружить отсутствие компонента в ячейках с фокусом и без оного. Теперь напишем обработчик события onColExit:
Code: Procedure TForm1.DBGrid1ColExit(Sender: TObject); Begin. If DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField then. DBLookupCombo1.Visible := false. Else. If DBGrid1.SelectedField.FieldName = DBCheckBox1.DataField then. DBCheckBox1.Visible := false. Else. If DBGrid1.SelectedField.FieldName = DBComboBox1.DataField then. DBComboBox1.Visible := false; End;
Организуйте обработку события onKeyPress как показано ниже:
Code: Procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char); Begin. If (key <> chr(9)) then. Begin. If (DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField) then. Begin. DBLookupCombo1.SetFocus; SendMessage(DBLookupCombo1.Handle, WM_Char, word(Key), 0); End. Else if (DBGrid1.SelectedField.FieldName = DBCheckBox1.DataField) then. Begin. DBCheckBox1.SetFocus; SendMessage(DBCheckBox1.Handle, WM_Char, word(Key), 0); End. Else if (DBGrid1.SelectedField.FieldName = DBComboBox1.DataField) then. Begin. DBComboBox1.SetFocus; SendMessage(DBComboBox1.Handle, WM_Char, word(Key), 0); End; End; End;
Наконец, последняя хитрость. Для удобства пользователя заголовку компонента нужно присвоить текущее логическое значение. С самого начала у меня была идея поручить это обработчику события onChange, но проблема в том, что событие может возникнуть неединожды. Итак, я должен снова воспользоваться функцией Windows API и послать компоненту соответствующее значение: "SendMessage(DBCheckBox1.Handle, BM_GetCheck, 0, 0)", которая возвращает 0 в случае если компонент невключен и любое другое число в противном случае.
Code: Procedure TForm1.DBCheckBox1Click(Sender: TObject); Begin. If SendMessage(DBCheckBox1.Handle, BM_GetCheck, 0, 0) = 0 then. DBCheckBox1.Caption := ' ' + 'Ложь' Else. DBCheckBox1.Caption := ' ' + 'Истина' End;
Это все. Надеюсь, Вы узнали для себя что-то новое. Я пробовал данную технологию с диалоговыми окнами. Делается достаточно просто и великолепно работает. Радует простота реализации. Вам даже не нужно знать как это работает, единственное, что Вам придется - заменить в тексте кода имена вымышленных компонентов на те, которые Вы реально хотите отображать в табличной сетке.
Ревизия.
Вышеприведенный код оказался несвободным от недостатков - после первой публикации совета дотошные программисты таки обнаружили две значительные недоработки. Первая проблема - если компонент имеет фокус, то для перемещения на следующую ячейку необходимо двукратное нажатие клавиши Tab. Вторая проблема возникает при добавлении новой записи.
Проблема # 1 - Необходимость двойного нажатия клавиши Tab.
Действительно, компонент, используемый для имплантации существует сам по себе, а не является частью табличной сетки. В случае, когда DBGrid имеет фокус, то для перемещения на следующую ячейку необходимо двукратное нажатие клавиши Tab. Первое нажатие клавиши перемещает фокус из имплантированного компонента на текущую ячейку, находящуюся под этим компонентом, и только второе нажатие клавиши Tab перемещает нас в следующую ячейку. Попробуем это исправить.
Для начала в форме, содержащей табличную сетку, опишем логическую переменную WasInFloater следующим образом:
Code: Type. TForm1 = class(TForm). ... ... Private. { Private declarations }. WasInFloater : Boolean; ... ... End;
Затем для компонента LookupCombo напишем обработчик события onEnter, где присвоим переменной WasInFloater значение True. Это позволит нам понять где в данный момент находится фокус.
Code: Procedure TForm1.DBLookupCombo1Enter(Sender: TObject); Begin. WasInFloater := True; End;
И, наконец, создаем хитрый обработчик события onKeyUp, позволяющий исправить досадный недостаток.
Code: Procedure TForm1.DBGrid1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); Begin. If (Key in [VK_TAB]) and WasInFloater then. Begin. SendMessage(DBGrid1.Handle, WM_KeyDown, Key, 0); WasInFloater := False; End; End;
Данный код реагирует на нажатие клавиши и позволяет в случае, когда фокус передался из имплантированного элемента управления табличной сетеке, вторично эмулировать нажатие клавиши Tab (передается код нажатой клавиши, т.е. Tab). Это работает как для отдельной клавиши Tab, так и для комбинации Shift-Tab.
Проблема #2 - Новая запись исчезает, когда компонент получает фокус.
Вторая проблема - в случае, когда вы нажимаете в навигаторе кнопку "добавить", запись добавляется, но, когда Вы после щелкаете на одном из компонентов, имплантированных в табличную сетку, новая запись таинственным образом исчезает. Причина этого - странный флаг dgCancelOnExit в опциях DBGrid, который имеет значение True по умолчанию. Установите это в False и вышеназванная проблема исчезает.
По-моему, Borland неправильно поступил, назначив такое значение по умолчанию, флаг должен иметь значение False. Я все время сталкиваюсь с данной проблемой, да и не только я, судя по новостным конференциям. Данная опция действует в случае потери компонентом фокуса и отменяет последние результаты редактирования. Во всяком случае во всех моих проектах я первым делом сбрасываю данный флаг.
©Drkb::00385.
Взято с http://delphiworld.narod.ru.
| Помещение компонентов в StringGrid. |
Автор: Neil Rubenking.
Некоторое время тому назад такой вопрос уже ставился: возможно ли поместить элемент управления, например, CheckBox или ComboBox внутрь компонента ...Grid. Я сегодня помозговал и нашел неплохую, на мой взгляд, технологию. Это работает! Вот решение для тех, кто этим интересуется:
При создании компонента (в обработчике OnCreate), создайте его объекты Objects[C,R], например TCheckBox.Create(Self). Имейте в виду, что вы должны присвоить ячейкам Cells[C,R] какие-либо значения прежде, чем чем вы сможете иметь доступ к Objects[C,R]. Установите у вновь созданного компонента свойство Visible в FALSE, а свойство parent в SELF. Осуществите другую необходимую инициализацию. Имейте в виду, что вы должны внести необходимые модули в список uses, если создаете тип компонента, которого нигде кроме как на форме нет.
Создайте процедуру, берущую координаты колонки/строки и правильно позиционирующую соотвествующий объект в пределах прямоугольника ячейки, например:
Code: Procedure TForm1.FixObjPosn(vCol, vRow: LongInt); {Размещаем содержимое компонента в области прямоугольника ячейки}. Var. R: TRect; Begin. R := StringGrid1.CellRect(vCol, vRow); If StringGrid1.Objects[vCol, vRow] is TControl then. With TControl(StringGrid1.Objects[vCol, vRow]) do. If R.Right = R.Left then {прямоугольник ячейки невидим}. Visible := False. Else. Begin. InflateRect(R, -1, -1); OffsetRect(R, StringGrid1.Left + 1, StringGrid1.Top + 1); BoundsRect := R; Visible := True; End; End;
Помещение VCL компонентов в область заголовка.
| Помещение VCL компонентов в область заголовка. |
Здесь есть хитрость:
Нужно разместить все необходимые элементы управления в отдельной форме, которая должна отслеживать перемещение и изменение размеров основной формы. Данная форма будет всегда находится над областью заголовка основной формы.
Нижеприведенный проект включает в себя 2 формы и выпадающий список (combobox). После запуска программы список появляется в области заголовка главной формы. Два ключевых вопроса: 1) организация перехвата сообщения WM_MOVE главной формы; и 2) возвращение фокуса в главную форму после того, как пользователь нажмет на каком-либо элементе управления, способным иметь фокус (например, TComboBox, TButton и др.).
Я использую 32-битную Delphi 2.0 под Win95, тем не менее данный код должен работать с любой версией Delphi.
Вот исходный код главной формы:
Code: Unit Unit1; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Type. TForm1 = class(TForm). Procedure FormResize(Sender: TObject); Procedure FormShow(Sender: TObject); Procedure FormHide(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. Procedure WMMove(var Msg: TWMMove); message WM_MOVE; End; Var. Form1: TForm1; Implementation. Uses Unit2; {$R *.DFM}. Procedure TForm1.FormResize(Sender: TObject); Begin. With Form2 do. Begin. {Заменим мои магические числа реальной информацией SystemMetrics}. Width := Form1.Width - 120; Top := Form1.Top + GetSystemMetrics(SM_CYFRAME); Left := ((Form1.Left + Form1.Width) - Width) - 60; End; End; Procedure TForm1.FormShow(Sender: TObject); Begin. Form2.Show; End; Procedure TForm1.FormHide(Sender: TObject); Begin. Form2.Hide; End; Procedure TForm1.WMMove(var Msg: TWMMove); Begin. Inherited; If (Visible) then. FormResize(Self); End; End.
Вот исходный код для псевдо-заголовка. Данная форма может содержать любые элементы управления VCL, которые вы хотите установить в области заголовка главной формы. По существу, это - независимый диалог со следующими свойствами:
Code: Caption='' {NULL строка}. Height={высота области заголовка}. Width={высота всех компонентов на форме}. BorderIcons=[] {пусто}. BorderStyle=bsNone. FormStyle=fsStayOnTop.
И, наконец, исходный код для Form2:
Code: Unit Unit2; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Type. TForm2 = class(TForm). ComboBox1: TComboBox; Procedure FormCreate(Sender: TObject); Procedure ComboBox1Change(Sender: TObject); Procedure FormResize(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. End; Var. Form2: TForm2; Implementation. Uses Unit1; {$R *.DFM}. Procedure TForm2.FormCreate(Sender: TObject); Begin. Height := ComboBox1.Height - 1; Width := ComboBox1.Width - 1; End; Procedure TForm2.ComboBox1Change(Sender: TObject); Begin. Form1.SetFocus; End; Procedure TForm2.FormResize(Sender: TObject); Begin. ComboBox1.Width := Width; End; End.
Файл проекта (.DPR) довольно простой:
Code: Program Project1; Uses. Forms, Unit1 in 'Unit1.pas' {Form1}, Unit2 in 'Unit2.pas' {Form2}; {$R *.RES}. Begin. Application.Initialize; Application.CreateForm(TForm1, Form1); Application.CreateForm(TForm2, Form2); Application.Run; End.
Это все!
Хотя некоторые авторы книг утверждают:
"Вы не можете установить компоненты Delphi в заголовок окна, точнее, не существует никакого способа установить их там.".
Зато существует иллюзия...
©Drkb::00346.
Взято с http://delphiworld.narod.ru.
| Как очистить экран в консольном приложении? |
Автор: Олег Кулабухов.
Нужно просто использовать GetConsoleScreenBufferInfo() для ввода нескольких пустых строк.
Code: Program Project1; {$APPTYPE CONSOLE}. Uses. Windows; {$R *.RES}. Var. Sbi: TConsoleScreenBufferInfo; I: integer; Begin. Writeln('A Console Applicaiton'); Writeln('Press Enter To Clear The Screen'); GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), Sbi); Readln; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), Sbi); For i := 0 to sbi.dwSize.y do. Writeln; Writeln('Press Enter To End'); Readln; End.
| Нахождение угла между радиус вектором и осью OX. |
Code: Function angl(y,x:extended):extended; assembler; Asm. Fld y {СТЕК!!! Сначала у, потом х}. Fld x. Fpatan. End;
| Как создать круглую форму? |
Здесь приведён полный пример того, как создать круглую форму.
Не забудьте создать TButton, чтобы окно можно было закрыть.
Code: Unit Unit1; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, Buttons, StdCtrls; Type. TForm1 = class(TForm). Button1: TButton; Procedure FormCreate(Sender: TObject); Procedure Button1Click(Sender: TObject); Private. { Private-Deklarationen}. Procedure CreateParams(var Params: TCreateParams); override; Public. { Public-Deklarationen}. End; Var. Form1: TForm1; Implementation. {$R *.DFM}. { TForm1 }. Procedure TForm1.CreateParams(var Params: TCreateParams); Begin. Inherited CreateParams(Params); { удаляем заголовок и рамку }. Params.Style := Params.Style or ws_popup xor ws_dlgframe; End; Procedure TForm1.FormCreate(Sender: TObject); Var. FormRgn: hRgn; Begin. {clear form}. Form1.Brush.Style := bsSolid; //bsclear; { делаем круг формы }. GetWindowRgn(Form1.Handle, FormRgn); { удаляем старый объект }. DeleteObject(FormRgn); { делаем прямоугольник формы }. Form1.Height := 500; Form1.Width := Form1.Height; { создаём круглую форму }. FormRgn := CreateRoundRectRgn(1, 1, Form1.Width - 1, Form1.height - 1, Form1.width, Form1.height); { устанавливаем новое круглое окно }. SetWindowRgn(Form1.Handle, FormRgn, TRUE); End; Procedure TForm1.Button1Click(Sender: TObject); Begin. Form1.close; End; End.
| Что такое DirectShow? |
На этот раз речь пойдет о DirectShow. Для чего нам может понадобиться DirectShow?
DirectShow - это архитектура для воспроизведения, перехвата и обработки потоков мультимедиа. Звучит туманно? Поясняю - c помощью этого API можно:
Проигрывать мультимедийные файлы различного формата, такие как MPEG (Motion Picture Experts Group), AVI (Audio-Video Interleaved), MP3 (MPEG Audio Layer-3), DVD и конечно WAV;
Перехватывать видео-поток с различного рода TV-карт, видеокамер и т.п.;
Создавать нестандартные обработчики мультимедиа-потоков и свои собственные форматы файлов (что, впрочем, вряд ли понадобится простым смертным);
Обращаться непосредственно к видео и аудио потокам, чтобы выводить их на Surface DirectDraw (что для нас как раз интересно).
Звучит заманчиво. Но для чего это может понадобиться, спросите Вы, вспоминая родной и привычный MediaPlayer. Представьте себе, что Вы запрограммировали трехмерный мир, с анимированными спрайтами, трехмерными объектами и т.п. И в отсвете выстрелов очередной "бродилки-стрелялки", пораженный пользователь нового шедевра, видит видео клип, воспроизводимый прямо на костях очередного пораженного монстра. :-) Каково? Убедил? Тогда продолжим.
Кстати, раз уж упомянули DirectDraw - DirectShow интегрирован с DirectX так, что использует DirectDraw и DirectSound для вывода изображения и звука, и, при наличии аппаратного ускорения, автоматически им воспользуется.
Для работы с DirectShow Вам понадобятся:
Иметь некоторое представление о технологии COM (Component Object Model) - хотя быть знатоком этой технологии вовсе не обязательно - просто достаточно знать, что для получения COM-интерфейса нужно вызвать QueryInterface;
Скачать заголовочные файлы DirectShow API, переведенные на Delphi в рамках проекта JEDI - (http://www.delphi-jedi.org/DelphiGraphics/DirectX/DX5Media.zip) и либо поместить их в каталог Delphi\Lib либо добавить путь к каталогу, в котором они находятся в установках Delphi Library Path.
DirectShow скорее всего уже установлен на Вашем компьютере - он входит в стандартную поставку Windows 9x, Windows NT 4 (Service Pack 3 и выше), Windows 2000 (если Вы программируете для UNIX или DOS - то я вообще не понимаю зачем Вы читаете эту статью).
Основы DirectShow.
В концепции DirectShow мультимедийные данные - это поток, который проходит через несколько обрабатывающих блоков. Блоки, обрабатывающие поток данных, передают данные по цепочке друг другу, таким образом можно представить себе несколько "устройств", каждое из которых выполняет какую-то обработку данных и передает их соседнему "устройству". Эти "устройства" или "блоки обработки" данных называют фильтрами. Цепочка, по которой передаются данные, содержит несколько фильтров, связанных определенным образом.
В DirectShow имеются готовые фильтры, из которых, словно из детских кубиков, программист может выстроить ту или иную цепочку обработку данных, кроме того, конечно, можно создать свои, нестандартные фильтры.
Для создания такой "цепочки обработки" (которая, кстати, официально называются Filter Graph - "граф фильтров" или, в несколько вольном переводе - "схема соединения фильтров"), так вот для создания схемы соединения фильтров, предназначен самый базовый и лежащий в основе всех основ компонет DirectShow, под названием Filter Graph Manager - Менеджер Графа Фильтров.
Например, программа показывающая видео из AVI-файла может построить такой граф фильтров:
В этом примере пять фильтров, первый (File Source) просто читает данные с диска, второй фильтр (AVI Splitter) разделяет данные на кадры и передает упакованные видео данные фильтру AVI Decompressor, который их распаковывает и передает фильтру Default DirectSound Device, выводящему звук. AVI Decompressor передает распакованные данные фильтру Video Renderer, который выводит кадры видео на экран.
Фильтры делятся на три типа:
Фильтры-источники (Source filters) - эти фильтры просто получают данные из какого-то источника, с диска (как фильтр File Source (Async) на рисунке), с CD или DVD дисковода или с TV- карты или карты, к которой подключена цифровая видеокамера.
Фильтры-преобразователи (Transform filters) - эти фильтры как видно из названия преобразуют поток данных, проходящий через них каким-либо образом, например - разделяет поток данных на кадры, производят декомпрессию и т.п. На нашем рисунке к таким фильтрам относятся AVI Splitter и AVI Decompressor.
Фильтры вывода (Renderer filters) - фильтры, которые получают полностью обработанные данные и выводят их на монитор, звуковую карту, пишут на диск или выводят на еще какое-нибудь устройство.
Итак из фильтров-кубиков можно высстраивать граф. Делается это с помощью интерфейса IGraphBuilder. Создать объект типа IGraphBuilder можно так:
CoCreateInstance(CLSID_FilterGraph,nil,CLSCTX_INPROC_SERVER,IID_IGraphBuilder,MyGraphBuilder);
Здесь переменная MyGraphBuilder имеет тип IGraphBuilder; идентификатор класса CLSID_FilterGraph и IID_IGraphBuilder обьявлены в файле DShow.pas, поэтому не забудьте добавить.
Uses DShow.pas.
Итак, интерфейс IGraphBuilder получен. Можно построить граф фильтров, такой, какой нам нужно. Впрочем, все не так сложно, IGraphBuilder достаточно интеллектуален, он может сам, автоматически, построить граф, в зависимости от того какие файлы мы собираемся воспроизводить. Интерфейс IGraphBuilder имеет метод RenderFile, который получает имя файла в качестве параметра и, в зависимости от типа файла (которое определяется по расширению и по специальным сигнатурам в файле), сканирует реестр, в поисках необходимой для построения графа информации, создает необходимые фильтры и строит граф, предназначенный для воспроизведения файлов этого типа (WAV, AVI, MP3, MPG и т.д.).
После построения графа DirectShow готов к воспроизведению. Для управления потоком данных через граф обработки предназначен интерфейс IMediaControl - он имеет методы Run, Pause и Stop (названия говорят сами за себя).
Давайте попробуем все это на примере:
Code: Uses. ... DShow, ActiveX,ComObj; Var. MyGraphBuilder : IGraphBuilder; MyMediaControl : IMediaControl; Begin. CoInitialize(nil); {получаем интерфейс IGraphBuilder}. CoCreateInstance(CLSID_FilterGraph,nil,CLSCTX_INPROC_SERVER,IID_IGraphBuilder,MyGraphBuilder); {вызываем RenderFile - граф фильтров строится автоматически}. MyGraphBuilder.RenderFile('cool.avi',nil); {получаем интерфейс ImediaControl}. MyGraphBuilder.QueryInterface(IID_IMediaControl,MyMediaControl); {Примечание - MyMediaControl - переменная типа IMediaControl}. {проигрываем видео}. MyMediaControl.Run; {ждем пока пользователь не нажмет ОК (видео воспроизводится в отдельном (thread) потоке)}. ShowMessage('Нажмите ОК'); CoUninitialize; End;
| Пример использования DirectInput для опроса клавиатуры. |
Code: {****************************************************************************** * * * Придумал и написал Кода Виктор, Март 2002 * * * * Файл: main.pas * * Содержание: Пример использования DirectInput для опроса клавиатуры * * * ******************************************************************************} Unit main; Interface. Uses. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ComCtrls, StdCtrls, ExtCtrls; Type. TForm1 = class(TForm). Gb1: TGroupBox; Gb2: TGroupBox; Gb3: TGroupBox; LbRemark: TLabel; ImView: TImage; RbWM: TRadioButton; RgDI8: TRadioButton; LbKeys: TLabel; LbIndex: TLabel; BtnClose: TButton; Procedure FormCreate(Sender: TObject); Procedure btnCloseClick(Sender: TObject); Procedure FormDestroy(Sender: TObject); Private. { Private declarations }. Public. { Public declarations }. Procedure Hook( var Msg: TMsg; var Handled: Boolean ); Procedure Idle( Sender: TObject; var Done: Boolean ); End; Var. Form1: TForm1; Implementation. {$R *.DFM}. Uses. DirectInput8; //------------------------------------------------------------------------------ // Константы и глобальные переменные. //------------------------------------------------------------------------------ Var. LpDI8: IDirectInput8 = nil; LpDIKeyboard: IDirectInputDevice8 = nil; NXPos, NYPos: Integer; //------------------------------------------------------------------------------ // Имя: InitDirectInput(). // Описание: Производит инициализацию объектов DirectInput в программе. //------------------------------------------------------------------------------ Function InitDirectInput( hWnd: HWND ): Boolean; Begin. Result := FALSE; // Создаём главный объект DirectInput. If FAILED( DirectInput8Create( GetModuleHandle( 0 ), DIRECTINPUT_VERSION, IID_IDirectInput8, lpDI8, nil ) ) then. Exit; LpDI8._AddRef(); // Создаём объект для работы с клавиатурой. If FAILED( lpDI8.CreateDevice( GUID_SysKeyboard, lpDIKeyboard, nil ) ) then. Exit; LpDIKeyboard._AddRef(); // Устанавливаем предопределённый формат для "простогй клавиатуры". В боль- // шинстве случаев можно удовлетвориться и установками, заданными в структуре. // c_dfDIKeyboard по умолчанию, но в особых случаях нужно заполнить её самому. If FAILED( lpDIKeyboard.SetDataFormat( @c_dfDIKeyboard ) ) then. Exit; // Устанавливаем уровень кооперации. Подробности о флагах смотри в DirectX SDK. If FAILED( lpDIKeyboard.SetCooperativeLevel( hWnd, DISCL_BACKGROUND or. DISCL_NONEXCLUSIVE ) ) then. Exit; // Захвытываем клавиатуру. LpDIKeyboard.Acquire(); Result := TRUE; End; //------------------------------------------------------------------------------ // Имя: ReleaseDirectInput(). // Описание: Производит удаление объектов DirectInput. //------------------------------------------------------------------------------ Procedure ReleaseDirectInput(); Begin. // Удаляем объект для работы с клавиатурой. If lpDIKeyboard <> nil then // Можно проверить if Assigned( DIKeyboard ). Begin. LpDIKeyboard.Unacquire(); // Освобождаем устройство. LpDIKeyboard._Release(); LpDIKeyboard := nil; End; // Последним удаляем главный объект DirectInput. If lpDI8 <> nil then. Begin. LpDI8._Release(); LpDI8 := nil; End; End; //------------------------------------------------------------------------------ // Имя: UpdateKeyboardState(). // Описание: Обрабатывает клавиатурный ввод методом DirectInput. //------------------------------------------------------------------------------ Function UpdateKeyboardState(): Boolean; Var. BKeyBuffer: array [0..255] of Byte; I: Integer; Hr: HRESULT; Begin. Result := FALSE; // Производим опрос состояния клавиш, данные записываются в буфер-массив. If lpDIKeyboard.GetDeviceState( SizeOf( bKeyBuffer ), @bKeyBuffer ) = DIERR_INPUTLOST then. Begin. // Захватываем снова. LpDIKeyboard.Acquire(); // Производим повторный опрос. If FAILED( lpDIKeyboard.GetDeviceState( SizeOf( bKeyBuffer ), @bKeyBuffer ) ) then. Exit; End; // Изменяем координаты курсора. If bKeyBuffer[ DIK_NUMPAD4 ] = $080 then Dec( nXPos ); If bKeyBuffer[ DIK_NUMPAD6 ] = $080 then Inc( nXPos ); If bKeyBuffer[ DIK_NUMPAD8 ] = $080 then Dec( nYPos ); If bKeyBuffer[ DIK_NUMPAD2 ] = $080 then Inc( nYPos ); // Выводим список кодов нажатых клавиш. With Form1.lbKeys do. Begin. Caption := ''; For i := 0 to 255 do. If bKeyBuffer[ i ] = $080 then. If i <= 9 then Caption := Caption + Format( '0%d ', [ i ] ). Else Caption := Caption + Format( '%d ', [ i ] ); End; Result := TRUE; End; //------------------------------------------------------------------------------ // Имя: TForm1.Hook(). // Описание: Обрабатывает клавиатурный ввод подобно главной функции окна. //------------------------------------------------------------------------------ Procedure TForm1.Hook( var Msg: TMsg; var Handled: Boolean ); Var. I: Integer; Begin. If Msg.message <> WM_KEYDOWN then. Exit; // Изменяем координаты курсора. Case Msg.wParam of. VK_NUMPAD4: Dec( nXPos ); VK_NUMPAD6: Inc( nXPos ); VK_NUMPAD8: Dec( nYPos ); VK_NUMPAD2: Inc( nYPos ); End; // Выводим код нажатой клавиши. With Form1.lbKeys do. Begin. Caption := ''; // Бессмысленно писать for i := 0 to 255 do ... При обработке сообщения. // WM_KEYDOWN мы можем узнать состояние только одной клавиши - ведь массив. // не используется. Справедливоси ради надо сказать, что в Windows есть. // функция GetKeyboardState(), работающая с массивом и очень быстро. If Msg.wParam <= 9 then Caption := Caption + Format( '0%d ', [ Msg.wParam ] ). Else Caption := Caption + Format( '%d ', [ Msg.wParam ] ); End; // Блокируем дальнейшую обработку события. Handled := TRUE; End; //------------------------------------------------------------------------------ // Имя: TForm1.Idle(). // Описание: Вызывает функцию опроса состояния клавиатуры. //------------------------------------------------------------------------------ Procedure TForm1.Idle( Sender: TObject; var Done: Boolean ); Var. I: Integer; Begin. If rbWM.Checked then Application.OnMessage := Hook. Else. Begin. Application.OnMessage := nil; // Если данные от клавиатуры не получены. If not UpdateKeyboardState() then. Begin. MessageBox( Form1.Handle, 'Потеряно устройство управления!', 'Ошибка!', MB_ICONHAND ); Form1.Close(); End; End; // Проверяем выход курсора за пределы диапазона. If nXPos < 0 then nXPos := 0; If nXPos + 10 > 140 then nXPos := 130; If nYPos < 0 then nYPos := 0; If nYPos + 10 > 140 then nYPos := 130; // Рисуем курсор. With imView.Canvas do. Begin. FillRect( Canvas.ClipRect ); Brush.Color := clRed; Rectangle( nXPos, nYPos, nXPos + 10, nYPos + 10 ); Brush.Color := clWhite; End; Done := FALSE; End; //------------------------------------------------------------------------------ // Имя: TForm1.FormCreate(). // Описание: Производит инициализацию DirectInput при старте программы. //------------------------------------------------------------------------------ Procedure TForm1.FormCreate(Sender: TObject); Begin. If not InitDirectInput( Form1.Handle ) then. Begin. MessageBox( Form1.Handle, 'Ошибка при инициализации DirectInput!', 'Ошибка!', MB_ICONHAND ); ReleaseDirectInput(); Halt; End; // Приводим UI в соответствующий вид. LbKeys.Caption := ''; // Назначаем обработчик Idle-события. Компонент TTimer не позволит раскрыть. // всех преимуществ использования DirectInput. Application.OnIdle := Idle; End; //------------------------------------------------------------------------------ // Имя: TForm1.btnCloseClick(). // Описание: Закрывает программу. //------------------------------------------------------------------------------ Procedure TForm1.btnCloseClick(Sender: TObject); Begin. Form1.Close(); End; //------------------------------------------------------------------------------ // Имя: TForm1.FormDestroy(). // Описание: Вызывается при удалении программы из памяти. //------------------------------------------------------------------------------ Procedure TForm1.FormDestroy(Sender: TObject); Begin. ReleaseDirectInput(); End; End.
Содержание.
- Работа с последовательными портами 2.
- Как работать со всеми ячейками таблицы в WebBrowsere.
- Не работает функция Writeln.
- Вывод надписи поверх всех окон.
- Пишем свой текст в Меню.
- Сохранение и чтение из потока данных объекта.
- Пишем ScreenSaver в Delphi.
- Написание программ на чистом API.
- Написание программ на чистом API 2.
- Писать в порт принтера.
- Как писать Win32API приложения на Delphi.
- Что нужно давать WSAAsyncSelect в качестве параметра handle.
- Использование функции wsprintf.
- Примеры форматирования строк с использованием API-функции wvsprintf.
- Как импортировать данные из Excel в StringGrid.
- Обмен информацией в формате XML.
- Динамическое создание меню по XML-файлу.
- Парсер подавляющего большинства нотаций XML.
- Разбор XML.
- XML сериализация объекта Delphi.
- XML сериализация объектов - Заключение.
- Компонент для XML сериализации.
- Использование XML в среде Delphi.
- Крестики - нолики с коброй.
- Пример компонента типа XPButton.
- Включить Drop Shadow Effect окна в XP.
- Использовать визуальный стиль XP для своего приложения.
- Показать символы для пароля в XP.
- Создать Системную Точку Восстановления (XP).
- Преобразование даты и времени в строковый вид YYYYMMDDHHNNSS и обратно.
- Технология dbExpress (статья).
- Драйверы доступа к данным.
- Отладка приложений с технологией dbExpress.
- Библиотека KOL.
- Список зарезервированных слов Local SQL BDE.
- Каковы текущие ограничения BDE?
- Delphi Run-Time Error Codes.
- Типы файлов в среде Дельфи.
- Как заставить стартовать дельфи без заставки?
- Как мне избавиться от выскакивающего окна CPU при ошибках?
- Как заставить стартовать дельфи без проекта?
- Список префиксов Internal Error.
- Проблема использования компонентов в D6/7 от предыдущих версий.
- Как использовать клавишу-акселератор в TTabsheets?
- Можно ли динамически изменять свойство owner компонента во время выполнения программы?
- Как определить наличие сопроцессора?
- Как узнать серийный номер аудио CD?
- Декомпиляция в Delphi.
- Как сделать меню как в Дельфи?
- Kylix Tutorial. Часть 1. Установка.
- Как включить/выключить спикер?
- Свойства.
- Обзор Kylix.
- Использование SQLMonitor (DBExpress).
- Как разделить обработку OnClick и OnDblClick? Ведь OnClick будет вызываться всегда, и перед DblClick.
- Какой формат данных предпочесть в Delphi? DBase или Paradox?
- Как восстановить индекс Paradox?
- Получение имени конфигурации HardWare profile.
- Получение уровня процессора.
- Получение количества установленных процессоров.
- Работа с базами данных.
- Конфигурация и настройка.
- 10 вещей которые убедят вас перейти на сервер 2005.
- Распространение приложений с технологией dbExpress.
- Работа с сессиями.
- Обработка исключений.
- Поддержка блокировок.
- Поддержка курсоров.
- Работа с индексами.
- Работа с запросами.
- Работа с таблицами.
- Доступ к данным.
- Управление транзакциями.
- Схемы и возможности.
- Работа с форматами данных.
- Database index out of date error.
- BDE Error 2B04 Too many open files.
- Как создать DBase базу данных?
- Как получить текущее время?
- Как восстановить целостность автоинкрементного поля?
- Как остановить или запустить IB сервис?
- Ошибка: lock manager out of room.
- Ошибка о файле SAA.AAA.
- Как скопировать таблицу из одной базы данных в другую?
- Как получить дату BIOS?
- Ограничения Foxpro.
- Использование ADO (статья).
- Как получить информацию о BIOS в Windows 9x?
- Ограничения Paradox.
- Как получить средний цвет между двумя цветами?
- Как получить информацию о BIOS в Windows NT/2000/XP?
- CUR ---> BMP.
- Как работать с DWG файлами (AutoCAD)?
- Компонент для работы с PCX файлами.
- TXT ---> GIF.
- BMP ---> AVI (для TAnimate).
- Как сделать 24bit dithering?
- Как сделать greyscale dithering?
- Как повернуть элипс?
- Как получить handle на editbox в Internet Explorer?
- Как сделать свои собственные сообщения при компилляции?
- Как очистить буфер клавиатуры?
- Как найти список параллельных портов?
- Как получить размер физической установленной памяти?
- Как получить инфу о SCSI дисках?
- Скрытые настройки Дельфи.
- Как проверить инсталлирован ли MS Word?
- Как заменить значение переменных для текста MS Word?
- Как узнать версию MS Word?
- Конвертация римских цифр в арабские.
- Как сделать виртуальный диск?
- Как сменить пароль для таблицы Paradox?
- Borland Delphi 7 - миграция в сторону .Net.
- Русификация Kylix.
- Что из себя представляет TWebBrowser?
- Где можно найти документацию на WebBrowser?
- Перечислители.
- Как получить полный исходник HTML?
- Как получить POST данные?
- Что такое DOM?
- Как скрыть своё приложение?
- Липкие окошки.
- Объекты соединения с источниками данных.
- Bitmap.PixelFormat - подробности.
- Сравнительный анализ технологий CORBA и COM.
- Что такое MIDAS (multi-tired distributed application service)?
- Что такое реестр?
- Что такое сокет?
- Как выключить, перезагрузить или завершить Windows?
- ADO+Delphi5: постоянные ошибки BOF... или EOF...
- Арифметика указателей.
- Описание протокола ARP (Address Resolution Protocol).
- ASCII драйвер для CSV-файлов.
- Использование BDE приложений в Peer-To-Peer сети.
- Протокол блокировки BDE.
- Вызов c-шной функции с переменным числом параметров.
- Посылать и считывать данные с COM порта, а также менять параметры (биты данных, четность).
- Пример программирования com портов.
- Помещение компонентов в DBGrid.
- Помещение VCL компонентов в область заголовка.