-
Content Count
2892 -
Joined
-
Last visited
-
Days Won
128
Everything posted by Remy Lebeau
-
I have no idea what you are referring to. Please provide a screenshot.
-
Here is a dirtier hack that shouldn't require overriding WndProc at all (untested!): procedure TForm12.cmniOpenRecentAdvancedDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; State: TOwnerDrawState); begin // pop the HDC state that TForm saved... RestoreDC(ACanvas.Handle, -1); // Prevent the OS from drawing the arrow... ExcludeClipRect(ACanvas.Handle, ARect.Left, ARect.Top, ARect.Right, ARect.Bottom); // save the new state so TForm will restore it... SaveDC(ACanvas.Handle); end; procedure TForm12.cmniOpenRecentDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; Selected: Boolean); begin ... // OnAdvancedDrawItem is fired after OnDrawItem, so don't exclude here... // ExcludeClipRect(ACanvas.Handle, ARect.Left, ARect.Top, ARect.Right, ARect.Bottom); end;
-
Not true. Something DOES happen. It is just not what you are expecting to happen. Your code has two logic flaws: 1) You are inadvertently invoking pointer arithmetic rather than string concatenation. " " is a string literal of type 'const char[2]', where its 1st element is a space character, and its 2nd element is a null terminator, eg: const char literal[2] = {' ', '\0'}; So, in the expression: numbers[t] + " " You are adding a char[] array to an integer, so the compiler will implicitly decay the char[] array into a char* pointer to its first element, and then the integer will advance that pointer by the number of characters which the integer specifies. So, on the 1st loop iteration, you are effectively assigning the TEdit::Text property a char* pointer to the literal's character at index 0, ie the space character, as-if you had written the code like this: const char literal[2] = {' ', '\0'}; const char *temp = &literal[0]; temp += numbers[t]; // effectively: temp += 0, aka &literal[0] Edit1->Text = temp; Which works fine, as the resulting char* pointer being assigned to the TEdit::Text is a valid null-terminated C-string of length 1 character, so you end up with just the space character in the TEdit::Text, as expected. Then, on the 2nd loop iteration, you are effectively assigning the TEdit::Text property a char* pointer to the literal's character at index 1, ie the null terminator, eg: const char literal[2] = {' ', '\0'}; const char *temp = &literal[0]; temp += numbers[t]; // effectively: temp += 1, aka &literal[1] Edit1->Text = temp; Which also works fine, as the resulting char* pointer being assigned to the TEdit::Text is a valid null-terminated C-string of length 0 characters, so you end up clearing the TEdit::Text. However, on the 3rd loop iteration, you are effectively assigning the TEdit::Text property a char* pointer to the literal's character at index 2, which doesn't exist, so you end up invoking undefined behavior by reading into invalid memory. const char literal[2] = {' ', '\0'}; const char *temp = &literal[0]; temp += numbers[t]; // effectively: temp += 2, aka &literal[2] Edit1->Text = temp; // UB, literal[2] doesn't exist !!! To fix that, you need to explicitly convert each number into a String object yourself before adding " " to it. The compiler will NOT do that conversion automatically for you. You can use the RTL's IntToStr() function, or the String's class constructor that takes an integer as a parameter. 2) your final loop is attempting to display only one element from the numbers array on each iteration. You are completely replacing the content of TEdit::Text with a new string value. As such, by the time that loop is finished, only the last element in the numbers array would be visible in the TEdit. To fix that, you would need to change this: Edit1 -> Text = ...; To this instead: Edit1 -> Text = Edit1 -> Text + ...; So that each loop iteration is appending to the current TEdit::Text value, rather than replacing it. With all of that said, try this instead: void __fastcall TForm1::Button1Click(TObject *Sender) { int numbers[3]; int t; for(t = 0; t < 3; ++t) { numbers[t] = t; } Edit1->Clear(); for(t = 0; t < 3; ++t) { Edit1->Text = Edit1->Text + IntToStr(numbers[t]) + " "; // or: // Edit1->Text = Edit1->Text + String(numbers[t]) + " "; } } That being said, I would suggest another change - don't assign the TEdit::Text on each loop iteration. Use a local String variable first, and then assign that to the TEdit::Text after the loop is finished. Don't make the UI do more work than it actually needs to, eg: void __fastcall TForm1::Button1Click(TObject *Sender) { int numbers[3]; int t; for(t = 0; t < 3; ++t) { numbers[t] = t; } String str; for(t = 0; t < 3; ++t) { str += (IntToStr(numbers[t]) + " "); // or: // str += (String(numbers[t]) + " "); // alternatively: // str.cat_sprintf(_D("%d "), numbers[t]); } Edit1->Text = str; }
-
All of that code is irrelevant. You asked what the Destroying property is meant for. But none of that code is setting or reading that property. So, where is it ACTUALLY being used? If you can't find that, then the property is likely no longer being used at all and could be removed so you don't have to worry about it anymore. I question why it was ever needed in the first place, since the Form's ComponentState property has a csDestroying flag available. Unless it is not the Form's destruction that you were keeping track of...
-
Yes, because objects are reference types. You are passing in a pointer to a TStringList object, so it doesn't matter whether the pointer itself is passed by value or by reference, you still have to dereference the pointer either way to access the object.
-
Offline Help updates available from Embarcadero
Remy Lebeau replied to DelphiUdIT's topic in Delphi IDE and APIs
Agreed. I HATE that they did that. I used to refer to the old docs pretty often, and now its all gone. I have the source code for D5-XE3 and 10.3, but not for XE4-10.2 or 11.x. -
How to register a shell property sheet for a single file type?
Remy Lebeau replied to FPiette's topic in Windows API
Just FYI, I have just now looked back at an old project of mine, which was first written back in the XP days before SystemFileAssociations was introduced. It still registers a custom PropertySheetHandler for a specific file extension under HKCR\<FileExtProgID>\ShellEx\PropertySheetHandlers and HKLM\SOFTWARE\Windows\CurrentVersion\Shell Extensions\Approved, and it still works fine on Win7 and Win10 (I didn't test on Win11). If SystemFileAssociations also works, good for it. -
I have just now fixed the two issues you mentioned: https://github.com/IndySockets/Indy/issues/456
-
I'm a professional developer who worked for a company that used C++Builder (almost) exclusively for almost 20 years.
-
Why does CoInitializeEx returns 1 or $80010106 ?
Remy Lebeau replied to maXcomX's topic in Windows API
Aggregating the FTM means your COM object would need to call the CoCreateFreeThreadedMarshaler() function and save the IUnknown interface it gives you, and then have your object's QueryInterface() method return that interface when it is asked for IID_IMarshal. However, nothing in the ActivateAudioInterfaceAsync() documentation mentions aggregating the FTM at all. All it says is that your completionHandler object needs to implement the IAgileObject interface, nothing more. IAgileObject is an indicator that lets the system know that the object is free-threaded and thus can be called across apartment boundaries without marshaling. You DO NOT need to implement TAggregatedObject in this situation. TAggregatedObject and IAgileObject are completely different and related things. Right, because you did not hook up the TAggregatedObject.Controller to point at another COM object. As you can see in the code snippet you posted, TAggregatedObject.QueryInterface() simply delegates to another COM object. Correct. Yes. You would need something like this: TActivateAudioInterfaceCompletionHandler = class(TInterfacedObject, IActivateAudioInterfaceCompletionHandler, IAgileObject) public function ActivateCompleted(activateOperation: IActivateAudioInterfaceAsyncOperation): HRESULT; stdcall; end; function TActivateAudioInterfaceCompletionHandler.ActivateCompleted(activateOperation: IActivateAudioInterfaceAsyncOperation): HRESULT; stdcall; begin // do something... end; ... ActivateAudioInterfaceAsync( ..., TActivateAudioInterfaceCompletionHandler.Create as IActivateAudioInterfaceCompletionHandler, ... ); -
https://docwiki.embarcadero.com/RADStudio/en/Parameters_(Delphi)#Untyped_Parameters Yes. No. It would not return anything at all, because the parameter is being passed in by value, not by var reference. So, it is essentially just a local variable that is a copy of the caller's variable. As such, you are just assigning the object to a local variable, and the caller's variable is left untouched. The object's refcount will be incremented when the local variable is assigned to, and then be decremented when the local variable goes out of scope. The caller will never see the object at all. It is not the object itself that goes out of scope, just the interface variable that refers to the object. But no, the variable is not assigned nil, it is already out of scope, its value doesn't matter anymore. But, when the variable does go out of scope, if it has a non-nil value then the refcount of the object it is referring to will be decremented. The object is automatically destroyed only when its refcount falls to 0. Never call Free() on an interfaced object. Use reference counting instead. And, there is no FreeAndNil() member method, only a standalone function. So SomeObject.FreeAndNil() is not valid, only FreeAndNil(SomeObject) is valid. And, it is only valid on an object variable, not an interface variable. In any case, if you need an interface variable, that has not gone out of scope yet, to be set to nil automatically when the object it refers to is destroyed, you need to declare the interface variable as [weak].
-
It depends. You did not show the actual declaration and implementation of CreateAndReturnAnObject(), so it is possible that it could go either way. For instance, if the parameter is an untyped var, then the created object WILL NOT have its reference count incremented when assigned to the parameter, and so the object will die (or worse, be leaked) when CreateAndReturnAnObject() exits: function TSomeclass.CreateAndReturnAnObject(var MyObject): Boolean; begin MyObject := TMyInterface.Create; // <-- refcount IS NOT incremented! Result := True; end; To solve that, you would need to use an explicit type-cast: function TSomeclass.CreateAndReturnAnObject(var MyObject): Boolean; begin IMyInterface(MyObject) := TMyInterface.Create; // <-- refcount IS incremented! Result := True; end; // Or: function TSomeclass.CreateAndReturnAnObject(var MyObject): Boolean; begin MyObject := TMyInterface.Create as IMyInterface; // <-- refcount IS incremented! Result := True; end; Or, call _AddRef() directly: function TSomeclass.CreateAndReturnAnObject(var MyObject): Boolean; var Intf: IMyInterface; begin Intf := TMyInterface.Create; // <-- refcount IS incremented here MyObject := Intf; Intf._AddRef; // <-- refcount is incremented again Result := True; end; // <-- refcount is decremented here On the other hand, if the parameter is typed as a var reference to IMyInterface, then the reference count WILL be incremented as expected: function TSomeclass.CreateAndReturnAnObject(var MyObject: IMyInterface): Boolean; begin MyObject := TMyInterface.Create; // <-- refcount IS incremented! Result := True; end; The calling code you have shown would compile in both cases (once you drop the var at the call site, see below), so it is important to know what the parameter is actually typed as to begin with. Specifying var when passing a variable to a function parameter is not valid syntax to begin with. You can use var only when declaring a variable/parameter, but not when passing it around. Pascal is not C#, or other languages, that require you to be explicit about how a variable is passed to a function parameter. It WILL be auto-incremented when it is assigned to a variable/parameter of a valid interface type.
-
I didn't have any issue with the PACKAGE being named sgcIndy, that is clearly a custom name that is different than Indy's own packages. My only issue was with your WEBSITE ADVERTISING your package as just "Indy" instead of as "sgcIndy".
-
Blocking the app does not require blocking the UI thread. Your code should not sit there blocking the main UI thread waiting until the worker thread ends. Let the code in the UI thread return control back to the VCL's main message loop. Have the thread notify your code when it has finished its work. Simply prevent the user from interacting with your UI in the meantime, if that is what you need to do. Both of the solutions presented to you above will accomplish that for you. You should read the following article: Modality, part 1: UI-modality vs code-modality It is written from the perspective of the Win32 API, but since the VCL is based on Win32, the same concept applies in this case. That requires message handling. Of course, you will get that with an Application.ProcessMessages() loop, but that opens up your code to a host of issues, which have already been pointed out earlier. If you are going to process messages anyway, you may as well let the VCL do it for you. Don't block your UI thread.
-
Why does CoInitializeEx returns 1 or $80010106 ?
Remy Lebeau replied to maXcomX's topic in Windows API
Unfortunately, as I already pointed out earlier, the VCL's TApplication constructor calls OleInitialize() before the ComObj unit has a chance to call CoInitialize/Ex(), and OleInitialize() internally calls CoInitialize(nil), so if you require CoInitializeEx() with any mode other than COINIT_APARTMENTTHREADED then you will have to do your COM work in a separate worker thread instead of in the UI thread. The ComObj unit is not the only unit that uses InitProc. InitProc is a chained list of initialization procedures, so you can't use Assigned(InitProc) alone as an indicator of whether you should call CoInitializeEx() or not. If you need CoInitializeEx() called, then just call it yourself unconditionally, and just be sure to pay attention to whether it fails or not. Returning S_FALSE is NOT a failure condition! A thread cannot change its COM threading mode once it has been set. That is what the RPC_E_CHANGED_MODE error code tells you. That IS a failure condition! If you need to use a different COM threading model than the calling thread has already been set to, you will have to move your COM work to another thread that is able to be set to the desired COM model. Typically, but not necessarily always. It really depends on each app's particular requirements. -
In that kind of example, I would probably opt to use the TThread.OnTerminate event instead, eg: type TMainForm = class(TForm) private ProcessingThread: TThread; procedure EnableUI; procedure DisableUI; procedure ThreadFinished(Sender: TObject); procedure ThreadTerminated(Sender: TObject); ... end; procedure TMainForm.BtnClick(Sender: TObject); begin if Assigned(ProcessingThread) then Exit; DisableUI; try ProcessingThread := TThread.CreateAnonymousThread( procedure begin // do work here end ); ProcessingThread.OnTerminate := ThreadFinished; try ProcessingThread.Start; except FreeAndNil(ProcessingThread); raise; end; except EnableUI; end; end; procedure TMainForm.ThreadFinished(Sender: TObject); begin ProcessingThread := nil; EnableUI; end; procedure TMainForm.ThreadTerminated(Sender: TObject); begin ProcessingThread := nil; Close; end; procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin CanClose := not Assigned(ProcessingThread); if not CanClose then begin ProcessingThread.OnTerminate := ThreadTerminated; ProcessingThread.Terminate; // display a "Please wait" UI ... end; end;
-
SSL not found when using idHttp in docker image
Remy Lebeau replied to Die Holländer's topic in Indy
When loading OpenSSL, Indy checks for a whole list of different filenames, including the 4 you have shown above. The order in which that list is attempted can be influenced by setting Indy's CanLoadSymLinks and LoadSymLinksFirst settings before the libs are loaded. By default, it will attempt to load the unversioned files first, so if that is problematic (because they map to a newer unsupported version of OpenSSL) then you should turn off the CanLoadSymLinks or LoadSymLinksFirst setting so the version-specific libs are attempted first/solely. There are public IdOpenSSLSetCanLoadSymLinks() and IdOpenSSLSetLoadSymLinksFirst() functions in the IdSSLOpenSSLHeaders unit for that purpose. See https://www.indyproject.org/2018/05/04/changes-for-how-openssl-is-loaded-on-nix-platforms/ -
SSL not found when using idHttp in docker image
Remy Lebeau replied to Die Holländer's topic in Indy
What does Indy's IdSSLOpenSSLHeaders.WhichFailedToLoad() function return after the error has occurred? Indy's default TIdSSLIOHandlerSocketOpenSSL component supports up to OpenSSL 1.0.2, it cannot use OpenSSL 1.1.x+, so you need to use this work-in-progress SSLIOHandler instead for newer OpenSSL versions. -
You are probably thinking of the TIdTCPStream class. You can assign a target TIdTCPConnection to it, and then pass it to the IOHandler.ReadStream() method of a source TIdTCPConnection. You can then tell ReadStream() to either read a specific number of bytes, or to read endlessly until the source socket disconnects. Any bytes read in from the source will be written out to the target.
-
Why does CoInitializeEx returns 1 or $80010106 ?
Remy Lebeau replied to maXcomX's topic in Windows API
Can you be more specific? -
Why does CoInitializeEx returns 1 or $80010106 ?
Remy Lebeau replied to maXcomX's topic in Windows API
Per https://stackoverflow.com/a/20019023/65863: And also: https://learn.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-oleinitialize So, there you go. If your project uses the ComObj unit, it will call CoInitialize/Ex() when Vcl.Forms.TApplication.Initialize() is called, which won't matter since the constructor of Vcl.Forms.TApplication will have already called OleInitialize() beforehand, which calls CoInitializeEx(): constructor TApplication.Create(AOwner: TComponent); var ... begin inherited Create(AOwner); if not IsLibrary then FNeedToUninitialize := Succeeded(OleInitialize(nil)); ... end; -
Why does CoInitializeEx returns 1 or $80010106 ?
Remy Lebeau replied to maXcomX's topic in Windows API
That is S_FALSE. That is RPC_E_CHANGED_MODE. Per the documentation: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex This means that you are calling CoInitializeEx() on a thread that has already called CoInitialize/Ex() successfully, where S_FALSE means you are trying to set the same concurrency model that has already been assigned to the thread, whereas RPC_E_CHANGED_MODE means you are specifying a concurrency model that is not compatible with the thread's current concurrency model. For instance, are you calling CoInitializeEx() in the main UI thread? The RTL's System.Win.ComObj unit initializes COM in the main UI thread during program startup, using the global CoInitFlags variable to decide whether to use CoInitialize() or CoInitializeEx(). -
1 is S_FALSE. Per the documentation: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex This means that you are calling CoInitializeEx() on a thread that has already called CoInitialize/Ex() successfully, and you are specifying the same concurrency model that has already been assigned to the thread. For instance, are you calling CoInitializeEx() in the main UI thread? The RTL's System.Win.ComObj unit initializes COM in the main UI thread during program startup, using the global CoInitFlags variable to decide whether to use CoInitialize() or CoInitializeEx().
-
Best way to embed a binary structure so it is linked/compiled in
Remy Lebeau replied to alank2's topic in General Help
Put the raw data into a separate binary file, and then add an .rc script to your project (or use the Resources and Images dialog) to compile the data file as an RCDATA resource in your final executable's resources. Then, in your code, you can use a TResourceStream whenever you want to access the resource data at runtime. See Resource Files Support (just ignore the part about using a Multi-Device project, this works in a VCL project, too). -
No recent updates, still pending review: https://github.com/indySockets/indy/pull/299 Correct. It is just built on top of Indy. Their website says: https://www.esegece.com/products/indy/features-overview I agree. As already pointed out, Indy's license does allow them to sell their derivative work. However, I don't like that their website is advertising their product as "Indy", it really should be advertised as "sgcIndy" instead. @esegece I would really appreciate it if you changed the product naming on your website accordingly, and actually explain on it what your product actually is. It is not Indy itself, it is a modification/addon to Indy. Also, your "Quick Start" guide suggests that you have made custom modifications to Indy's default OpenSSL SSLIOHandler, rather than introducing a new SSLIOHandler, as the pending pull request is doing. I would be interested in knowing what kind of modifications you have made to enable that in the default SSLIOHandler, given the extensive and breaking API changes that were introduced in OpenSSL 1.1.x.