-
Content Count
3088 -
Joined
-
Last visited
-
Days Won
139
Everything posted by Remy Lebeau
-
Anyone else having a problem with this site running very slow lately?
-
Destroying a TThread recreating TThread the destroying again hangs Process
Remy Lebeau replied to JackT's topic in Delphi IDE and APIs
Yes, you can, as long as they have different signatures. What you can't have is Delphi-style named constructors that have the same signature. -
https://en.delphipraxis.net/topic/7289-line-break-after-i-type-if/
-
Destroying a TThread recreating TThread the destroying again hangs Process
Remy Lebeau replied to JackT's topic in Delphi IDE and APIs
Yes, there is - use the TThread.OnTerminate event to signal your waiting code, such as via a TEvent object. Just know that the event handler is called in the main thread, so if that will cause a sync issue, you can always override the TThread.DoTerminate() method to call the OnTerminate handler directly (or do anything else) without syncing it, as DoTerminate() is called in the worker thread and calls the OnTerminate handle via TThread.Synchronize() by default. -
So you are saying it was working the other day in 13 and now its not? Or, were you using an older version before and then upgraded to 13 and it stopped working?
-
Can't set Property Editor for Set or Enum
Remy Lebeau replied to Cobalt747's topic in Delphi IDE and APIs
What are you trying to accomplish, exactly? Do the default property editors work correctly without registering your custom editors? Why are you registering the editor for Boolean instead of TTestValue? -
Worse case, you can try manually setting the modal dialog's PopupParent to the active form (the VCL should already be doing that, but maybe its buggy?).
-
The 1st patch was supposed to fix a problem with missing Indy components. It didn't work. There is a 2nd patch to fix that. https://www.indyproject.org/2025/09/28/indy-patches-for-rad-studio-13-florence/
-
Hmm, I don't think they should have re-declared IAccessible. They should have had Winapi.UIAutomation use Winapi.oleacc instead. Even the SDK uiautomationcore.h header doesn't redeclare IAccessible. The new IAccessibleEx inherits from the IAccessible in oleacc.h. Agreed. I would say it needs more work.
-
cgrc.exe is for the IDE's internal use when invoking rc.exe with brcc32-style parameters. If a script wants to invoke rc.exe then it should probably just invoke it directly.
-
Recover from call to dll that doesn't return
Remy Lebeau replied to Renate Schaaf's topic in Windows API
Have you considered moving your audio processing into a separate process of its own that your main program can communicate with. If the processing times out then your main program can kill and restart that separate process. That way, you don't have to kill your main program. -
It is possible with some additional tweaking. You can go into the compiler's linker options and set the PE header OS Version and SubSystem Version fields to 5.0 for Win2K or 5.1 for XP. Also, if you use the System.Threading unit then make a copy of it, modify it to call GetTickCount() instead of GetTickCount64() on pre-Vista systems, and then add the copy to your project.
-
It's not a bug. Starting with 13, the Windows SDK is no longer bundled with the IDE, it needs to be installed separately, and rc.exe is part of the SDK: https://docwiki.embarcadero.com/RADStudio/Florence/en/What's_New#Installation_Process_Updates A new open-source resource compiler named resinator has been added in 13 and is now the default: https://docwiki.embarcadero.com/RADStudio/Florence/en/What's_New#Resource_Compiler So, if you want to continue using rc.exe, you need to install the Windows SDK (or copy it from an earlier installation, as you found). At the project level, you can configure which resource compiler to use (brcc32, resinator, or rc): https://docwiki.embarcadero.com/RADStudio/Florence/en/Resource_Compiler_Options
-
New Delphi features in Delphi 13
Remy Lebeau replied to David Heffernan's topic in RTL and Delphi Object Pascal
I'm human and even I don't understand the question you asked it. You can't write code that targets just RAD Studio itself, because it is not a separate compiler of its own. It uses either the Delphi compiler or the C++Builder compiler. RAD Studio simply bundles them together under one IDE. That's also why you can't have Delphi(Standalone) + RADStudio or C++Builder(Standalone) + RADStudio installed together. The standalone versions are just slimmed down versions of RAD Studio. One IDE, multiple personalities. -
It was added in Delphi 11 Alexandria: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/What's_New#RTL:_TZipFile
-
https://docwiki.embarcadero.com/RADStudio/Florence/en/What's_New#Installation_Process_Updates comdef.h is one of many SDK headers affected by this change. As _bstr_t and _com_error are Win32 classes, you will need to install the Windows SDK to continue using them.
-
Seconded.
-
I'm assuming you are using this one? https://github.com/edwinyzh/ZipMaster In what way does it not work? Are you getting an error? Data corruption? Something else? What does your code look like that is not working? If ZipMaster doesn't suit your needs, then just use a different Zip library. There are plenty of them available.
-
Sounds like you upgraded an existing project to use a newer version of TVirtualTree, where that event has changed signature. You would need to update your existing event handler accordingly.
-
Where is the list of strings kept inside TStrings?
Remy Lebeau replied to JohnLM's topic in RTL and Delphi Object Pascal
TStrings is an abstract base class. You cannot instantiate it directly (as your example is trying to do), and it does not hold any data of its own. It is the responsibility of descendant classes to decide how to handle the strings as they need. For instance, TStringList holds its strings in a memory list (as you noted), whereas TComboBox stores its strings inside of its window, whereas TRadioGroup exposes its strings as captions of child buttons, etc. If you want to view the entire contents of a base TStrings at debug time, then if you KNOW the TStrings is actually a TStringList then you can simply type-cast and debug it directly. Otherwise, you must copy the individual strings into something that is viewable in the debugger, like TStringList. TStrings itself does not hold any data that can be viewed directly. Otherwise, you can use the TStrings's properties to iterate and view the individual strings, eg: function inlist(myTs: TStrings): TStringList; var s: string; i: Integer; ts: TStringList; begin Result := TStringList.Create; try if myTs is TStringList then begin ts := TStringList(myTs); ... Result.Assign(ts); // <-- debug breakpoint end else begin for i := 0 to myTs.Count-1 do begin s := myTs.Strings[i]; ... Result.Add(s); // <-- debug breakpoint end; end; except Result.Free; raise; end; end; The code you have shown does not even compile, as TStrings does not have a constructor that takes a parameter. But even if the code did compile, what your code is doing is just plain wrong, as it is creating memory leaks and accessing invalid memory. It should look more like this instead: function inlist(myts: TStrings): TStringList; var tsL: TStringList; begin tsL := TStringList.Create; try tsL.Assign(myts); except tsL.Free; raise; end; Result := tsL; // <-- debug breakpoint end; procedure TForm1.btn3Click(Sender: TObject); var ts: TStringList; begin ts := inlist(Memo1.Lines); try ... finally ts.Free; end; end; Nope. TStrings itself doesn't even know the strings exist, let alone where they are stored. The descendant classes handle those details. Nowhere at all. -
TPen.OnChange and Invalidate in custom control at design-time
Remy Lebeau replied to Dmitry Onoshko's topic in VCL
Correct. The change doesn't take effect visually until the next time the control is painted. And since you are not detecting the change in real time, you are not able to trigger an immediate repaint. When you use the Object Inspector to modify the value of a sub-property of your GridPen, the Inspector does not create a new TPen object to assign to your GridPen property's setter method. Instead, it uses your GridPen property's getter to access the original TPen object and directly update its sub-property that you are modifying. That is why you need to use the TPen's OnChange event to recognize changes in the sub-properties. The same thing happens when a user of your component changes the value of a sub-property in code at runtime. They don't write code like this (but they can if they want to): NewPen := TPen.Create; NewPen.Assign(MyComponent.GridPen); // <-- GridPen GETTER called NewPen.Style := ...; MyComponent.GridPen := NewPen; // <-- GridPen SETTER called NewPen.Free; Instead, they write code like this: MyComponent.GridPen.Style := ...; // <-- GridPen GETTER called and TPen.OnChange triggered // effectively the same as: // MyComponent.FGridPen.Style := psDash; -
As Anders suggested, you can use Process Monitor to see exactly which file(s) the IDE is trying to access, and whether they succeed or fail. The problem is likely not with the .droj file itself, but with another file that it refers to.
-
[Logical advise needed] TCP persistent connection custom protocol server
Remy Lebeau replied to FearDC's topic in Indy
Try something like this (based on your earlier example): type TOnUserConnect = procedure(AConn: TIdContext) of object; TOnUserDisconnect = procedure(AConn: TIdContext) of object; TOnUserDataIn = procedure(AConn: TIdContext; const AData: String) of object; TOnUserDataOut = procedure(AConn: TIdContext; const AData: String) of object; type // server listener TMyServer = class(TIdCustomTCPServer) protected procedure InitComponent; override; private FOnUserConnect: TOnUserConnect; FOnUserDisconnect: TOnUserDisconnect; FOnUserDataIn: TOnUserDataIn; FOnUserDataOut: TOnUserDataOut; procedure DoConnect(AContext: TIdContext); override; // note: thread safety procedure DoDisconnect(AContext: TIdContext); override; // note: thread safety function DoExecute(AContext: TIdContext): Boolean; override; // note: thread safety procedure DoParseProtocol(AContext: TIdContext; const AData: String); public LWaitFor: String; // command separator property OnUserConnect: TOnUserConnect read FOnUserConnect write FOnUserConnect; property OnUserDisconnect: TOnUserDisconnect read FOnUserDisconnect write FOnUserDisconnect; property OnUserDataIn: TOnUserDataIn read FOnUserDataIn write FOnUserDataIn; property OnUserDataOut: TOnUserDataOut read FOnUserDataOut write FOnUserDataOut; end; procedure TMyServer.InitComponent; begin inherited InitComponent; ContextClass := TMyClient; end; procedure TMyServer.DoConnect(AContext: TIdContext); begin if Assigned(FOnUserConnect) then TThread.Queue(nil, procedure begin if Assigned(FOnUserConnect) then FOnUserConnect(AContext); end ); end; procedure TMyServer.DoDisconnect(AContext: TIdContext); begin if Assigned(FOnUserDisconnect) then TThread.Queue(nil, procedure begin if Assigned(FOnUserDisconnect) then FOnUserDisconnect(AContext); end ); end; function TMyServer.DoExecute(AContext: TIdContext): Boolean; var AData: String; begin (AContext as TMyClient).SendQueue; AData := AContext.Connection.IOHandler.WaitFor(LWaitFor, True, False, nil, 5000); if AData <> '' then begin TThread.Queue(nil, procedure begin if Assigned(FOnUserDataIn) then FOnUserDataIn(AConn, AData); DoParseProtocol(AConn, AData); end ); end; Result := AContext.Connection.Connected; end; procedure TMyServer.DoParseProtocol(AConn: TIdContext; const AData: String); var AUser: TMyClient; begin AUser := AConn as TMyClient; if AData.Equals('Validate') then begin AUser.FNick := '<parsed data>'; AUser.QueueData('Hello'); end else if AData.Equals('<bad command>') then begin AConn.Binding.CloseSocket; end; // this is the actual protocol parser, from here user connection will be // verified, protocol parsed, responses written, other helper classes // will check for bans, shared memory access will be performed, file // contents will be read from hard drive and sent back to users, // thread-unsafe objects will be used, users will get disconnected, // basically all the main load will be performed here end; ... uses ..., IdThreadSafe; type // user connection TMyClient = class(TIdServerContext) protected // FQueue: TIdThreadSafeStringList; ... procedure SendData(const AData: String); procedure SendQueue; public constructor Create(AConn: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override; destructor Destroy; override; procedure QueueData(const AData: String); end; constructor TMyClient.Create(AConn: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); begin inherited Create(AConn, AYarn, AList); ... FQueue := TIdThreadSafeStringList.Create; end; destructor TMyClient.Destroy; begin ... FQueue.Free; inherited Destroy; end; procedure TMyClient.QueueData(const AData: String); begin FQueue.Add(AData); end; procedure TMyClient.SendData(const AData: String); begin Connection.IOHandler.Write(AData); if Assigned((Server as TMyServer).FOnUserDataOut) then TThread.Queue(nil, procedure begin if Assigned((Server as TMyServer).FOnUserDataOut) then (Server as TMyServer).FOnUserDataOut(Self, AData); end ); end; procedure TMyClient.SendQueue; var QueueList: TStringList; SendList: TStringList; I: Integer; begin SendList := nil; try QueueList := FQueue.Lock; try if QueueList.Count = 0 then Exit; SendList := TStringList.Create; SendList.Assign(QueueList); QueueList.Clear; finally FQueue.Unlock; end; for I := 0 to SendList.Count-1 do SendData(SendList[I]); finally SendList.Free; end; end; ... Why? You don't need the thread handle for this task. But, that being said, it is technically possible if you do need it for some reason - you can type-cast the TIdContext.Yarn property to TIdYarnOfThread, and then use the TIdYarnOfThread.Thread.Handle property. handle. -
No. What you are looking at is just a namespace within the XML document. XML namespaces are formatted as URIs, but they are not required to refer to actual live sites. They just need to be unique within the XML document.
-
[Logical advise needed] TCP persistent connection custom protocol server
Remy Lebeau replied to FearDC's topic in Indy
You get that for free with TIdTCPServer, as each client runs in its own thread. Sending the answer from the main thread itself is not a good idea. If the send blocks, not only will you block all other requests and answers, but also block the UI, too. This goes back to my earlier suggestion that you should use a per-client queue for outgoing messages. The main thread can put the answer in the client's queue, and then the server's OnExecute event can send the client's queue when it is safe to do so, ie between reads of incoming messages. By default, no. Blocking sockets wait forever for operations to finish. But, if desired, you can use the TIdContext.Binding.SetSockOpt() method to set a timeout for the SO_SNDTIMEO option on the underlying socket. If a send times out, a failure will be reported back to you. At which point, you don't know the state of the socket, or how many bytes it may have sent before timing out, so the only sane thing you can do is close that socket and let that client reconnect to your server. That may or may not be a good design choice. For example, if Client1 and Client2 want to send a message to Client3 at the same time, then you would need to synchronize those messages. But, if Client1 wants to send a message to Client2 while Client3 wants to send a message to Client4 at the same time, then there is no need to synchronize those messages. So, unless the requests are accessing UI resources, there is not a good reason for the main UI thread to be processing the client requests. TIdTCPServer already disconnects active client sockets during its shutdown. But, if you must do it manually, then using Connection.Binding.CloseSocket() would be safer than Connection.Disconnect() especially in a multi-threaded environment. That said, make sure your server's event handlers are exiting properly during server shutdown. This is another reason NOT to use the main UI thread to process requests. If the main thread is blocked shutting down the server then it can't handle subsequent synchronization requests, thus causing a deadlock (client threads are blocked waiting on the main thread, and the main thread is blocked waiting on the client threads). So, do not perform synchronizations with the main thread during server shutdown, or else perform the shutdown in a separate thread leaving the main thread open to handle synchronizations until the server is fully shutdown.