I'm not very knowledgeable about Object Pascal
or Delphi, but I'll try to provide a reasonable answer.
Security with the original file
The reason for this is to prevent two applications from accessing the same file, Excel creates a LOCK in the file and this generates some locks and even when it is not in LOCK >, there may be conflicts if your software is running XLS at the same time as Excel is clicked on Ctrl + B .
In order to avoid conflicts the best way is to generate a copy of the file, for this I used this function (source: link ):
function GetTempFile(const Extension: string): string;
var
Buffer: array[0..MAX_PATH] of Char;
begin
repeat
GetTempPath(SizeOf(Buffer) - 1, Buffer);
GetTempFileName(Buffer, '~', 0, Buffer);
Result := ChangeFileExt(Buffer, Extension);
until not FileExists(Result);
end;
Selecting the XLS file
In the case I chose to select both XLS and XLSX, as I believe it is more likely to use the latest format (especially if it is a software for Windows), in this way, it is possible to support both formats (depending on the version installed from Excel):
var
selectedFile: String;
PromptForFileName(selectedFile, 'Documento do Excel (*.xls, *.xlsx)|*.xls; *.xlsx', '', 'Selecione...', 'C:\', False)
Reading the XLS file
I created a function to read (with password and no password) combined with the function GetTempFile
, it returns Boolean
and the parameter var handler
returns the file opened. To open a file without a password do something like openExcel(handler; 'arquivo.xls'; '');
, with password openExcel(handler; 'arquivo.xls'; 'senha');
function openExcel(var handler: Variant; selectedFile: String; pass: String): Boolean;
begin
EndExcel();
tmpFile := GetTempFile('.~tp');
CopyFile(pchar(selectedFile), pchar(tmpFile), true);
xlsExcel := CreateOLEObject('Excel.Application');
xlsExcel.Visible := False;
try
handler := xlsExcel.WorkBooks.Open(selectedFile, Password := password, ReadOnly := True);
Result := True;
except
showmessage('excepet');
EndExcel();
handler := Unassigned;
Result := False;
end;
end;
Closing the XLS
Note that the EXCEL.EXE
process does not end, even if you close the application, because it is a ActiveX
, ie it runs separately, so I created a function called EndExcel
to finish whenever necessary and clean the variable.
procedure EndExcel();
begin
if VarIsEmpty(xlsExcel) = False then
begin
xlsExcel.Quit;
xlsExcel := Unassigned;
end;
if tmpFile <> '' then
begin
DeleteFile(tmpFile);
end;
end;
Authenticating the XLS
Authentication will sometimes be necessary and is a requirement that you mentioned, for this I created an event that works in combination with openExcel
, to call it just run authExcel('arquivo.xls');
. It will open prompt
to select a file, the function itself will test if a password is needed, if it is necessary then a prompt will appear asking you to enter the password, follow the function:
function authExcel(fileStr: String) : Variant;
var
password: String;
buttonSelected: Integer;
xlsTmp: Variant;
begin
password := InputBox('Autenticação', 'Senha do arquivo', '');
if password = '' then
begin
ShowMessage('Nenhuma senha digitada');
Result := Unassigned;
end
else
begin
if openExcel(xlsTmp, fileStr, password) then
begin
Result := xlsTmp
end
else
begin
buttonSelected := MessageDlg('Senha inválida, tentar novamente?', mtCustom, [mbYes, mbCancel], 0);
if buttonSelected = mrYes then
begin
Result := authExcel(fileStr);
end
else
begin
ShowMessage('Cancelado');
Result := Unassigned;
end;
end;
end;
end;
Searching in the first column
I would make a array
that would bring all results, but I chose to create function with search by "wildcard" (if necessary) or by keyword, this search is done in the column you want, but as the case is search in the first one then I added a parameter to change as needed, this function returns a multidimensional array of this type array of array of string
The function looks like this:
findInExcel(xls: Variant; col: Integer; query: String; limitCols: Integer): TStringArrayArray
col
refers to the column you want to query, query
will be the search and limitCols
is to avoid copying extra columns to the right, also note that after using findInExcel
I used EndExcel()
, however remove this part if you still need handler
(xls: Variant), use examples below:
Search in the first column, which contains the word a
and no columns limit on the right (the function itself will try to limit):
findInExcel(handler, 1, 'a', 0);
EndExcel();
Search in the first column, which contains anything (the function will try to avoid empty columns) and with a 4-column limit on the right:
findInExcel(handler, 1, '*', 4);
EndExcel();
Search in the second column, which starts with the letter A (for example A0001
, A0002
) and with a 5-column limit on the right:
findInExcel(handler, 2, 'A*', 5);
EndExcel();
Search in the first column, which ends with the letter B (for example 0001B
, 2B
) and no right column limit (the function itself will try to limit):
findInExcel(handler, 1, '*B', 0);
EndExcel();
How to configure the file pas
:
- Add to
uses
ComObj
and StrUtils
for functions to work
- Add to
type
this TStringArrayArray = Array of Array of String;
- Add the global variables
xlsExcel: Variant;
and tmpFile: String;
Usage example (tested in Delphi7):
I'm not going to talk about FireBird because I do not know what your structure / modeling is, so I'm going to assume that the problem is getting the Excel data only.
In the example each time a loop is looped in the first for
an Excel row is returned, and every time a loop is looped in the second for
a column is returned:
for i := 0 to High(encontrados) do
begin
for j := 0 to High(encontrados[i]) do
begin
if encontrados[i][j] <> '' then //Evita dados em branco
ShowMessage('Linha: ' + IntToStr(i) + ' - Coluna: ' + IntToStr(j) + ' = ' + encontrados[i][j]);
end;
end;
For an xls file like this:
+----+----+----+----+
| a1 | a2 | a3 | a4 |
+----+----+----+----+
| b1 | b2 | b3 | b4 |
+----+----+----+----+
The example will return this (note that in arrays numbers always start from 0
, if you need to solve this in your database just add +1
):
Line: 0 - Column: 0 = a1
Line: 0 - Column: 1 = a2
Line: 0 - Column: 2 = a3
Line: 0 - Column: 3 = a4
Line: 1 - Column: 0 = b1
Line: 1 - Column: 1 = b2
Line: 1 - Column: 2 = b3
Line: 1 - Column: 3 = b4
Complete example:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComObj, StrUtils;
type
TStringArrayArray = Array of Array of String;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
tmpFile: String;
selectedFile: String;
password: String;
xlsExcel: Variant;
xlsDocument: Variant;
consultarEm: Integer;
encontrados: TStringArrayArray;
implementation
{$R *.dfm}
procedure EndExcel();
begin
if VarIsEmpty(xlsExcel) = False then
begin
xlsExcel.Quit;
xlsExcel := Unassigned;
end;
if tmpFile <> '' then
begin
DeleteFile(tmpFile);
end;
end;
function GetTempFile(const Extension: String): String;
var
Buffer: array[0..MAX_PATH] of Char;
begin
repeat
GetTempPath(SizeOf(Buffer) - 1, Buffer);
GetTempFileName(Buffer, '~', 0, Buffer);
Result := ChangeFileExt(Buffer, Extension);
until not FileExists(Result);
end;
function openExcel(var handler: Variant; selectedFile: String; pass: String): Boolean;
begin
EndExcel();
tmpFile := GetTempFile('.~tp');
CopyFile(pchar(selectedFile), pchar(tmpFile), true);
xlsExcel := CreateOLEObject('Excel.Application');
xlsExcel.Visible := False;
try
handler := xlsExcel.WorkBooks.Open(selectedFile, Password := password, ReadOnly := True);
Result := True;
except
showmessage('excepet');
EndExcel();
handler := Unassigned;
Result := False;
end;
end;
function authExcel(fileStr: String) : Variant;
var
password: String;
buttonSelected: Integer;
xlsTmp: Variant;
begin
password := InputBox('Autenticação', 'Senha do arquivo', '');
if password = '' then
begin
ShowMessage('Nenhuma senha digitada');
Result := Unassigned;
end
else
begin
if openExcel(xlsTmp, fileStr, password) then
begin
Result := xlsTmp
end
else
begin
buttonSelected := MessageDlg('Senha inválida, tentar novamente?', mtCustom, [mbYes, mbCancel], 0);
if buttonSelected = mrYes then
begin
Result := authExcel(fileStr);
end
else
begin
ShowMessage('Cancelado');
Result := Unassigned;
end;
end;
end;
end;
function findInExcel(xls: Variant; col: Integer; query: String; limitCols: Integer) : TStringArrayArray;
var
pages: Integer;
currentPage: Integer;
currentLine: Integer;
currentCol: Integer;
found: Integer;
lines: Integer;
cols: Integer;
cells: Integer;
dataRow: String;
resultados: TStringArrayArray;
coringa: Boolean;
coringaReverse: Boolean;
findAll: Boolean;
begin
if col < 1 then
begin
ShowMessage('Coluna para consulta não especificada');
Result := null;
end
else
begin
pages := xls.Worksheets.Count;
if pages > 0 then
begin
cells := 0;
found := 0;
cols := 0;
currentPage := 0;
findAll := '*' = query;
coringa := AnsiPos('*', query) > 0;
coringaReverse := AnsiPos('*', query) = 1;
query := StringReplace(query, '*', '', [rfReplaceAll, rfIgnoreCase]);
while currentPage < pages do
begin
currentLine := 0;
lines := xls.Worksheets[currentPage + 1].UsedRange.Rows.Count;
while currentLine < lines do
begin
dataRow := xls.Worksheets[currentPage + 1].Cells[currentLine + 1, col];
if coringaReverse then
dataRow := ReverseString(dataRow);
if (findAll and (dataRow <> '')) or (coringa and (AnsiPos(query, dataRow) = 1)) or (dataRow = query) then
begin
cells := xls.Worksheets[currentPage + 1].UsedRange.Cells.Count;
found := found + 1;
if cols < cells then
cols := cells;
if (limitCols > 0) and (cells > limitCols) then
cols := limitCols;
if cells > 0 then
SetLength(resultados, found, cols);
currentCol := 0;
while (currentCol < cols) and (cells > 0) do
begin
dataRow := xls.Worksheets[currentPage + 1].Cells[currentLine + 1, currentCol + 1];
resultados[found - 1][currentCol] := xls.Worksheets[currentPage + 1].Cells[currentLine + 1, currentCol + 1];
currentCol := currentCol + 1;
end;
end;
currentLine := currentLine + 1;
end;
currentPage := currentPage + 1;
end;
Result := resultados;
end
else
begin
ShowMessage('Não há páginas neste documento');
end;
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
EndExcel();
//Remova esta parte se quiser
if MessageDlg('Deseja realmente sair?', mtCustom, [mbYes, mbCancel], 0) = mrCancel then
Action := caNone;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
j: Integer;
begin
if PromptForFileName(selectedFile, 'Documento do Excel (*.xls, *.xlsx)|*.xls; *.xlsx', '', 'Selecione...', 'C:\', False) then
begin
if openExcel(xlsDocument, selectedFile, '') = False then
xlsDocument := authExcel(selectedFile);
if VarIsEmpty(xlsDocument) = False then
begin
consultarEm := StrToInt(InputBox('Infomarção', 'Qual coluna gostaria de consultar', '1'));
if consultarEm > 0 then
begin
encontrados := findInExcel(xlsDocument, consultarEm, '*', 4);
EndExcel();
if High(encontrados) = -1 then
begin
ShowMessage('Não foram encontrados resultados');
end
else
begin
for i := 0 to High(encontrados) do
begin
for j := 0 to High(encontrados[i]) do
begin
if encontrados[i][j] <> '' then //Evita dados em branco
ShowMessage('Linha: ' + IntToStr(i) + ' - Coluna: ' + IntToStr(j) + ': ' + encontrados[i][j]);
end;
end;
end;
end
else
begin
ShowMessage('Coluna inválida');
end;
end;
end
else
begin
ShowMessage('Nenhum arquivo selecionado');
end;
end;
end.