Oracle connection using Ole DB and ODBC - delphi

Im trying to make a 32bit oracle connection on a Windows 10 machine using ADO in Delphi 11.2.
For Ole DB I use this connection string:
Provider=OraOLEDB.Oracle;Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=[HOST])(PORT=[PORT]))(CONNECT_DATA=(SID=[SID])(SERVER=DEDICATED)));User ID=[USERNAME];Password=[PASSWORD];
and for ODBC I use this connection string:
Driver={Microsoft ODBC for Oracle};Server=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=[HOST])(PORT=[PORT]))(CONNECT_DATA=(SID=[SID])(SERVER=DEDICATED)));User ID=[USERNAME];Password=[PASSWORD];
I want to use the second connection (ODBC) because a lot of old programs use this to connect to Oracle and I dont want to recompile and change the old code.
My problem is that the OLE DB connection works fine, but the ODBC connection gives a "[Microsoft][ODBC driver for Oracle][Oracle]Error while trying to retrieve text for error ORA-01019." error.
I can also see it tries to resolve the "{Microsoft ODBC for Oracle}" to a MSDASQL provider for the ODBC connection.
Now comes the wierd part.
If I do a simple ADO connection to oracle DB using the OLE DB Connection, then disconnect and then connect using the ODBC connection, then it works fine and connect - until I restart the program.
For connection I use this simple Delphi code using ADO
ADO := TADOConnection.Create(Nil);
Try
ADO.ConnectionString := ConnectionStr;
ADO.LoginPrompt := False;
Try
Try
ADO.Connected := True;
if ADO.Connected then
MessageBox(0, Pchar('Connected to Database.' + sLineBreak + sLineBreak + 'Using Provider: ' + ADO.Provider + sLineBreak + sLineBreak + 'Connection String:' + sLineBreak + ConnectionStr + sLineBreak + sLineBreak + 'Used Conncetions String:' + sLineBreak + ADO.ConnectionString), Pchar('Connected'), MB_OK+MB_ICONINFORMATION);
Result := True;
Except
On E: Exception do
raise Exception.Create('Failed to connect.' + sLineBreak + sLineBreak + 'Using Provider: ' + ADO.Provider + sLineBreak + sLineBreak + 'Connection String:' + sLineBreak + ConnectionStr + sLineBreak + sLineBreak + 'Used Conncetions String:' + sLineBreak + ADO.ConnectionString + sLineBreak + SLineBreak + 'Error:' + sLineBreak + E.Message);
end;
Finally
if ADO.Connected then
ADO.Connected := False;
end;
Finally
ADO.Free;
end;
I suspect the OLE DB initialize something that makes the ODBC connection possible. If I do it the other way around and try to connect to the ODBC first and then the OLE DB, then is gives a "Catastrophic failure." exception.
Anyone have an idea why my ODBC connection is not working (without the wierd OLE DB "hack") ?

Related

Delphi 10.4 + msbuild = unstable binary

