create and edit password protected excel file - excel

i am using delphi7 and i want to create password protected excel file in my program and also want to edit it by unprotect it using code only.
my program requirement is that i have to log data in excel file but it will not be edited by end user.So how can i do it using delphi7?
var
xls, xlw: Variant;
begin
{load MS Excel}
xls := CreateOLEObject('Excel.Application');
{open your xls-file}
xlw := xls.WorkBooks.Open(FileName := 'd:\book1.xls', Password := 'qq',
ReadOnly := True);
{save with other file name}
xlw.SaveAs(FileName := 'd:\book2.xls', Password := 'password');
{unload MS Excel}
xlw := UnAssigned;
xls := UnAssigned;
end;
i tried but got Program in which program only save password protected file and after creating password protected excel file i do not able to edit same file again.
So please help me.

Related

Inno Setup. AfterDownload event for a long list of files

Suppose to use the example CodeDownloadFiles.iss and I would like to move downloaded files from the Temporary Download folder. Well, I tried to use the function OnDownloadProgress but it seems that this code is performed before the download file effectively appears in the temp path:
procedure MoveTempFileDownloaded(FileName: String);
begin
FileCopy(ExpandConstant('{tmp}\' + FileName), ExpandConstant('{src}\storage\' + FileName), false);
DeleteFile(ExpandConstant('{tmp}\' + FileName));
end;
the upper procedure is that one should move the files (I have some heavy files) but when the following event will run, the current file will be the next one, so the file will be not copied.
function OnDownloadProgress(const Url, FileName: String; const Progress, ProgressMax: Int64): Boolean;
begin
if Progress = ProgressMax then
begin
MoveTempFileDownloaded(FileName);
end;
Result := True;
end;
In effect what I want to do is to store a list of Components files because if the user would reinstall the package will not need to re-download any files.
This is a sample of component that installer check before to run download:
FileName := 'MyDB1.sqlite';
ComponentIsSelected := CheckFileToDownload('https://example.com/MyFiles/'+FileName, FileName, 'Database\ABC');
if ComponentIsSelected = True then begin
DownloadPage.Add('https://example.com/MyFiles/'+FileName, FileName, '');
end;
at the beginning of the Inno file there is this function:
function CheckFileToDownload(const Url, FileName, ComponentName: String): Boolean;
begin
ComponentIsSelected := False;
if WizardIsComponentSelected(ComponentName) then begin
if FileExists(ExpandConstant('{src}\storage\'+FileName)) = False then begin
Inc(DownloadCount);
ComponentIsSelected := True;
end;
end;
end;
It will be easy in the [Files] to copy temporary files but my files are heavy I would not to get disk space problems. How could I copy a single downloaded file while they are perfectly downloaded?
By the way, I should perform the selection of Components with ExtraDiskSpaceRequired and get the size from the internet using DownloadTemporaryFileSize to increase the Disk Required, but I still have to do it, before I would solve this issue.
By the way, do you think is more simple to find a way to change the temporary temp folder location of downloaded files or to try to move files as I'm doing?
Thanks in advance for any suggestions

Save Excel file inside Delphi service [duplicate]

I have console Delphi application which works with Excell though OLE.
The examle of code is pretty simple
procedure SaveBaseRecordsToFile(BaseName: string; PaymentRecords: TPaymentRecords);
var
i: integer;
Excel: Variant;
begin
try
Excel:=CreateOleObject('Excel.Application');
Excel.DisplayAlerts:=False;
except
on E: Exception do begin
LogWriter.WriteLog(E.ClassName + ': ' + E.Message);
exit;
end;
end;
try
Excel.Workbooks.Add;
//Excel.Worksheets.Add;
Excel.Worksheets[1].Cells[2, 1].Value := 'Account number';
Excel.Worksheets[1].Cells[2, 2].Value := 'Sum';
for i := 0 to Length(PaymentRecords) - 1 do begin
Excel.Worksheets[1].Cells[i + 3, 1].Value := PaymentRecords[i].NUMBER;
Excel.Worksheets[1].Cells[i + 3, 2].Value := PaymentRecords[i].SUMMA;
end;
Excel.ActiveSheet.Name := 'SHEET1';
Excel.Application.Workbooks[1].SaveAs(ExtractFilePath(ParamStr(0)) + BaseName + '.xls', 56);
Excel.Workbooks.Close;
Excel.Quit;
finally
Excel := Unassigned;
end;
end;
When I run this application in interactive mode (by myself) it wors perfect.
But when I try to run it through standart Microsoft Scheduler I see in log of my applications such records:
21:41:40.523: EOleSysError: Отказано в доступе, ProgID: "Excel.Application" (Access denied from Russian)
If I set in schedule task option "Run with highest privileges" I see in log of my applications such records:
20:12:04.475: EOleException: Метод SaveAs из класса Workbook завершен неверно (SaveAs method of Workbook class finished incorrectly from Russian)
Is there any way to run application with OLE call through Microsort Scheduler?
Or maybe I can work with Excel without OLE (it's pretty simple operations as you could see in my examle)? How can I do it?
The fundamental issue is that Excel needs to run in an interactive desktop. Running through the task scheduler, as you have configured it, runs it in a non-interactive desktop. That's not a supported mode of operation for Excel: http://support.microsoft.com/kb/257757
Your only real hope of success is to create the Excel file without using COM automation of Excel. There are many libraries to help you do that.

Inserting images to an Excel sheet in Delphi

I have a question here, I have an application and I need to add images to it. I have tried:
Sheet.Shapes.AddPicture(G_V.Prog_Dir+'pic.BMP',false,true, 190, 10+(15*rowcount), 100, 100 );
it works just fine, but I don't want to give parameters, I want to insert pictures to specified (and parametric) cells because I need to add picture to the last column of the page; this excel needs to be printed I must mention that. So I tried:
Sheet.Range['E'+inttostr(rowcount),'E'+inttostr(rowcount)].Select;
Sheet.Pictures.Insert(G_V.Prog_Dir+'pic.BMP');
It looks OK at first sight, however I think this code links images to the sheet. For example, I send the created Excel to another computer and these images cannot be seen (I don't recall the exact error) and when I searched it, I found out that receiving computer needs to have images at the exact path. As a solution to this, "Sheet.Shapes.AddPicture" recommended but
as I stated before, I need another solution here.
I didn't see anybody experiencing this kind of problem, I hope someone helps me out.
You can use this code to import picture. You do not need exact path if you use AddPicture function with this parameters. it copies photo and paste it to excel.
Set p = ActiveSheet.Shapes.AddPicture(FileName:=PictureFileName,_
linktofile:=msoFalse, savewithdocument:=msoCTrue, _
left:=TargetCell.Left, Top:=TargetCell.Top, Width:=-1,_
Height:=TargetCell.Height)
The simple solution is to add the picture to the clipboard and then paste it into the sheet.
implementation
uses
VCL.Clipbrd, VCL.Graphics, Excel2000;
procedure TForm1.Test;
var
Bitmap: TBitmap;
Excel: TExcelApplication;
Worksheet: TExcelWorksheet;
begin
Bitmap:= TBitmap.Create;
try
Bitmap.LoadFromFile('c:\test.bmp');
Clipboard.Assign(Bitmap);
Excel:= TExcelApplication.Create(self);
Worksheet:= Excel.Worksheets[0];
Worksheet.Range['a1','a1'].Select;
Worksheet.Paste;
finally
Bitmap.Free;
end;
end;
This destroys the previous contents of the clipboard, which is bad form.
You can save/restore the clipboard data using the following routines (based on: https://www.devexpress.com/Support/Center/Question/Details/Q93874)
procedure TForm1.RestoreClipboardData(SourceDataStream: TMemoryStream);
var
AData: THandle;
ADataPtr: Pointer;
begin
if SourceDataStream.Size = 0 then Exit;
Clipboard.Open;
try
AData := GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, SourceDataStream.Size);
try
ADataPtr := GlobalLock(AData);
try
SourceDataStream.Position := 0;
SourceDataStream.ReadBuffer(ADataPtr^, SourceDataStream.Size);
SetClipboardData(CF_BITMAP, AData);
finally
GlobalUnlock(AData);
end;
except
GlobalFree(AData);
raise;
end;
finally
Clipboard.Close;
end;
end;
procedure TForm1.SaveClipboardData(DestDataStream: TMemoryStream);
var
AData: THandle;
ADataPtr: Pointer;
begin
if DestDataStream = nil then raise Exception.Create('Dest is nil');
Clipboard.Open;
try
AData := GetClipboardData(CF_BITMAP);
if AData = 0 then Exit;
ADataPtr := GlobalLock(AData);
try
DestDataStream.Size := GlobalSize(AData);
DestDataStream.Position := 0;
DestDataStream.Write(ADataPtr^, DestDataStream.Size);
finally
GlobalUnlock(AData);
end;
finally
Clipboard.Close;
end;
end;

Excel 2007 gets CSV file wrong?

I am using Delphi 7 to generate CSV file from data acquired from COM port. Now, this seems to work OK since any other spreadsheet software reads generated file properly (Open Office and Libre Office spreadshhet software, for instance). Excel 2007 does the following. Instead of these two column values:
1.976139e-2<TAB>22.98027
i got these two column values:
1.98E+04<TAB>2.298.027
Note that generated file opened in any text editor (Notepad++ for instance) has proper values.
What could be the problem here ?
Thanks,
Josip
If the setting in Delphi do not fit your needs you could change them before loading the CSV. But you should make sure to reset them afterwards.
var
Excel, WorkBook: Variant;
KeepDecimalSeparator, KeepThousandsSeparator: String;
KeepUseSystem: Boolean;
begin
Excel := CreateOleObject('Excel.Application');
Excel.Visible := true;
try
KeepDecimalSeparator := Excel.DecimalSeparator;
KeepThousandsSeparator := Excel.ThousandsSeparator;
KeepUseSystem := Excel.UseSystemSeparators;
Excel.DecimalSeparator := '.';
Excel.ThousandsSeparator := ',';
Excel.UseSystemSeparators := false;
WorkBook := Excel.WorkBooks.Open('C:\Temp\1.csv');
finally
Excel.DecimalSeparator := KeepDecimalSeparator;
Excel.ThousandsSeparator := KeepThousandsSeparator;
Excel.UseSystemSeparators := KeepUseSystem;
end;
end;
Try to use quotes.
"1.976139e-2"<TAB>"22.98027"

Inno Setup - How to read an INF file during the Setup

i need to know how to read a value from INF file [.inf], during the setup. I want the installer to check the version of the program that i am going to update, This program version is not stored in the registry or any other file, is only in the .inf file. Then is a must to get the version from it.
I got your answers, #Tlama and i cannot use a DLL to get version of the software.
This program only save the current version in the INF file.
What i want to do, is to make the installer to check the current versión of the software that i am working with, and display that version in a label text.
The inf information is this:
NetVersion=1.1.1.1
PatchVersion=2.0.1
ProductName=SoftwareX
I just need the PatchVersion to display after where it says version: #### :
this is the code i am trying to fix:
function GetInfsam: String;
var
sVersion : String;
Begin
sVersion := '';
GetIniString('', 'PatchVersion', 'sVersion', '{app}\Sam.inf');
Result := sVersion;
end;
Procedure InitializeWizard7();
var
L2Ver1 : Tlabel;
L2Ver2 : Tlabel;
Begin
L2Ver1:= TLabel.Create(WizardForm);
L2Ver1.Transparent:= True;
L2Ver1.AutoSize:= False;
L2Ver1.WordWrap:= True;
L2Ver1.Font.name:= 'Agency FB';
L2Ver1.Font.Size:= 12;
L2Ver1.Font.Color:= clwhite;
L2Ver1.Caption:= 'Version:';
L2Ver1.Parent:= WizardForm.SelectdirPage;
L2Ver1.Left := 5;
L2Ver1.top := 260;
L2Ver1.Width := 150;
L2Ver1.Height := 40;
L2Ver2:= TLabel.Create(WizardForm);
L2Ver2.Transparent:= True;
L2Ver2.AutoSize:= False;
L2Ver2.WordWrap:= True;
L2Ver2.Font.name:= 'Agency FB';
L2Ver2.Font.Size:= 12;
L2Ver2.Font.Color:= clwhite;
L2Ver2.Caption:= GetInfsam;
L2Ver2.Parent:= WizardForm.SelectdirPage;
L2Ver2.Left := L2Ver1.Width + L2Ver1.Left + 8;
L2Ver2.top := 260;
L2Ver2.Width := 100;
L2Ver2.Height := 40;
End;
Please, i need help to fix my code.
How to read INF file ?
INF files are just sort of INI files with the specified syntax. So to work with INF files you need to treat them as ordinary INI files. Assuming you have a INF file like this:
[Add.Code]
File.dll=File.dll
[File.dll]
File=http://www.code.com/file.dll
FileVersion=1,0,0,143
You can read the FileVersion key by using GetIniString this way:
procedure InitializeWizard;
var
Version: string;
begin
Version := GetIniString('File.dll', 'FileVersion', '', 'c:\File.inf');
if Version <> '' then
MsgBox('File version: ' + Version, mbInformation, MB_OK);
end;
Update:
1. Malformed INF file
According to your update, if the content of your INF file looks like this:
NetVersion=1.1.1.1
PatchVersion=2.0.1
ProductName=SoftwareX
then it's not a well formed INF file, but a name value pair text file saved with INF extension. Real INF files must have a valid [] section for each key value set, but this section is missing in your file.
2. GetIniString function cannot be called with empty Section parameter value
You must not call the GetIniString function with empty Section parameter value, because for the internally called GetPrivateProfileString function it means to return all section names for a given file, not value of a specified key. So for instance the following call is invalid, because the first parameter Section cannot be empty:
GetIniString('', 'KeyName', 'Default', 'c:\File.xxx');
3. How to work with a name value pair text file ?
You'll just need to work with that file as with a text file. For a key value text file handling would be ideal to use the TStringList class, or at least in Delphi. In InnoSetup unfortunately the TStringList class doesn't have published properties needed for a key value content manipulation, so you'll need to make your own key value text file parsing function. Here's the one for getting value for a given key. As the key value delimiter is supposed to be the = sign. This function returns a key value when succeed to find a AKeyName key in a given AFileName file or default ADefault value when fails:
function GetKeyValue(const AKeyName, AFileName, ADefault: string): string;
var
I: Integer;
KeyPos: Integer;
KeyFull: string;
FileLines: TArrayOfString;
begin
Result := ADefault;
if LoadStringsFromFile(AFileName, FileLines) then
begin
KeyFull := AKeyName + '=';
for I := 0 to GetArrayLength(FileLines) - 1 do
begin
FileLines[I] := TrimLeft(FileLines[I]);
KeyPos := Pos(KeyFull, FileLines[I]);
if KeyPos > 0 then
begin
Result := Copy(FileLines[I], KeyPos + Length(AKeyName) + 1, MaxInt);
Break;
end;
end;
end;
end;
To read a value of the PatchVersion key from the Sam.inf file expected in the currently selected install path you can use something like this. This script will update the label value whenever your user change the text in the directory edit box and when the directory selection page is going to be displayed:
var
// target version label must be declared globally
L2Ver2: TLabel;
procedure DirEditChange(Sender: TObject);
var
FilePath: string;
begin
// assign the expected INF file path
FilePath := AddBackslash(WizardForm.DirEdit.Text) + 'Sam.inf';
// read the PatchVersion key value, return N/A if not found
L2Ver2.Caption := GetKeyValue('PatchVersion', FilePath, 'N/A');
end;
procedure InitializeWizard;
begin
// create the target label as before
L2Ver2 := TLabel.Create(WizardForm);
...
// bind the DirEditChange method to the directory edit's OnChange event
WizardForm.DirEdit.OnChange := #DirEditChange;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
// if the page has been turned to the select directory page, update the
// label caption by firing the assigned OnChange event method manually
if (CurPageID = wpSelectDir) then
DirEditChange(nil);
end;
All my INF files have a structure similar to INI files.
If that's your case too I suggest you to open your INF as if it was INI, using (as suggested by TLama)
in setup functions:
function GetIniInt(const Section, Key: String; const Default, Min, Max: Longint; const Filename: String): Longint;
function GetIniString(const Section, Key, Default, Filename: String): String;
in preprocessor:
str ReadIni(str 1, str 2, str 3, str? 4)
Description (from Inno Setup help)
Reads the value from an INI file. Argument 1 must be the name of the INI file, argument 2 – the name of a section in the INI file, the third argument is the key in the section to read. Last optional argument can be used to provide the default value that will be returned on failure, if it is omitted, an empty string is returned.

Resources