Jump to content

Remy Lebeau

Members
  • Content Count

    2276
  • Joined

  • Last visited

  • Days Won

    92

Everything posted by Remy Lebeau

  1. 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.
  2. 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.
  3. 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); ...
  4. Remy Lebeau

    Where can I download Indy document?

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

    How to save 'zip' file to external SD card?

    Permissions are just string values. The string value of EXTERNAL_MANAGE_STORAGE is 'android.permission.MANAGE_EXTERNAL_STORAGE'. However, granting that permission appears to be different than granting other permissions, per Android's documentation: In earlier versions, TPath.GetSharedDocumentsPath() used to call Android's Context.getExternalFilesDir() method, but nowadays it calls Environment.getExternalStoragePublicDirectory() instead. Maybe Google changed where that latter API's folder is located, compared to the former API? I don't know. One thing to note from the Context.getExternalFilesDir() documentation: So, you might want to check if your device is emulating shared storage or not. Also, Delphi's Androidapi.IOUtils unit has a public GetExternalDocumentsDir() function, which calls Android's Context.getExternalFilesDir() method. Maybe try using that function instead of using TPath.GetSharedDocumentsPath().
  6. Remy Lebeau

    Take image and sent it via email

    Use an emulator
  7. True, which is why they are not being auto-generated right now. Almost. There are some small differences between older and newer DPKs.
  8. Remy Lebeau

    Recomnended Email and Webserver Components

    I'm aware of that. Though, there are still environments that do use old versions. And Indy does still support old compilers for now. Indy 11 will drop the old compilers, so maybe at that time, or maybe in Indy 12, we'll drop old OpenSSL, too
  9. Remy Lebeau

    How to save 'zip' file to external SD card?

    In case you have to call RequestPermissions(), are you waiting for request_Result() to be called before you attempt to access the SD card? RequestPermissions() is asynchronous, so your code logic should look something like this: procedure TForm_Main.SaveZipFile; var iZipFile: TZipFile; begin iZipFile := TZipFile.Create; try iZipFile.Open(System.IOUtils.TPath.Combine(System.IOUtils.TPath.GetSharedDocumentsPath, 'Folder-Content.zip'), zmWrite); //... finally iZipFile.Free; end; end; procedure TForm_Main.request_Result(const aPermissions: TClassicStringDynArray; const aGrantResults: TClassicPermissionStatusDynArray); begin if (Length(aGrantResults) = 1) and (aGrantResults[0] = TPermissionStatus.Granted) then SaveZipFile; end; procedure TForm_Main.request_Permissions(aPermission: String); begin PermissionsService.RequestPermissions([aPermission], request_Result); end; ... sWriteExternalStorage := JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE); if PermissionsService.IsPermissionGranted(sWriteExternalStorage) then SaveZipFile else request_Permissions(sWriteExternalStorage); Though, I would probably want to generalize that a bit further, eg: procedure TForm_Main.SaveZipFile; var iZipFile: TZipFile; begin iZipFile := TZipFile.Create; try iZipFile.Open(System.IOUtils.TPath.Combine(System.IOUtils.TPath.GetSharedDocumentsPath, 'Folder-Content.zip'), zmWrite); // ... finally iZipFile.Free; end; end; procedure TForm_Main.doTask_withPermission(const aPermission: String; AProc: TProc); begin if PermissionsService.IsPermissionGranted(aPermission) then begin AProc; end else begin PermissionsService.RequestPermissions([aPermission], procedure(const aPermissions: TArray<String>; const aGrantResults: TArray<TPermissionStatus>); begin if (Length(aGrantResults) = 1) and (aGrantResults[0] = TPermissionStatus.Granted) then begin AProc; end; end ); end; end; ... doTask_withPermission( JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE), SaveZipFile ); No. It is a permission like any other. The user has to grant access to it. Are you getting any kind of runtime error about the file not being created? If not, have you tried looking through the device to see if the file is getting created somewhere else that you are not expecting? On Android, TPath.GetSharedDocumentsPath() attempts 2 different folder paths - first, it tries to retrieve the user's Documents folder, and if that fails then it retrieves the user's Downloads folder and replaces the last subfolder in the path with a hard-coded 'Documents'.
  10. Remy Lebeau

    How to save 'zip' file to external SD card?

    What problem are you having with it, exactly? Can you be more specific? What does your code look like that is not working? Did you grant permission to your app to access the external SD card? Do you have code that prompts the user if permission has not been granted yet?
  11. That is the approach that Indy took a long time ago. It has a master database of its units and their characteristics, and then there is a Package Generator app that reads in that database and spits out DPK files (no DPROJ, CBPROJ, or GROUPPROJ files at this time) for all of the supported compiler versions (well, not all of them, as the app hasn't been updated in a long time - packages for modern compiler versions have largely just been copied from earlier versions and edited by hand).
  12. Remy Lebeau

    Recomnended Email and Webserver Components

    Yes. Unfortunately, it still would have been a big update, since OpenSSL basically redesigned their API in v1.1.0. All of the structures were changed, and many functions were either renamed or dropped. So, while making the existing SSLIOHandler component be more version-aware to support both pre-1.1.0 and post-1.1.0 libraries would have been ideal, the API changes really warranted new SSLIOHandler components to keep the two API designs separate. But, to be fair, I wasn't involved in the development of the new components, so I couldn't say if the new design is actually overkill or not.
  13. Remy Lebeau

    IdSNTP library Delphi 10.3

    TIdSNTP, and Indy in general, does not connect you to the Internet. You have to already be connected beforehand. It would be more accurate to say that you are using TIdSNTP to connect to a remote time server that is accessible over the Internet. What type of exception are you seeing, exactly? What does it say? Please be more specific.
  14. Remy Lebeau

    Recomnended Email and Webserver Components

    None taken, and yes, there are several PRs that have been pending for too long. But, during that time, there have also been several other user-submitted PRs and non-PR changes alike that have been merged. So, it is not like I'm ignoring everything that gets submitted to me. Regarding the PRs that are still pending, most of them were not ready-to-merge as-is at the time they were submitted, which is why I didn't just blindly merge them. Looking at them now, I can probably merge the smaller ones soon, but if the "big" PR you are referring to is the OpenSSL 1.1+ one, that is a HUGE PR to merge - 246 changed files, and 200 comments/commits trying to iron out its issues. I haven't reviewed its code in full yet.
  15. Remy Lebeau

    KeyDown and Shift state

    Well, not exactly the same. Keep in mind that keybd_event() (and mouse_event(), too) can only inject 1 event at a time into the input queue, thus other events from other sources could interweave in between the synthetic events. That may have subtle side-effects. Wherever SendInput() is designed to inject multiple events atomically into the queue, thus no interweaving can happen anymore.
  16. Remy Lebeau

    KeyDown and Shift state

    The value you are passing to the last (cbSize) parameter of SendInput() is wrong. It wants you to give it the size of a single TInput, but you are giving it the full size of your entire array instead. You are also using the wrong TInput field inside of the array. Since you are sending virtual key codes and not hardware scan codes, you should be using the TInput.ki.wVk field rather than the TInput.ki.wScan field. Try this instead: class procedure TRoutinesEx.ShiftTab; var Inputs: array[0..3] of TInput; begin ZeroMemory(@Inputs, SizeOf(Inputs)); Inputs[0].Itype := INPUT_KEYBOARD; Inputs[0].ki.wVk := VK_SHIFT; Inputs[1].Itype := INPUT_KEYBOARD; Inputs[1].ki.wVk := VK_TAB; Inputs[2].Itype := INPUT_KEYBOARD; Inputs[2].ki.dwFlags := KEYEVENTF_KEYUP; Inputs[2].ki.wVk := VK_TAB; Inputs[3].Itype := INPUT_KEYBOARD; Inputs[3].ki.dwFlags := KEYEVENTF_KEYUP; Inputs[3].ki.wVk := VK_SHIFT; SendInput(Length(Inputs), Inputs[0], SizeOf(TInput)); end;
  17. Remy Lebeau

    KeyDown and Shift state

    Tab (and arrows) is reserved for focus navigation by default, that is true. But, if you want to handle it yourself, you can request it from the OS so it arrives as normal messages. For instance, by intercepting WM_GETDLGCODE to return DLGC_WANTTAB. Some child controls have properties to handle this for you, but TForm does not, so you have to implement it manually.
  18. Remy Lebeau

    Recomnended Email and Webserver Components

    Indy does not implement OAuth at this time, that is true. There are open tickets for adding that feature. However, Indy does work fine with GMail if you enable 2FA and use an app-specific password. Indy has WIP support for OpenSSL 1.1+/TLS 1.3 in this GitHub pull request: https://github.com/indySockets/indy/pull/299 Indy's official GitHub repo has the latest version: https://github.com/IndySockets/Indy/ Indy is still supported (by me). Though, development of new feature for it tends to be a bit on the slow side, for a number of reasons, namely that I'm a sole developer, I don't have a lot of free time, I don't have a working IDE right now, etc etc etc. However, Indy is open-source, users are always free and encouraged to submit new code. Most don't, though.
  19. Remy Lebeau

    FileOpen dialog not showing all files

    What do you mean?
  20. Remy Lebeau

    Cross platform HTTP client with proxy support?

    Indy's support for OpenSSL 1.1+/TLS 1.3 is a WIP that is available in this pull request: https://github.com/IndySockets/Indy/pull/299 Correct, Indy does not support platform-specific implementations at this time. Delphi's native HTTP client libraries do, though. Did you report that to Embarcadero? Note that Indy is not specifically tied to OpenSSL exclusively. That is just Indy's default SSL/TLS library since it is cross-platform, but you can use any other library you want, all you need is a TIdSSLIOHandlerSocketBase-derived class that wraps the library's API. Some 3rd party SSL/TLS libraries provide such a class, but for others you will have to write (or find) your own class. There are only a handful of virtual methods that need to be implemented (namely, to handle the handshake, to read/write data, etc).
  21. I can't answer that. I have very little experience working with certificates.
  22. The original curl request of this discussion thread uses the "-F" parameter, whereas your request uses the "-d" parameter instead. If you read curl's documentation, the "-F" parameter sends an HTTP "multipart/form-data" request, whereas the "-d" parameter sends an HTTP "application/x-www-form-urlencoded" request. The TIdHTTP.Post() method has separate overloads for those types of requests. Your code is using the wrong overload. You need to use the overload that takes a TStrings, not a TIdMultipartFormDataStream, eg: Result := False; IdHTTP := TIdHTTP.Create(nil); try //'curl https://api.dropbox.com/oauth2/token \ -d code=<AUTHORIZATION_CODE> \ -d grant_type=authorization_code \ -u <APP_KEY>:<APP_SECRET>' ssl := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP); ssl.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; ssl.SSLOptions.Mode := TidSSLMode.sslmUnassigned; ssl.SSLOptions.VerifyMode := []; ssl.SSLOptions.VerifyDepth := 0; //ssl.OnStatusInfoEx := __IdSSLIOHandlerSocketOpenSSL1StatusInfoEx; IdHTTP.HandleRedirects := True; IdHTTP.IOHandler := ssl; IdHTTP.Request.BasicAuthentication := True; IdHTTP.Request.Username := 'MY_APP_KEY'; IdHTTP.Request.Password := 'MY_APP_SECRET'; try Params := TStringList.Create; try Params.Add('code=MY_AUTH_CODE'); Params.Add('grant_type=authorization_code'); aStatus := IdHTTP.Post('https://api.dropbox.com/oauth2/token', Params); Result := True; finally Params.Free; end; except on E: EIdHTTPProtocolException do begin aStatus := ErrorResult(IdHTTP.ResponseText); end; end; (**) finally IdHTTP.Free; end;
  23. How are you configuring the SSLIOHandler? Does the Windows service have permission to access the certificate file?
  24. Remy Lebeau

    TIdServerIOHandlerSSLOpenSSL root certficate error

    Duplicate of
  25. Remy Lebeau

    Threaded FTP and Timeouts

    Are you using TIdFTP in Active mode (TIdFTP.Passive=False, which is the default) or in Passive mode (TIdFTP.Passive=True)? Different modes use different timeouts, but since you are setting them all, you certainly should be getting a timeout error one way or the other. However, your try..except is catching only exceptions derived from EIdSocketError, but for instance EIdAcceptTimeout (which TIdFTP raises in Active mode if the server doesn't connect to TIdFTP within the ListenTimeout interval) is not derived from EIdSocketError. In the code you have shown, I don't really see a need to have the try..except differentiate which type of exception is raised. It should be catching all of them. You are also not synchronizing with the UI thread when accessing your UI controls. Try something more like this instead: constructor TLoadThread.Create; begin inherited Create(True); FreeOnTerminate := True; end; procedure TLoadThread.Execute; begin try Form2.FTP1.Connect; try Form2.Ftp1.Get('file1.txt', TPath.Combine(TPath.GetTempPath, 'file1.txt'), True, False); Form2.Ftp1.Get('file2.txt', TPath.Combine(TPath.GetTempPath, 'file2.txt'), True, False); finally Form2.FTP1.Disconnect; end; except on e: Exception do begin TThread.Synchronize(nil, procedure begin if e is EIdSocketError then Form2.AddToMemo('Error: ' + IntToStr(EIdSocketError(e).LastError) + ' ' + e.Message) else Form2.AddToMemo('Error: ' + e.Message); end ); end; end; end; procedure TForm2.ThreadTerminated(Sender: TObject); begin xThread := nil; Loading := False; AddToMemo('Thread Terminated'); end; procedure TForm2.FTP1Status(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string); begin TThread.Queue(nil, procedure begin AddToMemo(AStatusText); end ); end; procedure TForm2.AddToMemo(const AMsg: string); begin Memo1.Lines.Add(DateTimeToStr(Now()) + ' ' + AMsg); end; Alternatively, you can move your exception logging to the thread's OnTerminate event handler instead, eg: constructor TLoadThread.Create; begin inherited Create(True); FreeOnTerminate := True; end; procedure TLoadThread.Execute; begin Form2.FTP1.Connect; try Form2.Ftp1.Get('file1.txt', TPath.Combine(TPath.GetTempPath, 'file1.txt'), True, False); Form2.Ftp1.Get('file2.txt', TPath.Combine(TPath.GetTempPath, 'file2.txt'), True, False); finally Form2.FTP1.Disconnect; end; end; procedure TForm2.ThreadTerminated(Sender: TObject); var exc: Exception; begin xThread := nil; Loading := False; if TThread(Sender).FatalException <> nil then begin exc := Exception(TThread(Sender).FatalException); if exc is EIdSocketError then AddToMemo('Error: ' + IntToStr(EIdSocketError(exc).LastError) + ' ' + e.Message) else AddToMemo('Error: ' + e.Message); end; AddToMemo('Thread Terminated'); end; procedure TForm2.FTP1Status(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string); begin TThread.Queue(nil, procedure begin AddToMemo(AStatusText); end ); end; procedure TForm2.AddToMemo(const AMsg: string); begin Memo1.Lines.Add(DateTimeToStr(Now()) + ' ' + AMsg); end;
×