Jump to content

Remy Lebeau

Members
  • Content Count

    2322
  • Joined

  • Last visited

  • Days Won

    94

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Windows Notification in Exe2 when Exe2 started from Exe1

    Then you will run into problems running your code on Windows 8.1 and later, since GetVersionEx() will report as Windows 8.0 in them. Modern Delphi versions can handle that privilege for you, as you can enable and configure the manifest in the Project Options: https://docwiki.embarcadero.com/RADStudio/en/Application_Options#Manifest_File_.28Windows_Only.29 But, why do you think you need "requireAdministrator"? You don't need admin rights to send notifications. Does Exe2 do other things that require admin rights? However, you will need a custom manifest in order to specify the Windows versions that your code supports, as the IDE does not currently provide options for configuring that list: https://learn.microsoft.com/en-us/windows/win32/sysinfo/targeting-your-application-at-windows-8-1 Only if Exe1 is doing other things that need admin rights. You do not need admin rights to launch Exe2 from Exe1. What about it, exactly, are you asking about? Why? If your code was already supporting them, there is no reason to remove them.
  2. Remy Lebeau

    How to draw a semitransparent ellipse on a canvas?

    I was thinking more along the lines of creating a 32bit Bitmap with an alpha channel and then using the Win32 AlphaBlend() API to draw the bitmap onto the Canvas (I forgot that TCanvas.Draw() has an opacity parameter).
  3. Remy Lebeau

    ENG-US keyboard automatically added on app start

    The VCL certainly does not install new keyboards.
  4. Remy Lebeau

    Windows Notification in Exe2 when Exe2 started from Exe1

    TPlatformNotificationCenter is looking for Windows Windows 8 and higher, yes. Does Exe2 have an app manifest that specifies your code supports Windows 8 and higher? TOSVersion uses the Win32 GetVersionEx() function internally when initializing its Major and Minor properties. GetVersionEx() provides accurate values regardless of app manifest only up to Windows 8.0, but from Windows 8.1 onward, it lies if you do not manifest your app those versions, per Microsoft's documentation: Your screenshots are suggesting that you are running your app on Windows 10 or higher, and your app is manifested only up to Windows 7, not for Windows 8 or higher. When running Exe2 directly in Explorer, or via CreateProcess() in Exe1, GetVersionEx() should be reporting what your app is actually manifested for. But, when launching Exe2 directly from the IDE, its would seem that GetVersionEx() is picking up the IDE's manifest rather than your app's manifest, which is odd. In any case, if you want to use TNotificationCenter, make sure you specify support for Windows 8 in your app manifest.
  5. Remy Lebeau

    Windows Notification in Exe2 when Exe2 started from Exe1

    Is Exe2 a console app or a GUI app? Does it initialize the COM library before creating the TNotificationCenter object? Internally, your Exe2 code is basically doing the following: The TNotificationCenter.Supported property returns False when the TCustomNotificationCenter.FPlatformNotificationCenter member is nil. TCustomNotificationCenter.FPlatformNotificationCenter is initialized in the TCustomNotificationCenter constructor using a call to TBaseNotificationCenter.InternalGetInstance(). On Windows, TBaseNotificationCenter.InternalGetInstance() calls System.Win.Notification.TPlatformNotificationCenter.GetInstance(). TPlatformNotificationCenter.GetInstance() returns the TNotificationCenterWinRT.NotificationCenter property. The NotificationCenterWinRT.NotificationCenter property getter attempts to create a TNotificationCenterWinRT singleton object, returning nil if it fails to be created. TNotificationCenterWinRT's constructor calls TToastNotificationManager.Statics.CreateToastNotifier(). The TToastNotificationManager.Statics property getter imports WinRT's Windows.UI.Notifications.ToastNotificationManager class, raising an exception if it fails. Then calls ToastNotificationManager.CreateToastNotifier() So, it sounds like Exe2 is unable to import WinRT's ToastNotificationManager class, which ultimately causes TNotificationCenter.Supported to return False. The WinRT API returns HRESULT error codes when things fail, unfortunately the RTL does not capture those error codes when bubbling WinRT failures up the call chain. However, the exceptions do have error messages on them, so if you attach the IDE's debugger to the Exe2 process before it creates the TNotificationCenter object, then you should be able to see those error messages (as well as step into the RTL's source code directly). For instance, have Exe1 pass a command-line argument to Exe2, such as '/debug', and then have Exe2's startup code check for that argument and if present then run a short wait loop that breaks once the Win32 IsDebuggerPresent() function returns True. That will give you an opportunity to debug Exe2 when it is launched by Exe1.
  6. Remy Lebeau

    Windows Notification in Exe2 when Exe2 started from Exe1

    The function is also leaking the ProcessInfo.hThread handle. Since that handle is not being returned to the caller, it needs to be closed before the function exits.
  7. Remy Lebeau

    How to draw a semitransparent ellipse on a canvas?

    You can't, well not directly anyway. You can draw it opaquely onto an in-memory bitmap first, and then draw that bitmap onto your target Canvas with alpha/transparency applied to it as needed.
  8. Remy Lebeau

    Desktop path crossplatform?

    Small nitpicks.. you can't use {$ELSEIF} with {$IFDEF}, you would need to use {$ELSE}{$IFDEF} instead. Otherwise, use {$IF} in order to use {$ELSEIF}. I would suggest using RTL functions to handle the path delimiters. function OSGetUserDesktopPath: string; begin {$if defined(MSWINDOWS)} winApiUtils.GetFolderPath(CSIDL_DESKTOPDIRECTORY, Result); Result := InclueTrailingPathDelimiter(Result); {$elseif defined(OSX)} Result := TPath.Combine(TPath.GetHomePath, 'Desktop'); Result := IncludeTrailingPathDelimiter(Result); {$elseif defined(IOS)} Assert(false, 'OSGetUserDesktopPath not implemented on IOS'); Result := ''; {$elseif defined(ANDROID)} Assert(false, 'OSGetUserDesktopPath not implemented on ANDROID'); Result := ''; {$else} {$Message Error 'Missing Target'} {$ifend} end;
  9. Delphi doesn't use .DEF files to begin with. But even if it did, .DEF files are simply not preprocessed files, so they cannot have directives to generate different code for different compilation setups. To do what you are asking, the project setup must use a separate .DEF file for each platform.
  10. Why? What exactly is different between them? For that matter, why are you using .DEF files in Delphi at all? That should not be necessary. You need to use separate projects if you need to include a different set of files. Not really, except maybe making/finding a preprocessor for .DEF files, or using the project's Build Events to automate the swapping of the .DEF file.
  11. I was able to connect to your specified URL using a web browser with TLS 1.2, and it received a response just fine. So you should be able to get a similar response using Indy with TLS 1.2, too. Have you tried using a packet sniffer, like Wireshark, to look at the actual TLS handshake that TIdSSLIOHandlerSocketOpenSSL is sending, to make sure it is really attempting to use TLS 1.2, and not say TLS 1.0 instead? Which version of the OpenSSL DLLs are you using, exactly? Does Indy's IsOpenSSL_TLSv1_2_Available() function in the IdSSLOpenSSLHeaders unit return True or False when the error occurs? Offhand, that code looks ok, as far as setting up the SSLIOHandler, though I do have a few comments (not related to the TLS error) about the rest of the code: Why are you setting the Request.ContentType and Request.ContentLength properties when you are not sending any data to the server? Why are you setting the Request.Accept property, instead of using the default? Both values include '*/*', so you are not really gaining anything, especially since the server's response is JSON, which is not specified in either value, so caught by '*/*' anyway. Why are you setting the Request.Host property to a different hostname than is specified in the URL? Do not set the Request.AcceptEncoding manually, let TIdHTTP manage that for you. If you want to support compressed responses, you need to assign a compressor component to the TIdHTTP.Compressor property, in which case TIdHTTP will then set the Request.AcceptEncoding property based on the compressor's actual capabilities.
  12. Remy Lebeau

    Indy 9.0.18 on Delphi 6. Where can i get 9.0.50?

    Be sure to read https://github.com/IndySockets/Indy/wiki/Updating-Indy (though, it doesn't appear to cover how to uninstall Indy 9, so you'll have to work that out for yourself). Is there a problem with it? Can you be more specific? If you have recompiled Computil.exe and it works on XP as well as up to Win11, then can you send it to me so I can check it back into Indy's GitHub repo? Thanks. Make sure you compile IndySystem60.dpk first. You need to uninstall the earlier Indy from the IDE before you can install Indy 10. Same issue. Same issue. That is the Indy package that came pre-installed in the IDE. You should be able to find it in the IDE's "Install Packages" dialog. Yeah! Sounds about right. Indy 10 has a different directory structure than Indy 9, yes. This will be cleaned up in Indy 11. Yes.
  13. Remy Lebeau

    Using a C++ Class in Delphi

    You can't avoid implementing the C++ class in a DLL or BPL, unfortunately. However, Delphi can use the C++ object directly if it is derived from a Delphi class (ie, TObject or descendant), or is accessible using an abstract interface that is also defined on the Delphi side, or is accessible via/as a COM object. Otherwise, you are stuck using plain C-style wrapper functions, as you already have.
  14. Remy Lebeau

    TLS v1.3

    Delphi releases ship with the latest Indy available at the time. 10.6.2.0 is actually newer than 10.6.2.5520, it is just that the build number got lost and reset back to 0 when Indy migrated from SVN to GitHub (see https://github.com/IndySockets/Indy/issues/292). I think so, yes. Though, I have now merged just those two files into the main code, so the PR should now be able to contain only the new units (once the author updates his branch). So, in theory, you can download the latest main code, and then download the new units on top of it. You will need the updated OpenSSL 1.1x/3.x DLLs. Indy doesn't provide downloads for those at this time, so you'll have to get them from elsewhere.
  15. If you are trying to delete/rename remote files on the FTP server, TFtpClient has methods for that purpose:
  16. Remy Lebeau

    Windows VCL application pauses when window is not focused...

    Definitely not a good design choice. Long-running procedures should be moved to a worker thread.
  17. Or, use TList<TMyFrame> instead, which doesn't try to own objects.
  18. Remy Lebeau

    Exception catching in a service

    You signal the thread to terminate, and then you wait for the thread to actually terminate right then and there, before you exit from the OnStop/OnShutdown event handler. The service enters a StopPending state before triggering the event, and then it enters the Stopped state once the event exits (or, in the case of the OnStop event, it enters back into the Running state if you set the event's Stopped parameter to False). Note that using TThread.WaitFor() in this situation is not a good idea, since the service still needs to report status back to the SCM at regular intervals while the SCM is waiting for the service to stop itself. If the thread takes a long time to terminate, the SCM will fail after some time if the service doesn't actively tell the SCM that the stop is taking a long time. What I do instead is use TThread.Handle with WaitForSingleObject() (or equivalent) in a loop, using a timeout calculated from the TService.WaitHint property (usually WaitHint-100). When WFSO reports the thread has fully terminated, I break the loop and move on. When WFSO times out, I call TService.ReportStatus() and keep looping.
  19. Remy Lebeau

    Exception catching in a service

    FYI, it is generally not a good idea to use the TService.OnExecute event at all, since it requires you to take control of the message loop that processes SCM requests. If you mess up the loop, the service won't respond to the SCM correctly. When there is no OnExecute handler assigned at all, TService's default behavior is sufficient to let the service process and response to SCM requests correctly. You should instead spawn your own thread from the TService.OnStart event, and terminate that thread in the TService.OnStop and TService.OnShutdown events. Then you can do whatever you want inside of your thread.
  20. Remy Lebeau

    Sending Email via GMail Using OAuth 2.0 via Indy

    REST is just standard HTTP, usually with JSON data. Indy doesn't have any REST-specific components, but pretty much anything you can with other REST components can also be done manually with TIdHTTP. You use the token pretty much the same way you use any other SASL credentials with TIdSMTP: Add TIdSASLXOAuth2 to the TIdSMTP.SASLMechanisms collection, and set the TIdSMTP.AuthType property to satSASL. Assign a TIdUserPassProvider to the TIdSASLXOAuth2.UserPassProvider property, and then assign the retrieved token to the TIdUserPassProvider.Password property. Alternatively, use the TIdSASLXOAuth2.OnGetAccessToken event to retrieve the token on-demand. Use TIdMessage and TIdSMTP as needed (fill out email, configure Host/Port, UseTLS, etc, and then call TIdSMTP.Connect(), TIdSMTP.Send(), TIdSMTP.Disconnect(), etc).
  21. Remy Lebeau

    WideString.c_bstr() operation in 11.2

    So, the real problem is that the BSTR is empty inside of the actual CodeSoft COM object's Open() method, rather than the TLB wrapper's Open() method? If so, then that implies the problem is with either TAutoArgs or OleFunction() not passing the BSTR to the COM object correctly. If the BSTR is not empty inside of the TLB wrapper method, then WideString is working properly, and the problem is elsewhere.
  22. Remy Lebeau

    Enable protocol TLS 1.3 on Windows 7

    By default, (lib)curl uses OpenSSL for SSL/TLS. Guess what else uses OpenSSL by default? Indy, which is pre-installed in Delphi.
  23. Remy Lebeau

    WideString.c_bstr() operation in 11.2

    You need to escape the '\' character in a string literal, eg: String LabelName = "C:\\FullPathToLabelFile.lab"; // <-- note the double slashes! /* NOTE: you really should be using the L"" prefix or _D() macro instead! String LabelName = L"C:\\FullPathToLabelFile.lab"; or: String LabelName = _D("C:\\FullPathToLabelFile.lab"); */ Or, when using a Clang compiler, you can use a raw string literal instead, which doesn't require escaping, eg: String LabelName = LR"(C:\FullPathToLabelFile.lab)"; /* Alternatively, using the _D() macro: String LabelName = _D(R"(C:\FullPathToLabelFile.lab)"); */ Other than that, the rest of that code snippet looks fine. So, whatever problem you are having is not in your code, but is in the RTL itself. Since Open() takes a raw BSTR pointer, the problem has to be at the call site, ie with the construction of the UnicodeString or WideString objects. You are using a narrow string literal when initializing the UnicodeString, instead of using a wide string literal. So, that will invoke a narrow-to-wide data conversion at runtime, which could cause the UnicodeString to end up empty if something goes wrong in the conversion. Did you check for that? The WideString constructor that takes a UnicodeString will set a null BSTR pointer if the UnicodeString is empty. Otherwise, it simply calls the Win32 SysAllocStringLen() function, which returns a null BSTR pointer on failure. Did you check for that? So, either the UnicodeString is empty to begin with (which it should not be in your case, given the code shown), or SysAllocStringLen() is failing due to insufficient system memory. Did you check for that? Try something like this to help narrow down the problem further: String LabelName = "C:\\FullPathToLabelFile.lab"; if (LabelName.IsEmpty()) { throw ...; } WideString wLabelName(LabelName); if (wLabelName.IsEmpty()) { throw ...; } ... CodeSoftDocument = CodeSoft->Documents->Open(wLabelName.c_bstr(), 0); ...
  24. Remy Lebeau

    Where can I download Indy document?

    https://www.indyproject.org/2021/02/10/links-to-old-indy-website-pages-are-currently-broken/
×