I just implemented a CI/CD environment with Delphi 10.4+gitlab+build machine
Everything worked apparently correct, with the build process running smoothly, but when using the program, it randomly show errors that never occurs compared if I build manually via "shift+F9" (build) inside Delphi itself.
The build process:
call "C:\\Program Files (x86)\\Embarcadero\\Studio\\21.0\\bin\\rsvars.bat"
msbuild /target:rebuild /property:config=Debug;DCC_BuildAllUnits=true /p:platform=Win32 project.dproj
Also, I tried other msbuild variations like:
msbuild /target:rebuild /property:config=Debug /p:platform=Win32 project.dproj
(and also with 2 steps (target:clean and target:build)
I also compared msbuild dcc32.exe command with "Delphi" dcc32.exe command, and they are basically equal but for an additional (inexistent) folder for msbuild output:
-I"c:\program files (x86)\embarcadero\studio\21.0\lib\Win32\release\EN";...
The weirdest thing is: after doing a msbuild build, if I open my project with Delphi, do a clean+compile the program became weirder, giving other errors, but if I do full build (shift+F9) then it runs smoothly again
To me, it looks like some of the components used aren't properly compiled under msbuild or are used with other parameters, but I have no idea of what could it be or how to find it.
Error I got after building with cmd msbuild + clean at Delphi + compile at Delphi:
First chance exception at $0019FB95. Exception class $C0000096 with message 'privileged instruction at 0x0019fb95'. Process project.exe (11324)
Followed by:
First chance exception at $0040AE84. Exception class $C0000005 with message 'access violation at 0x0040ae84: read of address 0x0000000b'. Process project.exe (11324)
Looking at the error, it is raised in this code:
procedure TAdvancedField.asImageLoadToBitMap(var outBitmap:TBitmap); var FS : TMemoryStream;
FirstBytes: AnsiString;
Graphic : TGraphic;
begin try
FS := TMemoryStream.Create;
(Self as TBlobField).SaveToStream(FS);
fs.Position := 0;
SetLength(FirstBytes, 8);
FS.Read(FirstBytes[1], 8);
//Graphic := nil; //I add this line after my debug, and commented to confirm
//if Copy(FirstBytes, 1, 2) = 'BM' then
//begin
// Graphic := TBitmap.Create;
//end else
//if FirstBytes = #137'PNG'#13#10#26#10 then
//begin
// Graphic := TPngImage.Create;
//end else
//if Copy(FirstBytes, 1, 3) = 'GIF' then
//begin
// Graphic := TGIFImage.Create;
//end else
//if Copy(FirstBytes, 1, 2) = #$FF#$D8 then
//begin
// Graphic := TJPEGImage.Create;
// TJPEGImage(Graphic).CompressionQuality := 100;
//end;
if Assigned(Graphic) then
begin
try
FS.Seek(0, soFromBeginning);
Graphic.LoadFromStream(FS); //The error raised HERE
outBitmap.Assign(Graphic);
except
end;
Graphic.Free;
end
else
begin
outBitmap.Assign(nil);
end;
finally
fs.Free;
end;
end;
As you can see, the Graphic variable isn't initialized anywhere. But with a clean Delphi build it runs perfectly with Assigned(Graphic) always returning false, but after msbuild it returns as true. If this is correct to other similar cases it'll lead to unexpected behavior all across the project.
Another situation:
I put a button at my main form with a single raise exception, + application exception handler + JCVStackTrace handler, and this is the captured stack:
[017B7DE5] pcnConversao.RegTribISSQNToStr (Line 1301, "pcnConversao.pas" + 1) + $104
[006B6973] Vcl.Controls.TWinControl.ArrangeControl + $147
[006BB36F] Vcl.Controls.TWinControl.DoKeyUp + $73
[005A4998] Vcl.StdCtrls.TCustomCheckBox.SetChecked + $0
[006BB4CF] Vcl.Controls.TraverseControls + $37
[006BB36F] Vcl.Controls.TWinControl.DoKeyUp + $73
[0067E227] Vcl.Forms.TCustomForm.CreateWnd + $97
[006BA8BC] Vcl.Controls.TWinControl.WMInputLangChange + $8
[004F4A18] System.Classes.TStreamWriter.WriteLine + $C
[006BB47A] Vcl.Controls.TWinControl.WMChar + $2
[006BB36F] Vcl.Controls.TWinControl.DoKeyUp + $73
[005A4998] Vcl.StdCtrls.TCustomCheckBox.SetChecked + $0
[004F4A18] System.Classes.TStreamWriter.WriteLine + $C
By the way, pcnConversao is only used indirectly by my project
The correct would be:
[01709661] UModulo.TModulo.BitBtn3Click (Line 568, "UModulo.pas" + 0) + $11
[006BB3E7] Vcl.Controls.TWinControl.WndProc + $693
[005A4A10] Vcl.StdCtrls.TButtonControl.WndProc + $6C
[006BB547] Vcl.Controls.DoControlMsg + $23
[006BB3E7] Vcl.Controls.TWinControl.WndProc + $693
[0067E29F] Vcl.Forms.TCustomForm.WndProc + $6DB
[006BA934] Vcl.Controls.TWinControl.MainWndProc + $2C
[004F4A90] System.Classes.StdWndProc + $14
[006BB4F2] Vcl.Controls.TWinControl.DefaultHandler + $E6
[006BB3E7] Vcl.Controls.TWinControl.WndProc + $693
[005A4A10] Vcl.StdCtrls.TButtonControl.WndProc + $6C
[004F4A90] System.Classes.StdWndProc + $14
Note:
Looks like the different stack is directly related with Stack Frames checked at Delphi Compiler->Compiling->Code Generation, still no related with the diverging way of the compiler at not initialized variable
The problem was exactly that uninitialized variable. Still don't know why the behavior was different from the default build trough the IDE, but initializing as nil solved the problem

Using Delphi JOSE & JWT library, how do you sign a JWT using RS256

All their examples use HS*** with none in RS*** and trying to change the examples to suite dont seem to be working.
My problem seems to be getting the private key loaded for signing. I'm using a PEM in a string, setting up the claims, using this
Procedure RunTest2b;
var
LToken: TJWT;
LSigner: TJWS;
LKey: TJWK;
LAlg: TJOSEAlgorithmId;
s: String;
begin
LToken := TJWT.Create;
try
LToken.Claims.Subject := 'Paolo Rossi';
LToken.Claims.Issuer := 'Delphi JOSE Library';
LToken.Claims.IssuedAt := Now;
LToken.Claims.Expiration := Now + 1;
// Signing algorithm
LAlg := TJOSEAlgorithmId.RS256;
LSigner := TJWS.Create(LToken);
LKey := TJWK.Create(gPrivateKey);
try
// With this option you can have keys < algorithm length
LSigner.SkipKeyValidation := True;
LSigner.Sign(LKey, LAlg);
s := 'Header: ' + LSigner.Header + #13#10 +
'Payload: ' + LSigner.Payload + #13#10 +
'Signature: ' + LSigner.Signature + #13#10 +
'Compact Token: ' + LSigner.CompactToken;
if s = '' then;
finally
LKey.Free;
LSigner.Free;
end;
finally
LToken.Free;
end;
end;
This fails in the sign method saying "Unable to load private key:" and a bunch of weird characters which makes it look like maybe I have a wide string when I should have an ansistring, but changing it doesn't seem to help.
I have also tried using the TBase64.Decode and TBase64.UrlDecode to transform the key before I pass it into the sign method without success.
Can anyone see where I'm making a mistake ?
I recently jump thru a few hoops to do some JWT testing using JOSE. I didn't sign anything, but did have to use the PEM to verify the JWT which was using RS. While doing so I made the mistake of concatenating the PEM string into a single line of characters without preserving the line feeds. I wonder if you made the same mistake with your keys?
i.e. bad PEM format
myPem := '-----BEGIN PUBLIC KEY-----'
+ 'A23BBjhasdfbewisudvnacwerf823rdsvcp2'
+ 'bDenDfsub893rghvsaefawerd'
+ '-----END PUBLIC KEY-----';
i.e. good PEM format
myPem := '-----BEGIN PUBLIC KEY-----'
+ #13#10 + 'A23BBjhasdfbewisudvnacwerf823rdsvcp2'
+ #13#10 + 'bDenDfsub893rghvsaefawerd'
+ #13#10 + '-----END PUBLIC KEY-----';

How do I include line breaks in an e-mail message created with ShellExecute?

I can successfully send an email with ShellExecute. The To address is correct, the Sender Address is correct, and the Subject is correct. The body of the email is correct, except there are no line breaks at all and everything appears as a single paragraph with no line breaks. The default email client in my case is Windows 8.1 mail.
My question is, can ShellExecute be used so the line breaks are retained? I am not looking to send the email directly with Indy. All I need to do is to send an email to the default email client and have it formatted correctly.
procedure TForm1.Email1Click(Sender: TObject);
var
iGridTableItem: TcxCustomGridTableItem;
iName, iDate, iEmail, iSubject, iBody, iParam: string;
begin
iGridTableItem := cxGrid1DBTableView1.DataController.
GetItemByFieldName('EMail');
if iGridTableItem.EditValue <> null then
iEmail := iGridTableItem.EditValue;
iGridTableItem := cxGrid1DBTableView1.DataController.
GetItemByFieldName('Name');
if iGridTableItem.EditValue <> null then
iName := iGridTableItem.EditValue;
iGridTableItem := cxGrid1DBTableView1.DataController.
GetItemByFieldName('Date');
if iGridTableItem.EditValue <> null then
iDate := DateToStr(iGridTableItem.EditValue);
iSubject := 'ImageEn EBook';
iBody := 'Dear Mr. ' + iName + ',' + sLineBreak + sLineBreak +
'PayPal has advised me that you purchased xxxxx on ' + iDate +
'.' + ' Thank-you for your purchase.' + sLineBreak + sLineBreak + 'You may ' +
'download the xxx at' + sLineBreak +
'http://www.xxxxx.xxx/xxx/EBook/xxx101.zip' + sLineBreak +
'Best regards,' + sLineBreak + 'William Miller' + sLineBreak +
'Adirondack Software and Graphics ' + sLineBreak + 'Email: xxx#xxxxxxxx.xxx'
iParam := 'mailto:' + iEmail + '?subject=' + iSubject + '&Body=' + iBody;
ShellExecute(Form1.Handle, 'open', PChar(iParam), nil, nil, SW_SHOWNORMAL);
end;
Different mail apps support the mailto protocol differently. Not all apps allow multiple parameters to be specified together, etc. So using mailto to send emails is going to be very spotty on different machines.
That being said, you are essentially invoking a URL, just one with the mailto: protocol scheme. As such, you need to url-encode reserved characters, like spaces and line breaks. You might not want to use Indy to send the mail, but you can use it to encode your parameter values, at least:
uses
..., IdURI;
iParam := 'mailto:' + iEmail + '?subject=' + TIdURI.ParamsEncode(iSubject) + '&Body=' + TIdURI.ParamsEncode(iBody);

Delphi multiple .ini files operation

Recently, I encountered a little issue, which causes an access violation when trying to read .ini files.
My question is, can I load multiple .ini files (e.g. settings.ini and data.ini) within one and the same procedure? For instance, I have two ini files, which I write in and read from.
Here is a shortened proc, which writes data:
//writing to file uninstall.ini
try
ini := TIniFile.Create(edPath.Text + '\Uninstall.ini');
ini.WriteString('Uninstall', 'qfProgramName', Label4.Caption);
ini.WriteString('Uninstall', 'qfUninstPath', edPath.Text);
finally
ini.Free;
end;
And then, there is this this code (in the same procedure)
configini := configini.Create(ExtractFilePath(Application.ExeName) + '\quickfix.ini');
sectionsCount := getMaxSectionIndex(ExtractFilePath(Application.ExeName) + '\quickfix.ini');
startmenuLoc := GetProperDir(_STARTMENU);
desktopLoc := GetProperDir(_DESKTOP);
for I := 1 to sectionsCount do begin
currentSection := 'qfShortcut_' + IntToStr(I);
shortcutFile := configini.ReadString(currentSection, 'qfShTarget', '');
shortcutDesc := configini.ReadString(currentSection, 'qfShDescription', '');
shortcutFullPath := installPath + '\' + shortcutFile;
shortcutDest := configini.ReadString(currentSection, 'qfShPath', '');
displayName := configini.ReadString(currentSection, 'qfDisplayName', '');
showmessage(startmenuLoc + '\' + displayName + '.lnk');
showmessage(shortcutFullPath);
if shortcutDest = 'spStartMenu' then CreateLink(shortcutFullPath, startmenuLoc + '\' + displayName + '.lnk', '', '');
if shortcutDest = 'spDesktop' then CreateLink(shortcutFullPath, desktopLoc + '\' + displayName + '.lnk', '', '');
The line with the second loaded file crashes with the access violation. It's not excluded, that the trouble comes from some other location, however, I would like you guys to take a look, maybe someone will see another problem.
This snippet is to create shortcuts on the desktop and in start menu, loading data from .ini files. File "quickfix.ini" is also loaded once at OnCreate, but freed then.
Try replacing
configini := configini.Create(ExtractFilePath(Application.ExeName) + '\quickfix.ini');
with:
configini := TIniFile.Create(ExtractFilePath(Application.ExeName) + '\quickfix.ini');
You are trying to call Create on an uninitialized variable rather than on a class.

TIdHTTPRequestInfo.FormParams in name/value pair

I am using Indy10. My server is processing a form post. ARequestInfo.FormParams properly contains the unparsed form parameters. But ARequestInfo.Params.count is 0. Is there a way I can have ARequestInfo.Params have the parsed form parameters? Or is there a way to parse ARequestInfo.FormParams?
It seems I have to write my own parsing routine when this should already be encapsulated in the object. Or perhaps I am missing a method.
Update Doing some more digging I have found when doing a post within our LAN everything works ok. But when the post is done from a browser outside of our LAN it does not.
Try to set the ParseParams property at your TIdHTTPServer.Or you can make a descendant of the TIdHTTPRequestInfo class for accessing the protected method named DecodeAndSetParams to parse the parameters by yourself.Here is the example.
uses
IdCustomHTTPServer;
type
THTTPRequest = class(TIdHTTPRequestInfo);
procedure TForm1.Button1Click(Sender: TObject);
var
Request: THTTPRequest;
begin
Request := THTTPRequest.Create;
Request.DecodeAndSetParams('firstparam=1&secondparam=2&thirdparam=3');
ShowMessage('Param count: ' + IntToStr(Request.Params.Count) +
sLineBreak + sLineBreak +
Request.Params[0] + sLineBreak +
Request.Params[1] + sLineBreak +
Request.Params[2] + sLineBreak
);
Request.Free;
end;

Resources