Jump to content

Anders Melander

Members
  • Content Count

    2561
  • Joined

  • Last visited

  • Days Won

    133

Everything posted by Anders Melander

  1. Anders Melander

    Applying hue change to font graphic on the fly

    Please tag your post "Graphics32" or otherwise mention that's what you're using. My first guess would be that you forgot to set the ALpha pixel value in ApplyColorShift, but then I realized that you aren't drawing the ScrollerText bitmap anywhere... So, is it correct that you aren't getting any text even without the call to ApplyColorShift?
  2. Always? I haven't done my own benchmarking but I thought arrays also outperformed linked lists for the simple search+insert/delete case: https://kjellkod.wordpress.com/2012/08/08/java-galore-linkedlist-vs-arraylist-vs-dynamicintarray/ (yes, I know it's about Java. potato, potato...) I mostly use linked lists for stuff like MRU/LRU caches, and only because I haven't bothered learning a more suitable structure (for caches, that is).
  3. Anders Melander

    Displaying an independent background in a Delphi app

    If I understand you correctly then you could just set ScreenImg.Bitmap.BitmapAlign=baTile and then display the other stuff on top of that with a TBitmapLayer.
  4. Anders Melander

    Displaying an independent background in a Delphi app

    It looks like you're using a Graphics32 TImage32 control to display the bitmap. You should have stated that as otherwise your code makes no sense. I don't really understand what it is you are trying to do. Would you mind posting a picture/mockup of your desired output and another of the experienced output?
  5. Anders Melander

    Parallel Resampling of (VCL-) Bitmaps

    Note that in Graphics32 I had to revert the change of the Box filter radius from 0.5 back to the original radius of 1. See: https://github.com/graphics32/graphics32/issues/209 Since you're using a radius of 0.5 you might have the same issue.
  6. Object references are pointers. That was the whole point and the failure to understand that was the cause of the problem. Apart from that, I agree that most people probably rarely, if ever, use them directly. Sure but it's an important data structure to know. Would you hire a developer who couldn't create a linked list? I know I wouldn't. That really depends on what you do and how you do it. Common? Yes. Most common? Not a chance. I can't remember when I last used a TStringList and I do a lot of string processing and UI stuff. I certainly wouldn't use it to handle CSV files. There's far too much of the format it can't handle properly. While I agree with this, it's not advice I would give a beginner. There are more important things to learn in the beginning and that one will just get in the way of that.
  7. As long as your employer prefers to have you produce the easiest (for you) solution in the shortest amount of time, I guess that's one way to achieve that goal... Sure, for hobby development this is fine - no problem. But for professional development, that approach is simply lazy. Personally, I would have "done my own research", weighed the available options, and decided which best suited the solution I was looking for.
  8. I would really suggest that you postpone messing with pointers until you have a better understanding of the language. Use an array or a TList instead; They perform better. Also, objects aren't really suited for double-linked lists as you will need a full (but empty) object for the head node. That said, the problem appears to be that you aren't aware that an object reference is itself a pointer and that local variables are temporary and allocated on the stack. Thus... Tmp^.Next := @Node ...assigns the address of the Node pointer (and not the address of the Node object) to the Next field. The correct statement would look more like this: Tmp.Next := Node; Notice that the dereference operator (^) is optional so I didn't use it. I think it often just makes the code harder to read.
  9. Anders Melander

    Does C++ Builder have an input - message box?

    ...and InputBox and InputQuery
  10. Yes. You write an event handler and assign it to the event: procedure TMyForm.LayerConstrainHandler(Sender: TObject; const OldLocation: TFloatRect; var NewLocation: TFloatRect; DragState: TRBDragState; Shift: TShiftState); begin // Snap to nearest 10 pixels if [Shift] is down. // Note that the roQuantized layer option provides similar functionality. if (ssShift in Shift) then begin NewLocation.Left := Trunc((NewLocation.Left + 5) / 10) * 10; NewLocation.Top := Trunc((NewLocation.Top + 5) / 10) * 10; NewLocation.Right := Trunc((NewLocation.Right + 5) / 10) * 10; NewLocation.Bottom := Trunc((NewLocation.Bottom + 5) / 10) * 10; end; end; ... var Layer := TRubberbandLayer.Create(MyImage.Layers); Layer.OnConstrain := LayerConstrainHandler; Layer.Options := Layer.Options + [roConstrained]; ... The code I posted isn't runnable as-is. It's just an example from the top of my head. Replace TSomeType with whatever type you use to identify your object. Typo on my part. The Notify method should take a TObjectLayernotification as a parameter. It's just an example of how to draw a marching ants selection in a layer but I suggest you postpone that until you have a better grasp of the fundamentals and have gotten the other stuff working. I just remembered that it is it's actually possible to animate TRubberBandLayer via the FrameStippleCounter property. For example, if you modify the ImgView_Layers example and place a 100 mS TTimer on the mainform with this code: procedure TMainForm.Timer1Timer(Sender: TObject); const StippleSize = 4; // The size of the default stipple pattern begin if (RBLayer = nil) or (not RBLayer.Visible) then exit; var NewStippleCounter := RBLayer.FrameStippleCounter+0.5; // Handle overflow if (NewStippleCounter >= StippleSize) then NewStippleCounter := NewStippleCounter - StippleSize; RBLayer.FrameStippleCounter := NewStippleCounter; RBLayer.Changed; end; That really depends on your use case. If you just need a bitmap with the final image, then you can use the PaintTo method to draw the flattened image on another bitmap. If you want to save your image as objects then you're on your own; You'll have to write code to save the image state, including layers, to some custom file format. Sure. Create a github (or whatever you prefer) repository for it.
  11. Anders Melander

    Anyone know why?

    Never mind. I see that it's an Indian company...
  12. Anders Melander

    Anyone know why?

    They do seem to be looking for a pretty experienced developer but does that imply grey beard? And how do you conclude that the salary is minimal?
  13. Anders Melander

    Why do I have this??

    You need to show us a bit more of your code. How and when is the form created, how and when is it destroyed, where is Destroying set and read, etc., etc.?
  14. Yes, that was the initial observation but if you reread the thread you will see that the original problem was that for some file types, it didn't work. Probably those that already have stuff registered under SystemFileAssociations. FWIW I think this is a bug in Windows. Regardless, unless one has to support XP, SystemFileAssociations should be used since it's the only way to safely extend file types that you don't "own".
  15. Yes, that's my conclusion too. I'm on Win7, 10 and 11 here. This is the relevant code I use for registration: TPropertySheetHandlerFactory class 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 = 'pasfile'; sFileExtension = '.pas'; sClassName = 'DelphiPropSheetShellExt'; resourcestring // Description of our shell extension. sDescription = 'Drag and Drop Component Suite property sheet demo'; ... //////////////////////////////////////////////////////////////////////////////// type TPropertySheetHandlerFactory = class(TShellExtFactory) protected function HandlerRegSubKey: string; override; end; function TPropertySheetHandlerFactory.HandlerRegSubKey: string; begin Result := 'PropertySheetHandlers'; end; //////////////////////////////////////////////////////////////////////////////// initialization TPropertySheetHandlerFactory.Create(ComServer, TDataModulePropertySheetHandler, CLSID_PropertySheetHandler, sClassName, sDescription, sFileClass, sFileExtension, ciMultiInstance); end. TShellExtFactory class //////////////////////////////////////////////////////////////////////////////// // // TShellExtFactory // //////////////////////////////////////////////////////////////////////////////// // Class factory for component based COM classes. // Specialized for Shell Extensions. //////////////////////////////////////////////////////////////////////////////// type TShellExtFactory = class(TVCLComObjectFactory) private FFileExtension: string; FFileClass: string; protected function GetProgID: string; override; function HandlerRegSubKey: string; virtual; abstract; function UseSystemFileAssociations: boolean; virtual; function OwnsFileExtension: boolean; virtual; public constructor Create(ComServer: TComServerObject; ComponentClass: TComponentClass; const ClassID: TGUID; const ClassName, Description, AFileClass, AFileExtension: string; Instancing: TClassInstancing); procedure UpdateRegistry(ARegister: Boolean); override; property FileClass: string read FFileClass write FFileClass; property FileExtension: string read FFileExtension write FFileExtension; end; ..... //////////////////////////////////////////////////////////////////////////////// // // TShellExtFactory // //////////////////////////////////////////////////////////////////////////////// constructor TShellExtFactory.Create(ComServer: TComServerObject; ComponentClass: TComponentClass; const ClassID: TGUID; const ClassName, Description, AFileClass, AFileExtension: string; Instancing: TClassInstancing); begin inherited Create(ComServer, ComponentClass, ClassID, ClassName, Description, Instancing); FFileClass := AFileClass; FFileExtension := AFileExtension; end; function TShellExtFactory.GetProgID: string; begin Result := ''; end; function TShellExtFactory.OwnsFileExtension: boolean; begin // Return True if it's safe to delete the file association upon unregistration. // Be careful that we don't delete file associations used by other applications Result := False; end; procedure TShellExtFactory.UpdateRegistry(ARegister: Boolean); var RegPrefix: string; RootKey: HKEY; ClassIDStr: string; Registry: TRegistry; begin ComServer.GetRegRootAndPrefix(RootKey, RegPrefix); ClassIDStr := GUIDToString(ClassID); if ARegister then begin inherited UpdateRegistry(ARegister); if UseSystemFileAssociations and (FileExtension <> '') then begin CreateRegKey(RegPrefix+'SystemFileAssociations\'+FileExtension+'\shellex\'+HandlerRegSubKey+'\'+ClassName, '', ClassIDStr, RootKey); end else if (FileClass <> '') then begin if (FileExtension <> '') and (GetRegStringValue(RegPrefix+FileExtension, '', RootKey) = '') then CreateRegKey(RegPrefix+FileExtension, '', FileClass, RootKey); CreateRegKey(RegPrefix+FileClass+'\shellex\'+HandlerRegSubKey+'\'+ClassName, '', ClassIDStr, RootKey); end; if (Win32Platform = VER_PLATFORM_WIN32_NT) then begin Registry := TRegistry.Create; try if (ComServer.PerUserRegistration) then Registry.RootKey := HKEY_CURRENT_USER else Registry.RootKey := HKEY_LOCAL_MACHINE; if Registry.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', False) then Registry.WriteString(ClassIDStr, Description); finally Registry.Free; end; end; end else begin if (Win32Platform = VER_PLATFORM_WIN32_NT) then begin Registry := TRegistry.Create; try if (ComServer.PerUserRegistration) then Registry.RootKey := HKEY_CURRENT_USER else Registry.RootKey := HKEY_LOCAL_MACHINE; if Registry.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', False) then Registry.DeleteKey(ClassIDStr); finally Registry.Free; end; end; if UseSystemFileAssociations and (FileExtension <> '') then begin DeleteDefaultRegValue(RegPrefix+'SystemFileAssociations\'+FileExtension+'\shellex\'+HandlerRegSubKey+'\'+ClassName, RootKey); DeleteEmptyRegKey(RegPrefix+'SystemFileAssociations\'+FileExtension+'\shellex\'+HandlerRegSubKey+'\'+ClassName, True, RootKey); end else if (FileClass <> '') then begin if (FileExtension <> '') and (OwnsFileExtension) and (GetRegStringValue(RegPrefix+FileExtension, '', RootKey) = FileClass) then DeleteDefaultRegValue(RegPrefix + FileExtension, RootKey); DeleteDefaultRegValue(RegPrefix+FileClass+'\shellex\'+HandlerRegSubKey+'\'+ClassName, RootKey); DeleteEmptyRegKey(RegPrefix+FileClass+'\shellex\'+HandlerRegSubKey+'\'+ClassName, True, RootKey); end; inherited UpdateRegistry(ARegister); end; SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil); end; function TShellExtFactory.UseSystemFileAssociations: boolean; begin Result := True; end; TVCLComObjectFactory class //////////////////////////////////////////////////////////////////////////////// // // TVCLComObjectFactory // //////////////////////////////////////////////////////////////////////////////// // Class factory for component based COM classes. // Does not require a type library. // Based on TComponentFactory and TComObjectFactory. //////////////////////////////////////////////////////////////////////////////// type TVCLComObjectFactory = class(TComObjectFactory, IClassFactory) private protected function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID; out Obj): HResult; stdcall; public constructor Create(ComServer: TComServerObject; ComponentClass: TComponentClass; const ClassID: TGUID; const ClassName, Description: string; Instancing: TClassInstancing); function CreateComObject(const Controller: IUnknown): TComObject; override; procedure UpdateRegistry(Register: Boolean); override; end; ... //////////////////////////////////////////////////////////////////////////////// // // TVCLComObjectFactory // //////////////////////////////////////////////////////////////////////////////// constructor TVCLComObjectFactory.Create(ComServer: TComServerObject; ComponentClass: TComponentClass; const ClassID: TGUID; const ClassName, Description: string; Instancing: TClassInstancing); begin inherited Create(ComServer, TComClass(ComponentClass), ClassID, ClassName, Description, Instancing, tmApartment); end; function TVCLComObjectFactory.CreateComObject(const Controller: IUnknown): TComObject; begin Result := TVCLComObject.CreateFromFactory(Self, Controller); end; function TVCLComObjectFactory.CreateInstance(const UnkOuter: IUnknown; const IID: TGUID; out Obj): HResult; begin if not IsLibrary then begin LockServer(True); try with TApartmentThread.Create(Self, UnkOuter, IID) do begin if WaitForSingleObject(Semaphore, INFINITE) = WAIT_OBJECT_0 then begin Result := CreateResult; if Result <> S_OK then Exit; Result := CoGetInterfaceAndReleaseStream(IStream(ObjStream), IID, Obj); end else Result := E_FAIL end; finally LockServer(False); end; end else Result := inherited CreateInstance(UnkOuter, IID, Obj); end; type TComponentProtectedAccess = class(TComponent); TComponentProtectedAccessClass = class of TComponentProtectedAccess; procedure TVCLComObjectFactory.UpdateRegistry(Register: Boolean); begin if Register then inherited UpdateRegistry(Register); TComponentProtectedAccessClass(ComClass).UpdateRegistry(Register, GUIDToString(ClassID), ProgID); if not Register then inherited UpdateRegistry(Register); end;
  16. 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.
  17. Anders Melander

    Anyone know why?

    Yes, now they all work from home... at other companies.
  18. 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.
  19. 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.
  20. 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.
  21. 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.
  22. Anders Melander

    Choosing a Mac

  23. 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. ↩
  24. 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.
×