unit SVAProcLib; interface uses Windows, SysUtils, Forms, DB, DBTables, DBGrids, Variants, StdCtrls, Classes, Controls, ComCtrls, ShellAPI, DBITypes; { DeleteFile есть и в Windows и в SysUtils. В программе исп-ся SysUtils.DeleteFile(f:string):boolean, для этого (!?) порядок должен быть: Windows, SysUtils } const { Цвета } ColorBlue = $FFCC99; // для заголовков в StringGrid1 ColorBBlue = $FF9966; // я-голубой ColorLBlue = $FFFFC0; // св-гол. ColorGrBlue = $A56E3A; // серо-голубой ColorBluGreen= $FFCC00; // св.син-зел ColorDarkGr = $603000; // для линий в заголовках =RGB(124,92,60) ColorBYellow = $80FFFF; // я-желтый ColorLYellow = $99FFFF; // св-жел. ColorYellow = $C0FFFF; // слабый желтый ColorPYellow = $CCFFFF; // бледно-желтый (pale yellow) ColorLBrown = $99CCFF; // св-коричневый ColorPBrown = $C0D8FF; // бл-кор. (pale brown) ColorLGreen = $CCFFCC; // св-зел ColorPGreen = $E4FFE4; // бл-зел (pale green) type // Для простого справочника (список наименований) TSprv = packed record Cod: integer; Name: array[0..79] of char; end; TLSprv = array of TSprv; TSprPNom = array of SmallInt; { Код => Порядковый № в List (c 0) } { простой справочник } TSprav = class(TObject) Spr : TLSprv; Count: integer; PNom : TSprPNom; MaxCod: integer; constructor Create(FName:string); procedure Clear; destructor Destroy; override; function CodToName(Cod:integer):string; end; procedure CreateSprv(FName:string); procedure CmBoxSprvTextShow(CmBox:TComboBox; Cod:integer; Sprav:TSprav); procedure CmBoxSpravCloseUp(CmBox:TComboBox; Tab:TTable; Pole: string; Sprav: TSprav); procedure RusKeyboard(Rus:boolean); function Space(l:integer):string; procedure DOStoWin(var s:string); procedure WinToDOS(var s:string); procedure Del2Char(var s:string; c:char); procedure Del2Space(var s:string); procedure Del2Minus(var s:string); procedure StringToChar(s:string; var c; n:integer); function Find(const Substr, S: string; StartPos:integer): integer; function FindBack(const Substr, S: string; StartPos:integer): integer; procedure StrDoubleChar(var s:string; c:Char); procedure CopyFile1(const SourceFileName, TargetFileName: string); function CopyFiles(PBuf:pchar; DestDir:string; Wnd:HWND):boolean; function AgeP(Date1,Date2: tDateTime): integer; function InputString(const ACaption, APrompt, ADefault: string; const MLength, PswB: byte): string; procedure CalcWidth(i:integer; DBGrid: TDBGrid); procedure CalcWidthLV(i:integer; ListView: TListView); procedure GetDiskList(var d, Dr: string); function xlColumnName(col:integer):string; function BoolToInt(Field:TField):integer; function ControlNumber(s:string; Count:integer):boolean; procedure CmBoxTextShow(CmBox:TComboBox; Cod:integer); function IndexByCode(Code:integer; Sprav:TSprav):integer; function NameByCode(Code:integer; Sprav:TSprav):string; procedure CmBoxSpravShow(CmBox:TComboBox; Cod:integer; Sprav:TSprav); procedure CmBoxCloseUp(CmBox:TComboBox; Tab:TTable; Pole:string); function FilesFound(FNames:string):boolean; function MakeFile(FName:string):boolean; function FileDate(FName: string): tDateTime; function StrFileSize(FSize:integer):string; function StrFDateTime(DT:integer):string; procedure DelFiles(FNames:string); function IndxControlDB(FName:string; INames:string):boolean; procedure DelIndexes(FName:string); function SaveToArchive(RarFileName, Keys, FNames:string; var Msg:string; BackFrm:TForm):boolean; procedure FRarUnpack(RarFile, Keys, FNames, DirTarget: string; BackFrm:TForm); function PackDBF(FName:string):boolean; procedure PackTable(Tbl: TTable); function FieldString(Field:TField):string; function CheckIndF(FName,IFName: string): boolean; procedure TableDBaseIII(FName:string; LangCode:byte); procedure IndxCreate(FN,IndF,KeyF: string); function Max2(X,Y:integer):integer; function Max3(X,Y,Z:integer):integer; function Min2(n1,n2:integer):integer; function Itogo(v:currency):string; implementation { ======== Для объекта TSprav ======== } constructor TSprav.Create(FName:string); var Tab: TTable; i: integer; s: string; begin Clear; MaxCod:= 0; if not FileExists(FName) then CreateSprv(FName); Tab:= TTable.Create(nil); with Tab do begin close; TableName:= FName; open; Count:= RecordCount; if Count > 0 then begin SetLength(Spr, Count); for i:= 0 to pred(Count) do begin Spr[i].Cod:= Fields[0].Value; s:= Fields[1].AsString; StringToChar(s, Spr[i].Name, 80); if Spr[i].Cod > MaxCod then MaxCod:= Spr[i].Cod; if i < pred(Count) then Next end end; close; Free; end { with Tab }; { Заполнение массива порядковых номеров } SetLength(PNom, 0); SetLength(PNom, succ(MaxCod)); // т.к.0-й не исп-ся for i:= 0 to MaxCod do PNom[i]:= -1; // -1 = кода нет if Count > 0 then for i:= 0 to pred(Count) do PNom[Spr[i].Cod]:=i; end; { Create } procedure TSprav.Clear; begin SetLength(Spr,0); SetLength(PNom,0); MaxCod:=0; Count:=0; end; destructor TSprav.Destroy; begin Clear end; function TSprav.CodToName(Cod:integer):string; begin Result:= ''; if (Cod > 0) and (Cod <= MaxCod) then if PNom[Cod] >= 0 then Result:= Spr[PNom[Cod]].Name end; { ========== Использование TSprav ============ } { --- Разные мелкие справочники - создание ---- } procedure CreateSprv(FName:string); begin with TTable.Create(nil) do begin TableName:= FName; with FieldDefs do begin Clear; Add('Cod',ftAutoInc,0,false); Add('Name',ftString,80,false); end; CreateTable; Free end; end; { ======== По Cod задать ComboBox.ItemIndex и ComboBox.Text ======== } { ---- для справочников типа TSprav ---- } procedure CmBoxSprvTextShow(CmBox:TComboBox; Cod:integer; Sprav:TSprav); var k: integer; // Cod = 1..Sprav.MaxCod begin k:= -1; if (Cod > 0) and (Cod <= Sprav.MaxCod) then k:= Sprav.PNom[Cod]; with CmBox do begin if (k >= 0) and (k < Items.Count) then begin ItemIndex:= k; Text:= Items[k]; end else begin ItemIndex:= -1; Text:='' end; end; end; { --- Выбор в ComboBox - для TSprav --- } procedure CmBoxSpravCloseUp(CmBox:TComboBox; Tab:TTable; Pole: string; Sprav: TSprav); var k: integer; Field: TField; begin k:= CmBox.ItemIndex; Field:= Tab.FieldByName(Pole); if k < 0 then begin CmBox.Text:= ''; if not Field.IsNull then Field.Value:= Null; exit end; CmBox.Text:= CmBox.Items[k]; k:= Sprav.Spr[k].Cod; if Field.AsInteger <> k then Field.AsInteger:= k end; { ==== Самостоятельные процедуры и функции общего назначения ===== } { --- Включение/выкл. русской раскладки клавиатуры ---- } procedure RusKeyboard(Rus:boolean); const Cod: array[false..true] of string = ('00000409','00000419'); var Layout: array[0..KL_NAMELENGTH] of char; begin LoadKeyboardLayout(StrCopy(Layout,PChar(Cod[Rus])),KLF_ACTIVATE); end; { ----- Строка пробелов ----- } function Space(l:integer):string; var i:integer; begin Result:= ''; for i:=1 to l do Result:= Result + ' '; end; { -------- Перевод кодировки из MS-DOS в Windows -------- } procedure DOStoWin(var s:string); var p: PAnsiChar; begin if s = '' then exit; p:= PAnsiChar(s); // указатель на s OEMToChar(p, p); s:= p end; { -------- Перевод кодировки из Windows в MS-DOS -------- } procedure WinToDOS(var s:string); var p: PAnsiChar; begin if s = '' then exit; p:= PAnsiChar(s); CharToOem(p,p); s:= p; end; { -- Удаление лишних двойных символов внутри строки -- } procedure Del2Char(var s:string; c:char); var k:integer; begin if length(s) = 0 then exit; k:= pos(c+c, s); while k > 0 do begin delete(s, k, 1); k:= pos(c+c, s); end end; { -- Удаление лишних пробелов внутри строки -- } procedure Del2Space(var s:string); begin Del2Char(s, ' ') end; { -- Удаление лишних черточек внутри строки -- } procedure Del2Minus(var s:string); begin Del2Char(s, '-') end; { --- string -> array of char --- } procedure StringToChar(s:string; var c; n:integer); var a:string; begin a:= TrimRight(s); FillChar(c,n,0); if length(a) > n then move(a[1],c,n) else move(a[1],c,length(s)); end; { ------ Поиск фрагмента Substr в строке S с позиции StartPos ------ } // любой регистр function Find(const Substr, S: string; StartPos:integer): integer; var i, j, d: integer; begin Result:= 0; d:= length(S) - length(Substr); if d < 0 then exit; for i:= StartPos to d + 1 do for j:= 1 to length(Substr) do if AnsiUpperCase(Substr[j]) <> AnsiUpperCase(S[i + j - 1]) then break else if j = length(Substr) then begin Result:= i; exit; end; end; { ------ Поиск назад (к началу строки) ------- } function FindBack(const Substr, S: string; StartPos:integer): integer; var i, j, d: integer; begin Result:= 0; d:= length(S) - length(Substr); if d < 0 then exit; if StartPos > d + 1 then exit; for i:= StartPos downto 1 do for j:= 1 to length(Substr) do if AnsiUpperCase(Substr[j]) <> AnsiUpperCase(S[i + j - 1]) then break else if j = length(Substr) then begin Result:= i; exit; end; end; { ------ Дублирование символа (кавычек) ------- } procedure StrDoubleChar(var s:string; c:Char); var i: integer; begin i:=0; while i < length(s) do begin inc(i); if s[i] = c then begin insert(c, s, i); inc(i) end; end; end; { --- Копирование НЕ на дискету --- } procedure CopyFile1(const SourceFileName, TargetFileName: string); var S, T : TFileStream; // uses Classes begin if not FileExists(SourceFileName) then exit; S:= nil; T:= nil; try SysUtils.DeleteFile(TargetFileName); S:= TFileStream.Create(SourceFileName, fmOpenRead); T:= TFileStream.Create(TargetFileName, fmOpenWrite or fmCreate); T.CopyFrom(S, S.Size); FileSetDate(T.Handle, FileGetDate(S.Handle)); finally if T <> nil then T.Free; if S <> nil then S.Free; end; end; { ------ если копировать пачку файлов чисто как Windows ------ } function CopyFiles(PBuf:pchar; DestDir:string; Wnd:HWND):boolean; { PBuf - указатель на строку Buf:array[0..4096] of char, где файлы перечислены через 1 символ #0 (#0#0 - конец) } var FO : TSHFileOpStruct; // uses ShellAPI begin FillChar(FO, sizeof(FO), #0); FO.Wnd:= Wnd; // окно, где показывать FO.wFunc:= FO_COPY; // еще бывает FO_DELETE, FO_MOVE, FO_RENAME (?) FO.pFrom:= PBuf; FO.pTo:= PChar(DestDir); FO.fFlags:= 0; Result:= (SHFileOperation(Fo) = 0) and not Fo.fAnyOperationsAborted; Application.ProcessMessages; { после этого может быть активным чужое окошко - проблемка, однако Self.Activate; Application.ProcessMessages; - не помогает } // если просто 1 файл, то достаточно: // if CopyFile('C:\MKB.rar', 'D:\MKB.rar', true) then ShowMessage('OK!'); { - - - - - - - - - - - - пример procedure TForm1.Button1Click(Sender: TObject); var s: string; begin s:= 'C:\MKB.rar'#0'C:\MKB1.rar'#0'C:\Ex06.rar'#0#0; if CopyFiles(PChar(s), 'D:\TEMP\', Handle) then ShowMessage('OK') end; } { или var Buffer : array[0..4096] of char; p : pchar; begin FillChar(Buffer, sizeof(Buffer), #0); p:= @Buffer; p:= StrECopy(p, 'C:\MKB.rar') + 1; // заполняется Buffer и указатель p // сдвигается на 1 дальше, чтобы все слова перечислялись через #0 p:= StrECopy(p, 'C:\MKB1.rar') + 1; p:= StrECopy(p, 'C:\Ex06.rar') + 1; StrECopy(p, 'C:\MKB.rar'); if CopyFiles(p, 'D:\TEMP\', Handle) then ShowMessage('OK') } end; { CopyFiles } { ---- Кол-во полных лет между датами Date1 и Date2 ---- } function AgeP(Date1,Date2: tDateTime): integer; var Y1,M1,D1,Y2,M2,D2: word; Age: integer; begin DecodeDate(Date2,Y2,M2,D2); DecodeDate(Date1,Y1,M1,D1); Age:= Y2 - Y1; if M2*31 + D2 < M1*31 + D1 then dec(Age); Result:= Age end; { ---- Из Dialogs.pas со своим упрощением ---- } function InputString(const ACaption, APrompt, ADefault: string; const MLength, PswB: byte): string; var Form: TForm; Prompt: TLabel; Edit: TEdit; Button1Left, ButtonTop, ButtonWidth, ButtonHeight, FrmClWidth, n: Integer; begin Result := ADefault; Form := TForm.Create(Application); with Form do try // параметры (свойства) формы Canvas.Font := Font; BorderStyle := bsDialog; Caption := ACaption; FrmClWidth := MLength * 8 + 32; if FrmClWidth < 240 then FrmClWidth := 240 else if FrmClWidth > 640 then FrmClWidth := 640; ClientWidth := FrmClWidth; Position := poScreenCenter; // Label.Caption := APrompt; Prompt := TLabel.Create(Form); with Prompt do begin Parent := Form; AutoSize := false; Caption := APrompt; Left := 16; Top := 16; Width := FrmClWidth - 32; n := Canvas.TextWidth(APrompt); if n > Width then Height := Height * (n div Width + 1); WordWrap := True; end; // Строка редактирования Edit := TEdit.Create(Form); with Edit do begin if PswB = 0 then PasswordChar := #0 else PasswordChar := '#'; Parent := Form; Left := Prompt.Left; Top := Prompt.Top + Prompt.Height + 5; Width := FrmClWidth - 32; MaxLength := MLength; Text := ADefault; SelectAll; end; // параметры кнопок ButtonTop := Edit.Top + Edit.Height + 15; ButtonWidth := 97; ButtonHeight := 25; Button1Left := (FrmClWidth div 2) - 20 - ButtonWidth; if Button1Left < 16 then Button1Left := 16; // кнопка 'OK' with TButton.Create(Form) do begin Parent := Form; Caption := 'OK'; ModalResult := mrOk; Default := True; SetBounds(Button1Left, ButtonTop, ButtonWidth, ButtonHeight); end; // кнопка 'Отказаться' with TButton.Create(Form) do begin Parent := Form; Caption := 'Отказаться'; ModalResult := mrCancel; Cancel := True; SetBounds(Form.ClientWidth - Button1Left - ButtonWidth, ButtonTop, ButtonWidth, ButtonHeight); Form.ClientHeight := Top + Height + 13; end; // Показ формы и получение результата if ShowModal = mrOk then Result := Edit.Text; finally Form.Free; end; end; { InputString } { ----- Подсчет ширины i-го столбца в DBGrid ----- } { для того чтобы подогнать все столбцы на всю ширину DBGrid. Предполагается, что ширина всех остальных столбцов уже задана. Если значение DBGrid.Width слишком мало, то никаких действий не будет} procedure CalcWidth(i:integer; DBGrid: TDBGrid); var j,n:integer; // не ранее FormShow begin with DBGrid do begin n:= ClientWidth; if dgIndicator in DBGrid.Options then dec(n,12); for j:= 0 to Columns.Count - 1 do if j<>i then begin dec(n, Columns[j].Width); if dgColLines in DBGrid.Options then dec(n) end; if n < 4 then exit; // critical error Columns[i].Width:= n; end; Application.ProcessMessages; end; { CalcWidth } { ----- Подсчет ширины i-го столбца в ListView ----- } { для того чтобы подогнать все столбцы на всю ширину ListView предполагается, что ширина всех остальных столбцов уже задана } // TListView : uses ComCtrls procedure CalcWidthLV(i:integer; ListView: TListView); // in FormActivate var j,n,n0:integer; begin with ListView do begin n:= ClientWidth; n0:= n; for j:= 0 to Columns.Count - 1 do if j<>i then dec(n, Columns[j].Width); if n < 4 then exit; // critical error Columns[i].Width:= n; Application.ProcessMessages; { В основном здесь и всё, но можно и проверить ScrollBar - например, до пересчета Columns[i].Width был HorScroll, а после - он исчез, появилась в конце строка, и затем исчез VertScroll. Тогда можно и увеличить Columns[i].Width } if ClientWidth <> n0 then Columns[i].Width:= n + ClientWidth - n0; end; end; { CalcWidthLV } { ----- Список разделов дисков ----- } procedure GetDiskList(var d, Dr: string); var // d - список разделов, Dr - соотв. ему список кодов устройств s: string; n: Integer; c: char; begin d:= ''; Dr:= ''; for c:= 'A' to 'Z' do begin s:= c + ':\'; n:= GetDriveType(Pchar(s)); case n of 0: { Тип диска невозможно определить }; 1: { не существует }; 2..5: { DRIVE_REMOVABLE (2), DRIVE_FIXED (3), DRIVE_REMOTE (4), DRIVE_CDROM (5) } begin d:= d + c; Dr:= Dr + IntToStr(n) end; DRIVE_RAMDISK: ; { 6 - Диск явяляется RAM диском } end; { case } end; end; { ----- Для Excel: № столбца -> буквы столбца ----- } function xlColumnName(col:integer):string; var k: integer; begin if Col < 27 then Result:= chr(64 + Col) // 'A'..'Z' else begin // 'AA'..'ZZ' k:= (Col - 1) div 26; Result:= chr(64 + k); k:= (Col - 1) mod 26; Result:= Result + chr(65 + k) end end; { --- BooleanField --> 1, 0, -1 --- } function BoolToInt(Field:TField):integer; begin if Field.IsNull then Result:= -1 // Null else if Field.AsBoolean then Result:= 1 // true else Result:= 0; // false end; { ------- Контроль цифрового номера -------- } function ControlNumber(s:string; Count:integer):boolean; var i:integer; // Count=0 - любое кол-во begin Result:=false; if Count<>0 then if length(s) <> Count then exit; for i:=1 to length(s) do if not(s[i] in ['0'..'9']) then exit; Result:=true; end; { ------- По Cod задать ComboBox.Text -------- } procedure CmBoxTextShow(CmBox:TComboBox; Cod:integer); var k: integer; // Cod = 1..CmBox.Items.Count begin k:= pred(Cod); with CmBox do begin if (k >= 0) and (k < Items.Count) then begin ItemIndex:= k; Text:= Items[k]; end else begin ItemIndex:= -1; Text:= '' end; end; end; { ------ По коду определить индекс в TSprav ------ } function IndexByCode(Code:integer; Sprav:TSprav):integer; begin if (Code < 1) or (Code > Sprav.MaxCod) then Result:= -1 else Result:= Sprav.PNom[Code] end; { ------ По коду определить Name в TSprav ------ } function NameByCode(Code:integer; Sprav:TSprav):string; var k:integer; begin k:= IndexByCode(Code, Sprav); if k >= 0 then Result:= Sprav.Spr[k].Name else Result:= '' end; { ----- По Коду задать CmBox.ItemIndex в TSprav ----- } procedure CmBoxSpravShow(CmBox:TComboBox; Cod:integer; Sprav:TSprav); begin CmBox.ItemIndex:= IndexByCode(Cod, Sprav); end; { ------------ ComboBox_CloseUp ------------- } // Выбор в ComboBox procedure CmBoxCloseUp(CmBox:TComboBox; Tab:TTable; Pole:string); var k: integer; Field: TField; begin k:= CmBox.ItemIndex; Field:= Tab.FieldByName(Pole); if k < 0 then begin CmBox.Text:= ''; if not Field.IsNull then Field.Value:= Null; exit end; if k < CmBox.Items.Count then begin CmBox.Text:= CmBox.Items[k]; inc(k); if Field.AsInteger <> k then Field.AsInteger:= k end end; { ---- Найдены ли файлы (можно с маской) ---- } function FilesFound(FNames:string):boolean; var SR: TSearchRec; begin Result:= true; if FindFirst(FNames, faAnyFile, SR) <> 0 then Result:= false; FindClose(SR); end; { ---- Создание пустого файла ---- } function MakeFile(FName:string):boolean; var f:file; begin Result:= true; AssignFile(f,FName); try rewrite(f); CloseFile(f); except Result:= false end; end; { ---- Дата файла ---- } function FileDate(FName: string): tDateTime; var Date0, Hnd: integer; begin Result:= 0; if not FileExists(FName) then exit; Hnd:= FileOpen(FName, 2); Date0:= FileGetDate(Hnd); Result:= FileDateToDateTime(Date0); FileClose(Hnd); end; { -- Размер файла (с разделителями) -- } function StrFileSize(FSize:integer):string; begin Result:= FloatToStrF(FSize, ffNumber, 9, 0); // SysUtils end; { --- Дата и время файла в виде <dd.mm.yyyy hh:mm> --- } function StrFDateTime(DT:integer):string; // Dates: 01.01.1980 - 31.12.2107 (128 years) // сутки 65536 и более - не знаю закономерности var D: tDateTime; FormatSettings: TFormatSettings; begin D:= FileDateToDateTime(DT); with FormatSettings do begin ShortDateFormat:= 'dd/mm/yyyy'; DateSeparator:= '.'; TimeSeparator:= ':'; LongTimeFormat:= 'hh:mm'; end; Result:= DateToStr(D, FormatSettings) + ' ' + TimeToStr(D, FormatSettings); { если с одним пробелом, то можно и короче: Result:= DateTimeToStr(D, FormatSettings); } end; { ---- Удаление файлов с маской ---- } procedure DelFiles(FNames:string); var SR: TSearchRec; sDir: string; begin sDir:= ExtractFilePath(FNames); if FindFirst(FNames, faAnyFile, SR) <> 0 then begin SysUtils.FindClose(SR); exit; end; repeat SysUtils.DeleteFile(sDir + SR.Name); until FindNext(SR) <> 0; SysUtils.FindClose(SR); end; { --------- Проверка индексных файлов в DB --------- } function IndxControlDB(FName:string; INames:string):boolean; // INames - перечисление имен через запятую (не более 32) // Сначала на наличие файлов PX, XG?, YG? // отсутствие PX не замечается - только при проверке др. индексов var Tab:TTable; s,f: string; GoodIndex: boolean; i,j,k: integer; c: char; eX, eY: string[3]; // Проверка простого наличия инд. файла function FileControl(ExtName:string): boolean; begin Result:= FileExists(ChangeFileExt(FName,ExtName)) end; // Проверка при загрузке БД с индексом function Indx1Control(IndxName:string):boolean; // true - OK begin Result:= true; Tab.Close; Tab.IndexName:= IndxName; try Tab.Open; except Result:= false; end; end; begin Result:= false; // проверка простого наличия инд. файлов if not FileControl('.PX') then exit; // остальные файлы eX:= '.XG'; eY:= '.YG'; i:= 0; c:= '0'; f:= Trim(INames); k:= Pos(',', f); while k > 0 do begin s:= Trim(Copy(f,1,k-1)); if s <> '' then begin if not FileControl(eX + c) then exit; if not FileControl(eY + c) then exit; inc(i); if i = 16 then begin eX:= '.XH'; eY:= '.YH'; end; j:= i mod 16; if j < 10 then c:= chr(48 + j) else c:= chr(55 + j); end; delete(f,1,k); k:= Pos(',', f); end; f:= Trim(f); if f <> '' then begin if not FileControl(eX + c) then exit; if not FileControl(eY + c) then exit; end; // Проверка при загрузке БД с индексом Tab:= TTable.Create(nil); Tab.TableName:= FName; f:= Trim(INames); GoodIndex:= Indx1Control(''); // остальные индексы k:= Pos(',', f); while (k > 0) and GoodIndex do begin s:= Trim(Copy(f,1,k-1)); if s <> '' then GoodIndex:= Indx1Control(s); delete(f,1,k); k:= Pos(',', f); end; f:= Trim(f); if (f <> '') and GoodIndex then GoodIndex:= Indx1Control(f); Result:= GoodIndex; Tab.Close; Tab.Free; end; { IndxControlDB } { -------- Удаление индексов в DB ---------- } procedure DelIndexes(FName:string); var c,d: char; eX, eY: string[3]; begin // до 32 индексов DeleteFile(ChangeFileExt(FName,'.PX')); for d:= 'G' to 'H' do begin eX:= '.X' + d; eY:= '.Y' + d; for c:= '0' to 'F' do begin DeleteFile(ChangeFileExt(FName, eX + c)); DeleteFile(ChangeFileExt(FName, eY + c)); end; end end; { ------ Создание Rar-архива с ожиданием окончания работы ------ } function SaveToArchive(RarFileName, Keys, FNames:string; var Msg:string; BackFrm:TForm):boolean; // RarFileName - полное имя (если расширения нет, то добавится .rar) var s, RarFile: string; k: integer; APChar: array[0..255] of char; si: TStartupInfo; // для запуска и ожидания WinRar pi: TProcessInformation; begin Result:= false; RarFile:= RarFileName; if AnsiUpperCase(ExtractFileExt(RarFile)) <> '.RAR' then RarFile:= RarFile + '.rar'; if not MakeFile(RarFile) then begin Msg:= 'Не удалось создать файл ' + RarFile; exit end; FindExecutable(PChar(RarFile), nil, APChar); DeleteFile(RarFile); if APChar[0] = #0 then begin Msg:= 'Не удалось найти программу WinRAR'; exit end; s:= APChar + ' a ' + Keys + ' "' + RarFile + '" ' + FNames; // Keys: -ep без папок-путей // запуск WinRar и ожидание окончания его работы ZeroMemory(@si,sizeof(si)); si.cb:= SizeOf(si); k:=0; if CreateProcess(nil, { No module name (use command line). } PChar(s), { Command line. } nil, { Process handle not inheritable. } nil, { Thread handle not inheritable. } False, { Set handle inheritance to FALSE. } 0, { No creation flags. } nil, { Use parent's environment block. } nil, { Use parent's starting directory. } si, { Pointer to STARTUPINFO structure. } pi) { Pointer to PROCESS_INFORMATION structure. } then begin k:=1; // процесс пошел if BackFrm = nil then WaitForSingleObject(pi.hProcess, INFINITE) // - так тоже можно, но хуже; else while WaitforSingleObject(pi.hProcess,50) = WAIT_TIMEOUT do BackFrm.Repaint; // для длительного процесса так лучше CloseHandle(pi.hProcess); CloseHandle(pi.hThread); end; if FileExists(RarFile) then begin Msg:= 'Создан архивный файл:'#10 + RarFile; Result:= true end else if k = 1 then Msg:= 'Процесс прерван при создании файла'#10 + RarFile else Msg:= 'Не удалось создать архивный файл'#10 + RarFile + #10'(Возможно, есть проблемы с WinRar)' end; { SaveToArchive } { -------- Восстановление файлов из архива ---------- } procedure FRarUnpack(RarFile, Keys, FNames, DirTarget: string; BackFrm:TForm); { RarFile - полное имя rar-файла Keys - ключи (можно '') FNames - файлы для извлечения; если несколько, то в кавычках и через пробел DirTarget - папка-приемник (можно '') BackFrm - окно-форма, откуда вызвана процедура } const titl = ' Восстановление данных из архива'; var s, FRarName: string; APChar: array[0..255] of char; si: TStartupInfo; // для запуска и ожидания WinRar pi: TProcessInformation; begin if DirTarget <> '' then if not DirectoryExists(DirTarget) then if not CreateDir(DirTarget) then begin Application.MessageBox(PChar('Не удаётся создать папку "' + DirTarget + '"'), titl, MB_ICONSTOP); exit end; ZeroMemory(@APChar, 256); FindExecutable(PChar(RarFile), nil, APChar); if APChar[0] = #0 then begin Application.MessageBox('Не удалось найти программу WinRAR', titl, MB_ICONSTOP); exit end; s:= APChar + ' x ' + Keys + ' "' + RarFile + '" ' + FNames; if DirTarget <> '' then s:= s + ' "' + DirTarget + '"'; // запуск WinRar и ожидание окончания его работы ZeroMemory(@si, sizeof(si)); si.cb:= SizeOf(si); if CreateProcess(nil, PChar(s), nil, nil, False, 0, nil, nil, si, pi) then begin if BackFrm = nil then WaitForSingleObject(pi.hProcess, INFINITE) // - так тоже можно, но хуже; else while WaitforSingleObject(pi.hProcess,50) = WAIT_TIMEOUT do BackFrm.Repaint; // для длительного процесса так лучше CloseHandle(pi.hProcess); CloseHandle(pi.hThread); Application.ProcessMessages; Application.MessageBox(PChar('Файл "' + ExtractFileName(RarFile) + '" обработан'), titl); end else Application.MessageBox(PChar('Не удалось обработать файл "' + FRarName + '"'), titl, MB_ICONWARNING); end; { --------- Упаковка DBF-таблицы --------- } function PackDBF(FName:string):boolean; // uses DBITypes begin Result:= false; try with TTable.Create(nil) do begin TableName:= FName; Exclusive:= true ; Open ; Result:= DbiPackTable(DBHandle, Handle, nil, szDBASE, true) = DBIERR_NONE; Close; Exclusive:= false; Free end; except end end; { --------- Упаковка таблицы DBF и DB --------- } procedure PackTable(Tbl: TTable); // uses DBITypes var hDb: hDBIDb; Props: CURProps; CrDesc: CRTblDesc; Save: Boolean; begin Save:= Tbl.Active; Tbl.Active:= True; try Check(DbiGetCursorProps(Tbl.Handle, Props)); if (Props.szTableType = szPARADOX) then begin { для PARADOX - не 100%, но даже если и появляются странные сообщения после процедуры, последствий - никаких } FillChar(CrDesc, SizeOf(CRTblDesc), 0); StrCopy(CrDesc.szTblName, Props.szName); StrCopy(CrDesc.szTblType, Props.szTableType); CrDesc.bPack:= True; Check(DbiGetObjFromObj(hDBIObj(Tbl.Handle), objDATABASE, hDBIObj(hDb))); Tbl.DisableControls; Tbl.Active:= False; Check(DbiDoRestructure(hDb, 1, @CrDesc, nil, nil, nil, False)); end else if (Props.szTableType = szDBASE) then begin Tbl.Active:= True; Check(DbiPackTable(Tbl.DBHandle, Tbl.Handle, nil, szDBASE, True)); end; finally Tbl.Active:= Save; Tbl.EnableControls; end; end; { PackTable } { ------ Field.AsString реально как есть в БД ------ } function FieldString(Field:TField):string; { бывают проблемы с Query при устаревших настройках BDE в dBase, и здесь они могут быть разрешены. Но не на 100% - возможно, только тогда, когда не создается временный файл _q, в который попали уже испорченные значения - т.е. при маленьких размерах. А Table и без этого обычно нормально работает } var PCh:array[0..254] of char; begin Field.GetData(@PCh); Result:= PCh; end; { -- Проверка наличия индексного файла в dbf и удаление признака, если нет и.ф.-- } function CheckIndF(FName,IFName: string): boolean; var FHandle,DT: integer; b:byte; begin Result:= true; if FileExists(IFName) then exit; try FHandle:= FileOpen(FName, fmOpenReadWrite); DT:= FileGetDate(FHandle); FileSeek(FHandle,28,0); FileRead(FHandle,b,1); if b > 0 then begin b:= 0; FileSeek(FHandle,28,0); FileWrite(FHandle,b,1); FileSetDate(FHandle, DT); end; FileClose(FHandle); except Application.MessageBox('Проблемы при проверке признака индексирования!', PChar('Файл: ' + FName),0); Result:= false end end; { CheckIndF } { -------- Проверка и установка код.страницы dbf-файла -------- } procedure TableDBaseIII(FName:string; LangCode:byte); var FHandle,DT: integer; D: tDateTime; Y,M,day: word; b: array[0..5] of byte; begin if not FileExists(FName) then exit; FHandle:= FileOpen(FName, fmOpenReadWrite); DT:= FileGetDate(FHandle); D:= FileDateToDateTime(DT); DecodeDate(D,Y,M,day); Y:= Y - 1900; FileRead(FHandle,b[0],4); FileSeek(FHandle,28,0); FileRead(FHandle,b[4],2); if (b[0] <> 3) or (b[1] <> Y) or (b[2] <> M) or (b[3] <> day) or (b[4] <> 0) or (b[5] <> LangCode) then begin b[0]:= 3; b[1]:= Y; b[2]:= M; b[3]:= day; b[4]:= 0; b[5]:= LangCode; FileSeek(FHandle,0,0); FileWrite(FHandle,b[0],4); FileSeek(FHandle,28,0); FileWrite(FHandle,b[4],2); FileSetDate(FHandle, DT); DeleteFile(ChangeFileExt(FName,'.MDX')); end; FileClose(FHandle); end; { TableDBaseIII } { --- Создание инд. файла в DBF, если нет MDX --- } procedure IndxCreate(FN,IndF,KeyF: string); var Tab: TTable; IFN: string; begin if not FileExists(FN) then exit; IFN:= ChangeFileExt(FN,'.mdx'); if not FileExists(IFN) and CheckIndF(FN, IFN) then begin Tab:= TTable.Create(nil); Tab.TableName:= FN; Tab.AddIndex(IndF, KeyF, [ixExpression]); Tab.Free; end; end; { IndxCreate } { ----- Максимальное из 2 чисел ------ } function Max2(X,Y:integer):integer; begin if X > Y then Result:= X else Result:= Y end; { ----- Максимальное из 3 чисел ------ } function Max3(X,Y,Z:integer):integer; begin Result:= Max2(Max2(X,Y),Z) end; { ----- Минимальное из 2 чисел ------ } function Min2(n1,n2:integer):integer; begin if n1 < n2 then Result:= n1 else Result:= n2 end; { ------- Итоговая сумма прописью ------- } function Itogo(v:currency):string; Const sot : array[1..9] of string[9] = ('сто','двести','триста', 'четыреста','пятьсот','шестьсот','семьсот','восемьсот','девятьсот'); des : array[2..9] of string[11] = ('двадцать','тридцать','сорок', 'пятьдесят','шестьдесят','семьдесят','восемьдесят','девяносто'); tsat: array[1..9] of string[5] = ('один','две','три','четыр','пят', 'шест','сем','восем','девят'); edin: array[1..9] of string[6] = ('один','два','три','четыре','пять', 'шесть','семь','восемь','девять'); tedin: array[1..2] of string[4] = ('одна','две'); var LDig: int64; mlrd,mln,tys,r : word; s:string; Kop,b:byte; kk : string[2]; function Right(s:string):string; begin if length(s) < 3 then Result:=s else begin Result:= Copy(s, length(s)-1, 2) end; end; Procedure Mile(m:word; n:byte); var a : string; begin case n of 9 : a:='миллиард'; 6 : a:='миллион'; 3 : a:='тысяч'; 0 : a:='руб'; end; b:= m div 100; { сотни } if b>0 then s:= s + sot[b] + ' '; m:= m mod 100; b:= m div 10; { десятки } if b<>1 then begin if b>1 then s:= s + des[b] + ' '; b:= m mod 10; { единицы } if b>0 then if (n=3) and (b in [1,2]) then s:= s + tedin[b] + ' ' else s:= s + edin[b] + ' '; s:= s + a; case b of 1 : if n=3 then s:= s + 'а' else if n=0 then s:= s + 'ль'; 2..4 : case n of 0 : s:= s + 'ля'; 3 : s:= s + 'и'; 6,9 : s:= s + 'а' end else if n>3 then s:= s + 'ов' else if n=0 then s:= s + 'лей' end; s:= s + ' ' end else begin b:= m mod 10; if b=0 then s:= s + 'десять' else s:= s + tsat[b] + 'надцать'; s:= s + ' ' + a; case n of 0 : s:= s + 'лей '; 3 : s:= s + ' '; 6,9 : s:= s + 'ов ' end end end; begin if v<0 then v:= -v; if v > 999999999999.0 then begin Result:=Format('%.2f руб.',[v]); exit end; LDig:= round(v * 100); Kop:= LDig mod 100; // копейки LDig:= LDig div 100; // целые рубли - все r:= LDig mod 1000; // рубли 1..999 LDig:= LDig div 1000; // т.р. Tys:= LDig mod 1000; // тысячи LDig:= LDig div 1000; // млн.р. Mln:= LDig mod 1000; // миллионы Mlrd:= LDig div 1000; // миллиарды s:=''; if v<1 then s:='Ноль рублей ' else begin if Mlrd>0 then Mile(Mlrd,9); if Mln>0 then Mile(Mln,6); if tys>0 then Mile(tys,3); Mile(r,0) end; kk:= Format('%.2d',[Kop]); s:= s + kk + ' копе'; if kk[1]='1' then s:= s + 'ек' else case kk[2] of '1': s:= s + 'йка'; '2'..'4': s:= s + 'йки'; else s:= s + 'ек'; end; if s[1] in ['а'..'я'] then s[1]:= chr(ord(s[1])-32); Result:=s end { -- Itogo -- }; end.