Jump to content

Remy Lebeau

Members
  • Content Count

    2343
  • Joined

  • Last visited

  • Days Won

    95

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Component issues when installing sasl-oauth

    What are the actual error messages? Changes are, those components are linked to the version of Indy that is pre-installed with the IDE, and will need to be recompiled if you install a different version of Indy, as a few method signatures did change to accommodate the OAuth SASL. That is odd, not sure what to make of that.
  2. Have you considered using TCP/UDP sockets? Indy ships with Delphi and runs on both Windows and Android.
  3. Remy Lebeau

    Type within a class

    This is documented in Embarcadero's DocWiki: Nested Type Declarations
  4. Extended RTTI via the System.Rtti unit was introduced in Delphi 2010. Why filter by visibility rather than by writability? if (LRttiProperty <> nil) and LRttiProperty.IsWritable then
  5. I tend to stay away from those functions since they use Variant, which is usually unnecessary overhead. But it depends on your particular needs. Integral properties are handled by GetOrdProp() and SetOrdProp() (as in, Ordinal)
  6. You can't take a pointer to a property, like you are trying to do. You should look into using RTTI instead. Someone else was asking a similar question on StackOverflow just yesterday: https://stackoverflow.com/questions/76729720/how-do-i-get-a-pointer-to-a-property-in-delphi
  7. They are. But Delphi automatically handles reference counting for interfaces, whereas C++ does not, hence the use of _di_... smart pointers on the C++ side to mimic the Delphi behavior.
  8. Has anyone else noticed that lately, when quoting a message in a reply, then placing the cursor inside the quote and pressing Enter a few times, no longer splits up the quote into 2 quotes with editable whitespace between them? It used to do this, and it was a very handy feature, but lately it hasn't been working for me, and it makes replying to quotes much more difficult. Now I have to do a lot of copy/pasting and dragging around of quotes to get the same result that inserting Enters used to accomplish.
  9. Remy Lebeau

    Splitting up quotes doesn't work anymore

    I was not aware of that option. Or maybe I was but wasn't using it. I just now tried it and it works, so I guess I'll start using it 😉
  10. And rename the new project, of course 😁
  11. Remy Lebeau

    How do I upgrade an old 2007 project ?

    I think you meant SetWindowLongPtr() instead.
  12. It's been a long time since I tried this, but it used to be that if you simply didn't have any project loaded in the IDE and then edited the Project Options, they would save as defaults. Not sure if that is still the case nowadays. Another approach would be to create Option Sets that contain your desired settings, and then you can apply those Sets to new projects as needed. https://docwiki.embarcadero.com/RADStudio/en/Option_Sets_Overview
  13. Remy Lebeau

    Splitting up quotes doesn't work anymore

    Didn't used to be 😞 Used to be able to just quote an entire message and then break it up as I address each part of it. I can still accomplish that, but it's several more steps now, so more tedious, but I'm getting the hang of it. Still has some ugly quirks, though. I suppose so. Other forums have that option. Would like the original functionality back, though. But this added as a new separate feature could be useful, too. Depends on how big the selections are. Quoting a few sentences, sure. Quoting paragraphs/code snippets, maybe not so much, if you have to scroll to make the selection.
  14. Remy Lebeau

    TFruit class moved to component

    Yes, certainly. And very much as you described it. You have a base component class named TSQLBuilderProvider, which has virtual/abstract methods as needed. Then you derive several DB-specific classes from it, which implement those methods. Each class can have its own DB-specific properties as needed. Then you have a TSQLBuilder component, which has a published property of type TSQLBuilderProvider. Now, you can drop a TSQLBuilder component and a DB-specific TSQLBuilderProvider component onto your Form, and assign the Provider to the Builder. And then the Builder can use the abstract/virtual methods to interact with the Provider. For that, it would make sense to expose additional properties on each DB-specific Provider component. TSQLBuilderProviderFD could have published properties to assign TFDConnection and TFDQuery components to. TSQLBuilderProviderUni could have published properties to assign TUniConnection and TUniQuery components to. And so on. Then the implemented methods can use those properties as needed. Yes. That is basically what I described above. That is why they should be exposed by the individual Provider components, not by the Builder component.
  15. Next time, read the documentation, and also just look at the declarations (ie, the DocumentElement property returns an _di_IXMLNode, not an IXMLNode*). Typically no, although you can do so manually if you want to, ie, to free the array's memory when you want to reuse the variable for a new array. Yes. The array's memory is freed when the object goes out of scope, or is reassigned. Just any any other good RAII-style class does. Yes No
  16. Remy Lebeau

    Wow64EnableWow64FsRedirection

    Per the documentation, Wow64EnableWow64FsRedirection() is deprecated, you should be using Wow64DisableWow64FsRedirection() and Wow64RevertWow64FsRedirection() instead.
  17. Did you read the documentation? https://docwiki.embarcadero.com/Libraries/en/Xml.XMLDoc.TXMLDocument You are creating the TXMLDocument without an Owner, so it behaves like an interfaced object, but you are not treating it as an interfaced object. You need to change your pMEGConfig variable from being a TXMLDocument* raw pointer to being a _di_IXMLDocument smart pointer that manages the interface's reference count. You need to do the same for your IXMLNode* raw pointers, too. Use _di_IXMLNode instead. Also, because they are reference counted objects, make sure they go out of scope (or are explicitly Release() 'ed) before you call CoUninitialize(). Also, do not 'new' an Exception that you are going to 'throw'. Throw it by value rather than by pointer. This is the one and only place where Delphi-style classes (ie, classes derived from TObject) are allowed to be created without 'new' in C++Builder. Try this: if (FAILED(CoInitializeEx(0, COINIT_SPEED_OVER_MEMORY))) { return; } try { TDOMVendor *vendor = DOMVendors->Find(_D("MSXML")); if (vendor == NULL) { throw Exception(_D("vendor MSXML XML wasn't found'")); } _di_IXMLDocument pMEGConfig = new TXMLDocument(NULL); // or: _di_IXMLDocument pMEGConfig = NewXMLDocument(); pMEGConfig->DOMVendor = vendor; pMEGConfig->LoadFromFile(_D("An XML FILE")); pMEGConfig->Active = true; _di_IXMLNode ROOT = pMEGConfig->DocumentElement; ... } __finally { CoUninitialize(); } Alternatively: if (FAILED(CoInitializeEx(0, COINIT_SPEED_OVER_MEMORY))) { return; } try { DefaultDOMVendor = _D("MSXML"); _di_IXMLDocument pMEGConfig = LoadXMLDocument(_D("An XML FILE")); _di_IXMLNode ROOT = pMEGConfig->DocumentElement; ... } __finally { CoUninitialize(); }
  18. Remy Lebeau

    custom TTreeView

    You are storing custom allocated data in the TTreeNode.Data property. To free that memory, you can override the TreeView's virtual Delete() method, which is called for every TTreeNode object that gets destroyed at runtime, eg: type TmyDBTreeView = class(TTreeView) protected procedure Delete(Node: TTreeNode); override; end; procedure TmyDBTreeView.Delete(Node: TTreeNode); begin inherited Delete(Node); // fires the OnDeletion event handler Dispose(PNodeRec(Node.Data)); Node.Data := nil; end; However, a better way to handle this is to not use the TTreeNode.Data property at all. Leave that untouched so users of your component can use it for their own application data. Instead, you should create a custom descendant of TTreeNode to hold your component's extra data, and then you can override the TreeView's virtual CreateNode() method to return new instances of your custom Node class, eg: type TmyDBTreeNode = class(TTreeNode) public IdNode: integer; IdParent : integer; end; TmyDBTreeView = class(TTreeView) protected function CreateNode: TTreeNode; override; end; function TmyDBTreeView.CreateNode: TTreeNode; begin Result := TmyDBTreeNode.Create(Items); end; Then, any place that you were accessing PNodeRec(Node.Data)^ before, you can instead access TmyDBTreeNode(Node), for example: // if DataSource.DataSet.FieldByName(IDParentNode).AsInteger = PNodeRec(Node.Data)^.IdNode then if DataSource.DataSet.FieldByName(IDParentNode).AsInteger = TmyDBTreeNode(Node).IdNode then function TmyDBTreeView.GetNodeIdParent(Node: TTreeNode): integer; begin //Result := PNodeRec(Node)^.IdParent; Result := TmyDBTreeNode(Node).IdParent; end; function TmyDBTreeView.GetNodeId(Node: TTreeNode): integer; begin //Result := PNodeRec(Node)^.IdNode; Result := TmyDBTreeNode(Node).IdNode; end; And when adding new nodes to the TreeView, you don't need to use Add...Object() methods anymore, you can just use the normal Add...() methods instead, eg: //New(NRec); //NRec^.IdNode := DataSource.DataSet.FieldByName(IDNode).AsInteger; //NRec^.IdParent := DataSource.DataSet.FieldByName(IDParentNode).AsInteger; //Node := Items.AddObject(nil, DataSource.DataSet.FieldByName(DataField).AsString, NRec); Node := Items.Add(nil, DataSource.DataSet.FieldByName(DataField).AsString); TmyDBTreeNode(Node).IdNode := DataSource.DataSet.FieldByName(IDNode).AsInteger; TmyDBTreeNode(Node).IdParent := DataSource.DataSet.FieldByName(IDParentNode).AsInteger; // New(DNode); // DNode^.IdNode := DataSource.DataSet.FieldByName(FIDNode).AsInteger; // DNode^.IdParent := DataSource.DataSet.FieldByName(FIDParentNode).AsInteger; // NewNode := Items.AddChildObject(Node, Field.AsString, DNode); NewNode := Items.AddChild(Node, Field.AsString); TmyDBTreeNode(NewNode).IdNode := DataSource.DataSet.FieldByName(FIDNode).AsInteger; TmyDBTreeNode(NewNode).IdParent := DataSource.DataSet.FieldByName(FIDParentNode).AsInteger; --- That being said, I notice a number of other issues with your code, that are unrelated to node management. 1. Why do you have a Sleep() loop inside of your TmyDBTreeView destructor? Not only is a loop redundant when you could simply calculate the wanted duration in a single call, but calling Sleep() during destruction really doesn't belong there to begin with. 2. Any use of Items.(Begin|End)Update() should be protected with a try..finally block. 3. This code is wrong: procedure TmyDBTreeView.CMGetDataLink(var Message: TMessage); begin Message.Result := Integer(FDataLink); end; If you ever try to use your component in 64bit, this will fail as it will truncate the returned pointer. The Message.Result is a NativeInt, not an Integer, so cast accordingly. 4. Inside of TmyDBTreeView.SetDataSource(), since you are calling FreeNotification() on a new DataSource, you should call RemoveFreeNotification() on any DataSource that was previously assigned. Although, I think this is completely redundant since you are not actually storing a reference to the DataSource, you are just assigning it to your DataLink, so you could just remove use of FreeNotification() altogether and let the DataLink handle that for you. Also, SetDataSource() is assigning the new DataSource to your DataLink only if csLoading is enabled, which means it is being assigned only during DFM streaming. But if a user of your component decides to change the DataSource at runtime after the DFM is loaded, you are not updating your DataLink. 5. in TmyDBTreeView.DeleteNodeFromTreeOnly(), you are looping incorrectly. Since you are looping forwards, if a node is deleted then your loop counter will get out of sync and end up skipping the next node. To account for that, you need to either: a. loop backwards instead of forwards, eg: procedure TmyDBTreeView.DeleteNodeFromTreeOnly(id_node: integer); var i: integer; begin for i := Items.Count - 1 downto 0 do begin //if PNodeRec(Items[i])^.IdNode = id_node then Items[i].Delete; if TmyDBTreeNode(Items[i]).IdNode = id_node then Items[i].Delete; end; end; b. change the loop so your 'i' variable is incremented only when a node is NOT deleted, eg: procedure TmyDBTreeView.DeleteNodeFromTreeOnly(id_node: integer); var i, cnt: integer; begin i := 0; cnt := Items.Count; while i < cnt do begin //if PNodeRec(Items[i])^.IdNode = id_node then Items[i].Delete then if TmyDBTreeNode(Items[i]).IdNode = id_node then Items[i].Delete then Dec(cnt) else Inc(i); end; end;
  19. Remy Lebeau

    GetCryptLibHandle Issue on ios

    I'm not familiar with those libraries. In fact, I've never even heard of them before. Indy uses static linking on iOS devices. It uses dynamic loading on every other platform, including iOS Simulator. https://docwiki.embarcadero.com/RADStudio/en/OpenSSL At the time when iOS support was first added to Indy, Apple supported only static libraries on iOS. I think that has changed in recent years, but Indy has not been updated to support that yet. Contact the author of those other libraries and find out why they are trying to access an Indy function that doesn't exist on iOS devices.
  20. Remy Lebeau

    Passive, non interactive custom form

    Override the Form's CreateParams() method to enable the WS_EX_NOACTIVATE extended style: procedure CreateParams(var Params: TCreateParams); override; ... procedure TMyForm.CreateParams(var Params: TCreateParams); begin inherited; Params.WindowClass.ExStyle := Params.WindowClass.ExStyle or WS_EX_NOACTIVATE; end;
  21. Remy Lebeau

    Placing Child Behind Parent

    Yes, but that is not a big deal at all.
  22. Remy Lebeau

    Placing Child Behind Parent

    OK, so then simply make the TRectangle be the Owner of the TImage, but make the TRectangle and TImage be siblings of the same parent, instead of making the TRectangle be the parent of the TImage. This way, the TImage can go behind the TRectangle, and you can move the TImage whenever the TRectangle is moved and vice versa, unless the Ctrl key is down. This is exactly how VCL controls like TLabeledEdit are implemented, for example.
  23. Remy Lebeau

    Placing Child Behind Parent

    Of course not. VCL does not support that either. Why would you ever want a child to go behind its parent?
  24. Remy Lebeau

    Cecking Application Execution

    That code can't possibly work as you propose. For one thing, you can't determine parent/child relationships using EnumProcesses(). It only gives you a flat array of all the running process IDs at that moment, there is no guarantee as to their order in the array. Second, you are simply looking for the caller's passed-in PID in the array, and when found then you are opening a HANDLE to that process and querying its PID - the same PID that you already have! GetProcessId() does not give you a parent PID. The only APIs I am aware of that can provide you with a parent PID are: CreateToolhelp32Snapshot() + Process32(First|Next)() NtQueryInformationProcess() So, for example, using Toolhelp32: function GetParentProcessID(const AProcessID: DWORD): DWORD; var hSnapshot: THandle; pe: PROCESSENTRY32; begin Result := 0; hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if hSnapshot <> INVALID_HANDLE_VALUE then try pe.dwSize := SizeOf(dwSize); if Process32First(hSnapshot, pe) then repeat if pe.th32ProcessID = AProcessID then begin Result := pe.th32ParentProcessID; Exit; end; until not Process32Next(hSnapshot, pe); finally CloseHandle(hSnapshot); end; end; Or, using NTDLL: function GetParentProcessID(const AProcessID: DWORD): DWORD; var hProcess: THandle; pbi: PROCESS_BASIC_INFORMATION; begin Result := 0; hProcess := OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, AProcessID); if hProcess <> 0 then try if NtQueryInformationProcess(hProcess, ProcessBasicInformation, @pbi, SizeOf(pbi), nil) = STATUS_SUCCESS then Result := DWORD(pbi.InheritedFromUniqueProcessId); finally CloseHandle(hProcess); end; end;
  25. Remy Lebeau

    Cecking Application Execution

    It is also possible to check if your process is being executed by a .lnk shortcut, by using the GetStartupInfo() API and checking the STARTUPINFO.dwFlags field for the STARTF_TITLEISLINKNAME flag.
×