type
TCallBack=procedure (Position,Size:Longint); {Для индикации процесса копирования}
procedure FastFileCopy(Const InfileName, OutFileName: String; CallBack: TCallBack);
Const BufSize = 3*4*4096; { 48Kbytes дает прекрасный результат }
type
PBuffer = ^TBuffer;
TBuffer = array [1..BufSize] of Byte;
var
Size : integer;
Buffer : PBuffer;
infile, outfile : File;
SizeDone,SizeFile: Longint;
begin
if (InFileName <> OutFileName) then
begin
buffer := Nil;
AssignFile(infile, InFileName);
System.Reset(infile, 1);
try
SizeFile := FileSize(infile);
AssignFile(outfile, OutFileName);
System.Rewrite(outfile, 1);
try
SizeDone := 0; New(Buffer);
repeat
BlockRead(infile, Buffer^, BufSize, Size);
Inc(SizeDone, Size);
CallBack(SizeDone, SizeFile);
BlockWrite(outfile,Buffer^, Size)
until Size < BufSize;
FileSetDate(TFileRec(outfile).Handle,
FileGetDate(TFileRec(infile).Handle));
finally
if Buffer <> Nil then Dispose(Buffer);
System.close(outfile)
end;
finally
System.close(infile);
end;
end else
Raise EInOutError.Create(’File cannot be copied into itself’);
end;
Июнь
1,
2008
— Рубрика: Delphi
Метки: Pascal, Копирование
Продолжим нашу работу над медиа-проигрывателем, основанным на Windows Media Player. Хочется подчеркнуть, что кардинально изменить что-либо не удастся, поэтому в этой статье я просто расскажу о небольших усовершенствованиях и на этом мы остановимся.
Обычно во время воспроизведения мы должны видеть название файла, который проигрывается в данное время. Давайте снабдим проигрыватель этой функцией. Для этого изменим обработчик пункта меню “Открыть” на следующее:
if OpenDialog.Execute then
begin
MediaPlayer.URL:=OpenDialog.FileName;
Form1.Caption:=’SMP - ‘+ExtractFileName(OpenDialog.FileName);
end;
Как работает добавленная строка? Функция ExtractFileName() возвращает имя файла из указанного пути - то, что нам нужно. “SMP” - это сокращённо “Simple Media Player”
Наконец, значение присваивается заголовку формы.
Что ещё можно добавить? Да, действительно странный проигрыватель… Можно сделать окошко “О программе”. Для этого в меню добавьте соответствующий пункт и назовите его “О программе…” По правилам Windows любая надпись, приводящая к открытию диалогового окна, должна заканчиваться многоточием - “…” Рекомендую запомнить это правило и всегда и везде его использовать. Окно можно создать “с нуля”: File - New - Form (в разных версиях Delphi названия пунктов меню могут отличаться). А ещё можно воспользоваться окном, подготовленным разработчиками - открываем File - New - Other, переходим на вкладку Forms, выбираем “About box” и нажимаем “ОК”. В результате создаётся форма. В ней напишите название программы, своё имя, версию… Далее создаём обработчик для созданного пункта меню
AboutBox.ShowModal;
Пытаемся запустить программу, но Delphi выдаёт предупреждение, что второй модуль не связан с первым и предлагает это сделать. Соглашаемся, и приложение запускается.
Вот и всё. Конечно, такому проигрывателю далеко до Winamp, Light Alloy и даже до стандартного Windows Media Player, но основы, я думаю, понятны. Если вы повнимательнее изучите список ActiveX-компонент, то найдёте там много интересного.
Май
28,
2008
— Рубрика: Delphi
Метки: File, Winamp, медиа-проигрыватель
ростейший медиа-проигрыватель можно создать стандартными средствами Windows. Существуют модули, которые доступны из любого языка программирования - это ActiveX. Delphi в этом плане не исключение. Встроенный в систему Windows Media Player имеет собственный ActiveX-модуль, который мы легко можем использовать в своей программе. Правда, кардинально изменить в нём что-либо не получится. В данной статье я покажу пример, как использовать этот компонент.
Для начала ActiveX-компонент (сокращённо AX) нужно интегрировать в оболочку Delphi. Делается это легко: открываем диалоговое окно Component - Import ActiveX Control. В разных версиях Delphi этот пункт меню может называться по-разному, но ключевым остаётся слово ActiveX. В открывшемся окне в списке компонент найдите строку Windows Media Player. Нажмите кнопку Install. В появившемся окне можно указать, в какой пакет следует установить компонент. Можно оставить всё по умолчанию и нажать ОК. После этого на вкладке ActiveX палитры компонент появится кнопка WindowsMediaPlayer. Теперь можно приступить к созданию проигрывателя.
Для начала поместите компонент на форму - щёлкните по его значку и затем по форме. Вы увидите знакомое изображение - изображение проигрывателя. Его внешний вид зависит от версии установленного в системе компонента. Примерный вид того, что вы увидите, изображён на рисунке 1.
рис. 1
рис. 2
Для начала давайте сделаем возможность открытия нужного файла. За путь к файлу, который нужно воспроизводить, отвечает свойство URL типа WideString (строка). Давайте сделаем для программы меню, куда и добавим нужный нам пункт. Поместите на форму TMainMenu (страница Standard). Дважды щёлкните по значку TMainMenu, лежащему на форме. Откроется дизайнер меню. Выделите единственный пункт в заголовке меню и в его свойстве Caption напишите “Файл”. Затем щёлкните по созданному пункту и снизу появистя ещё одна пустая строка. Там и введите название нашего пункта - “Открыть”. Следующей строкой давайте создадим разделитель для меню - в свойстве Caption поставьте один-единственный символ - минус (”-”). Следующей строкой сделайте пункт “Выход”. Ну вот, программа уже имеет меню, хотя и небольшое. Теперь нужно связать пункт меню с диалогом открытия файла. Найдите компонент TOpenDialog на странице Dialogs и поместите его на форму. Активируйте свойство Filter и нажмите на кнопку с тремя точками (”…”). В появившемся окне введите желаемые типы файлов. В поле Filter Name нужно ввести описание типов файлов, а в поле Filter - сам фильтр. Пример на рисунке 2.
Чтобы с компонентом было удобно работать, измените его свойство Name на MediaPlayer.
Выделите компонент и перейдите в окно Object Inspector. Свойств (Properties) у компонента не очень много, а вот событий (Events) гораздо больше. Но мы используем лишь некоторые из них.
Для удобства, компонент MainMenu1 переименуем в MainMenu, а OpenDialog1 - в OpenDialog.
Пришло время запрограммировать пункт меню “Открыть”. Откройте дизайнер меню и дважды щёлкните по нужному пункту - откроется редактор кода. В этом месте следует написать:
if OpenDialog.Execute then
MediaPlayer.URL:=OpenDialog.FileName;
Метод Execute открывает диалог и, если пользователь не нажал “Отмена”, то функция возвращает значение True, а в FileName записывается путь к выбранному файлу. Затем мы присваиваем свойству URL проигрывателя выбранный файл. Запустите программу и попробуйте открыть файл. Всё должно работать, файл должен воспроизводиться.
Теперь давайте приведём окно проигрывателя к нормальному виду. Сам проигрыватель нужно растянуть на всю ширину окна - измените его свойство Align на alClient. Саму форму стоит сделать поменьше, т.к. наш проигрыватель пока что воспроизводит только аудио-файлы. На мой взгляд, для формы можно установить ширину (Width) равной 300, а высоту (Height) - 200. Наконец, можно изменить вид окна и установить BorderStyle в значение bsSizeToolWin, а заголовок (Caption) - “Simple Media Player”.
Теперь запрограммируем пункт “Выход” - для этого следует написать в его обработчике всего одну строку:
Self.Close;
Этот метод закрывает текущую форму (Self - объект, выбранный по умолчанию, форма) и завершает приложение.
Теперь нужно сделать в меню элементарные команды управления воспроизведением - Start, Stop, Pause. Конечно, в проигрывателе и без того есть существующие кнопки, но они должны иметь своё отражение и в меню.
Для начала добавьте в заглавную строку меню (туда, где “Файл”) пункт “Управление”. Затем добавьте два пункта - “Старт / Стоп” и “Пауза”. Запрограммировать эти команды легко. Для управление воспроизведением служит набор методов Controls, а узнать текущее состояние проигрывателя можно из свойства PlayState. Вот как выглядит обработчик пункта “Старт / Стоп”:
if MediaPlayer.PlayState =wmppsPlaying then
MediaPlayer.Controls.Stop
else
MediaPlayer.Controls.Play;
А вот обработчик “Паузы”:
if MediaPlayer.PlayState =wmppsPlaying then
MediaPlayer.Controls.Pause;
Запустите программу и проверьте её работоспособность.
рис. 3
Как видите, мы создали сравнительно простой, но рабочий проигрыватель. В следующий раз мы продолжим его разработку. Примерное изображение окна программы после выполнения всех действий, описанных в статье, показано на рисунке 3.
Май
20,
2008
— Рубрика: Delphi
Метки: медиа-проигрыватель
Алгоритм пирамидальной сортировки (heapsort) - один из самых быстрых алгоритмов сортировки.
Program heapsort;
{$APPTYPE CONSOLE}
type
tkey = integer;
int = integer;
const N = 10;
var a,b : array [0..N+1] of tkey;
function parent(x : int) : int;
begin
result:=x shr 1;
end;
function left(x : int) : int;
begin
result := x shl 1;
if result > a[0] then result := N+1;
end;
function right(x:int):int;
begin
result := x shl 1 + 1;
if result > a[0] then result := N+1;
end;
procedure swap(i,j : int);
var temp : tkey;
begin
temp := a[i];
a[i] := a[j];
a[j] := temp;
end;
procedure moveup(x : int);
begin
while (a[x] > a[parent(x)]) and (parent(x) > 0) do begin
swap(x, parent(x));
x := parent(x);
end;
end;
procedure movedown(x : int);
var max : integer;
begin
if a[left(x)] > a[right(x)] then max := left(x)
else max := right(x);
while (a[max] > a[x]) and (max <= a[0]) do begin
swap(max, x);
x := max;
if a[left(x)] > a[right(x)] then max := left(x)
else max := right(x);
end;
end;
procedure update(x : int; k : tkey);
begin
a[x] := k;
moveup(x);
movedown(x);
end;
procedure add(k : tkey);
begin
inc(a[0]);
update(a[0], k);
end;
procedure delete(x : int);
begin
swap(x, a[0]);
dec(a[0]);
update(x, a[x]);
end;
procedure hsort;
var i:int;
begin
a[0] := 1;
a[1] := b[1];
for i := 2 to N do
add(b[i]);
for i := 1 to N do
delete(1);
end;
var i : int;
begin
randomize;
fillchar(a, sizeof(a), 0);
fillchar(b, sizeof(b), 0);
for i := 1 to N do
b[i] := random(10);
writeln(’Non-sorted elements’);
for i := 1 to N do
write(b[i], ‘ ‘);
writeln;
hsort;
writeln(’Sorted elements’);
for i := 1 to N do
write(a[i], ‘ ‘);
readln;
end.
Май
19,
2008
— Рубрика: Delphi
Метки: Пирамидальная сортировка
Главное о чем стоит упомянуть это, что ваш хранитель экрана будет работать в фоновом режиме и он не должен мешать работе других запущенных программ. Поэтому сам хранитель должен быть как можно меньшего объема. Для уменьшения объема файла в описанной ниже программе не используется визуальные компоненты Delphi, включение хотя бы одного из них приведет к увеличению размера файла свыше 200кб, а так, описанная ниже программа, имеет размер всего 20кб!!!
Технически, хранитель экрана является нормальным EXE файлом (с расширением .SCR), который управляется через командные параметры строки. Например, если пользователь хочет изменить параметры вашего хранителя, Windows выполняет его с параметром “-c” в командной строке. Поэтому начать создание вашего хранителя экрана следует с создания примерно следующей функции:
Procedure RunScreenSaver;
Var S : String;
Begin
S := ParamStr(1);
If (Length(S) > 1) Then Begin
Delete(S,1,1); { delete first char - usally “/” or “-” }
S[1] := UpCase(S[1]);
End;
LoadSettings; { load settings from registry }
If (S = ‘C’) Then RunSettings
Else If (S = ‘P’) Then RunPreview
Else If (S = ‘A’) Then RunSetPassword
Else RunFullScreen;
End;
Поскольку нам нужно создавать небольшое окно предварительного просмотра и полноэкранное окно, их лучше объединить используя единственный класс окна. Следуя правилам хорошего тона, нам также нужно использовать многочисленные нити. Дело в том, что, во-первых, хранитель не должен переставать работать даже если что-то “тяжелое” случилось, и во-вторых, нам не нужно использовать таймер.
Процедура для запуска хранителя на полном экране - приблизительно такова:
Procedure RunFullScreen;
Var
R : TRect;
Msg : TMsg;
Dummy : Integer;
Foreground : hWnd;
Begin
IsPreview := False; MoveCounter := 3;
Foreground := GetForegroundWindow;
While (ShowCursor(False) > 0) do ;
GetWindowRect(GetDesktopWindow,R);
CreateScreenSaverWindow(R.Right-R.Left,R.Bottom-R.Top,0);
CreateThread(nil,0,@PreviewThreadProc,nil,0,Dummy);
SystemParametersInfo(spi_ScreenSaverRunning,1,@Dummy,0);
While GetMessage(Msg,0,0,0) do Begin
TranslateMessage(Msg);
DispatchMessage(Msg);
End;
SystemParametersInfo(spi_ScreenSaverRunning,0,@Dummy,0);
ShowCursor(True);
SetForegroundWindow(Foreground);
End;
Во-первых, мы проинициализировали некоторые глобальные переменные (описанные далее), затем прячем курсор мыши и создаем окно хранителя экрана. Имейте в виду, что важно уведомлять Windows, что это - хранителя экрана через SystemParametersInfo (это выводит из строя Ctrl-Alt-Del чтобы нельзя было вернуться в Windows не введя пароль). Создание окна хранителя:
Function CreateScreenSaverWindow(Width,Height : Integer;
ParentWindow : hWnd) : hWnd;
Var WC : TWndClass;
Begin
With WC do Begin
Style := cs_ParentDC;
lpfnWndProc := @PreviewWndProc;
cbClsExtra := 0; cbWndExtra := 0; hIcon := 0; hCursor := 0;
hbrBackground := 0; lpszMenuName := nil;
lpszClassName := ‘MyDelphiScreenSaverClass’;
hInstance := System.hInstance;
end;
RegisterClass(WC);
If (ParentWindow 0) Then
Result := CreateWindow(’MyDelphiScreenSaverClass’,'MySaver’,
ws_Child Or ws_Visible or ws_Disabled,0,0,
Width,Height,ParentWindow,0,hInstance,nil)
Else Begin
Result := CreateWindow(’MyDelphiScreenSaverClass’,'MySaver’,
ws_Visible or ws_Popup,0,0,Width,Height, 0,0,hInstance,nil);
SetWindowPos(Result,hwnd_TopMost,0,0,0,0,swp_NoMove or swp_NoSize or swp_NoRedraw);
End;
PreviewWindow := Result;
End;
Теперь окна созданы используя вызовы API. Я удалил проверку ошибки, но обычно все проходит хорошо, особенно в этом типе приложения.
Теперь Вы можете погадать, как мы получим handle родительского окна предварительного просмотра ? В действительности, это совсем просто: Windows просто передает handle в командной строке, когда это нужно. Таким образом:
Procedure RunPreview;
Var
R : TRect;
PreviewWindow : hWnd;
Msg : TMsg;
Dummy : Integer;
Begin
IsPreview := True;
PreviewWindow := StrToInt(ParamStr(2));
GetWindowRect(PreviewWindow,R);
CreateScreenSaverWindow(R.Right-R.Left,R.Bottom-R.Top,PreviewWindow);
CreateThread(nil,0,@PreviewThreadProc,nil,0,Dummy);
While GetMessage(Msg,0,0,0) do Begin
TranslateMessage(Msg); DispatchMessage(Msg);
End;
End;
Как Вы видите, window handle является вторым параметром (после “-p”).
Чтобы “выполнять” хранителя экрана - нам нужна нить. Это создается с вышеуказанным CreateThread. Процедура нити выглядит примерно так:
Function PreviewThreadProc(Data : Integer) : Integer; StdCall;
Var R : TRect;
Begin
Result := 0; Randomize;
GetWindowRect(PreviewWindow,R);
MaxX := R.Right-R.Left; MaxY := R.Bottom-R.Top;
ShowWindow(PreviewWindow,sw_Show); UpdateWindow(PreviewWindow);
Repeat
InvalidateRect(PreviewWindow,nil,False);
Sleep(30);
Until QuitSaver;
PostMessage(PreviewWindow,wm_Destroy,0,0);
End;
Нить просто заставляет обновляться изображения в нашем окне, спит на некоторое время, и обновляет изображения снова. А Windows будет посылать сообщение WM_PAINT на наше окно (не в нить !). Для того, чтобы оперировать этим сообщением, нам нужна процедура:
Function PreviewWndProc(Window : hWnd; Msg,WParam,
LParam : Integer): Integer; StdCall;
Begin
Result := 0;
Case Msg of
wm_NCCreate : Result := 1;
wm_Destroy : PostQuitMessage(0);
wm_Paint : DrawSingleBox; { paint something }
wm_KeyDown : QuitSaver := AskPassword;
wm_LButtonDown, wm_MButtonDown, wm_RButtonDown, wm_MouseMove :
Begin
If (Not IsPreview) Then Begin
Dec(MoveCounter);
If (MoveCounter <= 0) Then QuitSaver := AskPassword;
End;
End;
Else Result := DefWindowProc(Window,Msg,WParam,LParam);
End;
End;
Если мышь перемещается, кнопка нажала, мы спрашиваем у пользователя пароль:
Function AskPassword : Boolean;
Var
Key : hKey;
D1,D2 : Integer; { two dummies }
Value : Integer;
Lib : THandle;
F : TVSSPFunc;
Begin
Result := True;
If (RegOpenKeyEx(hKey_Current_User,'Control Panel\Desktop',0,
Key_Read,Key) = Error_Success) Then
Begin
D2 := SizeOf(Value);
If (RegQueryValueEx(Key,'ScreenSaveUsePassword',nil,@D1,
@Value,@D2) = Error_Success) Then
Begin
If (Value 0) Then Begin
Lib := LoadLibrary('PASSWORD.CPL');
If (Lib > 32) Then Begin
@F := GetProcAddress(Lib,’VerifyScreenSavePwd’);
ShowCursor(True);
If (@F nil) Then Result := F(PreviewWindow);
ShowCursor(False);
MoveCounter := 3; { reset again if password was wrong }
FreeLibrary(Lib);
End;
End;
End;
RegCloseKey(Key);
End;
End;
Это также демонстрирует использование registry на уровне API. Также имейте в виду как мы динамически загружаем функции пароля, используюя LoadLibrary. Запомните тип функции?
TVSSFunc ОПРЕДЕЛЕН как:
Type
TVSSPFunc = Function(Parent : hWnd) : Bool; StdCall;
Теперь почти все готово, кроме диалога конфигурации. Это запросто:
Procedure RunSettings;
Var Result : Integer;
Begin
Result := DialogBox(hInstance,’SaverSettingsDlg’,0,@SettingsDlgProc);
If (Result = idOK) Then SaveSettings;
End;
Трудная часть -это создать диалоговый сценарий (запомните: мы не используем здесь Delphi формы!). Я сделал это, используя 16-битовую Resource Workshop (остался еще от Turbo Pascal для Windows). Я сохранил файл как сценарий (текст), и скомпилированный это с BRCC32:
SaverSettingsDlg DIALOG 70, 130, 166, 75
STYLE WS_POPUP | WS_DLGFRAME | WS_SYSMENU
CAPTION “Settings for Boxes”
FONT 8, “MS Sans Serif”
BEGIN
DEFPUSHBUTTON “OK”, 5, 115, 6, 46, 16
PUSHBUTTON “Cancel”, 6, 115, 28, 46, 16
CTEXT “Box &Color:”, 3, 2, 30, 39, 9
COMBOBOX 4, 4, 40, 104, 50, CBS_DROPDOWNLIST | CBS_HASSTRINGS
CTEXT “Box &Type:”, 1, 4, 3, 36, 9
COMBOBOX 2, 5, 12, 103, 50, CBS_DROPDOWNLIST | CBS_HASSTRINGS
LTEXT “Boxes Screen Saver for Win32 Copyright (c) 1996 Jani
Jдrvinen.”, 7, 4, 57, 103, 16,
WS_CHILD | WS_VISIBLE | WS_GROUP
END
Почти также легко сделать диалоговое меню:
Function SettingsDlgProc(Window : hWnd; Msg,WParam,LParam : Integer): Integer; StdCall;
Var S : String;
Begin
Result := 0;
Case Msg of
wm_InitDialog : Begin
{ initialize the dialog box }
Result := 0;
End;
wm_Command : Begin
If (LoWord(WParam) = 5) Then EndDialog(Window,idOK)
Else If (LoWord(WParam) = 6) Then EndDialog(Window,idCancel);
End;
wm_Close : DestroyWindow(Window);
wm_Destroy : PostQuitMessage(0);
Else Result := 0;
End;
End;
После того, как пользователь выбрал некоторые установочные параметры, нам нужно сохранить их.
Procedure SaveSettings;
Var
Key : hKey;
Dummy : Integer;
Begin
If (RegCreateKeyEx(hKey_Current_User,
‘Software\SilverStream\SSBoxes’,
0,nil,Reg_Option_Non_Volatile,
Key_All_Access,nil,Key,
@Dummy) = Error_Success) Then Begin
RegSetValueEx(Key,’RoundedRectangles’,0,Reg_Binary,
@RoundedRectangles,SizeOf(Boolean));
RegSetValueEx(Key,’SolidColors’,0,Reg_Binary, @SolidColors,SizeOf(Boolean));
RegCloseKey(Key);
End;
End;
Загружаем параметры так:
Procedure LoadSettings;
Var
Key : hKey;
D1,D2 : Integer; { two dummies }
Value : Boolean;
Begin
If (RegOpenKeyEx(hKey_Current_User,
‘Software\SilverStream\SSBoxes’,0,
Key_Read,
Key) = Error_Success) Then Begin
D2 := SizeOf(Value);
If (RegQueryValueEx(Key,’RoundedRectangles’,nil,@D1,
@Value, @D2) = Error_Success) Then
Begin
RoundedRectangles := Value;
End;
If (RegQueryValueEx(Key,’SolidColors’,nil,@D1,
@Value,@D2) = Error_Success) Then
Begin
SolidColors := Value;
End;
RegCloseKey(Key);
End;
End;
Легко? Нам также нужно позволить пользователю, установить пароль. Я честно не знаю почему это оставлено разработчику приложений! Тем не менее:
Procedure RunSetPassword;
Var
Lib : THandle;
F : TPCPAFunc;
Begin
Lib := LoadLibrary(’MPR.DLL’);
If (Lib > 32) Then Begin
@F := GetProcAddress(Lib,’PwdChangePasswordA’);
If (@F nil) Then F(’SCRSAVE’,StrToInt(ParamStr(2)),0,0);
FreeLibrary(Lib);
End;
End;
Мы динамически загружаем (недокументированную) библиотеку MPR.DLL, которая имеет функцию, чтобы установить пароль хранителя экрана, так что нам не нужно беспокоиться об этом.
TPCPAFund ОПРЕДЕЛЕН как:
Type
TPCPAFunc = Function(A : PChar; Parent : hWnd; B,C : Integer) : Integer; StdCall;
(Не спрашивайте меня что за параметры B и C !
Теперь единственная вещь, которую нам нужно рассмотреть, - самая странная часть: создание графики. Я не великий ГУРУ графики, так что Вы не увидите затеняющие многоугольники, вращающиеся в реальном времени. Я только сделал некоторые ящики.
Procedure DrawSingleBox;
Var
PaintDC : hDC;
Info : TPaintStruct;
OldBrush : hBrush;
X,Y : Integer;
Color : LongInt;
Begin
PaintDC := BeginPaint(PreviewWindow,Info);
X := Random(MaxX); Y := Random(MaxY);
If SolidColors Then
Color := GetNearestColor(PaintDC,RGB(Random(255),Random(255),Random(255)))
Else Color := RGB(Random(255),Random(255),Random(255));
OldBrush := SelectObject(PaintDC,CreateSolidBrush(Color));
If RoundedRectangles Then
RoundRect(PaintDC,X,Y,X+Random(MaxX-X),Y+Random(MaxY-Y),20,20)
Else Rectangle(PaintDC,X,Y,X+Random(MaxX-X),Y+Random(MaxY-Y));
DeleteObject(SelectObject(PaintDC,OldBrush));
EndPaint(PreviewWindow,Info);
End;
И последнее - глобальные переменные:
Var
IsPreview : Boolean;
MoveCounter : Integer;
QuitSaver : Boolean;
PreviewWindow : hWnd;
MaxX,MaxY : Integer;
RoundedRectangles : Boolean;
SolidColors : Boolean;
Затем исходная программа проекта (.dpr). Красива, а!?
program MySaverIsGreat;
uses
windows, messages, Utility; { defines all routines }
{$R SETTINGS.RES}
begin
RunScreenSaver;
end.
Ох, чуть не забыл! Если, Вы используете SysUtils в вашем проекте (например фуекцию StrToInt) вы получите EXE-файл больше чем обещанный в 20k. Если Вы хотите все же иметь20k, надо как-то обойтись без SysUtils, например самому написать собственную StrToInt процедуру.
Если все же очень трудно обойтись без использования Delphi-форм, то можно поступить как в случае с вводом пароля: форму изменения параметров хранителя сохранить в виде DLL и динамически ее загружать при необходимости. Т.о. будет маленький и шустрый файл самого хранителя экрана и довеска DLL для конфигурирования и прочего (там объем и скорость уже не критичны).
Май
11,
2008
— Рубрика: Delphi
Метки: EXE-файл, ScreenSaver
JNI определяет 210 прикладных функций. Доступ к ним из С/С++-функции можно получить через интерфейсный указатель JNIENV*, который передается каждой С/С++-функции, представлющей реализацию собственного метода. Все функции разделены на 14 групп:
информация о версии JNI;
операции с классами;
исключения (EXCEPTIONS);
обработка глобальных и локальных ссылок;
операции с объектами;
доступ к данным объекта;
вызов методов объекта (INSTANCE METHOD);
доступ к статическим данным объекта;
вызов методов класса (CLASS METHOD);
операции со строковыми объектами;
операции с массивами;
регистрация собственных методов;
операции с мониторами (MONITOR OPERATIONS);
интерфейс с JVM.
Использование JNI функций необходимо только в том случае, если С/С++-функция осуществляет какое-либо взаимодействие с JVM: вызов JAVA-методов, доступ к данным, создание JAVA-объектов и т.д.
Ниже приведен пример JAVA-программы, которая выводит на печать количество свободной памяти на диске С для платформы WIN32. Для этого используется собственный метод и соответствующая реализационная С/С++-функция, вызывающая при своей работе функцию WIN32 API.
// Файл APP.JAVA
PUBLIC CLASS APP {
PUBLIC STATIC VOID MAIN(STRING ARGS[]) {
SYSTEMSPECIFIC SS = NEW SYSTEMSPECIFIC();
TRY {
LONG BYTES = SS.GETCDRIVEFREESPACE();
IF (BYTES != -1) {
LONG KB = BYTES / 1024;
SYSTEM.OUT.PRINTLN(”на диске C:\\ свободно ” + KB + ” KB”);
}
ELSE {
SYSTEM.OUT.PRINTLN(”произошла ошибка в С/С++-функции”);
}
}
CATCH (UNSATISFIEDLINKERROR E) {
SYSTEM.OUT.PRINTLN(”метод не найден (” + E + “)”);
}
}
}
CLASS SYSTEMSPECIFIC {
STATIC {
TRY {
SYSTEM.LOADLIBRARY(”SYSSPEC”);
}
CATCH (UNSATISFIEDLINKERROR E) {
SYSTEM.OUT.PRINTLN(”библиотека не найдена (” + E + “)”);
}
}
NATIVE LONG GETCDRIVEFREESPACE();
}
// Файл SYSTEMSPECIFIC.CPP
#INCLUDE “SYSTEMSPECIFIC.H”
#INCLUDE
JNIEXPORT JLONG JNICALL JAVA_SYSTEMSPECIFIC_GETCDRIVEFREESPACE (JNIENV *, JOBJECT) {
DWORD SCTRPERCLSTR, BYTESPERSCTR, FREECLSTR, CLSTR;
BOOL RES = GETDISKFREESPACE(”C:\\”, &SCTRPERCLSTR, &BYTESPERSCTR, &FREECLSTR, &CLSTR);
RETURN RES == TRUE ? SCTRPERCLSTR * BYTESPERSCTR * FREECLSTR : -1;
}
Для успешной компиляции кода JAVA и С/С++ на платформе WIN32 необходимо правильно установить переменные окружения PATH, LIB, INCLUDE и CLASSPATH (многие из этих значений можно задать как параметры соответствующих компиляторов).
Если записать исходные тексты предыдущего примера в файлы APP.JAVA и SYSTEMSPECIFIC.CPP соответственно, то для их компиляции необходимо выполнить следующие команды (предполагается, что исходные файлы находятся в каталоге C:\TEST\NATIVE):
C:\TEST\NATIVE> JAVAC APP.JAVA
C:\TEST\NATIVE> JAVAH -JNI SYSTEMSPECIFIC
C:\TEST\NATIVE> CL -W3 SYSTEMSPECIFIC.CPP -FESYSSPEC.DLL -TP -LD -MD -LINK JAVAI.LIB
Для запуска программы необходимо выполнить: C:\TEST\NATIVE> JAVA APP
на диске С:\ свободно 324567 KB
C:\TEST\NATIVE>
Для трансляции С/С++-файлов можно использовать любой компилятор, допускающий создание 32-битных DLL.
Май
7,
2008
— Рубрика: C++
Метки: JNI, функции
procedure FileCopy(Const SourceFileName, TargetFileName: String);
var
S,T : TFileStream;
begin
S := TFileStream.Create(sourcefilename, fmOpenRead );
try
T := TFileStream.Create(targetfilename, fmOpenWrite or fmCreate);
try
T.CopyFrom(S, S.Size ) ;
FileSetDate(T.Handle, FileGetDate(S.Handle));
finally
T.Free;
end;
finally
S.Free;
end;
end;
Май
3,
2008
— Рубрика: Delphi
Метки: FileCopy, Копирование, методом потока
JNI использует целый набор типов для своих функций и для формальных параметров С/С++-функций, представляющих реализацию собственных методов. Все эти типы описаны в файле JNI.H, который включается в любой заголовочный файл для JNI. Файл JNI.H использует стандартную технику препроцессирования с макросом _CPLUSPLUS. Тем самым, в зависимости от того, какой (С++ или С) код компилируется, будут создаваться две немного отличающиеся версии описания типов. Каждая из них требует определенного синтаксиса доступа.
Файл JNI_MD.H содержит системно-зависимые описания JINT, JLONG и JBYTE. В этом же файле определены макросы JNIEXPORT и JNICALL. Тип VOID используется без переопределения.
Следует отметить, что для представления строковых объектов JNI использует сокращенный вариант формата UTF-8.
Первым аргументом С/С++-функции, представляющей реализацию собственного метода, является указатель на структуру JNIENV. Смысл этого указателя определяет важную идею, лежащую в основе реализации JNI. Если отвлечься от конкретного языка, то указатель на JNIENV, или интерфейсный указатель (JNI INTERFACE POINTER), является указателем на массив указателей, каждый из которых указывает на прикладную функцию JNI. Только через этот указатель С/С++-функция может получить доступ к функциям и ресурсам JNI. В случае С++ (макрос _CPLUSPLUS определен) тип JNIENV является структурой, а в случае С - указателем на структуру. В силу этого, для доступа к функциям JNI в С и С++ применяется различный синтаксис:
// C
JNIEXPORT VOID JNICALL JAVA_SYSTEMSPECIFIC_DOSPECIFIC(JNIENV* ENV, JOBJECT THIS) {
JINT VERSION = (*ENV)->GETVERSION(ENV);
Е
}
// C++
JNIEXPORT VOID JNICALL JAVA_SYSTEMSPECIFIC_DOSPECIFIC(JNIENV* ENV, JOBJECT THIS) {
JINT VERSION = ENV->GETVERSION();
Е
}
Главным преимуществом такой организации функций JNI является легкость модификации и дальнейшего расширения интерфейса.
Указатель на JNIENV действителен только в текущем потоке (THREAD). JVM гарантирует передачу одного и того же интерфейсного указателя всем методам, вызываемым из данного потока. Тем самым запрещено передавать интерфейсный указатель другому потоку. Если методы вызываются из разных потоков, то в этом случае каждый метод получает различные интерфейсные указатели.
Если С/С++-функция представляет реализацию нестатического собственного метода, то вторым параметром функции является объект типа JOBJECT. Данный параметр является ссылкой на JAVA-объект, для которого был вызван соответствующий собственный метод. Если функция представляет статический собственный метод, то вторым параметром является объект типа JCLASS, определяющий JAVA-класс, для которого вызван собственный метод класса (CLASS METHOD).
Последующие параметры С/С++-функции соответствуют параметрам собственного метода (если собственный метод их не содержит, то реализующая его С/С++-функция имеет только два описанных выше параметра).
Май
3,
2008
— Рубрика: C++
Метки: JNI, структуры данных
Имя С/С++-функции формируется путем последовательного соединения следующих компонентов:
префикс JAVA_;
полное квалифицированное имя класса;
символ подчеркивания (”_”);
имя метода;
для перегружаемых (OVERLOADED) методов - два символа подчеркивания (”_ _”) с последующей сигнатурой метода.
Использование имен с сигнатурой на конце необходимо только в случае перегрузки двух или более собственных методов (перегрузка с обычным методом не важна, так как обычные методы не будут находиться в создаваемой библиотеке, что, однако, не допускает наличия собственного и обычного метода с одинаковыми именами и сигнатурами).
Для соответствия лексиграфическим правилам С/С++ и использования UNICODE-кодировки, применяются дополнительные правила преобразования, представленные в табл. 2.
Таблица 2Исходный символ Результирующая последовательность
“_” _1
“;” _2
“[" _3
символ UNICODE с кодом ХХХХ _0ХХХХ
Ниже приведен пример JAVA-класса с собственными методами:
PACKAGE TESTPACKAGE;
ABSTRACT CLASS TEST {
PUBLIC NATIVE VOID M1(STRING[] SA, OBJECT O, INT[][] IA2);
PUBLIC NATIVE FLOAT[] M1(DOUBLE D, TEST T);
PUBLIC NATIVE TEST M3(INT I);
}
и соответствующие им имена С/С++-функций:
JNIEXPORT VOID JNICALL JAVA_TESTPACKAGE_TEST_M1___3LJAVA_LANG_STRING_2LJAVA_LANG_OBJECT_2_3_3I
(JNIENV *, JOBJECT, JOBJECTARRAY, JOBJECT, JOBJECTARRAY);
JNIEXPORT JFLOATARRAY JNICALL JAVA_TESTPACKAGE_TEST_M1__LJAVA_LANG_DOUBLE_2LTESTPACKAGE_TEST_2
(JNIENV *, JOBJECT, JOBJECT, JOBJECT);
JNIEXPORT JOBJECT JNICALL JAVA_TESTPACKAGE_TEST_M3
(JNIENV *, JOBJECT, JINT);
Рассмотрим типы параметров, которые получает на входе С/С++-функция при ее вызове.
Май
1,
2008
— Рубрика: C++
Метки: С++, функции
Сигнатура метода - это сокращенная форма записи параметров метода и типов возвращаемого значения. Следует подчеркнуть, что в сигнатуру не входят ни имя метода, ни имена параметров. JNI формирует сигнатуры в соответствии с правилами, представленными в табл. 1.
Таблица 1Знак сигнатуры JAVA-тип
Z BOOLEAN
B BYTE
C CHAR
S SHORT
Internet INT
J LONG
F FLOAT
V VOID
D DOUBLE
L полное квалифицированное имя класса полное квалифицированное имя класса
[ тип тип[]
(типы аргументов) возвращаемый тип полная сигнатура метода
Проиллюстрируем эти правила на примерах:
метод LONG M1(INT N, STRING S, INT[] ARR);
сигнатура (ILJAVA/LANG/STRING;[I)J;
метод VOID M2(FLOAT N, BYTE[][] ARR, RUNTIME R);
сигнатура (F[[BLJAVA/LANG/RUNTIME;)V.
Полная информация о правилах образования сигнатуры метода представлена в файле SIGNATURE.H.
Апрель
30,
2008
— Рубрика: C++
Метки: JNI, Сигнатура метода