Does anyone know how (if) I can obtain bug fixes for Delphi for known, fixed bugs (specifically QC report #125506 relating to indexes in Clientdatasets)
I understand that it has been fixed in XE7 but I object(!!!) to paying £1,000+ to update from XE6 to XE7 when I have only had XE6 for a matter of months, I have spent the time identifying the bug and the ONLY reason I have for moving from XE6 to XE7 is to fix the bug (rant over)!
Apologies for posting this as an "answer" but there are a few things worth mentioning that won't fit comfortably in a comment (or two).
As you've probably gathered, the "Resolution comments" on QC #125506 don't say anything useful, in particular about what was changed or where e.g. in DBClient.Pas or Midas.Dll. I've just run Beyond Compare on the sources of DBClient.Pas in XE6 & XE7, and the changes are minimal: The declaration of TCustomClientDataSet has had a local class declaration of "TPersistDataPacket" added to it and there are a few consequential changes, but whether they bear on QC #125506 is impossible to say. I wondered about quoting the changes here, but decided not to in view of possible copyright or T&C problems.
The versions of MidasLib.Pas in XE6 and XE7 are identical, but the size of the 32-bit release build of MidasLib.Dcu has increased marginally, from 241447 to 241646 bytes. Oddly, Midas.Dll has actually reduced in size, from 451960 to 437632 bytes.
A couple of obvious suggestions:
I'm not sure if the trial version of XE7 includes the Datasnap stuff, but in view of the expense, it would definitely be worth "trying before you buy" if you can manage it. If it doesn't include Datasnap, you might nevertheless see if you can get hold of a copy of the XE7 Midas.Dll - once upon a time Borland-as-was was quite liberal in allowing the latest Midas.Dll to be used with earlier versions. Might be worthwhile asking about both these points on the EMBA newgroups.
In QC #125506, the reporter seems to have run into the problem when using the CDS IndexFieldNames property. If that's how your getting it, have you tried defining a persistent IndexDef instead? The following code works for me (tested on the Authors table of the Sql Server Pubs demo database).
Dynamically adding/using an IndexDef.
procedure TDefaultForm.AddIndex(AFieldName: String; CaseInsensitive: Boolean);
var
AIndexDef : TIndexDef;
AIndexName : String;
Options : TIndexOptions;
BM : TBookmark;
begin
if CDS1.IndexDefs.GetIndexForFields(AFieldName, CaseInsensitive) = Nil then begin
BM := CDS1.GetBookmark;
try
CDS1.DisableControls;
AIndexName := 'By' + AFieldName;
Options := [];
if CaseInsensitive then
Options := Options + [ixCaseInsensitive];
AIndexDef := TIndexDef.Create(CDS1.IndexDefs, AIndexName, AFieldName, Options);
CDS1.IndexName := AIndexName;
finally
CDS1.GotoBookmark(BM);
CDS1.FreeBookmark(BM);
CDS1.EnableControls;
end;
end;
end;
procedure TDefaultForm.btnAddClick(Sender: TObject);
begin
AddIndex('au_lname', True);
end;
Related
I have been asked to correct an issue (not related to this question) in a legacy Delphi program. After fixing some issues with missing components, I am now stuck with some GDI Plus functionality, which stops me from compiling the program. One of the functions where this is used is:
function TDownLoadItem.LoadRawBitmapFromStream(var bm: TBitmap): Boolean;
var
image: TGPBitmap;
begin
Result := False;
if Content.Size = 0 then
exit;
// NOTE: Content is a TMemoryStream, declared globally.
image := GDIPlusHelper.LoadBitmapFromStream(Content); // <== This is where the problem is....
try
bm.Width := image.GetWidth;
bm.Height := image.GetHeight;
with TGPGraphics.Create(bm.Canvas.Handle) do
try
DrawImage(image, 0, 0, image.GetWidth, image.GetHeight);
Result := True;
finally
Free;
end;
finally
image.Free;
end;
end;
I think (not sure) the last Delphi version used was 2006, I am on Delphi Rio 10.3.
Online I have managed to find GDI+ 1.2, but this does not solve the problem. The procedure LoadBitmapFromStream does not exit in these libraries. GDIPlusHelper was apparently renamed to GDIPlusHelpers and most code has changed from classes to interfaces. I suspect an older edition of the GDI Plus libraries were used, but I cannot find these.
Reworking the code would be too complex as it would require Content to be an IStream instead of a TMemoryStream. Also, simply using a TBitmap is not feasible either as other code (not shown) uses functionality specific to TGPBitmap (e.g. RotateFlip).
Any suggestions on how to fix/work around this? Thanks in advance!
Having problem storing an array in a TQueue. Any idea where I go wrong?
Code works fine in Delphi XE 5 but not in Delphi 10 Seattle.
(I can't decide if this is a bug or how it should work. Tried searching embarcadero for clues but failed.)
procedure TForm1.Button1Click(Sender: TObject);
var
FData: TQueue<TBytes>;
FsData: TQueue<String>;
arr: TBytes;
begin
FData := TQueue<TBytes>.Create;
FsData := TQueue<String>.Create;
try
setlength(arr, 3);
arr[0] := 1;
arr[1] := 2;
arr[2] := 3;
FData.Enqueue(arr);
Memo1.Lines.Add('Count, array:' + IntToStr(FData.Count)); // 0?
FsData.Enqueue('asada');
Memo1.Lines.Add('Count, string:' + IntToStr(FsData.Count)); // 1
finally
FData.Free;
FsData.Free;
end;
end;
This is a defect introduced in XE8. Here's the simplest reproduction that I can produce.
{$APPTYPE CONSOLE}
uses
System.Generics.Collections;
var
Queue: TQueue<TArray<Byte>>;
begin
Queue := TQueue<TArray<Byte>>.Create;
Queue.Enqueue(nil);
Writeln(Queue.Count);
end.
The output is 1 in XE7 and 0 in XE8 and Seattle.
This has already been reported to Embarcadero: RSP-13196.
The implementation of Enqueue looks like this:
procedure TQueue<T>.Enqueue(const Value: T);
begin
if IsManagedType(T) then
if (SizeOf(T) = SizeOf(Pointer)) and (GetTypeKind(T) <> tkRecord) then
FQueueHelper.InternalEnqueueMRef(Value, GetTypeKind(T))
else
FQueueHelper.InternalEnqueueManaged(Value)
else
case SizeOf(T) of
1: FQueueHelper.InternalEnqueue1(Value);
2: FQueueHelper.InternalEnqueue2(Value);
4: FQueueHelper.InternalEnqueue4(Value);
8: FQueueHelper.InternalEnqueue8(Value);
else
FQueueHelper.InternalEnqueueN(Value);
end;
end;
When T is a dynamic array, the FQueueHelper.InternalEnqueueMRef branch is chosen. This in turn looks like this:
procedure TQueueHelper.InternalEnqueueMRef(const Value; Kind: TTypeKind);
begin
case Kind of
TTypeKind.tkUString: InternalEnqueueString(Value);
TTypeKind.tkInterface: InternalEnqueueInterface(Value);
{$IF not Defined(NEXTGEN)}
TTypeKind.tkLString: InternalEnqueueAnsiString(Value);
TTypeKind.tkWString: InternalEnqueueWideString(Value);
{$ENDIF}
{$IF Defined(AUTOREFCOUNT)}
TTypeKind.tkClass: InternalEnqueueObject(Value);
{$ENDIF}
end;
end;
Note that there is no entry for TTypeKind.tkDynArray. Because these two methods are inlined, the inliner manages to compress it all down to nothing. No action is performed when you Enqueue a dynamic array.
Back in the good old days of XE7 the code looked like this:
procedure TQueue<T>.Enqueue(const Value: T);
begin
if Count = Length(FItems) then
Grow;
FItems[FHead] := Value;
FHead := (FHead + 1) mod Length(FItems);
Inc(FCount);
Notify(Value, cnAdded);
end;
No scope for type specific defects there.
I don't think that there's an easy workaround for you. Perhaps the most expedient way to proceed is to take the code for the XE7 TQueue and use that in place of the broken implementation from XE8 and Seattle. For the record, I've given up on the Embarcadero generic collections and use my own classes.
The back story here is that in XE8, Embarcadero decided to address a deficiency in their implementation of generics. Whenever you instantiate a generic type, copies of all the methods are created. For some methods, identical code is generated for different instantiations.
So it is quite common for TGeneric<TFoo>.DoSomething and TGeneric<TBar>.DoSomething to have identical code. Other compilers for other languages, C++ templates, .net generics, etc., recognise this duplication and merge together identical generic methods. The Delphi compiler does not. The end result is a larger executable than strictly necessary.
In XE8 Embarcadero decided to tackle this in what I regard was utterly the wrong way. Instead of attacking the root cause of the issue, the compiler, they decided to change the implementation of their generic collection classes. If you look at the code in Generics.Collections, you will see that it has been completely re-written in XE8. Where previously the code from XE7 and earlier was readable, from XE8 it is now exceedingly complex and opaque. This decision had the following consequences:
The complex code contained many errors. Many of these were found shortly after XE8 was released and have been fixed. You have stumbled upon another defect. One thing that we have learnt is that Embarcadero's internal test suite does not exercise their collection classes sufficiently. It is manifestly clear that their tests are inadequate.
By changing their library rather than the compiler, they have patched up the RTL classes. The original issue with generic code bloat remains for third party classes. Had Embarcadero fixed the issue at source then not only could they have retained the simple and correct collection class code from XE7, but all third generic code would have benefited.
I want to use Async Pro in my Delphi XE3. I found a version A407 on SourceForge, which seems to be the latest. When I try to install the runtime package A407_R100.bpl I get an error that a data length is longer than 2GB. When I fix this (with some guesswork) I get 4 other errors. I can try to fix those as well, but I'm afraid I will have to patch so much of the code that it won't work anymore.
Is there a version of Async Pro which works with XE3? Or at least clear and proven instructions how to patch the code?
update
Here I found an AsyncPro library which seems to be more up-to-date; at least the packages are named A407_*140.bpl instead of A407_*100.bpl. I still had a couple of errors in this part of the code in AwAbsPd.pas:
procedure InitializeUnit;
var
TmpDateSeparator : char;
TmpDateFormat : string[15];
TmpDateTime : TDateTime;
begin
{Set Unix days base}
TmpDateFormat := ShortDateFormat;
TmpDateSeparator := DateSeparator;
DateSeparator := '/';
ShortDateFormat := 'mm/dd/yyyy';
TmpDateTime := StrToDateTime('01/01/1970');
UnixDaysBase := Trunc(TmpDateTime);
DateSeparator := TmpDateSeparator;
ShortDateFormat := TmpDateFormat;
Although SysUtils is in the "uses" clause I got errors that ShortDateFormat and DateSeparator weren't defined. So I hard-coded them:
procedure InitializeUnit;
var
TmpDateSeparator : char;
TmpDateFormat : string[15];
TmpDateTime : TDateTime;
// added stevenvh
var
DateSeparator: char;
ShortDateFormat: String;
ShortTimeFormat: String;
// end addition
begin
// added stevenvh
DateSeparator := '-';
ShortDateFormat := 'yyyy-mm-dd';
ShortTimeFormat := 'HH:mm:ss';
// end addition
{Set Unix days base}
TmpDateFormat := ShortDateFormat;
TmpDateSeparator := DateSeparator;
DateSeparator := '/';
ShortDateFormat := 'mm/dd/yyyy';
TmpDateTime := StrToDateTime('01/01/1970');
UnixDaysBase := Trunc(TmpDateTime);
DateSeparator := TmpDateSeparator;
ShortDateFormat := TmpDateFormat;
Nearly there! Both runtime and designtime packages compile, but when I try to install the designtime package I get an error that "01/01/1970" is not a valid date. This is not an error in the above code, because it remains the same "01/01/1970" when I change the date in the code.
Turns out there is only 1 other file which includes "01/01/1970" as text, but this is a .ocx file, so I'm not sure how or even if I should patch this.
Acording to this blog post:
http://blog.kassebaum.eu/?p=379
Async Proffesional is currently maintained by Roman Kassebaum but only for latest versions of RAD studio (both Delphi and CBuilder).
The menioned blog links to next source forge page:
http://sourceforge.net/projects/turbopowerasyncprofessionalnew/?source=navbar
Infromation on the page indicates that the project is closed and that it moved to github but no link is provided.
After doing some searching on GitHub I found the projects page
https://github.com/TurboPack/AsyncPro
Anyway since Roman Kassebaum is maintaing the project to be compatible with newest Delphi version it might not work for you.
So I strongly recomend you get in contact with Roman Kassebaum as he will best know which version should you use with your Delphi XE3 instalation or what needs to be fixed to make it compatible.
The official AsyncPro version moved to GitHub. You can find it under TurboPack. It supports the latest Delphi and C++Builder version.
I also created a branch for XE3. You can find it under TurboPack XE3.
Use FormatSettings.ShortDateFormat, FormatSettings.DateSeparator, ... instead of introducing your own variables. That would be closest to the original.
A cleaner approach would be using the date/time functions with a formatsettings overload instead of temporarily changing the global formatsettings.
Data processing with XE3 programs take up to 10 times more than with same programs compiled with XE2. This is known issue (probably refers mainly to TStringField), reported to QC 111942, but it is not fixed yet. Does anybody have a fix / workaround for this issue?
TIA Branko
Same in XE5. Plus extra traffic and all this client-server thing require >5 Mbit per second (!) to work normally. I am using only TFDConnection and TFDQuery. Specially for MySQL the speed is the same with Delphi components and with third-party driver (libmysql.dll). If you have no FireDAC you can replace TFDQuery with TSQLQuery. Here is a procedure how to fill a string grid:
procedure SelGrid(sql:ansiString;Q:TFDQuery;grid:TStringGrid);
var i: integer;
begin
Q.Close;
Q.SQL.Text:='';
Q.Open(sql);
grid.ColCount:=Q.FieldCount;
grid.RowCount:=1;
while not Q.Eof do begin
for i := 0 to grid.ColCount-1 do grid.Cells[i,grid.RowCount-1]:=Q.Fields.Fields[i].AsString;
grid.RowCount:=grid.RowCount+1;
Q.Next;
end;
Q.Close;
if grid.RowCount>1 then grid.RowCount:=grid.RowCount-1;
grid.Row:=0;
//AutoSizeGridColumns(grid,30,200);
end;
This is VCL string grid. Of course you muse deal with updates and so on, but you'll have no more performance problems.
It's always been strange that there's never been a Description property on the TService in Delphi's VCL. Even to this day, Delphi XE2 doesn't have it yet. It's such a simple and common thing, that I'm wondering why it's not there.
I know how to create it myself, but my point is I shouldn't have to. I was wondering if there's any technical reason why Description of a service doesn't come built-in to Delphi's VCL? Because it seems so simple for them to implement.
Setting it requires ChangeServiceConfig2 API function which was introduced with XP & Win2003, the service class in Delphi was written before that, and for a long time, Windows NT4 and 2000 were the baseline for the Delphi RTL.
Also for some unknown reason, Borland (and successors) have been adverse to using dynamic binding on Windows API functions, preferring either static bindings to DLLs or late but non-optional bindings (don't ask me why, it makes no sense to me), and using the previous function would have required either having Win2003 as minimum version or using dynamic binding.
So I don't think it was a deliberate decision, but is more a consequence of company policy on dynamic bindings and plain old code maintainance neglect/oversight.
You can use like that.
procedure TMyService.ServiceAfterInstall(Sender: TService);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create(KEY_READ or KEY_WRITE);
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKey('\SYSTEM\CurrentControlSet\Services\' + Name, false) then
begin
Reg.WriteString('Description', 'All details you can write to here.');
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;