-
Content Count
2942 -
Joined
-
Last visited
-
Days Won
133
Posts posted by Remy Lebeau
-
-
12 hours ago, Fr0sT.Brutal said:x64 could require transition to SetWindowLong.
I think you meant SetWindowLongPtr() instead.
-
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
-
5 hours ago, Sherlock said:Quoting is indeed rather tedious.
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.
5 hours ago, Sherlock said:But a workaround for this might be the possibility to write an answer and select blocks from the original post and then click quote selection.
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.
5 hours ago, Sherlock said:Works pretty good, and will give you the result you wanted, without all the Copypasta.
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.
-
2 hours ago, WalkingAway said:But anyway, is it possible to achieve something like this (I mean totally in IDE, design time):
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.
2 hours ago, WalkingAway said:For every of them then can be setted up Query + Connection: for FD - TFDQuery and TFDConnection, for UniDAC - TUniConnection and TUniQuery and so one
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.
2 hours ago, WalkingAway said:Something similar to this - drop TSQLBuilder to the form and then there is some abstract property TSQLBuilderProvider, but if on form on f.e. TSQLBuilderProviderFD or TSQLBuilderProviderUniDac, it was possible to set it in design time.
Yes. That is basically what I described above.
2 hours ago, WalkingAway said:2) Second thing: property that makes sence for one TSQLBuilder, but not usable / posible for other.
That is why they should be exposed by the individual Provider components, not by the Builder component.
-
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.
-
11 hours ago, JackT said:I didn't realise I had to use smart pointers for interfaces
Next time, read the documentation, and also just look at the declarations (ie, the DocumentElement property returns an _di_IXMLNode, not an IXMLNode*).
11 hours ago, JackT said:should I be setting dynamic arrays to NULL to destroy them
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.
11 hours ago, JackT said:or does the C++ Compiler handle it
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.
11 hours ago, JackT said:For example will WSA get destroyed correctly when it goes out of scope
Yes
11 hours ago, JackT said:or do I need to do something else ?
No
-
Per the documentation, Wow64EnableWow64FsRedirection() is deprecated, you should be using Wow64DisableWow64FsRedirection() and Wow64RevertWow64FsRedirection() instead.
-
21 hours ago, JackT said:I am also having trouble with TXMLDocument and IXMLNODE. I can use TXMLDocument in a VCL builder application and Delphi without a problem but as soon as I try to use it in a C++ Builder command line program I get silent failures
Did you read the documentation?
https://docwiki.embarcadero.com/Libraries/en/Xml.XMLDoc.TXMLDocument
QuoteWhen TXMLDocument is created without an Owner, it behaves like an interfaced object. That is, when all references to its interface are released, the TXMLDocument instance is automatically freed. When TXMLDocument is created with an Owner, however, it behaves like any other component, and is freed by its Owner. When you add a TXMLDocument component from the component palette to a form or data module, it is automatically created with an Owner. When the TXMLDocument component is created using the global LoadXMLDocument function (or by a function that the XML Data Binding wizard generates to return the root node of the document), the function creates a TXMLDocument instance without an Owner.
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(); }
-
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;
-
1
-
-
13 hours ago, AlanScottAgain said:I'm trying to compile FB4D for ios. FB4D is dependent the JOSE library.
I'm not familiar with those libraries. In fact, I've never even heard of them before.
13 hours ago, AlanScottAgain said:On ios I get: [DCC Error] JOSE.OpenSSL.Headers.pas(168): E2003 Undeclared identifier: 'GetCryptLibHandle' and the relevant code is IFDEFed out due to - {$IFNDEF STATICLOAD_OPENSSL}
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.
13 hours ago, AlanScottAgain said:How can I get this to work on iOS, please.
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.
-
4 hours ago, CoMPi74 said:I am afraid such a form can be activated (gets focus) when clicked. I want to avoid such behaviour.
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;
-
9 minutes ago, Mike Warren said:That's what I'm going to have to do. It just means I'll have to manually move the TImage whenever the TRectangle is moved.
Yes, but that is not a big deal at all.
-
15 minutes ago, Mike Warren said:I have a component I create at runtime that's inherited from TRectangle. One of the additions is that I have a TImage to create an associated "icon" that sits next to the rectangle. These 2 items are normally moved together, but can be moved independently by holding the control key down. If the user decided to overlap these 2 components, then showing the complete rectangle is more desirable.
I can't make the parent the image, because that will sometimes be hidden, which would hide the rectangle, which must always be visible.
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.
-
1
-
-
12 minutes ago, Mike Warren said:Is it possible to make a child FMX component display behind the parent?
Of course not. VCL does not support that either. Why would you ever want a child to go behind its parent?
-
On 7/6/2023 at 1:08 PM, KodeZwerg said:Heres a faster way to get Parent ProcessID.
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;
-
1
-
37 minutes ago, timfrost said:With the Windows API you can find information about your own process from its process ID, and in this "process information" is the process ID of the parent process that started your process. From this you can find whether the name of that process is Explorer or EXE1 (or something else).
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.
-
9 hours ago, dummzeuch said:I am trying to convert an import lib file for a dll meant for Visual Studio (therefore in COFF format) to OMF format to be used in C++ Builder:
Why? For an import lib, you should instead simply create a new import lib file for C++Builder using the IMPLIB (32bit) or MKEXP (64bit) tool on the DLL itself. There is no point in converting import libs, only static libs.
-
3
-
-
9 hours ago, A.M. Hoornweg said:Isn't this what modern internet browser do, having separate child processes for tabs contained in a common host process ?
Yes, they do, but those child processes are specifically designed to interoperate and sync with their host process. That is not the case when you simply host someone else's UI inside of your own. That is when bad things can happen, because the child process doesn't know it's being hosted and so the parent and child don't sync their states together.
-
10 hours ago, timfrost said:You can get the command window handle by calling GetProcessWindow(pi.dwProcessID), where pi is the PROCESS_INFOMATION available after calling Create Process;
There is no GetProcessWindow() function in the Win32 API, since windows are not tied to processes, only to threads. So I'm assuming that function is implemented in your own code, yes? Probably a combination of EnumWindows() and GetWindowThreadProcessId(), though using EnumThreadWindows() with the pi.dwThreadID instead might make more sense.
10 hours ago, timfrost said:and then calling SetParent to set the parent of the command window as your target component.
That can be very dangerous if you are not careful.
Is it legal to have a cross-process parent/child or owner/owned window relationship?
-
The '%f' specifier in ...printf() style functions expects 'double' (or 'long double' if you use '%Lf'), not 'float'. There is no specifier that accepts 'float'. So, you will have to cast the values when passing them in, eg:
snprintf(buffer, sizeof(buffer), "%f", (double) *zMap); // or: double(*zMap), or static_cast<double>(*zMap)
Alternatively, you can format the 'float' to a C++ 'std::string' first, and then copy it into your buffer, eg:
std::ostringstream oss; oss << *zMap; strncpy(buffer, oss.str().c_str(), sizeof(buffer)-1);
Or:
strncpy(buffer, std::format("{}", *zMap).c_str(), sizeof(buffer)-1);
Of course, being a Delphi guy, you could just use the Delphi RTL in C++Builder to convert the 'float' to a 'System::String' first, and then copy it into your buffer, eg:
strncpy(buffer, AnsiString(FloatToStr(*zMap)).c_str(), sizeof(buffer)-1); // or simpler: strncpy(buffer, AnsiString(*zMap).c_str(), sizeof(buffer)-1);
Or, simply get rid of the buffer altogether and use the 'std::string'/'System::(Ansi)String' as-is instead.
That being said, your two examples are logically equivalent, so if the above approaches don't fix the issue, then make sure the pointer being returned by SomeFunctionThatReturnsABuffer() is actually valid to begin with (ie, that the memory buffer is not being freed before you copy the data, etc).
-
1
-
-
I question the design of the code shown. I would have opted to implement it more like this instead:
procedure AnalyzeException(const E: Exception); begin // ...Do something with the object ... end; procedure TestCase (ReRaiseException {or: SwallowException}: Boolean); var OneThousand, Zero: integer; d: double; begin Zero := 0; OneThousand := 1000; try d := OneThousand / Zero; except on E: Exception do begin AnalyzeException(E); if ReRaiseException {or: not SwallowException} then raise; end; end; end;
-
1 hour ago, FabDev said:With this code you can detect if Clipboard contains Text or a bitmap :
...
But how to detect if clipboard contains file ?
Unfortunately, you will not be able to do that with FMX's native interfaces, at least on Windows (I didn't look at other platforms). You will have to resort to using the Win32 API directly.
On Windows, FMX's default IFMXClipboardService implementation natively supports only the CF_UNICODETEXT and CF_DIB standard clipboard formats. For other clipboard formats, FMX does offer IFMXExtendedClipboardService. However, it only supports clipboard formats that are registered at runtime via its RegisterCustomFormat() method, which on Windows is based on the Win32 RegisterClipboardFormat() API. But files are stored on the Windows clipboard using pre-defined Shell clipboard formats that are not registered at runtime (ie, CF_HDROP, CF_FILENAME, CFSTR_FILEDESCRIPTOR+CFSTR_FILECONTENTS, CFSTR_SHELLIDLIST, etc), so they cannot be accessed with IFMXExtendedClipboardService at this time. I have just now opened a QualityPortal ticket to request that feature be added in a future Delphi release:
RSP-41923: Allow IFMXExtendedClipboardService to access custom formats by ID, not just by name
QuoteMust we use a specific code for each OS ?
For Windows, definitely yes. For other platforms, it depends on how their system clipboards store files, and how FMX implements support for them, but I suspect the answer will be yes for them as well.
-
@bravesofts This was already covered on your StackOverflow question yesterday:
delphi Thread ITask.ExecuteWork not wait until Task finish in mobile devices?
Guess you didn't like what you were told there? So you came here instead, just to be told the same things. Funny how the same knowledge can span across multiple forums, huh? 😉
-
1
-
-
On 6/23/2023 at 4:58 AM, JackT said:I made a function called about in my concrete class called TBridge::About derived for a pure abstract delphi class which I can call sucessfully.
Can you please show the code in SolidBridge.h, as well as the Delphi code it is based on?
On 6/23/2023 at 4:58 AM, JackT said:However when I do so Application->MessageBox falls over as it doesn't seem able to create a font resource or lacks resources.
What do you mean, exactly? VCL's TApplication::MessageBox() method simply calls the Win32 API MessageBox() function, using either TApplication::ActiveFormHandle or TApplication::Handle as the owner window for the dialog. So, it does not use any VCL font resources.
Is there way to change the default options for a new project?
in Delphi IDE and APIs
Posted
And rename the new project, of course 😁