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;
Related
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;
Is there a method or compiler directive or some way of assuring certain components, such as queries or database connections get set to active=false or disconnected when you run a build/compile? Seems so often these are turned on by something else and you don't notice it until its too late.
My particular install is Delphi 7
The Set Component Properties feature of GExperts is able to do that.
i think the best option would be to subclass stock connection component and in your own one override .Loaded method like that
if not csDesigning in Self.ComponentState then
if not Self.ActiveInDFM {new boolean published property} then
if Self.Active then Self.Active := false;
inherited;
http://docwiki.embarcadero.com/Libraries/XE3/en/System.Classes.TComponentState
http://docwiki.embarcadero.com/Libraries/XE3/en/System.Classes.TComponent.Loaded
By (ab)using Delphi Form Designer stupidness you can use it even without actually installing your new component into IDE Palette - just give it the same name as to the stock component class, then put your own method as last in the form's interface-uses list: thus in design-time you would have stock component and when compiling it would be transparently substituted with your own one.
Or you can sub-class it right above the very form declaration like (for another component):
type
TPanel = class(ExtCtrls.TPanel)
private
...
TForm1 = class(TForm) ....
I guess this approach might be seen as analogue to aspect-oriented programming, using limitations of IDE in developer-benefitting way.
Another approach might be some script, that cleans .Active properties in DFM on save or before build, but this way is complex for
i may be harder to integrate with stand-alone build severs (new script for each different CI framework tried)
it would reset Active property for design-time as well. This is a proper thing to do, from rigorous point of view. Yet this might be not very convenient.
You may just use similar code in your Form's and DataModule's .Loaded method (you would have to override it instead connection's method then).
You can copy-paste the same code into every Form's Loaded method.
procedure TMyForm.Loaded; // override
var c: TComponent; i: integer;
begin
try
for i := 0 to Self.ComponentsCount - 1 do begin
c := Self.Components[i];
if c is TCustomConnection then
with TCustomConnection(c) do // Hate those redundant typecasts!
if Connected then Connected := false;
if c is TDataSet then
with TDataSet(c) do // Delphi could took a lesson from Component Pascal
if Active then Active := false;
if c is ... // transactions, stored procedures, custom libriaries...
end;
finally
inherited;
end;
end;
This seems to be less sly way - thus most reliable. Yet that is a lot if copy-paste, and if you would later add some new component or library, that may ask for modifying copy-pasted code in all the forms.
You may centralize this code in some MyDBUtils unit into global procedure like Disconnect(const owner: TComponent); and then
procedure TMyForm.Loaded; // override
var c: TComponent; i: integer;
begin
try
MyDBUtils.Disconnect(Self);
finally
inherited;
end;
end;
This approach also has drawbacks though:
This would make MyDBUtils unit tightly coupled with all and every the database-related libs and components you might use. For large inherited projects, consisting of different binary modules, historically based on different db-access libraries and in process of migration, thus pulling all the access libraries into every binary module.
It can be overcome by ad hoc DI framework, but then the opposite can happen: you risk under-delivering, you may just forget to inject some library or component handler into the project that actually use it or got modified to use it.
If your form would have some components, whose connectivity should NOT be reset (object arrays as datasets, in-memory tables, in-memory NexusDB or SQLite databases, etc), you'd have to come up with ad hoc non-obvious convention to opt them out.
In my applications, I set my connection's Tag property to 1 at design time. In the OnBeforeConnect event, I check Tag, and if it is equal to 1, I abort the connection and set it to 0.
I used to use Indy back in the Delphi 6 days, and I am playing with Indy 10 now. What I want to do is incredibly simple, but I don't see a simple way of doing it, so I must be missing something.
What I want to do is something like this:
Here is the actual code I am using:
procedure TForm1.btnGetURLClick(Sender: TObject);
begin
moHeader.Lines.Clear;
moBody.Lines.Clear;
try
moBody.text := IdHttp1.Get(edURL.text);
finally
end;
end;
When the request is complete, the http_result should contain the HTML from the URL specified. This doesn't seem to work however, so I get the feeling I should perhaps be using the IOHandler property or the OnWork event of the component - however the usage doesn't seem obvious to me, and I couldn't find any working examples with google. I am sure this is something that has been done before, so any help would be appreciated.
Additional Information:
In the spirit of being more specific, I want to know:
1. Am I doing this right to begin with (or did I miss something?).
2. If so, why might it not be working.
3. It is always possible that there is a bug in the combination of compiler/os/Indy I am using. (Although it should be working).
I should mention, I always get a popup "Connection Closed Gracefully". This seems to be an exception, and it could be interfering with the result of the function. I attempted to trap this with a TRY...FINALLY, but it doesn't work. Probably because Indy is triggering the exception in the background after the Get method runs I suppose.
Finally, here is a screencast of the program running to clear up any confusion:
http://screencast.com/t/NDMzNTQ5
I expect the HTML to fill the second memo box.
i think you have the TIdHTTP.HandleRedirects property set to false, if you get the error "HTTP/1.1 302 Found" you can try this
var
http_result:string;
Begin
IdHTTP1.HandleRedirects:=True;
http_result := IdHTTP1.Get('http://www.google.com');
End;
Another option, would be to use synapse. This is all that is needed to retrieve a webpage using this library:
uses
...,HTTPSEND;
var
Result : TStrings;
if HTTPGetText('http://www.google.com',Result) then
// do something with result
Synapse is a lightweight TCPIP library. The library is being actively maintained and the current version runs fine in Delphi 2009/2010. It is NOT a component based framework, so it is very easy to use with other threading techniques (OmniThreadLibrary or AsyncCalls for example).
You have to set the property HandleRedirects to true.
There's no need for a form, using GExperts components to code I got this:
var
IdHTTP: TIdHTTP;
IdHTTP := TIdHTTP.Create(Self);
with IdHTTP do
begin
Name := 'IdHTTP';
AllowCookies := True;
HandleRedirects := True;
HTTPOptions := [hoForceEncodeParams];
end;
Just paste this in your unit, it should be all you need.
Iirc if the website redirects, you also need to override some handler (onredirect or so). But this was also the case in indy9 iirc.
This question has lingered open for quite some time, so I am closing it out. My solution was to just use Synapse, as one of the posters suggested. It works on windows/Linux/Mac OS with minimal modifications, and works fine in libraries/threads.
This code in a GUI application compiles and runs:
procedure TForm1.Button1Click(Sender: TObject);
begin
Self := TForm1.Create(Owner);
end;
(tested with Delphi 6 and 2009)
why is Self writable and not read-only?
in which situations could this be useful?
Edit:
is this also possible in Delphi Prism? (I think yes it is, see here)
Update:
Delphi applications/libraries which make use of Self assignment:
python4delphi
That's not as bad as it could be. I just tested it in Delphi 2009, and it would seem that, while the Self parameter doesn't use const semantics, which you seem to be implying it should, it also doesn't use var semantics, so you can change it all you want within your method without actually losing the reference the caller holds to your object. That would be a very bad thing.
As for the reason why, one of two answers. Either a simple oversight, or what Marco suggested: to allow you to pass Self to a var parameter.
Maybe to allow passing to const or var parameters?
It could be an artefact, since system doesn't have self anywhere on the left of := sign.
Assigning to Self is so illogical and useless that this 'feature' is probably an oversight. And as with assignable constants, it's not always easy to correct such problems.
The simple advice here is: don't do it.
In reality, "Self" is just a name reference to a place on the stack that store address pointing to object in the heap. Forcing read-only on this variable is possible, apparently the designer decided not to. I believe the decision is arbitrary.
Can't see any case where this is useful, that'd merely change a value in stack. Also, changing this value can be dangerous as there is no guarantee that the behavior of the code that reference instance's member will be consistence across compiler versions.
Updated: In response to PatrickvL comment
The 'variable' "Self" is not on the
stack (to my knowledge, it never is);
Instead it's value is put in a
register (EAX to be exact) just before
a call to any object method is made. –
Nope, Self has actual address on the memory. Try this code to see for yourself.
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(Integer(#Self)));
end;
procedure TForm1.Button2Click(Sender: TObject);
var
newform: TForm;
p: ^Integer;
begin
Self.Caption := 'TheOriginal';
newform := TForm.Create(nil);
try
newform.Caption := 'TheNewOne';
// The following two lines is, technically, the same as
// Self := newform;
p := Pointer(#Self);
p^ := Integer(newform);
ShowMessage(Self.Caption); // This will show 'TheNewOne' instead of 'TheOriginal'
finally
Self.Free; // Relax, this will free TheNewOne rather than TheOriginal
end;
end;
Sometimes, when you want to optimize a method for as far as you can take it (without resorting to assembly), 'Self' can be (ab)used as a 'free' variable - it could just mean the difference between using stack and using registers.
Sure, the contents of the stack are most probably already present in the CPU cache, so it should be fast to access, but registers are even faster still.
As a sidenote : I'm still missing the days when I was programming on the Amiga's Motorola 68000 and had the luxury of 16 data and 16 address registers.... I can't believe the world chose to go with the limited 4 registers of the 80x86 line of processors!
And as a final note, I choose to use Self sometimes, as the Delphi's optimizer is, well, not optimizing that well, actually. (At least, it pales compared to what trickery one can find in the various LLVM optimizers for example.) IMHO, and YMMV of course.
I've given up on the Delphi 7 debugger and am pretty much relying on outputdebugstrings. Is there a standard function I can call to get the contents of an object as a string like the debugger would if I set a breakpoint?
Not exactly what your looking for, but you can use RTTI to get access to the values of various published properties. The magical routines are in the TypInfo unit. The ones you are probably most interested in are GetPropList which will return a list of the objects properties, and GetPropValue which will allow you to get the values of the properties.
procedure TForm1.DumpObject( YourObjectInstance : tObject );
var
PropList: PPropList;
PropCnt: integer;
iX: integer;
vValue: Variant;
sValue: String;
begin
PropCnt := GetPropList(YourObjectInstance,PropList);
for iX := 0 to PropCnt-1 do
begin
vValue := GetPropValue(YourObjectInstance,PropList[ix].Name,True);
sValue := VarToStr( vValue );
Memo1.Lines.Add(PropList[ix].Name+' = '+sValue );
end;
end;
for example, run this with DumpObject(Self) on the button click of the main form and it will dump all of the properties of the current form into the memo. This is only published properties, and requires that the main class either descends from TPersistent, OR was compiled with {$M+} turned on before the object.
Rumor has it that a "reflector" like ability will be available in a future version of Delphi (possibly 2010).
Consider something like Codesite which is a much more complete tracing solution. It allows you to output much more complex info, and then search, print, and analyse the data. But for your purposes, you can simply send an object to it with Codesite.Send('Before', self); and you get all the RTTI available properties in the log. Do an "After" one too, and then you can compare the two in the Codesite output just by selecting both. It's saved me many times.
if delphi 7 is the .NET version, then you could do (some of) that with reflection. (not easy, but not terribly hard). if it's the normal, compiled thing, then it's a hard problem and the debugger is you best bet, apart from specialized printing functions/methods.