Disclaim: I am asking to "Lazarus" because I don't really care about mode Delphi or mode ObjFPC...
I am translating some code from "Delphi 11" to Lazarus and found those:
TProc = reference to procedure(const AControl: TControl);
....
proc := MyProcedure;
This seems to be some managed reference to method for event callback, if I am correct.
How would I make something equivalent under Lazarus ?
You can use FPC from the main branch, which has this feature since May of last year. However, it will only be in the next major version release, which is not on the horizon, as far as I know.
Official announcement of the feature:
Feature announcement: Function References and Anonymous Functions
Related
Could you please help me to understand what is going on with FPU Control Word in my Delphi application, on Win32 platform.
When we create a new VCL application, the control word is set up to 1372h. This is the first thing I don't understand, why it is 1372h instead of 1332h which is the Default8087CW defined in System unit.
The difference between these two:
1001101110010 //1372h
1001100110010 //1332h
is the 6th bit which according to documentation is reserved or not used.
The second question regards CreateOleObject.
function CreateOleObject(const ClassName: string): IDispatch;
var
ClassID: TCLSID;
begin
try
ClassID := ProgIDToClassID(ClassName);
{$IFDEF CPUX86}
try
Set8087CW( Default8087CW or $08);
{$ENDIF CPUX86}
OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IDispatch, Result));
{$IFDEF CPUX86}
finally
Reset8087CW;
end;
{$ENDIF CPUX86}
except
on E: EOleSysError do
raise EOleSysError.Create(Format('%s, ProgID: "%s"',[E.Message, ClassName]),E.ErrorCode,0) { Do not localize }
end;
end;
The above function is changing control word to 137Ah, so it is turning on the 3rd bit (Overflow Mask). I don't understand why it is calling Reset8087CW after, instead of restoring the state of the word which was before entering into the function?
The 6th bit is reserved and ignored. Those two control words are in fact equal in the sense that the FPU behaves the same. The system just happens to set the reserved bit. Even if you attempt to set the value to $1332, the system will set it to $1372. No matter what value you ask the 6th bit to have, it will always be set. So, when comparing these values you have to ignore that bit. Nothing to worry about here.
As for CreateOleObject the authors decided that if you are going to use that function then you are also going to mask overflow when using the COM object, and indeed beyond. Who knows why they did so, and for 32 bit code only? Probably they found a bunch of COM objects that routinely overflowed, and so added this sticking plaster. It wasn't enough to mask overflow on creation, it also need to be done when using the object so The RTL designers chose to unmask overflow henceforth.
Or perhaps it was a bug. They decided not to fix it for 32 bit code because people relied on the behaviour, but they did fix for 64 bit code.
In any case this function does nothing very special. You don't need to use it. You can write your own that does what you want it to do.
Floating point control is a problem when working with interop. Delphi code expects unmasked exceptions. Code built with other tools typically masks them. Ideally you would mask exceptions when you call out of your Delphi code and unmask them on return. Expect other libraries to arbitrarily change the control word. Also be aware that Set8087CW is not thread safe which is a massive problem that Embarcadero have refused to address for many years.
There's no easy way forward. If you aren't using floating point in your program then you could simply mask exceptions and probably be fine. Otherwise you need to make sure that the control word is set appropriately at all points in all threads. In general that is close to impossible using the standard Delphi RTL. I personally handle this by replacing the key parts of the RTL with threadsafe versions. I have documented how to do so in this QC report: QC#107411.
Disclaimer: I debugged the questions in Delphi XE.
First, the second question.
If you look at the code of Set8087CW you will see that it stores the new FPU CW value in Default8087CW variable, and Reset8087CW restores FPU CW from Default8087CW; so the Reset8087CW call after Set8087CW does nothing at all, which is demonstrated by
Memo1.Lines.Clear;
Memo1.Lines.Add(IntToHex(Get8087CW, 4)); // 1372
Set8087CW( Default8087CW or $08);
Memo1.Lines.Add(IntToHex(Get8087CW, 4)); // 137A
Reset8087CW;
Memo1.Lines.Add(IntToHex(Get8087CW, 4)); // 137A
Evidently a bug.
Now the first question - it was interesting debugging exercise.
The Default8087CW value of Delphi VCL application is changed from hex 1332 to 1372 by Windows.CreateWindowEx function, called from Classes.AllocateHWnd, called from TApplication.Create, called from initialization section of Controls.pas unit.
Have a look at CreateWindowEx code - it explains what happens. I don't really want to discuss it further - the FPU support in Delphi is too messy and buggy.
I'm receiving an error when building the IdMessageHelper.pas unit in the IndyProtocols90 package. All instances of LoadFromStream and LoadFromFile are claiming there is an issue with the signature:
[Error] IdMessageHelper.pas(78): E2250 There is no overloaded version of 'LoadFromStream' that can be called with these arguments
procedure Internal_TIdMessageHelper_LoadFromStream(AMsg: TIdMessage; AStream: TStream;
const AHeadersOnly: Boolean; const AUsesDotTransparency: Boolean);
var
LMsgClient: TIdMessageClient;
begin
if AUsesDotTransparency then begin
AMsg.LoadFromStream(AStream, AHeadersOnly);
end else
begin
// clear message properties, headers before loading
AMsg.Clear;
LMsgClient := TIdMessageClient.Create;
try
Internal_TIdMessageClientHelper_ProcessMessage(LMsgClient, AMsg, AStream, AHeadersOnly, False);
finally
LMsgClient.Free;
end;
end;
end;
I see that IdMessageHelper is new to this version, but the method that's being called (IdMessage.LoadFromStream for example), the arguments for it haven't changed from the last few versions - at least not for the ones that I have the source.
procedure TIdMessage.LoadFromStream(AStream: TStream; const AHeadersOnly: Boolean = False);
var
LMsgClient: TIdMessageClient;
begin
// clear message properties, headers before loading
Clear;
LMsgClient := TIdMessageClient.Create;
try
LMsgClient.ProcessMessage(Self, AStream, AHeadersOnly);
finally
LMsgClient.Free;
end;
end;
I'm pretty sure I removed all previous versions and packages since this was a clean install of D2005.
The IdMessageHelper unit introduces new LoadFrom...() and SaveTo...() methods for the TIdMessage component, to add an AUsesDotTransparency parameter when loading/saving emails.
In Delphi 2005 and later, it does this by defining a class helper (which is a feature introduced in Delphi 2005) to add new methods to the TIdMessage component without having to modify the IdMessage.pas unit itself. This allows Indy to let people use familiar IdMessage1.LoadFrom...() and IdMessage1.SaveTo...() syntax when using the new functionality 1.
Things were done this way so as not to cause interface breaking changes in the IdMessage unit itself. I blogged about this new addition at the time the IdMessageHelper.pas unit was first added to Indy:
New TIdMessage helper
In your case, the error message is complaining about Line 78:
AMsg.LoadFromStream(AStream, AHeadersOnly);
That line is the new 3-parameter TIdMessageHelper.LoadFromStream() method attempting to call the pre-existing 2-parameter TIdMessage.LoadFromStream() method when AUsesDotTransparency is True:
procedure TIdMessage.LoadFromStream(AStream: TStream; const AHeadersOnly: Boolean = False);
I have tested this new class helper in later Delphi versions and it works fine for me. You should not be getting a compiler error, as there should not be any ambiguity.
However, I have not tested the class helper in Delphi 2005 specifically (as I do not have that version installed), so it is possible that the compiler error could be indicating that class helpers (being a new language feature at the time) were still a little buggy, and were fixed later on.
If you can't find the cause of the ambiguity, you can work around the issue by modifying IdMessageHelper.pas to undefine HAS_CLASS_HELPER for Delphi 2005 1, and then recompile Indy again.
1: In older versions of Delphi where class helpers are not available, IdMessageHelper.pas also defines several standalone TIdMessageHelper_LoadFrom...() and TIdMessageHelper_SaveTo...() functions, so people can still utilize the new AUsesDotTransparency functionality, just with a less-desirable calling syntax.
EDIT: it turns out that class helpers were very buggy in Delphi 2005, and were not officially supported until Delphi 2006:
Class helpers have now been formally introduced in the Win32 compiler [in Delphi 2006]. In Delphi 2005 class helpers were not formally available, and although you could use them they were actually quite buggy. It was quite easy to get internal compiler errors while using them, nothing you could complain to with Borland about as this feature was not officially supported.
So, I have now disabled the TIdMessageHelper helper class in Delphi 2005, and have updated the above mentioned blog article accordingly.
I have this code from a legacy Delphi 2010 application.
var InternalServices: TThemeServices;
function ThemeServices: TThemeServices;
begin
if InternalServices = nil then
InternalServices := ThemeServicesClass.Create;
Result := InternalServices;
end;
The compiler tells me that ThemeServicesClass.Create does not exist. How should I do this in Delphi XE8?
The code that you include in the question is lifted from the VCL's Themes unit. That code should not be compiled by you. It was probably always a mistake for your application to compile that code rather than using the code from the Themes unit.
In XE8 you should call the StyleServices method of Vcl.Themes. The name change (from ThemeServices to StyleServices) is to reflect that fact that the older XP theme support has now been augmented by VCL styles.
So far as I can ascertain, the code in your question should not be compiled by you. It should be removed. It may be part of a much greater piece of code that perhaps also performs dubious acts. Without full sight of that code it's not possible for us to give you definitive advice.
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;
Facts:
Successfull independent efforts to bring Rtti.TVirtualInterface introduced in Delphi XE2 to prior Delphi versions were made respectively by
Vincent Parrett in Delphi.Mocks.VirtualInterface unit (Delphi Mocks)
Stefan Glienke in DSharp.Core.VirtualInterface.pas unit (DSharp)
Findings:
TRttiIndexedProperty is derived from TRttiMember.
TRttiType and TRttiInstanceType depend on TRttiIndexedProperty.
Rtti.pas depends on TypInfo.pas where some breaking changes where also introduced.
Question:
Is there a hope that one day someone will make it possible to bring TRttiIndexedProperty on Delphi XE ?
TRttiIndexedProperty can't be back-ported to older Delphi versions because it depends on the compiler writing out RTTI data for indexed properties, what only Delphi XE2's compiler does. You can't read something that isn't there.
The only possibility you have would be to write this data by hand. So you have to write a parser that runs over all your code and generates the necessary type information for all indexed properties. And because your parser isn't the compiler you would also have to write little helper functions that write and read the indexed-property.
The output could be something like this:
TMyClass = class
private
...
public
property MyArray[Index: Integer]: TMyObject read GetMyArray write SetMyArray;
// autogenerated code
class procedure RegisterIndexedPropertyInfos(Registry: TMyRttiIndexedPropertyRegistry); static;
end;
// autogenerated code
class procedure TMyClass.RegisterIndexedPropertyInfos(Registry: TMyRttiIndexedPropertyRegistry): TMyRttiIndexedProperty;
begin
Registry.Register('MyArray', [TMyRttiIndex.Create('Index', TypeInfo(Integer))], TypeInfo(TMyObject), #TMyClass.GetMyArray, #TMyClass.SetMyArray);
end;
// When using RichRTTI you can omit this line and use the the RttiContext to find RegisterIndexedPropertyInfos
RegisterIndexedPropertyClass(TMyClass, #TMyClass.RegisterIndexedPropertyInfos);