Inno Setup String literal too long insert BLOB into table - database

I'm trying to write a script by inno setup to insert a raw in a table which has a BLOB column and I don't know how to write my code correctly ,My database is Oracle and my Inno setup compiler version is 5.5.3 as well.Does anyone help me to find a solution?
This is all my efforts :
var
Content: String;
try
ADOCommand := CreateOleObject('ADODB.Command');
ADOCommand.ActiveConnection := ADOConnection;
Stream := TFileStream.Create('D:\test.png', fmOpenRead);
Count := Stream.Size-1;
Stream.Seek(-Count, soFromEnd);
SetLength(Buffer, 1);
Content := '$' ;
for Index := 1 to Count do
begin
Stream.ReadBuffer(Buffer, 1);
Content:= Content + (Format('%2.2x', [Ord(Buffer[1])]));
end;
ADOCommand.CommandText :='insert into TestBinaryTable(id, blobData) values(1 ,''' + Content + ''')';
ADOCommand.Execute();

I do not think you should prefix the raw data with $.
Remove this line:
Content := '$' ;

Related

Drag & Drop Component Suite: Drop files and if path isn't exists get data from file

I use the Drag and Drop Component Suite for Delphi.
I try to create a drag & drop area which accepts files (ie, from Windows Explorer) and data (ie, from Outlook attachments). So, I use the demo (CombatTargetDemo) to learn how it works, and after this I create a wrapper class which creates a TDropComboTarget object:
constructor TDragAndDrop.Create( vpntOwner: TWinControl);
begin
fpntDragAndDrop := TDropComboTarget.Create(vpntOwner);
fpntDragAndDrop.Name := 'DropComboTarget_'+vpntOwner.Name;
fpntDragAndDrop.DragTypes := [dtCopy, dtLink];
fpntDragAndDrop.OnDrop := DropFiles;
fpntDragAndDrop.Target := vpntOwner;
fpntDragAndDrop.Formats := [mfFile, mfData];
end;
procedure TDragAndDrop.DropFiles(Sender: TObject; ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
var
intCnt: Integer;
pntStream: TStream;
strFileName: String;
strDragAndDropFile: String;
begin
try
fstlDroppedFilePaths.Clear;
fstlDroppedFilePaths.Assign(fpntDragAndDrop.Files);
for intCnt := 0 to fpntDragAndDrop.Data.Count-1 do begin
strFileName := fpntDragAndDrop.Data.Names[intCnt];
if (strFileName = '') then begin
strFileName := IntToStr(intCnt)+'_'+FormatDateTime('yyyymmddhhnnss', Now())+'.dat';
end;
strDragAndDropFile := GetDragAndDropSavePath+strFileName;
pntStream := TFileStream.Create(strDragAndDropFile, fmCreate);
try
pntStream.CopyFrom(fpntDragAndDrop.Data[intCnt], fpntDragAndDrop.Data[intCnt].Size);
finally
pntStream.Free;
end;
if FileExists(strDragAndDropFile, false) then begin
fstlDroppedFilePaths.Add(strDragAndDropFile);
end;
end;
except
end;
end;
First of all, the code works.
If I drop a Windows Explorer file on the area:
fpntDragAndDrop.Files.Count is 1 (contains the path+name from file)
fpntDragAndDrop.Data.Count is 1 (contains the file as a stream)
If I drop a file from Outlook on the area:
fpntDragAndDrop.Files.Count is 0 (contains nothing)
fpntDragAndDrop.Data.Count is 1 (contains the file as a stream)
Now my problem:
If I drop very large files from Windows Explorer, the component does the following:
Read the file header and add an item to fpntDragAndDrop.Files
Create a TMemoryStream and try to load the data from the file into the stream
Step 1 is perfect, but on step 2 I get an exception because of insufficient memory.
My solution:
I want that the component does Step 1. If Step 1 gives a result, then the component should skip Step 2. After this, the variables in the DropFiles procedure should have the following values:
If I drop a Windows Explorer file on the area:
fpntDragAndDrop.Files.Count is 1 (contaims the path+name from the file)
fpntDragAndDrop.Data.Count is 0 (No memory stream is loaded)
If I drop a file from Outlook on the area:
fpntDragAndDrop.Files.Count is 0 (comtains nothing)
fpntDragAndDrop.Data.Count is 1 (contains the file as a stream)
Does somebody have an idea? Or maybe the component has a setting for that?
I'm not overly familiar with this suite, but just browsing through its source, I think you can use the OnAcceptFormat event to reject formats you don't want on a per-drop basis.
So, even though you have enabled drops of mfData doesn't mean you have to actually accept a dropped stream (TDataStreamDataFormat) if a file path (TFileDataFormat or TFileMapDataFormat) is available. So, query the fpntDragAndDrop.DataObject to see what formats it actually holds, such as by passing it to the HasValidFormats() method of the various formats in the fpntDragAndDrop.DataFormats property.
For example:
fpntDragAndDrop.OnAcceptFormat := AcceptStreams;
...
procedure TDragAndDrop.AcceptStreams(Sender: TObject;
const DataFormat: TCustomDataFormat; var Accept: boolean);
var
Fmt: TCustomDataFormat;
i: Integer;
begin
if DataFormat is TDataStreamDataFormat then
begin
// FYI, TFileDataFormat should be in DataFormats[0],
// and TFileMapDataFormat should be in DataFormats[5],
// if you want to avoid this loop...
for i := 0 to fpntDragAndDrop.DataFormats.Count-1 do
begin
Fmt := fpntDragAndDrop.DataFormats[i];
if (Fmt <> DataFormat) and ((Fmt is TFileDataFormat) or (Fmt is TFileMapDataFormat)) then
begin
if Fmt.HasValidFormats(fpntDragAndDrop.DataObject) then
begin
Accept := False;
Exit;
end;
end;
end;
end;
Accept := True; // should already be True by default...
end;

Select items from a combobox and write index of selected value to an INI file during installation

How to make the user select an item from a combobox and then write it to an INI file as a number (01, 02, 03, ..., 18)? I generated this code but I do not know what else to do. If you could help me I would be very grateful
[INI]
Filename: "{app}\rev.ini"; Section: "steamclient"; Key: "RankLevel"; String: ""
[Code]
var
NewComboBox1: TNewComboBox;
procedure RedesignWizardForm;
begin
{ NewComboBox1 }
NewComboBox1 := TNewComboBox.Create(WizardForm);
with NewComboBox1 do
begin
Name := 'NewComboBox1';
Parent := WizardForm.SelectDirPage;
Left := ScaleX(0);
Top := ScaleY(120);
Width := ScaleX(145);
Height := ScaleY(21);
Text := 'Alcon';
Items.Text := 'Alcon' + #13#10 +
'Aguila' + #13#10 +
'Elite' + #13#10 +
'Pro';
ItemIndex := 0;
end;
NewComboBox1.TabOrder := 5;
end;
procedure InitializeWizard();
begin
RedesignWizardForm;
end;
The easiest solution is using a scripted constant in the INI section:
[INI]
Filename: "{app}\rev.ini"; Section: "steamclient"; \
Key: "RankLevel"; String: "{code:GetRankLevel}"
[Code]
{ ... }
function GetRankLevel(Param: string): string;
begin
Result := Format('%.2d', [NewComboBox1.ItemIndex + 1]);
end;
Though this will not write the INI file "in a custom page after the components section". It will write it only during the actual installation, what is the correct behavior, imho.
See also Save Inno Setup custom page field values to an INI file.

Inno Setup - Create a dynamic list of components/types from external source (file or folder contents)

I have a batch file (setting changer) that uses xcopy to list specific files formats in a specific folder, then allows me to type in one of the names and the script uses that name to copy that file to another location.
First xcopy creates a copy of the original as a backup (rolling backup only 1 copy) then does the file copy (extension is fixed in batch only body of filename needed This works great BUT I would love to try and do this in Inno Setup for a nice clean GUI.
I would like to populate the list of components/types from this list of files found in a specific fixed folder. Or even create an ini file with those names in (extra step but maybe better control). Main problem that might prevent this from being possible is not knowing how many entries it would be an array. If only 1 entry or file only 1 option (1 or a) if 4 then user can select 1 of 4 (a, b, c or d). I would extract the file name to create the name/description.
then on completion the same task as my batch would happen, backup (easy always the same name like start.ini) then copy the file e.g. example1.ini and overwrite start.ini
ini might be most flexible as I can imagine later adding new sections to change the actions performed.
Is this possible or stretching this program too far. No rush as my batch works for now but the less typing and manual steps the better for continued use.
I found an example to list contents to a dialog window but I could not figure how to use this to populate components. TLama - List all files in a directory
You cannot create the components dynamically on runtime (you can on compile-time).
But it's not difficult to implement a custom dynamic components-like page using the CreateCustomPage and the TNewCheckListBox.
Then in the CurStepChanged(ssInstall) you process the selected files/components as your need.
[Code]
const
SourcePath = 'C:\somepath';
var
CustomSelectTasksPage: TWizardPage;
ComponentsList: TNewCheckListBox;
procedure InitializeWizard();
var
FindRec: TFindRec;
SelectComponentsLabel: TNewStaticText;
begin
CustomSelectTasksPage :=
CreateCustomPage(
wpSelectComponents, SetupMessage(msgWizardSelectComponents),
SetupMessage(msgSelectComponentsDesc));
SelectComponentsLabel := TNewStaticText.Create(WizardForm);
SelectComponentsLabel.Parent := CustomSelectTasksPage.Surface;
SelectComponentsLabel.Top := 0;
SelectComponentsLabel.Left := 0;
SelectComponentsLabel.Width := CustomSelectTasksPage.Surface.Width;
SelectComponentsLabel.AutoSize := False;
SelectComponentsLabel.ShowAccelChar := False;
SelectComponentsLabel.WordWrap := True;
SelectComponentsLabel.Caption := SetupMessage(msgSelectComponentsLabel2);
WizardForm.AdjustLabelHeight(SelectComponentsLabel);
ComponentsList := TNewCheckListBox.Create(WizardForm);
ComponentsList.Parent := CustomSelectTasksPage.Surface;
ComponentsList.Top :=
SelectComponentsLabel.Top + SelectComponentsLabel.Height + ScaleY(8);
ComponentsList.Left := 0;
ComponentsList.Width := CustomSelectTasksPage.Surface.Width;
ComponentsList.Height := CustomSelectTasksPage.Surface.Height - ComponentsList.Top;
if FindFirst(ExpandConstant(AddBackslash(SourcePath) + '*.dat'), FindRec) then
begin
try
repeat
ComponentsList.AddCheckBox(FindRec.Name, '', 0, False, True, False, False, nil);
until not FindNext(FindRec);
finally
FindClose(FindRec);
end;
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
I: Integer;
FileName: string;
SourceFilePath: string;
TargetFilePath: string;
begin
if CurStep = ssInstall then
begin
for I := 0 to ComponentsList.Items.Count - 1 do
begin
if ComponentsList.Checked[I] then
begin
FileName := ComponentsList.Items[I];
SourceFilePath := AddBackslash(SourcePath) + FileName;
TargetFilePath := AddBackslash(ExpandConstant('{app}')) + FileName;
if FileCopy(SourceFilePath, TargetFilePath, False) then
begin
Log(Format('Installed "%s".', [FileName]));
end
else
begin
Log(Format('Failed to install "%s".', [FileName]));
end;
end;
end;
end;
end;

Moving the content of a folder to another one

Using Delphi 7, I need to move all the content (files, folders and subfolders) from one folder to another one. After some research, SHFileOpStruct seems to be the best option. Here is what I got so far:
function MoveDir(SrcDir, DstDir: string): Boolean;
var
FOS: TSHFileOpStruct;
begin
ZeroMemory(#FOS, SizeOf(FOS));
with FOS do
begin
wFunc := FO_MOVE; // FO_COPY;
fFlags := FOF_FILESONLY or
FOF_ALLOWUNDO or FOF_SIMPLEPROGRESS;
pFrom := PChar(SrcDir + #0);
pTo := PChar(DstDir + #0);
end;
Result := (SHFileOperation(FOS) = 0);
end;
But when using this function, the entire folder is moved to destination, not only it content. For example, if I use MoveDir('c:\test', 'd:\test') I get d:\teste\teste.
I already tried to change this line bellow, and it works when coping the files (FO_COPY) but not when moving.
pFrom := PChar(SrcDir + '\*.*' + #0);
Please, can someone help me with this? Would be great if I can do this without moving file by file, folder by folder...
Thanks!!
You should use your second version, without FOF_FILESONLY flag:
function MoveDir(SrcDir, DstDir: string): Boolean;
var
FOS: TSHFileOpStruct;
begin
ZeroMemory(#FOS, SizeOf(FOS));
with FOS do
begin
wFunc := FO_MOVE; // FO_COPY;
fFlags := FOF_ALLOWUNDO or FOF_SIMPLEPROGRESS;
pFrom := PChar(IncludeTrailingPathDelimiter(SrcDir) + '*.*'#0);
pTo := PChar(DstDir + #0);
end;
Result := (SHFileOperation(FOS) = 0);
end;

ADO - how to get primary key fields

I'm using ADO (Delphi & C++ Builder) and I would like get primary key fields (their names) that are in some TADOTable component. How to do it?
I found out that I need to use ADOConnection->OpenSchema but don't know how to use this..
Now I tried this:
int bounds[] = {0,2};
OleVariant A(bounds,1, varVariant);
A.PutElement(varEmpty,0);
A.PutElement(varEmpty,1);
A.PutElement("MyDBTable",2);
OleVariant EmptyParam;
EmptyParam.VType = VT_ERROR;
EmptyParam.VError = DISP_E_PARAMNOTFOUND;
TADODataSet *temp = new TADODataSet(NULL);
AdoConnection1->OpenSchema(siPrimaryKeys, A, EmptyParam, temp);
temp->Open();
temp->First();
while (!temp->Eof)
{
Memo1->Lines->Add(temp->Fields->Fields[0]->AsString);
temp->Next();
}
temp->Close();
delete temp;
When running this code I get: "Object or provider is not capable of performing requested operation."?
References for OpenSchema Method (ADO), examples can be found here
An example implemention in Delphi for Microsoft Access and MSSqlServer could look like this:
Procedure OpenPrimaryKeyInfo ( Connection:TAdoConnection
; DatabaseName , SchemaName , TableName : Variant
; Display:TAdodataset );
begin
Connection.OpenSchema( siPrimaryKeys
, VarArrayOf([ DatabaseName , SchemaName , TableName ])
, EmptyParam , Display );
end;
Example call for Microsoft Access:
OpenPrimaryKeyInfo( AdoConnection2 , UnAssigned , UnAssigned , 'TableX' , Adodataset1 );
Example call for MSSqlServer:
OpenPrimaryKeyInfo( AdoConnection1 , 'MyDataBase' , 'dbo' , 'TableX' , Adodataset1 );
You establish the connection and open it as usual (using TADOConnection.ConnectionString and TADOConnection.Open), and then ask for the schema using OpenSchema. The TADODataSet you provide as the last parameter will contain a RecordSet that you can use just like any other dataset.
Here's a quick sample I threw together (thanks to #bummi for the correction to the third parameter - Unassigned and Null both compiled, but didn't actually work when tested). I dropped a TADOConnection, TADODataSet, and TMemo on a new form and configured the TADOConnection quickly to point to a simple SQL Server Express database I have for some testing - I included the connection string; the only change I made to it was in the computer name provided in the Data Source portion).
procedure TForm3.FormShow(Sender: TObject);
var
i: Integer;
sLine: string;
begin
Memo1.Clear;
ADOConnection1.ConnectionString := 'Provider=SQLOLEDB.1;' +
'Integrated Security=SSPI;' +
'Persist Security Info=False;' +
'Initial Catalog=Contacts;' +
'Data Source=MyComputer\SQLEXPRESS';
ADOConnection1.Connected := True;
ADOConnection1.OpenSchema(siPrimaryKeys, Unassigned, EmptyParam, ADODataSet1);
sLine := '';
for i := 0 to ADODataSet1.FieldCount - 1 do
sLine := sLine + ADODataSet1.Fields[i].FieldName + #9;
Memo1.Lines.Add(sLine);
Memo1.Lines.Add('');
while not ADODataSet1.Eof do
begin
sLine := '';
for i := 0 to ADODataSet1.FieldCount - 1 do
sLine := sLine + ADODataSet1.Fields[i].AsString + #9;
Memo1.Lines.Add(sLine);
ADODataSet1.Next;
end;
end;
The possible values for the SchemaInfo value (the first parameter passed to OpenSchema) can be found in the ADODB unit - they're documented in the Delphi help file (note that the documentation says that not all of them are available via ADO):
TSchemaInfo = (siAsserts, siCatalogs, siCharacterSets, siCollations,
siColumns, siCheckConstraints, siConstraintColumnUsage,
siConstraintTableUsage, siKeyColumnUsage, siReferentialConstraints,
siTableConstraints, siColumnsDomainUsage, siIndexes, siColumnPrivileges,
siTablePrivileges, siUsagePrivileges, siProcedures, siSchemata,
siSQLLanguages, siStatistics, siTables, siTranslations, siProviderTypes,
siViews, siViewColumnUsage, siViewTableUsage, siProcedureParameters,
siForeignKeys, siPrimaryKeys, siProcedureColumns, siDBInfoKeywords,
siDBInfoLiterals, siCubes, siDimensions, siHierarchies, siLevels,
siMeasures, siProperties, siMembers, siProviderSpecific);

Resources