Delphi XE8 and SQLServer 2017.
I have a query (TADOQuery) connected to a SQLServer database. After opening the dataset (TADOQuery.Open), if I call TADOQuery.FieldByName('X').AsCurrency it returns 0, but if I call TADOQuery.FieldByName('X').AsFloat it returns 12.65 (correct value). Looking at the specific field in the table, the type is numeric(18,4). What's wrong?
This code runs the otherthing:
with TADOQuery1 do
begin
SQL.Clear;
SQL.Add('select X from Table1');
Open;
if FieldByName('X').AsCurrency > 0 then // <- Here is the problem
do something
else
do otherthing;
end;
This code runs the something:
with TADOQuery1 do
begin
SQL.Clear;
SQL.Add('select X from Table1');
Open;
if FieldByName('X').AsFloat > 0 then // <- Here is the problem
do something
else
do otherthing;
end;
Cannot reproduce this behavior.
here is a MRE, amount is a numeric(18,4) in table Tbl_test :
program SO68004040;
{$APPTYPE CONSOLE}
{$R *.res}
uses
ActiveX,
AdoDb,
System.SysUtils;
var
DbConn : TADOConnection;
Qry : TADOQuery;
begin
Coinitialize(nil);
try
DbConn := TADOConnection.Create(nil);
Qry := TADOQuery.Create(nil);
try
Qry.Connection := DbConn;
DbConn.ConnectionString := 'Provider=SQLOLEDB.1;Persist Security Info=True;Initial Catalog=TestCustomer;Data Source=localhost\SQLEXPRESS;Integrated Security=SSPI; ';
DbConn.Connected := True;
Qry.SQL.Text := 'SELECT * FROM Tbl_test';
Qry.Open;
while not Qry.Eof do
begin
Writeln(Format('AsCurrency: %.4f', [Qry.FieldByName('amount').AsCurrency]));
Writeln(Format('AsFloat: %.4f', [Qry.FieldByName('amount').AsFloat]));
Qry.Next;
end;
finally
Qry.Free;
DbConn.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
CoUninitialize();
Readln;
end.
Related
How can I extract domain suffix without entering http:// or https://?
For example, if I enter stackoverflow.com, I want to get the result of com.
I have this function, but I must enter http:// to get the result.
Is there any way to skip entering http:// and https://?
procedure TForm1.Button2Click(Sender: TObject);
function RatChar(S:String; C: Char):Integer;
var
i : Integer;
begin
i := Length(S);
//while (S[i] <> C) and (i > 0) do
while (i > 0) and (S[i] <> C) do
Dec(i);
Result := i;
end;
var
uri: TIdURI;
i: Integer;
begin
uri := TidURI.Create(Edit2.Text);
try
//Memo1.Lines.Add(uri.Protocol);
//Memo1.Lines.Add(uri.Host);
i := RatChar(uri.Host, '.');
Memo1.Lines.Add(Copy(uri.Host, i+1, Length(uri.Host)));
Memo1.Lines.Add(uri.Document);
finally
uri.Free;
end;
end;
uses
System.SysUtils;
var
u : string;
arr: TArray<string>;
begin
try
u := 'https://stackoverflow.com/questions/71166883/how-to-extract-domain-suffix';
arr := u.Split(['://'], TStringSplitOptions.ExcludeEmpty);
u := arr[High(arr)]; //stackoverflow.com/questions/71166883/how-to-extract-domain-suffix';
arr := u.Split(['/'], TStringSplitOptions.ExcludeEmpty);
u := arr[0]; //stackoverflow.com
arr := u.Split(['.'], TStringSplitOptions.ExcludeEmpty);
u := arr[High(arr)]; //com
writeln('Top-Level-Domain: ', u);
readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
According to suggestion Extracting top-level and second-level domain from a URL using regex it should run like this in Delphi:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.RegularExpressions;
var
url,
rePattern: string;
rMatch : TMatch;
rGroup : TGroup;
arr : TArray<string>;
begin
try
url := 'https://www.answers.com/article/1194427/8-habits-of-extraordinarily likeable-people';
//url := 'https://stackoverflow.com/questions/71166883/how-to-extract-domain-suffix';
rePattern := '^(?:https?:\/\/)(?:w{3}\.)?.*?([^.\r\n\/]+\.)([^.\r\n\/]+\.[^.\r\n\/]{2,6}(?:\.[^.\r\n\/]{2,6})?).*$';
rMatch := TRegEx.Match(url, rePattern);
if rMatch.Success then
begin
rGroup := rMatch.Groups.Item[pred(rMatch.Groups.Count)];
arr := rGroup.Value.Split(['.']);
writeln('Top-Level-Domain: ', arr[High(arr)]);
end
else
writeln('Sorry');
readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
However, this regular expression only works when www. is supplied.
I have an application that contains a ClientDataSet connected in a DataSetProvider that is connected in an TIBQuery (Delphi 6).
I run several queries and after each of them I run the EmptyDataSet, Close, and Free methods.
For example:
procedure TAggregator.Load(ASql: string);
begin
try
FQry.SQL.Text := ASql;
FCds.SetProvider(FDsp);
FCds.Open;
FCds.EmptyDataSet;
FCds.Close;
except
on e: Exception do
raise e;
end;
end;
This is the two files from my minimal app sample to reproduce the problem:
program CdsConsumptionTest;
{$APPTYPE CONSOLE}
uses
SysUtils,
Classes,
IBDatabase,
uAggregator in 'uAggregator.pas';
var
database: TIBDatabase;
transaction: TIBTransaction;
aggregator: TAggregator;
tables: TStringList;
index: integer;
begin
try
try
database := TIBDatabase.Create(nil);
transaction := TIBTransaction.Create(nil);
try
database.DefaultTransaction := transaction;
database.Params.Values['user_name'] := 'SYSDBA';
database.Params.Values['password'] := 'masterkey';
database.SQLDialect := 3;
database.LoginPrompt := false;
database.DatabaseName := 'C:\bases\17011\BASE.GDB';
tables := TStringList.Create;
aggregator := TAggregator.Create(database);
try
database.GetTableNames(tables);
Writeln('Connection successful!');
Write('Press ENTER to continue ...');
// After that you can see the memory being increased and no longer released
Readln;
for index := 0 to pred(tables.Count) do
begin
aggregator.Load('select * from ' + tables[index]);
end;
finally
tables.Free;
aggregator.Free;
end;
finally
database.Free;
transaction.Free;
end;
except
on e:Exception do
begin
Writeln('');
Writeln('ERROR! ' + e.Message);
Writeln('');
end;
end;
finally
Write('Process completed! Press ENTER to exit ...');
Readln;
end;
end.
and...
unit uAggregator;
interface
uses
IBQuery, DBClient, Provider, IBDatabase, SysUtils;
type
TAggregator = class
private
FQry: TIBQuery;
FCds: TClientDataSet;
FDsp: TDataSetProvider;
public
constructor Create(AIBDatabase: TIBDatabase); reintroduce;
destructor Destroy; override;
public
procedure Load(ASql: string);
end;
implementation
{ TAgregador }
constructor TAggregator.Create(AIBDatabase: TIBDatabase);
begin
inherited Create;
FQry := TIBQuery.Create(nil);
FQry.Database := AIBDatabase;
FDsp := TDataSetProvider.Create(nil);
FDsp.DataSet := FQry;
FCds := TClientDataSet.Create(nil);
FCds.SetProvider(FDsp);
FCds.PacketRecords := -1;
end;
destructor TAggregator.Destroy;
begin
FCds.Free;
FDsp.Free;
FQry.Free;
inherited;
end;
procedure TAggregator.Load(ASql: string);
begin
try
FQry.SQL.Text := ASql;
FCds.SetProvider(FDsp);
FCds.Open;
FCds.EmptyDataSet;
FCds.Close;
except
on e: Exception do
raise e;
end;
end;
end.
When I start the app I see that a lot of memory is allocated.
After pressing ENTER to start the queries I see by the Windows Task Manager that the memory is being incremented and never released.
Until the process reaches the point where I need to press ENTER to terminate the application and until then the memory that was allocated (even after the Free of the ClientDataSet) is not released.
In this small example it does not prove to be a big problem.
But in my actual application this is being raised an Out Of Memory type exception.
How I can solve this problem?
EDIT
Testing with FastMM4 I received the following report:
That I do not understand.
hello am doing a program in delphi console, xe2 delphi and indy for using sockets and the problem is that I have all the code done but when I connect to the server I receive no response to the ping pong.
the code is as follows:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, idTCPClient;
var
irc: TIdTCPClient;
code: string;
begin
try
irc := TIdTCPClient.Create(nil);
irc.Host := 'irc.freenode.net';
irc.Port := 6667;
irc.Connect;
irc.Socket.Write('NICK tester');
irc.Socket.Write('USER tester 1 1 1 1');
irc.Socket.Write('JOIN #tester');
if irc.Socket.Connected = True then
begin
Writeln('Yeah');
while (1 = 1) do
begin
code := irc.Socket.ReadString(9999);
if not(code = '') then
begin
Writeln(code);
end;
end;
end
else
begin
Writeln('Nay');
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
anyone can help me?
You are not sending a CRLF after each command you send. Use TIdIOHander.WriteLn() instead of TIdIOHandler.Write().
Also, your call to TIdIOHandler.ReadString() will not exit until exactly 9999 bytes have been received. That is not what you actually want to happen. IRC is a line-based protocol. You should be using TIdIOHandler.ReadLn() instead of TIdIOHandler.ReadString().
Try something more like this instead:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, idTCPClient;
var
irc: TIdTCPClient;
code: string;
begin
try
irc := TIdTCPClient.Create(nil);
try
irc.Host := 'irc.freenode.net';
irc.Port := 6667;
try
irc.Connect;
except
Writeln('Nay');
Exit;
end;
Writeln('Yeah');
irc.IOHandler.WriteLn('NICK tester');
irc.IOHandler.WriteLn('USER tester 1 1 1 1');
irc.IOHandler.WriteLn('JOIN #tester');
repeat
code := irc.IOHandler.ReadLn;
Writeln('[Recv] ' + code);
if TextStartsWith(code, 'PING ') then
begin
Fetch(code);
irc.IOHandler.WriteLn('PONG ' + code);
Writeln('[Sent] PONG ' + code);
end;
until False;
finally
irc.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
With that being said, you should be using the TIdIRC component instead. Try this:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, idIRC, idContext, idGlobal;
procedure IrcRaw(ASelf: Pointer; ASender: TIdContext; AIn: Boolean; const AMessage: String);
begin
Writeln(iif(AIn, '[Recv] ', '[Sent] ') + AMessage);
end;
var
irc: TIdIRC;
m: TMethod;
begin
try
irc := TIdIRC.Create(nil);
try
irc.Host := 'irc.freenode.net';
irc.Port := 6667;
irc.Nickname := 'tester';
irc.Username := 'tester';
m.Code := #IrcRaw;
m.Data := irc;
irc.OnRaw := TIdIRCRawEvent(m);
try
irc.Connect;
except
Writeln('Nay');
Exit;
end;
Writeln('Yeah');
irc.Join('#tester');
repeat
Sleep(10);
until SomeCondition;
finally
irc.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
I'm trying to execute this code (this is a minimal sample in order to use CreateOleObject) from inside of a dwscript
function GetFileVersion(const FileName: string): string;
var
V : OleVariant;
begin
V := CreateOleObject('Scripting.FileSystemObject');
Result := V.GetFileVersion(FileName);
end;
So far i tried this
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils,
ComObj,
ActiveX,
dwsComp,
dwsCompiler,
dwsExprs,
dwsCoreExprs;
procedure Execute;
var
LScript: TDelphiWebScript;
LUnit: TdwsUnit;
LProg: IdwsProgram;
LExec: IdwsProgramExecution;
begin
LScript := TDelphiWebScript.Create(NIL);
LUnit := TdwsUnit.Create(NIL);
try
LUnit.UnitName := 'Foo';
LUnit.Script := LScript;
// compile a simple script
LProg := LScript.Compile(
'function GetFileVersion(const FileName: string): string;'+sLineBreak+
'var'+sLineBreak+
' V : Variant;'+sLineBreak+
'begin'+sLineBreak+
' V := CreateOleObject(''Scripting.FileSystemObject'');'+sLineBreak+
' Result := V.GetFileVersion(FileName);'+sLineBreak+
'end;'+sLineBreak+
''+sLineBreak+
'PrintLn(GetFileVersion(''Foo''));'+sLineBreak+
''
);
if LProg.Msgs.HasErrors then begin
Writeln(LProg.Msgs.AsInfo);
Exit;
end;
try
LExec := LProg.Execute;
except
on E: Exception do
WriteLn(E.Message + sLineBreak + LExec.Msgs.AsInfo );
end;
Writeln(LExec.Result.ToString);
finally
LScript.Free;
end;
end;
begin
try
Execute;
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
But i'm getting this error message
Syntax Error: Unknown name "CreateOleObject" [line: 5, column: 8]
the question is how i can execute the CreateOleObject function using dwscript?
UPDATE
Following the Linas suggestions I could finally resolve the issue.
This is a sample working application
uses
SysUtils,
ComObj,
ActiveX,
dwsComp,
dwsCompiler,
dwsExprs,
dwsComConnector,
dwsCoreExprs;
procedure Execute;
var
LScript: TDelphiWebScript;
LUnit: TdwsUnit;
LProg: IdwsProgram;
LExec: IdwsProgramExecution;
LdwsComConnector : TdwsComConnector;
begin
LScript := TDelphiWebScript.Create(NIL);
LdwsComConnector:=TdwsComConnector.Create(nil);
LdwsComConnector.Script:=LScript;
LUnit := TdwsUnit.Create(NIL);
try
LUnit.UnitName := 'Foo';
LUnit.Script := LScript;
// compile a simple script
LProg := LScript.Compile(
'function GetFileVersion(const FileName: string): string;'+sLineBreak+
'var'+sLineBreak+
' V : OleVariant;'+sLineBreak+
'begin'+sLineBreak+
' V := CreateOleObject(''Scripting.FileSystemObject'');'+sLineBreak+
' Result := VarToStr(V.GetFileVersion(FileName));'+sLineBreak+
'end;'+sLineBreak+
''+sLineBreak+
'PrintLn(GetFileVersion(''C:\Bar\Foo.exe''));'+sLineBreak+
''
);
if LProg.Msgs.HasErrors then begin
Writeln(LProg.Msgs.AsInfo);
Exit;
end;
try
LExec := LProg.Execute;
except
on E: Exception do
WriteLn(E.Message + sLineBreak + LExec.Msgs.AsInfo );
end;
Writeln(LExec.Result.ToString);
finally
LScript.Free;
LdwsComConnector.Free;
end;
end;
begin
try
CoInitialize(nil);
try
Execute;
Readln;
finally
CoUninitialize;
end;
except
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
end.
This can be accomplished in two ways.
1 way: You must drop TdwsComConnector (from unit dwsComConnector) to your data module or form (or create it manually) and assign your script instance to it. e.g.:
dwsComConnector1.Script := LScript;
2 way:
interface
uses
dwsFunctions, dwsSymbols, dwsExprs;
type
TCreateOleObjectFunc = class(TInternalFunction)
procedure Execute(info : TProgramInfo); override;
end;
implementation
uses
OleAuto;
{ TCreateOleObjectFunc }
procedure TCreateOleObjectFunc.Execute(info : TProgramInfo);
begin
Info.ResultAsVariant := CreateOleObject(Info.ValueAsString['ClassName']);
end;
initialization
RegisterInternalFunction(TCreateOleObjectFunc, 'CreateOleObject', ['ClassName', cString], cVariant, True);
This will expose CreateOleObject function to DWScript so you could use it.
Also you should declare your V as OleVariant instead of Variant and change the line to Result := VarToStr(V.GetFileVersion(FileName)); to make it work properly.
I'm working on a component installer (only for Delphi XE2) and I would like to detect if the Delphi XE2 IDE is running. How would you detect it?
P.S. I know about the TAppBuilder window class name, but I need to detect also the IDE version.
These are the steps to determine if the Delphi XE2 is running
1) First Read the location of the the bds.exe file from the App entry in the \Software\Embarcadero\BDS\9.0 registry key which can be located in the HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE Root key.
2) Then using the CreateToolhelp32Snapshot function you can check if exist a exe with the same name running.
3) Finally using the PID of the last processed entry you can resolve the full file path of the Exe (using the GetModuleFileNameEx function) and then compare the names again.
Check this sample code
{$APPTYPE CONSOLE}
{$R *.res}
uses
Registry,
PsAPI,
TlHelp32,
Windows,
SysUtils;
function ProcessFileName(dwProcessId: DWORD): string;
var
hModule: Cardinal;
begin
Result := '';
hModule := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, dwProcessId);
if hModule <> 0 then
try
SetLength(Result, MAX_PATH);
if GetModuleFileNameEx(hModule, 0, PChar(Result), MAX_PATH) > 0 then
SetLength(Result, StrLen(PChar(Result)))
else
Result := '';
finally
CloseHandle(hModule);
end;
end;
function IsAppRunning(const FileName: string): boolean;
var
hSnapshot : Cardinal;
EntryParentProc: TProcessEntry32;
begin
Result := False;
hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if hSnapshot = INVALID_HANDLE_VALUE then
exit;
try
EntryParentProc.dwSize := SizeOf(EntryParentProc);
if Process32First(hSnapshot, EntryParentProc) then
repeat
if CompareText(ExtractFileName(FileName), EntryParentProc.szExeFile) = 0 then
if CompareText(ProcessFileName(EntryParentProc.th32ProcessID), FileName) = 0 then
begin
Result := True;
break;
end;
until not Process32Next(hSnapshot, EntryParentProc);
finally
CloseHandle(hSnapshot);
end;
end;
function RegReadStr(const RegPath, RegValue: string; var Str: string;
const RootKey: HKEY): boolean;
var
Reg: TRegistry;
begin
try
Reg := TRegistry.Create;
try
Reg.RootKey := RootKey;
Result := Reg.OpenKey(RegPath, True);
if Result then
Str := Reg.ReadString(RegValue);
finally
Reg.Free;
end;
except
Result := False;
end;
end;
function RegKeyExists(const RegPath: string; const RootKey: HKEY): boolean;
var
Reg: TRegistry;
begin
try
Reg := TRegistry.Create;
try
Reg.RootKey := RootKey;
Result := Reg.KeyExists(RegPath);
finally
Reg.Free;
end;
except
Result := False;
end;
end;
function GetDelphiXE2LocationExeName: string;
Const
Key = '\Software\Embarcadero\BDS\9.0';
begin
Result:='';
if RegKeyExists(Key, HKEY_CURRENT_USER) then
begin
RegReadStr(Key, 'App', Result, HKEY_CURRENT_USER);
exit;
end;
if RegKeyExists(Key, HKEY_LOCAL_MACHINE) then
RegReadStr(Key, 'App', Result, HKEY_LOCAL_MACHINE);
end;
Var
Bds : String;
begin
try
Bds:=GetDelphiXE2LocationExeName;
if Bds<>'' then
begin
if IsAppRunning(Bds) then
Writeln('The Delphi XE2 IDE Is running')
else
Writeln('The Delphi XE2 IDE Is not running')
end
else
Writeln('The Delphi XE2 IDE Is was not found');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
Addtional resources.
Detecting installed delphi versions
Check DebugHook <> 0.
The down side is that currently if your app is built with packages, DebugHook will return 0.
But normally this is would be a very elegant and simple test. Works great in D2009, I just noticed that it has the package dependency bug in XE2 (http://qc.embarcadero.com/wc/qcmain.aspx?d=105365).