-
Content Count
2771 -
Joined
-
Last visited
-
Days Won
147
Everything posted by Anders Melander
-
How to register a shell property sheet for a single file type?
Anders Melander replied to FPiette's topic in Windows API
My factory overrides GetProgID to return an empty string and TComObjectFactory.UpdateRegistry has this code: if ProgID <> '' then begin CreateRegKey(RegPrefix + ProgID, '', Description, RootKey); CreateRegKey(RegPrefix + ProgID + '\Clsid', '', ClassID, RootKey); CreateRegKey(RegPrefix + 'CLSID\' + ClassID + '\ProgID', '', ProgID, RootKey); end; Yes. -
How to register a shell property sheet for a single file type?
Anders Melander replied to FPiette's topic in Windows API
It's registered for a single extension. I'm not using your IShellExtInit implementation at all. I'm using the shell integration classes from the Drag and Drop Component Suite, so here's the full extent of changes I needed to make in order to register the property sheet handler: type TPropertySheetHandlerFactory = class(TDropContextMenuFactory) protected function HandlerRegSubKey: string; override; end; function TPropertySheetHandlerFactory.HandlerRegSubKey: string; begin Result := 'PropertySheetHandlers'; end; const // CLSID for this shell extension. // Modify this for your own shell extensions (press [Ctrl]+[Shift]+G in // the IDE editor to gererate a new CLSID). CLSID_PropertySheetHandler: TGUID = '{1067C264-8B1F-4B22-919F-DB5191C359CB}'; sFileClass = 'FoobarFile'; sFileType = 'File of foo'; sFileExtension = '.foobar'; sClassName = 'DelphiPropSheetShellExt'; resourcestring // Description of our shell extension. sDescription = 'Property Sheet example'; initialization TPropertySheetHandlerFactory.Create(ComServer, TDataModulePropertySheetHandler, CLSID_PropertySheetHandler, sClassName, sDescription, sFileClass, sFileExtension, ciMultiInstance); end. and here's the code to extract the file names (ShellExtInit.Initialize): function TDataModulePropertySheetHandler.ShellExtInit_Initialize(pidlfolder: PItemIDList; lpdobj: IDataObject; Hkeyprogid: HKEY): HResult; begin Result := NOERROR; FFiles.Clear; SetFolder(pidlFolder); // Save a reference to the source data object. FDataObject := lpdobj; try // Extract source file names and store them in a string list. // Note that not all shell objects provide us with a IDataObject (e.g. the // Directory\Background object). if (DataObject <> nil) then with TFileDataFormat.Create(dfdConsumer) do try if GetData(DataObject) then FFiles.Assign(Files); finally Free; end; finally FDataObject := nil; end; end; FWIW, I believe the thing that you were missing was registering the class ID in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved. The 4-space indent and capitalized keywords probably also had a negative influence 🙂 Source attached. PropertySheetHandler.zip -
How to register a shell property sheet for a single file type?
Anders Melander replied to FPiette's topic in Windows API
Works for me: I'll clean up the code and post it later... -
bde_2_fd FireDAC performances
Anders Melander replied to mario.arosio@gmail.com's topic in Databases
Did you do any of this? -
Should be pretty easy to implement with any of the libraries that can render text along a path. Image32, Graphics32, etc. Are there any particular effects you need, or are you looking at what's available for now?
-
Best Practice Question: Bidirectional EXE-to-EXE communication
Anders Melander replied to Alexander Halser's topic in RTL and Delphi Object Pascal
IMO the restrictions you've put on the implementation seem a bit arbitrary and are getting in the way of the solution. I would definitely go with an out-of-process COM server; It's a piece of cake to implement, it's natively supported by Delphi and you'll get IPC and threading thrown in for free. You could even have the service host it - or have it as a separate component that is used simultaneously from three modules. So why is it a problem that the exe needs to run once (to take care of the COM registration) before the client can access it? If you really can't register the COM server before the modules are run, then just have the modules do it themselves when they are run. No "installation" is necessary when you have complete control of all the components of the system. -
Well, at least it told you what happened so you can fix or work around it 🤦♂️
-
It sounds like you already know the answer to that one.
-
New blog post: Leveraging ChatGPT to generate a Delphi class along with CRUD code from a table schema
Anders Melander replied to Darian Miller's topic in Tips / Blogs / Tutorials / Videos
Yes, that's the formal definition of it. I'm more referring to professionalism as an attitude toward solving problems; Don't be lazy is the first thing I try to teach new developers. The vast majority of the work I do is horribly boring, but I don't get paid to write "interesting" code. I get paid to write high-quality code and if that means I have to write yet another damned factory, observer, or visitor class so be it. The effort spent will benefit everybody, myself included, down the road. I'm not against using a tool like ChatGPT as long as it is used as what it actually is. -
New blog post: Leveraging ChatGPT to generate a Delphi class along with CRUD code from a table schema
Anders Melander replied to Darian Miller's topic in Tips / Blogs / Tutorials / Videos
How is it disingenuous? I'm stating pretty clearly that I believe it's an amateurish approach. A professional would look at the problem and then think about how best to solve it within the given constraints. The problem with ChatGPT is not the quality of the code. The problem is that you're delegating the most important element of software development, namely design and architecture, to a construct that has no grasp of these concepts. But okay, what do I know. -
(Possibly) interesting stuff to read
Anders Melander replied to Tommi Prami's topic in Algorithms, Data Structures and Class Design
AVX-512... Risky (performance-wise) - unless you know exactly what hardware you're running on. -
New blog post: Leveraging ChatGPT to generate a Delphi class along with CRUD code from a table schema
Anders Melander replied to Darian Miller's topic in Tips / Blogs / Tutorials / Videos
Once again, it's just a party trick; A complete waste of time. What's demonstrated here is not how a professional solves a problem. If you're lucky the generated code is one possible solution, but regardless of how correct the code is there's almost no chance of it being the best solution to a given problem. -
Yes
-
Memory Leak when loading/unloading Delphi DLL
Anders Melander replied to Thomas B's topic in Windows API
I have not been able to reproduce the problem using the supplied DLL and host source. Compiled with Delphi 11.2 targeting both 32- and 64-bit. Virtual memory, private bytes, working set, and handles were stable. No leaks were identified. I suggest you test it on a clean system (or do a secure boot) to ensure that stuff isn't being injected into your process (e.g. via HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs). -
Keep a reference to the thread when you create it. Use TThread.Terminate to terminate it and use TThread.WaitFor to wait for it to actually terminate.
-
I agree that this is the better approach. Just remember to signal the thread to terminate (and wait for it to do so) when the service is stopping. Also, note that in the case of a COM-server-in-a-service there might not be a need for a separate thread since each COM connection will get its own thread anyway, depending on the apartment model used.
-
Just so you know, while all the code in this thread might be fine for "I'm a hobbyist and I don't know what the hell I'm doing"-level programming, it's nowhere near the best or correct way to solve your problem. With that said, the reason you're losing transparency is that you are operating on the rendered visual representation of the image. Therefore the transparency has been replaced with a background color. In order to resize a PNG without losing transparency you will need to use a method that supports alpha transparency. This means: Convert the PNG to a 32-bit RGBA bitmap. Resample (resize) this bitmap to the desired size. Convert the bitmap to PNG. There are various libraries that can do these steps for you. For example Graphics32 and probably also Image32. I'm guessing Image32 will probably be the easiest for you to understand.
-
It's not clear where the code you posted is located but regardless, exceptions inside TServiceThread.ProcessRequests are caught and handled (logged) there so they won't propagate to your handler. If you need to catch unhandled exceptions on the service level you should create a custom TServiceApplication and override the DoHandleException method. By default, it logs exceptions to the event log but you can change it to do whatever you need. This is what it would look like in Delphi: program ServiceProject; uses Vcl.SvcMgr, MyUnit in 'MyUnit.pas' {MyService: TService}; {$R *.RES} type TMyServiceApplication = class(TMyServiceApplication) protected procedure DoHandleException(E: Exception); override; end; procedure TMyServiceApplication.DoHandleException(E: Exception); begin ...whatever... end; begin // Get rid of TServiceApplication... Vcl.SvcMgr.Application.Free; // ...and use our custom service application class instead Vcl.SvcMgr.Application := TMyServiceApplication.Create(nil); // Usual service code follows... if not Application.DelayInitialize or Application.Installing then Application.Initialize; Application.CreateForm(TMyService, MyService); Application.Run; end.
-
How to enter unicode symbols into the Delphi IDE
Anders Melander replied to Dave Novo's topic in Delphi IDE and APIs
That method only applies to MS Office (Word, Excel, etc). AFAIK this is the only keyboard method that works universally: In the registry, under the HKEY_Current_User/Control Panel/Input Method, set the EnableHexNumpad value to "1" (it's a REG_SZ value in case you need to add it). Press and hold the Alt key. Press the + key on the numeric keypad (I hope you got one 🙂 ). Type the hexadecimal unicode value. Release the Alt key. -
Nested TParallel.For: Immediate deadlock
Anders Melander replied to Der schöne Günther's topic in RTL and Delphi Object Pascal
Okay so I've looked into this some more and I've got good news and bad news: The bad news is that this is probably "as designed". The good news, well I lied; There isn't any good news. If we look at other thread pool implementations "properly designed by skilled practitioners", the old Win32 thread pools also had a hard upper limit on the number of threads in a pool and suffered from the exact same problem. The newer Vista thread pools are a bit more clever but it still has an upper limit (I believe the default max is 500 threads) and suffered from the exact same problem. Same with .NET thread pools (which are a rewrite of Win32 thread pools); For CLR 2.0 the max is 25 threads per core and for 2.0SP1 the max is 250 per core. The reason for this tenfold increase in default max is actually to avoid experiencing deadlocks caused by running out of threads quite so often. Thus .NET too suffers from the exact same problem. See Concurrent Programming on Windows, chapter 7 for a discussion on all this. So the problem is that the RTL thread pool imposes a hard upper limit on the number of threads in it and that the limit is way too small. Ideally what we'd like, in this case, is for the growth algorithm to be a bit more intelligent and flexible but I doubt that will happen. The easy solution is probably to just increase the max and hope for the best 😕 . I still believe that the library should detect (and fail on) the simple case when all threads are blocked waiting for a thread to become available. It's relatively unlikely that this will occur in real code (there are many other things, not controlled by the PPL, that a thread can wait on) but it's so simple to implement that I believe it's worth the effort. -
Nested TParallel.For: Immediate deadlock
Anders Melander replied to Der schöne Günther's topic in RTL and Delphi Object Pascal
That was really helpful. Thanks. If you view the PPL as an opaque general-purpose library then documentation will not help. Even if the problem/limitation is known by the user of the library, its existence means that the PPL can only be used when running on known configurations (# of CPUs) and cannot safely be used with other libraries that might also use the PPL. In other words: It's a bug; The situation should either be handled or it should fail with an exception. As far as I can see the concrete problem is in TParallel.ForWorker64 on the statement RootTask.Start.Wait. All the inner loop tasks are hanging there, waiting for their code to execute, but since all threads are occupied (by the outer loop tasks), none will ever become available. Deadlock. This situation should be detectable and I think a simple solution would be to temporarily increase the size of the threadpool when it occurs. For example in TThreadPool.TThreadPoolMonitor.GrowThreadPoolIfStarved. -
Writing a property sheet shell extension (IShellPropSheetExt)
Anders Melander replied to FPiette's topic in Windows API
Yes; That's what I wrote. procedure InjectMyForm(HandleOfControlOnPropertyPage: HWND; MyForm: TForm); begin SetParent(MyForm.Handle, HandleOfControlOnPropertyPage); ...lots of stuff to do with positioning and window style... end; -
Writing a property sheet shell extension (IShellPropSheetExt)
Anders Melander replied to FPiette's topic in Windows API
I can't see any way to create a property sheet without using a dialog template. Maybe you can just use an empty dialog template and then inject your Delphi form when the page is displayed? -
Nested TParallel.For: Immediate deadlock
Anders Melander replied to Der schöne Günther's topic in RTL and Delphi Object Pascal
...and now I can again. Race conditions. Pfft! 🙂 -
Nested TParallel.For: Immediate deadlock
Anders Melander replied to Der schöne Günther's topic in RTL and Delphi Object Pascal
...and now I can't. I tried debugging it and the IDE froze. After a restart of the IDE I can't reproduce it anymore.