Jump to content

Anders Melander

Members
  • Content Count

    2771
  • Joined

  • Last visited

  • Days Won

    147

Everything posted by Anders Melander

  1. Anders Melander

    Anyone know why?

    This is my experience too. and unfortunately this too. Luckily we have both Delphi and C# projects and many works on both.
  2. Anders Melander

    Anyone know why?

    Yes, now they all work from home... at other companies.
  3. Anders Melander

    Anyone know why?

    Why? I haven't seen my client or the rest of my team for something like 4 years. They're in Norway, btw. The webcam on my main PC broke 3 years ago, so most of the team has literally never seen me. The others think I have short hair and am clean-shaven, but I stopped shaving and cutting my hair when Corona hit, so they wouldn't even recognize me now with a full beard and a ponytail. I've previously worked with teams in Ukraine, Sweden, and the UK that I never met physically and never had any practical need to meet. Sure, it's nice to get together for pizza and beer once in a while, but that's just a nice-to, not a need-to.
  4. Anders Melander

    Interface Reference counting

    Yes: Don't be lazy. This is the better approach: Don't use Assigned() unless you have a reason to. Assigned() just adds another layer that your brain has to parse. Testing against nil makes it immediately clear what's going on. Yes. Yes and no. Since the MyObject parameter is a value parameter you're not returning anything (besides the boolean). And since you're not returning a reference to the interface the ref count will become 0 when the method returns and the object will be destroyed. The debugger would have shown you this if you'd tried it.
  5. Anders Melander

    General Help Indeed...a long-shot here....

    Files in the Imports directory have been generated by the type library importer (generated pascal wrappers around COM libraries). This most likely means that some of the missing controls (e.g. the TSYSMAC_C control) are ActiveX controls. Probably ActiveX controls that were provided by Omrom for communication with the device. Googling for SYSMAC_C Omrom leads to a few hits. Notably, one that mentions a Sysmac_C ActiveX control. TTxEdit sounds like a custom edit control. Googling for Delphi TTxEdit lead me to a Korean message board with a message from 2007 with this link: ftp://ftp.nsk.su/pub/msdos/misc/ttxedit.arj Unfortunately, that site is long gone. You can probably replace the TTxEdit control with TEdit.
  6. Anders Melander

    Interface Reference counting

    Yes. Then there are no references to the object and it will be destroyed right before CreateAndReturnAnObject returns. IMO it would be better if you declare CreateAndReturnAnObject a function that returns an interface. MyObject is not an object; It's a reference (i.e. a pointer) to an object. First of all, it's important to know that a reference-counted object is destroyed when the reference count is decreased to 0. Now, when the object has been created it has a reference count of 0. When you assign the object to a variable (e.g. Result or "var MyObject") you create a reference and the ref count is increased to 1. The MyObject value returned from CreateAndReturnAnObject maintains the reference so the ref count isn't decremented. Only when MyObject goes out of scope, at the final "end;" will MyObject be nilled which decrements the ref count so it reached 0 and the object is destroyed. If your object is based on TInterfacedObject then I suggest you place breakpoints in all of the methods of TInterfacedObject and watch what goes on. For instance, it's good to know why the object starts with a ref count of -1 and how Delphi makes that work out in the end. There are some strange corner cases but basic reference counting in Delphi really is very simple and logical once you understand the rules.
  7. Anders Melander

    Choosing a Mac

  8. Anders Melander

    Dealing with multiple component versions using the same ide

    Here you go: https://bitbucket.org/anders_melander/delphilauncher/src/main/ The following was copy/pasted from the repo readme.md so the formatting is a bit messed up: 1) Create a folder to hold the environment. > DelphiLauncher\MyProject We call this the environment root folder. 2) Create a folder named Packages under the environment root folder. > DelphiLauncher\MyProject\Packages We call this the package folder. 3) Export the content of the HKEY_CURRENT_USER\Software\Embarcadero\BDS\<version>\Known Packages registry key to a reg file named packages.reg in the environment root folder. <version> denotes the version of Delphi. For example 20.0 for Delphi 10.3 4) Rename packages.reg to packages.txt > DelphiLauncher\MyProject\packages.txt 5) Edit packages.txt Remove the first 3 lines of the file so the file only contains the package list. 6) For all the design-time packages in the list and all the run-time packages they depend on, copy the files to sub-folders 1 of the package folder. > DelphiLauncher\MyProject\Packages\Graphics32\GR32_DD110.bpl > DelphiLauncher\MyProject\Packages\Graphics32\GR32_RD110.bpl > DelphiLauncher\MyProject\Packages\DevExpress\dcldxRibbonRS28.bpl etc 7) Zip the content of the package folder into a file named packages.zip in the environment root folder. > DelphiLauncher\MyProject\packages.zip The package folder can now be deleted. The folder and its content will be recreated by the script on demand. 😎 Add the packages.txt file to packages.zip. The packages.txt file can now be deleted. The file will be recreated by the script on demand. 9) Create a text file named environment.ini in the environment root folder. > DelphiLauncher\MyProject\environment.ini The file is an ini-file with the following content: [environment] delphi=<version> project=<name> revision=<value> where <version> is the version of Delphi used. It must match the value of <version> mentioned in step 3. > delphi=20.0 <name> is the name of the environment. > project=My project The value is displayed in the Delphi title bar. <value> is the environment revision number/id. > revision=1 The combination of <name><value> is used to detect if the environment has been updated and should be refreshed. If you change, add, or remove any package files, one or both of <name> and <value> must be changed. Typically only <value> is changed. 10) Copy the start.ps1 script from another environment into the environment root folder. > DelphiLauncher\MyProject\start.ps1 Note: Packages that are listed in packages.txt but do not exist on disk are ignored. Packages located in $(BDSBIN) are assumed to be Delphi's own and are left as-is. The paths in packages.txt are ignored. Only the file names are significant. It's a good idea to place a text file named versions.txt in the environment root folder that contains the name and version numbers of the libraries contained in the package sub-folders. This makes it easier to determine what an environment contains. > DevExpress 2022.1.3.0 > TeeChart 9.0.26.0 > madExcept 5.1.2 (madCollection 2.8.11.7) If the project will be used with several different versions of Delphi, just create an environment root folder for each version. > DelphiLauncher\MyProject\Delphi 10.3 DelphiLauncher\MyProject\Delphi 11.0 Revision control The following files define an environment and can be placed under revision control (e.g. checked into a Git repository): start.ps1 environment.ini packages.zip versions.txt (optional) All other files are transitory and can be ignored. For Git it might be a good idea to enable LFS for zip files on the repository if the file is large. YMMV. The names of the sub-folders are not important. The sub-folders are only used to better organize the files. ↩
  9. Anders Melander

    Bugs - where to write to fix it?

    https://quality.embarcadero.com But I can tell you right now that most, if not all, of your complaints aren't bugs and will never be implemented as new features or enhancements. Just because something doesn't work the way you'd like it to doesn't make it a bug.
  10. Anders Melander

    Not Threadsafe??

    I think he would like for the application not to appear "hung" while it's waiting for the database task to complete.
  11. Yes, and yes. See TCustomAffineLayer in Examples\Layers\RotLayer\GR32_RotLayer.pas
  12. Anders Melander

    Not Threadsafe??

    Yes, if your end is to make a mess of things, then threads will work wonders for you.
  13. Anders Melander

    Copy bitmap from one app to another app bitmap

    From the title it sounds as if you need to pass a bitmap between two applications, but your description makes it sound as if you need to pass a bitmap from a DLL to an application using that DLL. Between applications: Save the bitmap to a memory stream and use WM_COPYDATA to copy the data. From DLL to application: Just pass a pointer or a bitmap handle. Maybe work a bit more on the problem description?
  14. Anders Melander

    Not Threadsafe??

    I'm guessing the purpose of that code block is to keep the application responsive while the tread is executing, right? ProcessMessages is very rarely the solution - for anything. It's fine in quick and dirty, test code, but it doesn't belong in production code. While there is no problem with ProcessMessages in regard to threads in your code (you're not calling it from a thread), it often makes your UI vulnerable to undesired reentrancy. For example, let's say you have a button on a form with the following OnClick handler: procedure TMyForm.MyButtonClick(Sender: TObject); begin // Increment a counter to show we're busy Tag := Tag + 1; Caption := IntToStr(Tag); // Emulate a task that takes a long time for var i := 0 to 1000 do begin // Busy, busy... Sleep(100); // ...But stay "reponsive"... Application.ProcessMessages; end; // We're done; Decrement the counter Tag := Tag - 1; Caption := IntToStr(Tag); end; The problem here is that unless you take extra steps to prevent it, the user can just keep pressing the button (you'll see the counter keeps incrementing); You've created a recursion through ProcessMessages. The easiest way to prevent that is the disable the UI (e.g. Form.Enabled := False), but the best way is to not use ProcessMessages in the first place. Have you tried pressing the Windows close button (or just [Alt]+[F4]) while that code is executing? I'll bet that doesn't end well. You can create a message loop that processes selected messages instead. For example WM_PAINT and a few others are safe to handle. Here's one I've used in an application I worked on. It is called while doing work that updates a progress bar (with an Abort button). There's a bunch of stuff that only makes in that particular application, but I'm sure you get the meaning. type TFormRedacted = class(TForm) private FProgressStart: TStopWatch; FProgressThrottle: TStopWatch; FLastMessagePump: TStopwatch; FProgressEnableAbort: boolean; FProgressAborted: boolean; ... end; procedure TFormRedacted.ProcessProgressMessages(Force: boolean); var Msg: TMsg; begin if (not Force) and (FLastMessagePump.IsRunning) and (FLastMessagePump.ElapsedMilliseconds < MessagePumpRate * ProgressUpdateFactor) then exit; FLastMessagePump.Reset; var ProgressHandle := ButtonProgressAbort.Handle; // Indicate to madExcept freeze detection that we're not frozen FreezeDetection.IndicateApplicationNotFrozen; try // Allow threads to synchronize to avoid deadlock (e.g. busy loop showing progress waiting for thread to complete (e.g. spell check dictionary load)). CheckSynchronize; Msg.message := 0; try // Look for Escape key pressed. if (FProgressEnableAbort) and (Application.Active) and (GetAsyncKeyState(VK_ESCAPE) <> 0) then begin // Wait for Escape key to be released again. while (GetAsyncKeyState(VK_ESCAPE) <> 0) do Sleep(1); // Clear message queue of keyboard messages while (PeekMessage(Msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do begin if (Msg.message = WM_QUIT) then exit; end; PromptAbortProgress; // Wait for Escape key to be released so dismissing the abort prompt // dialog with escape doesn't trigger a new prompt. while (GetAsyncKeyState(VK_ESCAPE) <> 0) do Sleep(1); // Clear message queue of keyboard messages while (PeekMessage(Msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do begin if (Msg.message = WM_QUIT) then exit; end; end; // Process mouse move for all windows so hot tracking works. // Don't process mouse movement if a mouse key is down. // This tries to avoid recursions caused by scrollbar movement causing work that // ends up calling this method. while (PeekMessage(Msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE)) and (GetKeyState(VK_LBUTTON) = 0) do begin PeekMessage(Msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE); if (Msg.message = WM_QUIT) then exit; DispatchMessage(Msg); end; // Process mouse hover/enter/exit messages for all windows so button state will be updated while (PeekMessage(Msg, 0, WM_NCMOUSEHOVER, WM_MOUSELEAVE, PM_REMOVE)) do begin if (Msg.message = WM_QUIT) then exit; DispatchMessage(Msg); end; // Process timer message for all windows so animation works - THIS IS DANGEROUS SINCE ALL TIMERS WILL BE PROCESSED while (PeekMessage(Msg, 0, WM_TIMER, WM_TIMER, PM_REMOVE)) do begin if (Msg.message = WM_QUIT) then exit; DispatchMessage(Msg); end; // Process mouse messages for button so user can press Stop button while (PeekMessage(Msg, ProgressHandle, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) do begin if (Msg.message = WM_QUIT) then exit; DispatchMessage(Msg); end; // Process cursor update messages for all windows so cursor stays responsive while (PeekMessage(Msg, 0, WM_SETCURSOR, WM_SETCURSOR, PM_REMOVE)) do begin if (Msg.message = WM_QUIT) then exit; DispatchMessage(Msg); end; // Process progress bar messages - This includes WM_TIMER and WM_PAINT used for progress bar animation while PeekMessage(Msg, Progress.Handle, 0, 0, PM_REMOVE) do begin if (Msg.message = WM_QUIT) then exit; DispatchMessage(Msg); end; // Process paint messages for all windows so UI can repaint itself while PeekMessage(Msg, 0, WM_PAINT, WM_PAINT, PM_REMOVE) or PeekMessage(Msg, 0, WM_ERASEBKGND, WM_ERASEBKGND, PM_REMOVE) or PeekMessage(Msg, Handle, DXM_SKINS_POSTREDRAW, DXM_SKINS_POSTREDRAW, PM_REMOVE) or PeekMessage(Msg, 0, WM_PRINT, WM_PRINT, PM_REMOVE) or PeekMessage(Msg, 0, WM_PRINTCLIENT, WM_PRINTCLIENT, PM_REMOVE) do begin if (Msg.message = WM_QUIT) then exit; DispatchMessage(Msg); end; PeekMessage(Msg, 0, WM_NULL, WM_NULL, PM_NOREMOVE); // Avoid window ghosting due to unresponsiveness on Vista+ finally if (Msg.message = WM_QUIT) then begin PostQuitMessage(Msg.wParam); FProgressAborted := True; end; end; finally FLastMessagePump.Start; end; if (FProgressAborted) then AbortProgress; end; Here's another version of it: https://bitbucket.org/anders_melander/better-translation-manager/src/f96e7dcdba22667560178d32aebb5137484107f0/Source/amProgress.pas#lines-444
  15. Anders Melander

    ANN: Better Translation Manager released

    Ah, I see. No, that isn't really supported, but I can see how that would be beneficial. I've created an issue for it: https://bitbucket.org/anders_melander/better-translation-manager/issues/39 Assuming you are using BTMs resource module load functions (amLocalization.Utils unit), you will have to modify the following function in the amLanguageInfo unit: function LoadNewResourceModule(LocaleItem: TLanguageItem; var ModuleFilename: string): HModule; overload; For example, change this: ModuleFilename := TPath.ChangeExtension(Filename, '.'+FileType); ...to this (note that this hard codes the sub-folder name): var Folder := TPath.Combine(TPath.GetDirectoryName(Filename), '.\lang'); ModuleFilename := TPath.Combine(Folder, TPath.GetFileNameWithoutExtension(Filename)+'.'+FileType); If you are using the RTLs built-in loading mechanism then there isn't any way I can think of.
  16. Anders Melander

    Drawing text with GDI

    https://github.com/graphics32/graphics32/tree/master/Examples/Drawing/TextVPR
  17. Anders Melander

    bitmap is not displayed

    function WindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; begin case Msg of WM_DESTROY: begin PostQuitMessage(0); Exit(0); end; else Result := DefWindowProc(hWnd, Msg, wParam, lParam); end; end; procedure RunClient; begin UpdateWindow(LHWND); while (integer(GetMessage(LMsg, LHWND, 0, 0)) > 0) do begin if (LMsg.message = WM_QUIT) then begin PostQuitMessage(0); break; end; TranslateMessage(LMsg); DispatchMessage(LMsg); end; UnregisterClass(PChar(GClassName), LInst); end; The main problem was that GetMessage is declared to return a BOOL but it actually returns an integer. Specifically, it returns -1 when the message queue has been destroyed.
  18. This should of course not have been an abstract class. I have just committed an implementation of the class to the main branch: https://github.com/graphics32/graphics32/commit/aec3713e187d1300b111f0315380a38b50033fef
  19. Anders Melander

    bitmap is not displayed

    The Halt should probably be an Exit. Otherwise, there isn't much point in the PostQuitMessage.
  20. Anders Melander

    bitmap is not displayed

    It isn't a valid Windows GUI application at all so what do you expect us to say? Compare your code to @KodeZwerg's code. Can you spot the difference? Why do you want to do it this way in the first place?
  21. Anders Melander

    ANN: Better Translation Manager released

    If you mean the resource modules, then sure: amResourceModuleBuilder -b -n:2 -s:.\Test\Test.exe -o:.\Test\lang [...] See: Command Line Interface
  22. Anders Melander

    ANN: Better Translation Manager released

    https://bitbucket.org/anders_melander/better-translation-manager/issues/38
  23. Anders Melander

    ANN: Better Translation Manager released

    This is really a problem in your workflow, not a deficiency of the tool. In a company of your size, with an off-the-shelf product, you really should be using a build server to produce the deliverables and not relying on individual developers to produce the files. The language modules will never get out-of-sync or contain the wrong texts if they are produced against the most recent exe file. It doesn't matter if the xlat file (the translation project) was updated or not. Translations might be missing (i.e the source texts are not translated), but they will never be mismatched. Mismatched texts mean that you are using a language module that was produced for another version of the exe. If your exe contains a version resource, and you remember to bump the version with each new build, then the resource module loader will validate the language module version against the exe version on load. I would recommend you use a workflow similar to this: As I said, I can add a switch to have the command line tool save the project file but I can't see that it would have made a difference in your case.
  24. Anders Melander

    bitmap is not displayed

    Uh. This looks like a "classic" Windows application. While it definitely is possible to create a Windows GUI application that doesn't use the RTL or VCL it isn't really something that is done anymore. If really you want to write a Windows application that way there are a few things missing from your code. I suggest you start by reading the following: https://learn.microsoft.com/en-us/windows/win32/learnwin32/your-first-windows-program If all you want is an application that displays a form with a bitmap on it, here's one that does that: program NoVCL; {$APPTYPE GUI} uses Forms, ExtCtrls; var Form: TForm; Image: TImage; begin Form := TForm.CreateNew(nil); try Image := TImage.Create(Form); Image.Parent := Form; Image.AutoSize := True; Image.Picture.Bitmap.LoadFromFile('bitmap.bmp'); Form.ShowModal; finally Form.Free; end; end.
  25. Anders Melander

    Drawing text with GDI

    In addition to the pixel grid (or rounding as Peter labeled it) there are other reasons why you can't expect a linear relationship between font and text size; The TrueType font rasterizer takes stuff like hinting and kerning into account when deciding how to rasterize a character and place it relative to the other characters. You should probably look into that if you are doing WYSIWYG. Here's some light reading to get you started: https://www.joelonsoftware.com/2007/06/12/font-smoothing-anti-aliasing-and-sub-pixel-rendering/ https://blog.codinghorror.com/font-rendering-respecting-the-pixel-grid/ https://blog.codinghorror.com/whats-wrong-with-apples-font-rendering/ https://agg.sourceforge.net/antigrain.com/research/font_rasterization/index.html https://freetype.org/freetype2/docs/hinting/text-rendering-general.html https://en.wikipedia.org/wiki/Font_rasterization The performance mostly depends on how smart you are about it (cache what you can) and what method you use to draw the text. If you just use the GDI the performance penalty should be negligible as you will just be doing the same thing as the GDI does internally. If you want better performance and quality you can use one of the usual graphics libraries; They have the functionality to rasterize and render text.
×