-
Content Count
2903 -
Joined
-
Last visited
-
Days Won
129
Everything posted by Remy Lebeau
-
Add the "System.Generics.Collections" unit to your "uses" clause. https://docwiki.embarcadero.com/Libraries/en/System.Generics.Collections.TList I don't write projects for Android. I didn't say the code would actually compile, but it should give you some ideas of what to look.
-
How to register a shell property sheet for a single file type?
Remy Lebeau replied to FPiette's topic in Windows API
Your PropertySheelHandler should not have its own ProgID assigned to it. You can assign your PropertySheetHandler to other ProgIDs as needed, but it should not have its own ProgID. So, get rid of the "HKLM\SOFTWARE\Classes\CLSID\{AF6B...}\ProgID" subkey. -
How to register a shell property sheet for a single file type?
Remy Lebeau replied to FPiette's topic in Windows API
How would you know fore sure? You are not reporting every error that may occur. For example, if an extension does not have a ProgID yet, and you try to create a new ProgID but Reg.OpenKey() fails, you don't report that. Also, I see you creating your PropertySheetHandlers subkey only for the 1st extension in your array, because your code assumes that all extensions are using the same ProgID, which is not a guarantee, so you should be checking/creating the PropertySheetHandlers subkey for every ProgID you encounter. Try something more like this: procedure TMyDelphiPropSheetHandlerFactory.UpdateRegistry(ARegister: Boolean); var Reg : TRegistry; ClassID : String; Updated : Boolean; {$IFDEF REG_FILENAME} ProgNameVer : String; RegExtArray : TStringDynArray; Ext : String; ProgID : String; {$ENDIF} begin ClassID := GUIDToString(Class_MyDelphiPropSheetHandler); Updated := False; try Reg := TRegistry.Create; try {$IFDEF REG_FILENAME} Reg.RootKey := HKEY_LOCAL_MACHINE; ProgNameVer := ProgName + '.' + ProgVer; RegExtArray := SplitString(RegFileExts, ';'); if ARegister then begin inherited UpdateRegistry(ARegister); for Ext in RegExtArray do begin if not Reg.OpenKeyReadOnly('\Software\Classes\' + Ext) then begin if Reg.LastError = ERROR_FILE_NOT_FOUND then MyMessageBox('Ext="%s" doesn''t exist', [Ext]) else MyMessageBox('Can't open registry key for Ext="%s"', [Ext]); Continue; end; ProgID := Reg.ReadString(''); Reg.CloseKey; if ProgID = '' then begin ProgID := ProgNameVer; Reg.Access := KEY_SET_VALUE; if not Reg.OpenKey('\Software\Classes\' + Ext, True) then begin MyMessageBox('Can't open registry key to update Ext="%s"', [Ext]); Continue; end; Reg.WriteString('', ProgID); Reg.CloseKey; Updated := True; MyMessageBox('Created ProgID[%s]="%s"', [Ext, ProgID]); end else MyMessageBox('ProgID[%s]="%s"', [Ext, ProgID]); Reg.Access := KEY_SET_VALUE; if not Reg.OpenKey('\Software\Classes\' + ProgID + '\ShellEx\PropertySheetHandlers\' + ProgName, True) then begin MyMessageBox('Can''t create PropertySheetHandler="%s" for ProgID="%s"', [ProgName, ProgID]); Continue; end; Reg.WriteString('', ClassID); Reg.CloseKey; Updated := True; MyMessageBox('PropertySheetHandlers[%s]="%s"', [ProgID, ProgName]); MyMessageBox('Registered for Ext="%s"', [Ext]); end; end else begin for Ext in RegExtArray do begin if not Reg.OpenKeyReadOnly('\Software\Classes\' + Ext) then begin if Reg.LastError <> ERROR_FILE_NOT_FOUND then MyMessageBox('Can't open registry key for Ext="%s"', [Ext]); Continue; end; ProgID := Reg.ReadString(''); Reg.CloseKey; if SameText(ProgID, ProgNameVer) then begin Reg.Access := KEY_SET_VALUE; if not Reg.OpenKey('\Software\Classes\' + Ext, False) then begin MyMessageBox('Can't open registry key to update Ext="%s"', [Ext]); end else begin if Reg.DeleteValue('') then begin Reg.CloseKey; Updated := True; MyMessageBox('Deleted ProgID="%s" value for Ext="%s"', [ProgID, Ext]) end else begin Reg.CloseKey; MyMessageBox('Can''t delete ProgID="%s" value for Ext="%s"', [ProgID, Ext]); end; end; end; if ProgID <> '' then begin Reg.Access := KEY_SET_VALUE or _DELETE; if not Reg.OpenKey('\Software\Classes\' + ProgID + '\ShellEx\PropertySheetHandlers\', False) then begin if Reg.LastError <> ERROR_FILE_NOT_FOUND then MyMessageBox('Can''t open PropertySheetHandlers key for ProgID="%s"', [ProgID]); end else if Reg.DeleteKey(ProgName) or (Reg.LastError = ERROR_FILE_NOT_FOUND) then begin Reg.CloseKey; Updated := True; MyMessageBox('Deleted PropertySheetHandler="%s" for ProgID="%s"', [ProgName, ProgID]); end else begin Reg.CloseKey; MyMessageBox('Can''t delete PropertySheetHandler="%s" for ProgID="%s"', [ProgName, ProgID]); end; end; end; if Reg.DeleteKey('\Software\Classes\' + ProgNameVer) or (Reg.LastError = ERROR_FILE_NOT_FOUND) then begin Updated := True; MyMessageBox('Deleted registry key for ProgID="%s"', [ProgNameVer]) end else MyMessageBox('Can''t delete registry key for ProgID="%s"', [ProgNameVer]); inherited UpdateRegistry(ARegister); end; {$ELSE} Reg.RootKey := HKEY_CLASSES_ROOT; if ARegister then begin inherited UpdateRegistry(ARegister); Reg.Access := KEY_SET_VALUE; if Reg.OpenKey('\*\shellex\PropertySheetHandlers\' + ProgName, True) then begin Reg.WriteString('', ClassID); Reg.CloseKey; Updated := True; MyMessageBox('Created registry key for PropertySheetHandler="%s"', [ProgName]); end else MyMessageBox('Can''t create/open registry key for PropertySheetHandler="%s"', [ProgName]); end else begin if Reg.DeleteKey('\*\shellex\PropertySheetHandlers\' + ProgName) or (Reg.LastError = ERROR_FILE_NOT_FOUND) then begin Updated := True; MyMessageBox('Deleted registry key for PropertySheetHandler="%s"', [ProgName]); end else MyMessageBox('Can''t delete registry key for PropertySheetHandler="%s"', [ProgName]); inherited UpdateRegistry(ARegister); end; {$ENDIF} finally Reg.Free; end; finally if Updated then SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil); end; end; UPDATE: something else I just realized - you did not say whether you are running your code from a 32bit or 64bit executable. If 32bit on a 64bit system, you need to take WOW64 into account, by including the KEY_WOW64_32KEY or KEY_WOW64_64KEY flag in the TRegistry.Access property as needed. -
You can use ConnectivityManager.getAllNetworks() to iterate available networks, and ConnectivityManager.getLinkProperties() to get the LinkProperties of each network, for example (yes, it is in Java): private Network findVpnNetwork() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); Network[] networks = cm.getAllNetworks(); for (Network network : networks) { LinkProperties linkProperties = cm.getLinkProperties(network); List<LinkAddress> addresses = linkProperties.getLinkAddresses(); for (LinkAddress addr : addresses) { if (addr.getAddress().equals(VPN_ADDRESS)) { return network; } } } return null; } So, a Delphi translation to get available IP addresses might look something like this: function getIpAddresses: TList<JInetAddress>; begin Result := TList<JInetAddress>.Create; try var svc := TAndroidHelper.Context.getSystemService(TJContext.JavaClass.CONNECTIVITY_SERVICE); var cm := TJConnectivityManager.Wrap((svc as ILocalObject).GetObjectID); for var network in cm.getAllNetworks do begin for var addr in cm.getLinkProperties(network).getLinkAddresses do Result.Add(addr.getAddress); end; except Result.Free; raise; end; end;
-
How to register a shell property sheet for a single file type?
Remy Lebeau replied to FPiette's topic in Windows API
Creating a new ProgID just to define a PropertySheetHandler will not change which app opens the file type. Those are separate operations. Besides, in your example, there is no app registered for the file type anyway, since there is no ProgID registered for the file type (the file is being opened through a separately registered Thumbnail Handler instead). Simply don't create any Shell commands inside of your new ProgID, only register your ShellEx handler. -
How to register a shell property sheet for a single file type?
Remy Lebeau replied to FPiette's topic in Windows API
That is because the extension key has a Thumbnail Provider registered, which is registered via a "ShellEx" subkey on the extension key itself. However, since you want to register a Property Sheet handler instead, the extension key must have a ProgID registered with it, as I showed earlier. Why are you being so hesitant to define one? If you want your Property Sheet Handler to work correctly, you don't have a choice, this is simply how Property Sheet Handlers operate. A "ShellEx" subkey on the extension key itself will not work for a Property Sheet Handler. -
Indy with macOS crash with socket error #53 and #61, unable to catch
Remy Lebeau replied to softtouch's topic in Network, Cloud and Web
When the client's ConnectTimeout property is set to a non-infinite value, or if you are connecting the client in the context of the main UI thread and using TIdAntiFreeze, then Indy uses a worker thread to connect the client socket to the server in order to implement timeout handling. The call stack above is showing the exception being raised in the context of that worker thread, and there is no way for you to catch that specific exception directly from that thread. However, Indy will internally catch the exception for you and re-raise it in the context of the thread that is trying to connect the client. And you should be able to catch that exception normally. If not, then the RTL is likely not doing its job correctly (IIRC correctly, Delphi's exception handling on MacOS is less stable/reliable than on Windows). The code you have shown is fine, you should be able to catch the exception. So, you are just going to have to debug the code at runtime to find out why the exception is not being propagated to your code correctly. -
Not the raw data as it came directly from the TCP socket, no. By the time you are given access to the TIdHTTPRequestInfo object, all of the raw socket data has already been parsed and re-organized for Indy's purposes. That being said, you can get the (near-raw) headers from the TIdHTTPRequestInfo.RawHeaders property, and the body data from either the TIdHTTPRequestInfo.PostStream or TIdHTTPRequestInfo.FormParams property, depending on the request's media type. Basically, yes. The only other option is to log the raw socket data immediately as it comes off the socket, before Indy parses it. You can do that by assigning a TIdConnectionIntercept-derived object to the AContext.Connection.Intercept property in the server's OnConnect event. For instance, one of the TIdLog... components, like TIdLogFile,
-
How to register a shell property sheet for a single file type?
Remy Lebeau replied to FPiette's topic in Windows API
It is not outdated. I've used that info in the past to create shell handlers, and my machines have many 3rd party property sheet handlers installed that are registered the way I described. If your extension does not have a ProgID assigned to it (why?), you need to create and assign your own ProgID, even if just to create the Shellex subkey. All extension subkeys should have a ProgID subkey associated with them, because it is the ProgID subkey that contains the actual commands that tell Windows what to do with files that have those extensions. Multiple file extensions that share a common set of commands can be assigned to a single ProgID. -
How to register a shell property sheet for a single file type?
Remy Lebeau replied to FPiette's topic in Windows API
Per the documentation, Registering Shell Extension Handlers, you need to create your ShellEx subkey underneath the extension's ProgID subkey, not under the extension subkey, eg: HKEY_CLASSES_ROOT .arw (Default) = <MyArwProgID> MyArwProgID ShellEx PropertySheetHandlers MyDelphiPropSheetHandler (Default) = <MyClassID> For example: Reg := TRegistry.Create; try Reg.RootKey := HKEY_CLASSES_ROOT; if Reg.OpenKeyReadOnly('\.arw') then begin ProgID := Reg.ReadString(''); Reg.CloseKey; Reg.Access := KEY_CREATE_SUB_KEY or KEY_SET_VALUE; if (ProgID <> '') and Reg.OpenKey('\' + ProgID + '\ShellEx\PropertySheetHandlers\MyDelphiPropSheetHandler', True) then begin Reg.WriteString('', ClassID); Reg.CloseKey; end; end; finally Reg.Free; end; Note that you really should not be writing directly to HKEY_CLASSES_ROOT, you should be writing to either HKEY_LOCAL_MACHINE\Software\Classes\ or HKEY_CURRENT_USER\Software\Classes\ instead, per the documentation, HKEY_CLASSES_ROOT Key: For instance: Reg := TRegistry.Create; try if ForTheCurrentUserOnly then Reg.RootKey := HKEY_CURRENT_USER else Reg.RootKey := HKEY_LOCAL_MACHINE; if Reg.OpenKeyReadOnly('\Software\Classes\.arw') then begin ProgID := Reg.ReadString(''); Reg.CloseKey; Reg.Access := KEY_CREATE_SUB_KEY or KEY_SET_VALUE; if (ProgID <> '') and Reg.OpenKey('\Software\Classes\' + ProgID + '\ShellEx\PropertySheetHandlers\MyDelphiPropSheetHandler', True) then begin Reg.WriteString('', ClassID); Reg.CloseKey; end; end; finally Reg.Free; end; -
Delphi 11.1, Android API 31, FLAG_IMMUTABLE in Pending Intent
Remy Lebeau replied to nufus42's topic in FMX
I don't see any tickets open in QualityPortal about that issue yet. -
Is there a way to use a platform specific win32/win64 DEF file?
Remy Lebeau replied to alank2's topic in General Help
Tons of questions on StackOverflow on that very topic, for instance: How can I use "sizeof" in a preprocessor macro? sizeof() is not executed by preprocessor Does the sizeof operator work in preprocessor #if directives? Just to name a few... Since sizeof is a compile-time constant, I would use "if constexpr" instead: if constexpr (sizeof(ATypeName) > 20) { /*.... your code here ...*/ } -
If the Owner form assigns itself as the actual Owner (from the RTL's perspective) of the Child form, then the RTL will handle all of that for you automatically. All you would have to do in your own code is have the Owner form override the Notification() method. However, personally, I would just have the Owner form assign its own handlers to the Child form's OnClose and OnDestroy events instead. That way, the Owner, not the Child, decides whether or not to set Action=caFree in the OnClose event, and the Owner can still react to the Child being destroyed without having to override the Notification() method.
-
Is there a way to use a platform specific win32/win64 DEF file?
Remy Lebeau replied to alank2's topic in General Help
Yup. https://docwiki.embarcadero.com/RADStudio/en/C%2B%2B_Compilers There is 1 classic compiler (bcc32) and 2 separate clang compiler front-ends (bcc32c and bcc32x): -
Thanks. I have now added the .zip to Indy's repo.
-
Thanks. I would like to check in this .zip file to Indy's GitHub repo, but first can you please verify that version number? 1.0.2u was released on Dec 20 2019. What does Indy's OpenSSLVersion() function (in the IdSSLOpenSSL unit) actually return when using these .so files?
-
Is there a way to use a platform specific win32/win64 DEF file?
Remy Lebeau replied to alank2's topic in General Help
Embarcadero provides only one 64-bit compiler for Windows (unlike three 32-bit compilers for Windows), and it is clang-based. -
No, because .so files are not static-link files. Per the documentation, the Android compiler uses .a files for static linking, same as the iOS compiler. Anywhere you want, as long as it is on the compiler's search path.
-
Delphi 11.1 with TidThreadComponent and TThread.Synchronize
Remy Lebeau replied to ChrisChuah's topic in Indy
If you are doing a lot of syncing with the main UI thread, you are likely overwhelming the main thread with too many requests, leaving it with less opportunity to handle UI messages. Every time you call TThread.Synchronize() or TThread.Queue(), it puts the synced method into a list and then posts a message to the main thread to let the RTL know that the list is pending. Once the main thread processes that message, it runs through the entire list, calling each method in order, and doesn't process new messages again until after the list is empty. If you are syncing often, you are going to fill the main thread's message queue with pending requests, and so the main thread is going to spend its time checking and rechecking that list instead of doing other things. So, try to reduce how often you sync with the main thread. For instance, batch up your synced data and then sync the whole batch after X updates are cached, or after N seconds have elapsed, etc. Or, save off just your latest data and use a UI timer in the main thread to grab that data periodically. That means the main UI thread is doing too much work and is not processing UI messages often enough. You need to relieve the pressure on the main thread. No, this would affect VCL in much the same way, as most of the functionality for TThread.Synchronize()/TThread.Queue() is common RTL code that is shared by both frameworks. -
Delphi 11.1 with TidThreadComponent and TThread.Synchronize
Remy Lebeau replied to ChrisChuah's topic in Indy
Correct, because your event is being fired in a worker thread, so any access to UI controls must be synchronized with the main UI thread. TThread.Synchronize() runs synchronously. It will not return to the caller until the synched method has been called and exited. That means your worker thread will be blocked waiting on the main UI thread to call the method. TThread.Queue() runs asynchronously. It will return to the caller immediately, and the synched method will run in the background at some unspecified time when the main UI thread gets around to calling it. That means your worker thread will not be blocked waiting on the main UI thread, it will be free to do other things while the synched method waits to be called. Yes, because your worker thread can't do anything while TThread.Synchronize() is blocked waiting. You don't have that issue with TThread.Queue(). What do you mean? Please elaborate. -
Thanks, I have checked it in.
-
Unfortunately, when Indy fails to load an OpenSSL library into memory on a non-Windows platform, it does not report WHY the library failed to load. Sounds plausible, given that ARMv7 is 32bit whereas ARMv8 is 64bit. However, ARMv8 can run ARMv7 code, but only when running in ARMv7 mode. You will likely have to search around for (or compile yourself) OpenSSL 1.0.2 binaries for ARMv8. I don't have such binaries available (or else they would have already been checked in to Indy's GitHub repo) Indy isn't coded to support using OpenSSL statically except on iOS. You could try modifying the IdCompilerDefines.inc and IdSSLOpenSSLHeaders_static.pas files to support static OpenSSL on Android. But even then, you would still need suitable binaries for each CPU architecture that your app is targeting.
-
You would be correct. There is no "TCHAR maps to" option (or equivalent) in BCB6. Although you can manually define UNICODE to swap TCHAR between char and wchar_t during compiling, that has no effect on the linker or the startup code.
-
Is there a way to use a platform specific win32/win64 DEF file?
Remy Lebeau replied to alank2's topic in General Help
Then why did you post this in a Delphi forum? There is a separate forum for C++Builder: https://en.delphipraxis.net/forum/41-general-help/ Even in C++, what I stated earlier still applies. .DEF files are not pre-processed, so unless you preprocess the file yourself, you will need to use separate .DEF files for 32bit and 64bit compilations. -
Windows Notification in Exe2 when Exe2 started from Exe1
Remy Lebeau replied to NamoRamana's topic in Windows API
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.