-
Content Count
1053 -
Joined
-
Last visited
-
Days Won
23
Everything posted by aehimself
-
I never had to do anything fancy - reply to stop, start, pause, resume and shutdown events. Just out of my own curiosity and education - what are these features?
-
How to determine a resource and / or Resource suffix
aehimself replied to alnickels's topic in Network, Cloud and Web
Ouch. Why I always tend to overcomplicate things? π This is the correct solution. -
is there any "currency to words" routine for Delphi?
aehimself replied to Attila Kovacs's topic in Algorithms, Data Structures and Class Design
if Num > 999999999 then raise Exception.Create('Can''t express more than 999,999,999 in words'); This made me giggle π Seriously though, ours is somewhat similar. It's not this sophisticated though. -
is there any "currency to words" routine for Delphi?
aehimself replied to Attila Kovacs's topic in Algorithms, Data Structures and Class Design
We did something like this in a commercial application: it only handles numbers up to 9999 but in multiple languages if I'm not mistaken. To be completely honest, it's quicker just to write your own from scratch which will satisfy your needs rather than to look for one. The code I was talking about is about 50-100 lines in Delphi... Due to it's commercial nature I'll not be able to share sniplets, but it's basically these rules in lots of "if" statements π -
Agreed, this is a possibility in most cases; I just don't see a reasonable chance when it comes to Windows Services. Even if Embarcadero would decide to re-write the logic, they'd probably keep the event handlers and the class name so it stays backwards compatible... I hope π if not, most of us will be in a big trouble π
-
When my applications stop processing Windows messages it's always a very long processing in the VCL thread. Like reconnecting to a database, refreshing a huge dataset or I simply messed something up and my code got in an endless loop π This is when correct logging can save your life: I'm usually logging when an action starts and when it ends. When the application freezes just check which action started, which did not report back as finished.
-
How to determine a resource and / or Resource suffix
aehimself replied to alnickels's topic in Network, Cloud and Web
Hello, I will be honest I don't understand the question correctly, but if you'd like to identify if the received string is a JSON or an XML, you can use this: Uses System.JSON; [...] Var x: TJSONValue; begin x := TJSONObject.ParseJSONValue('this is the received file'); If x = nil Then Begin // File is NOT JSON -> it is XML (or other webserver generated error page) End Else Begin // File is JSON, values can now be accessed by object "x" End; end; In the x = nil branch you could make an XML-to-object parsing to confirm if it's XML and simply stop processing if not. I never worked with XMLs from my codes so I have no knowledge on how to do that, though π -
I personally never used SvCom but even if it's unsupported I would not worry about it. The way Windows handles services (service control messages) did not and could not change as it would break backwards compatibility (you wouldn't be able to install / start a service on Windows 2003, only on 2019 - never seen that, unless it was an API dependency in the business logic). A component to create a service application mostly contains a code to handle these SCMs, which are the same since Windows 2000.
-
I wrote all of my Windows service applications based on Delphi's TService and I did not find any showstoppers which made me want to change it. It's robust, small and gets the job done quite well. A small tip though. Since service applications are hard to debug, I started to implement the whole business logic as a simple Class which is being created and destroyed based on what is happening with the service. Change the .DPR like this: If Not FindCmdLineSwitch('console', True) Then Begin If Not Application.DelayInitialize Or Application.Installing Then Application.Initialize; Application.CreateForm(TService1, Service1); Application.Run; End Else StartWithConsole(TMyServiceClass); ...where StartWithConsole is just a small procedure which creates a console window with AllocConsole, creates the service class, redirects the logging output to StdOut and handles the console handlers (like Ctrl-C, etc). This way there's only one executable, which can be started as a command line application for easy debugging and as a Windows Service as well. P.s.: don't forget about creating a custom message pump, some components rely on Windows Messages to work properly!
-
I just checked my old codes and yes - they do take effect after the broadcast was sent. Must have mistaken it with setting it somewhere else in Windows...?
-
Also please note that in Windows several environmental variables requires a logoff-logon for the changes to take affect (like %PATH%).
-
Hello, I was just wondering if this is "normal" or just an anomaly on my side. Consider the following code: Set a breakpoint on sl.Add and execute. When the execution stops, press F8. However the Except block executes, the Delphi debugger does not continue the step-by-step debugging. I know, you can put a breakpoint on On E:Exception but it would be a lot nicer to see how the exception is flowing (especially through 5-10 units until it reaches the final handler). Is there a way to achieve this or it's just a limitation of the debugger? Cheers!
-
Shift-F8 will return at the end of the calling method, in this case the OnClick handler. It will NOT put you inside the Except block in any case. As for TStringList, I just chose something random, you can even put Raise Exception.Create('hello world'); instead. Also, I believe that when you try to access an uninitialized object it does not matter where it comes from, as it does not exist... I am afraid that @Fr0sT.Brutal will be right, and this - too - is going to be a limitation of the IDE. Too bad π
-
JSON as a way to save information in Firebird
aehimself replied to Alberto FornΓ©s's topic in Databases
Based on experience I am against this type of data storage for several reasons: - Some database engines can not search in BLOB fields. Now it might not be an issue in the beginning but it makes investigating data corruption / debugging processing issues a living nightmare - Downloading blob fields are slow, no matter what. Especially over the network. In our production databases I can select hundreds of thousands of rows with 150+ columns but only a fraction of that 2 fields: an ID and a BLOB storing an XML - Database engines are robust, they were built for having columns. While it can be hard / intimidating to see the relations at first time, they have no issues handling a larger number of columns - Unless the DB engine has built-in JSON support, you'll not be able to query and compare a specific value on database side, you'll always have to download and compare from code, increasing memory, network and CPU usage of your program. Considering a well-fed metal (our slang for a server with enough resource + overhead) running the database, this will always be significantly slower than letting the DB engine to take care of these The only reasonable case when storing a JSON / XML in a database I can see is if you receive that from an external source (like a REST server or a 3rd party sending your software some information) and you want to keep those chunks for auditing purposes. Sooner or later you'll face one of the issues I mentioned, though π We did the same about 8 years ago (only with XMLs) and now we are looking into opportunities to undo it... -
A quick and dirty way of adding this component in design time is to switch the platform back to Win32, drop the component and then switch back (even completely remove the Win32 platform). I usually have to do this with my 64bit applications using database access components.
-
Should I keep class alive (global) or not?
aehimself replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Hello, Of course, this information is included in the update file and (except for the download link) are published by the thread object as properties. For simplicity's sake I decided not to include them, but here you go π -
Should I keep class alive (global) or not?
aehimself replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
A thread with local variables was always my solution as well. When my application starts it launches a background thread which downloads the update definition file and notifies the main form to show the "Updates are available". Everything until here is not locking the UI. When the update form is shown and the user clicked "Yes" the main form calls a .Update method in the thread which downloads the update file, verifies it etc. and returns a simple bool if it was successful or not. This way no global variables were defined, no complex types were passed between threads and most of the work was done without locking the UI. Threads are a good thing. -
I am using Delphi 10.2.3 and wow, I wish I knew about this before I started my first project... now I have to go back and correct them all π I can confirm that s.ToJSON is producing the exact same output as it was originally requested: s.ToString with the replacements produces "\"value\\with\n{strange}chars/\"" s.ToJSON produces "\"value\\with\r\n{strange}chars\/\"" Lesson learned. Do not try to reinvent the wheel, just realize if I messed up the implementation. (I wonder why it has a .ToString if it is not producing a correct syntax though...)
-
Add aΒ system-menu item to all applications?
aehimself replied to PeterPanettone's topic in Windows API
I used the system menu to allow users to change the VCL style of my application. The code looked like this: Procedure TMyApplicationMainForm.FormShow(Sender: TObject); Var sysmenu: THandle; a: Integer; flags: Cardinal; Begin sysmenu := GetSystemMenu(Self.Handle, False); AppendMenu(sysmenu, MF_SEPARATOR, Word(-1), ''); For a := Low(_validstyles) To High(_validstyles) Do Begin flags := MF_STRING; If TStyleManager.ActiveStyle.Name = _validstyles[a] Then flags := flags Or MF_CHECKED; AppendMenu(sysmenu, flags, WM_USER + 1 + a, PWideChar(_validstyles[a])); End; End; Instead of Self.Handle I suppose you can supply a different form handle returned by FindWindow. This solution will send a WM_USER message to the form when the appended item is clicked but the foreign one has to process that correctly... I guess you can append to the system menu of an other application, but I doubt that it will ever work correctly. At least not this way π -
Although Delphi has a native JSON implementation (System.JSON unit) it is not perfect. It seems to be good in parsing, but assembly needs a bit of help. I use it in most of my latest codes though. To achieve what you'd like: procedure TForm1.Button1Click(Sender: TObject); const KEK = '"value\with' + sLineBreak + '{strange}chars/"'; Var s: TJSONString; begin s := TJSONString.Create(KEK.Replace('\', '\\').Replace(#9, '\t').Replace(sLineBreak, '\n').Replace(#10, '\n').Replace(#13, '\n')); Try Edit1.Text := s.ToString; Finally FreeAndNil(s); End; end; Without the loads of .Replace statements it tends to generate invalid JSONs. So far this chain always fit my needs. It needs a little bit of time to get used to it, but it's lightweight and fast. Edit: I don't think you actually have to escape forward slashes in JSON string literals, they seem to pass validation without them. I did not read the whitesheet though, so I could be wrong. Just add .Replace('/', '\/') in the chain to do that.
-
Hello guys, I am developing a lightweight, customized SQL client. The main form only has a PageControl, all business logic reside on a frame. When the user chooses a connection, a new tabsheet is created, and a new instance of the said frame is placed upon it. Simple, and works like a charm. To please some people I decided to allow VLC styling of the application but this introduced a really strange issue. If the popup menu resides on the frame and has an image list assigned, it seems like that the menu is not drawn properly: If I change the Style back to the system default (or simply unassign the imagelist component from the TPopupMenu), everything works perfectly: Menu has no owner draw set. What I tried so far: - Add a new menu item during runtime to force a refresh of the menu - Unassign and reassign the popup menu during runtime from the DBGrid component - Unassign and reassign the image list during runtime - Set all ImageIndexes to -1, even removed all menu items during design time - Added Vcl.Styles to the uses clause of the frame - Changing Style during runtime has no effect. If I change back to System default, popup menu start being drawn correctly - Behavior is the same on additional modal forms, menu is not drawn correctly if image list is assigned In the mean time, the popup menu on the main form works perfectly. Which is even more strange, if I assign the main forms popup menu to the DB grid during runtime, it is drawn correctly. I feel like this will be something to do with the menus not being a child of the main form, or like a special hook I'm missing (like how you are forcing styling on a SynEdit component) but I could be far from the truth. I'm running out of ideas. Did anyone face this issue already? Any tips which I could try to make this to work? Thanks all!
-
Uwe, Wow, a 3 year old bug... and indeed it does look the same! So without upgrading my Delphi installation I have no other choice than to forcefully remove icons when using Styles. Or not to use Styles in general π Thank you for the quick reply!
-
Passing back a string from an external program
aehimself replied to dummzeuch's topic in Windows API
I always used this code, which works fine. The only issue is if the other application is expecting an input (even a keypress to finish) because ReadFile will wait endlessly as far as I recall. It is easy to finetune it though. function GetDosOutput(CommandLine: string; var ReturnString: String; var ReturnCode: Cardinal): Boolean; const LOGON_WITH_PROFILE = $00000001; var SA: TSecurityAttributes; SI: TStartupInfo; PI: TProcessInformation; StdOutPipeRead, StdOutPipeWrite: THandle; WasOK: Boolean; Buffer: array[0..255] of AnsiChar; BytesRead: Cardinal; WorkDir: string; Handle: Boolean; begin Handle := False; ReturnCode := 255; Result := False; ReturnString := ''; SA.nLength := SizeOf(SA); SA.bInheritHandle := True; SA.lpSecurityDescriptor := nil; CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0); Try With SI Do Begin FillChar(SI, SizeOf(SI), 0); cb := SizeOf(SI); dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; wShowWindow := SW_HIDE; //wShowWindow := SW_MINIMIZE; hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin hStdOutput := StdOutPipeWrite; hStdError := StdOutPipeWrite; End; WorkDir := '.'; Handle := CreateProcess(nil, PChar(CommandLine), nil, nil, True, CREATE_NEW_PROCESS_GROUP or CREATE_NEW_CONSOLE, nil, PChar(WorkDir), SI, PI); CloseHandle(StdOutPipeWrite); If Handle Then Try Repeat WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil); If BytesRead > 0 Then Begin Buffer[BytesRead] := #0; ReturnString := ReturnString + Buffer; End; Until Not WasOK Or (BytesRead = 0); WaitForSingleObject(PI.hProcess, INFINITE); GetExitCodeProcess(PI.hProcess, ReturnCode); While (Length(ReturnString)<>0) And ((ReturnString[Length(ReturnString)]=#10) Or (ReturnString[Length(ReturnString)]=#13)) Do Delete(ReturnString,Length(ReturnString),1); Result := True; Finally CloseHandle(PI.hThread); CloseHandle(PI.hProcess); End; Finally CloseHandle(StdOutPipeRead); End; end; -
Do you need parsing too, or only to beautify the code? I wrote a unit which beautifies and simplifies (remove idents and formatting to reduce size) XML documents and it's using only string helper functions. It formats a ~14 MB XML file in about 300ms, however this heavily depends on the amount of tags. Since the structure is very close I suppose it can work with HTML. If you don't want to use components / libraries let me know, I can send you the code.
-
Hello, I'm sorry if this topic was discussed several times, I was able to find only a REALLY low amount of information and none of them seemed to work. I need to create a TSslHttpServer (in runtime) and serve the client requests. Everything went fine in my test app so I started to port it to it's final state but it refused to work. Port is opened but no events are being fired. Since I already met this with the standard TClientSocket / TServerSocket so I quickly put my message pump generator in the Repeat...Until Terminated cycle in my main thread. No joy, so I started to investigate. I already found that I should do something with the NOFORMS directive but I was unable to make it work. Result is always the same: connection to the opened port is possible, but no events are fired in my Delphi app, nor the connection responds. - I added the NOFORMS directive to the ICS install package and rebuilt all - I added the NOFORMS directive to my app and rebuilt all - I added the {$DEFINE NOFORMS} to my app's dpr - tried enabling or disabling the MultiThreaded property - Tried moving to my messagepump to SslHttpServer.OnMessagePump - Tried SslHttpServer.ProcessMessages, .MessagePump, .MessageLoop I also mixed the above, meaning tried each combination of each message processor method with each directive. TMyThread = Class(TThread) strict private myhttpsrv: TSslHttpServer; [...] Constructor TMyThread.Create; Begin myhttpsrv := TSSlHttpServer.Create(nil); myhttpsrv.OnClientConnect := WebServerClientConnect; myhttpsrv.OnMessagePump := WebServerMessagePump; [...] Procedure TMyThread.Execute; Begin Repeat If Not _httpsrv.ListenAllOK Then _httpsrv.Start; // myhttpsrv.ProcessMessages; // myhttpsrv.MessageLoop; // myhttpsrv.MessagePump; Until Terminated; [...] Procedure TMyThread.WebServerClientConnect(Sender: TObject); Begin WriteLn('Client connected...'); [...] Procedure TMyThread.WebServerMessagePump(Sender: TObject); Begin If PeekMessage(msg, 0, 0, 0, 0) Then Begin GetMessage(msg, 0, 0, 0); TranslateMessage(msg); DispatchMessage(msg); End; [...] What I am doing wrong? Any ideas on how I can make it work? Thanks!