-
Content Count
2343 -
Joined
-
Last visited
-
Days Won
95
Everything posted by Remy Lebeau
-
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.
-
Is it possible to send text from android to a windows laptop and back over wifi?
Remy Lebeau replied to JohnLM's topic in Cross-platform
Have you considered using TCP/UDP sockets? Indy ships with Delphi and runs on both Windows and Android. -
Type within a class
Remy Lebeau replied to AndrewHoward's topic in Algorithms, Data Structures and Class Design
This is documented in Embarcadero's DocWiki: Nested Type Declarations -
Overwrite wincontrol property only if needed
Remy Lebeau replied to PizzaProgram's topic in Algorithms, Data Structures and Class Design
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 -
Overwrite wincontrol property only if needed
Remy Lebeau replied to PizzaProgram's topic in Algorithms, Data Structures and Class Design
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) -
Overwrite wincontrol property only if needed
Remy Lebeau replied to PizzaProgram's topic in Algorithms, Data Structures and Class Design
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 -
I'm trying to call Application->Messagebox in a C++ Builder BPL
Remy Lebeau replied to JackT's topic in General Help
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 replies
-
- c++ builder bpl vcl
- delphi
-
(and 1 more)
Tagged with:
-
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.
-
Splitting up quotes doesn't work anymore
Remy Lebeau replied to Remy Lebeau's topic in Community Management
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 😉 -
Is there way to change the default options for a new project?
Remy Lebeau replied to softtouch's topic in Delphi IDE and APIs
And rename the new project, of course 😁 -
I think you meant SetWindowLongPtr() instead.
-
Is there way to change the default options for a new project?
Remy Lebeau replied to softtouch's topic in Delphi IDE and APIs
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 -
Splitting up quotes doesn't work anymore
Remy Lebeau replied to Remy Lebeau's topic in Community Management
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. -
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.
-
I'm trying to call Application->Messagebox in a C++ Builder BPL
Remy Lebeau replied to JackT's topic in General Help
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- 8 replies
-
- c++ builder bpl vcl
- delphi
-
(and 1 more)
Tagged with:
-
Per the documentation, Wow64EnableWow64FsRedirection() is deprecated, you should be using Wow64DisableWow64FsRedirection() and Wow64RevertWow64FsRedirection() instead.
-
I'm trying to call Application->Messagebox in a C++ Builder BPL
Remy Lebeau replied to JackT's topic in General Help
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(); }- 8 replies
-
- c++ builder bpl vcl
- delphi
-
(and 1 more)
Tagged with:
-
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;
-
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.
-
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;
-
Yes, but that is not a big deal at all.
-
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.
-
Of course not. VCL does not support that either. Why would you ever want a child to go behind its parent?
-
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;
-
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.