Jump to content

Remy Lebeau

Members
  • Content Count

    2349
  • Joined

  • Last visited

  • Days Won

    95

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Batch Reading Emails from Windows Explorer

    No, it does not, only emails in RFC822 format, like .EML files.
  2. Remy Lebeau

    Why do these 2 statments produce different results ?

    '#$02' (w/ quotes) is a string literal consisting of 4 distinct characters: '#', '$', '0', '2'. The string you are searching, 'bla,bla,#$02,bla', contains that 4-char substring in it, hence the result is > 0. #$02 (w/o quotes) is a single Char whose numeric value is 2. The string you are searching does not contain that character in it, hence the result is 0.
  3. Remy Lebeau

    Any luck using 10.4.1 on Windows 8.1?

    Support for OSX 64-bit was introduced in 10.3.2: https://blog.marcocantu.com/blog/2019-july-building-mocOS64-apps-delphi.html Support for Android 64-bit was introduced in 10.3.3: https://blog.marcocantu.com/blog/2019-delphi-android-64bit-rad-1033.html
  4. Remy Lebeau

    Find UDP Server

    Subnet broadcasting is supported only in IPv4, not in IPv6, you have to use multicasting instead.
  5. That parameter is what I was referring to.
  6. Remy Lebeau

    My custom component becomes NI>

    Run the component code in the debugger, and put a DATA BREAKPOINT on the pointer variable that is turning nil unexpectedly, and when the breakpoint is hit then look at the call stack to see who is setting the pointer to nil.
  7. That would be my assumption as well, since the underlying Win32 API (Read|Write)ProcessMemory() functions return the number of bytes actually read/written.
  8. Remy Lebeau

    Need help with IDhttp and Thread

    It is generally considered bad practice to ask the same question across multiple forums at the same time. It is rude to the people who decide to help you. If you are getting help somewhere, STAY THERE. I spent the last 3 days of my time helping you, just for you to now throw away everything I had given you and go somewhere else to start over. That is a slap in my face. Thanks for nothing.
  9. Remy Lebeau

    Need help with IDhttp and Thread

    Did you post this same issue on StackOverflow? https://stackoverflow.com/questions/64581935/
  10. Remy Lebeau

    Need help with IDhttp and Thread

    And your point of showing that code is .... what, exactly?
  11. Remy Lebeau

    Need help with IDhttp and Thread

    Why is it hard? TTask has constructors that take a TThreadPool as input. Create a single TThreadPool, configure it as needed, such as MaxWorkerThreads, and then pass that pool to each TTask you create.
  12. Remy Lebeau

    How to read a table every x time.

    Why not a simple timer instead of a sleeping loop?
  13. Remy Lebeau

    Need help with IDhttp and Thread

    Connect the TTask objects to a shared TThreadPool that limits how many threads can run at a time. Or, use a shared Win32 semaphore to limit how many TTask threads can send HTTP requests at a time.
  14. Remy Lebeau

    Need help with IDhttp and Thread

    Try something like this: procedure TForm1.Button1Click(Sender: TObject); var i: integer; lPaths: array of string; begin SetLength(lPaths, ListBox1.Items.Count); for i := 0 to ListBox1.Items.Count-1 do begin lPaths[i] := ListBox1.Items.Strings[i]; end; TParallel.&For(0, ListBox1.Items.Count-1, procedure(AIndex: Integer) var lPath: string; lHTTP: TIdHTTP; IdSSL: TIdSSLIOHandlerSocketOpenSSL; URI: TIdURI; begin lPath := lPaths[AIndex]; lHTTP := TIdHTTP.Create(nil); try lHTTP.ReadTimeout := 30000; lHTTP.HandleRedirects := True; IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP); IdSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; IdSSL.SSLOptions.Mode := sslmClient; lHTTP.IOHandler := IdSSL; lHTTP.AllowCookies := True; lHTTP.Request.UserAgent := 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'; lHTTP.Request.Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; lHTTP.Request.AcceptLanguage := 'en-GB,en;q=0.5'; lHTTP.Get(lPath); except on E: EIdHTTPProtocolException do begin if E.ErrorCode = 404 then begin TThread.Queue(nil, procedure begin Form1.ListBox2.Items.Add(lPath); end ); end; Exit; end; end; TThread.Queue(nil, procedure begin Form1.Memo1.Lines.Add(lPath); end ); end ); end; Alternatively: function TForm1.Download(lPath: string): ITask; begin Result := TTask.Run( procedure var lHTTP: TIdHTTP; IdSSL: TIdSSLIOHandlerSocketOpenSSL; URI: TIdURI; begin lHTTP := TIdHTTP.Create(nil); try lHTTP.ReadTimeout := 30000; lHTTP.HandleRedirects := True; IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP); IdSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; IdSSL.SSLOptions.Mode := sslmClient; lHTTP.IOHandler := IdSSL; lHTTP.AllowCookies := True; lHTTP.Request.UserAgent := 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'; lHTTP.Request.Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; lHTTP.Request.AcceptLanguage := 'en-GB,en;q=0.5'; lHTTP.Get(lPath); except on E: EIdHTTPProtocolException do begin if E.ErrorCode = 404 then begin TThread.Queue(nil, // or Synchronize() procedure begin ListBox2.Items.Add(lPath); end ); end; Exit; end; end; TThread.Queue(nil, // or Synchronize() procedure begin Memo1.Lines.Add(lPath); end ); end ); end; procedure TForm1.Button1Click(Sender: TObject); var i: integer; TaskList: TList<ITask>; T: ITask; begin TaskList := TList<ITask>.Create; try for i := 0 to ListBox1.Items.Count-1 do begin T := Download(ListBox1.Items.Strings[i]); TaskList.Add(T); end; while not TTask.WaitForAll(TaskList.ToArray, 500) do begin CheckSynchronize(); Update; end; finally TaskList.Free; end; end; TThread.Synchronize() is synchronous. It sends a request to the main UI thread and waits for it to be processed before exiting back to the calling thread. But the main UI thread is blocked waiting for the TParallel.For() loop to finish running, and so it cannot process TThread.Synchronize() requests in the meantime. Deadlock occurs. See Freeze when use TThread.Synchronize with TParallel or TTask.WaitForAll. TThread.Queue() is asynchronous. It posts a request to an internal queue and then exits immediately, allowing the calling code to continue running. The main UI thread will process the queue at a later time, in this case after the TParallel.For() loop has finished. That won't work, because TThread.Queue() is asynchronous. When you issue the request to read the ListBox string, it won't be retrieved in time for your TIdHTTP to actually use it. What you could do instead is copy the ListBox strings to a local array before starting the TParallel.For() loop, and then you can access that array directly in your anonymous procedure without needing to synchronize access to it. See the example above. Otherwise, don't use TParallel.For() when you need to perform actions synchronously in the main UI thread. See Using TThread.Synchronize with TTask.WaitForAll and the example above. That is because your lPath variable has not been assigned a value yet when you pass it to TIdHTTP.Get().
  15. No, I meant what I posted. An anonymous procedure is not necessary. TThread.ForceQueue() accepts both standard method closures and anonymous procedures as input, same as TThread.Synchronize() and TThread.Queue() do: type TThreadMethod = procedure of object; TThreadProcedure = reference to procedure class procedure ForceQueue(const AThread: TThread; const AMethod: TThreadMethod; ADelay: Integer = 0); overload; static; class procedure ForceQueue(const AThread: TThread; const AThreadProc: TThreadProcedure; ADelay: Integer = 0); overload; static; What I had left off by accident in my example was just the nil TThread parameter: TThread.ForceQueue(nil, SignForm.Show);
  16. Remy Lebeau

    Need help with IDhttp and Thread

    Did you try profiling your code to see where it is actually spending its time? You are starting a new TTask thread for each ListBox item, but running 100 threads simultaneously will not be faster than processing 100 items in batches of, say, 4-8 threads at a time. Creating more simultaneous threads does not mean the job will be completed faster. If anything, doing so will slow it down, because the OS can only handle so much work simultaneously, the more threads you have running the more time the OS has to spend switching between them. In general, you should not have more threads running than you have CPU cores. Have you tried using TParallel.For() instead? It uses a smaller pool of threads and will manage them according to its actual work load. Or, at the very least, you can use the TTask constructor that allows your tasks to use threads from a TThreadPool that you create. Also, don't pass TThread.Current to TThread.Queue(). That will link the queued operation to the thread, and if the thread terminates before the queued operation is performed, the operation will be canceled, and you won't see the thread's result in your UI. Better to pass nil instead in this case. Also, you are leaking the TIdHTTP object, and thus the TIdSSLIOHandlerSocketOpenSSL and TIdCookieManager objects. Also, there is no need to invoke your TRegEx logic in the context of the main UI thread (I wouldn't even use TRegEx at all), it should be invoked in the context of the TTask thread instead. Only the final ListBox addition (the only part of the thread code that actually touches the UI) should be queued, if it is to be performed at all.
  17. Remy Lebeau

    IPropertyStore

    Ideally yes. Or, you can just reset the variables locally before CoUninitialize() is called (don't forget to clear the PROPVARIANT, too), eg: var Store: IPropertyStore; v: PropVariant; InitHr: HResult; begin if OpenDialog1.Execute then begin InitHr := CoInitialize(nil); if FAILED(InitHr) and (InitHr <> RPC_E_CHANGED_MODE) then OleError(InitHr); try OleCheck(SHGetPropertyStoreFromParsingName(PChar(OpenDialog1.FileName), nil, GPS_READWRITE, IPropertyStore, store)); try OleCheck(store.GetValue(PKEY_Music_AlbumTitle, v)); try if v.vt = VT_EMPTY then Showmessage('Album Title not found') else Showmessage(v.bstrVal); finally PropVariantClear(v); // <-- add this end; finally store := nil; // <-- calls Release() end; finally if SUCCEEDED(InitHr) then CoUninitialize; end; end; end; However, Co(Un)Initialize() really should not be called more than 1 time per thread. For instance, in this case, they should be called at program startup/cleanup only, eg: private InitHr: HResult; procedure TMainForm.FormCreate(Sender: TObject); begin InitHr := CoInitialize(nil); if FAILED(InitHr) and (InitHr <> RPC_E_CHANGED_MODE) then OleError(InitHr); end; procedure TMainForm.FormDestroy(Sender: TObject); begin if SUCCEEDED(InitHr) then CoUninitialize; end; // the above would be better handled by moving Co(Uninitialize)() into // the main DPR directly, before TApplication is initialized, and after // TApplication.Run() is finished, respectively... ... procedure TMainForm.DoSomething; var Store: IPropertyStore; v: PropVariant; begin if OpenDialog1.Execute then begin OleCheck(SHGetPropertyStoreFromParsingName(PChar(openDialog1.FileName), nil, GPS_READWRITE, IPropertyStore, store)); OleCheck(store.GetValue(PKEY_Music_AlbumTitle, v)); try if v.vt = VT_EMPTY then Showmessage('Album Title not found') else Showmessage(v.bstrVal); finally PropVariantClear(v); // <-- still needed end; end; end;
  18. Another option I can think of is to delay showing the secondary Form until after the Main Form is done fully showing itself. For instance, via TThread.ForceQueue(), eg: procedure TMainForm.FormShow(Sender: TObject); begin SetScreensLang; if MustSignIn then begin TabControl1.ActiveTab := PersonalTabItem; TThread.ForceQueue(SignForm.Show); // <-- end else begin CheckAlerts; TabControl1.ActiveTab := AlertsTabItem; AlertsTabItemClick(nil); end; end;
  19. Remy Lebeau

    Need help with IDhttp and Thread

    That StackOverflow post already showed working code. If it is not working for you, then you are likely using it incorrectly, but we can't see the code you are using. Please show YOUR actual code that you are having trouble with, and explain HOW it is not working for you exactly.
  20. That should be easy enough to account for, using Random(16) to generate 4 random bits that can be shifted into the UInt64's bits 0, 31, 32, and 63 which the shifted Random(MaxInt) values will never fill in, eg: var Num, Bits: UInt64; Num := (UInt64(Random(maxInt)) shl 32) or UInt64(Random(maxInt)); Bits := UInt64(Random(16)); Num := Num or (Bits and 1); Num := Num or ((Bits and 2) shl 30); Num := Num or ((Bits and 4) shl 30); Num := Num or ((Bits and 8) shl 60);
  21. That makes no sense. {$ZEROBASEDSTRINGS} is local to the unit only. Its use depends on whether YOU want to access YOUR strings inside YOUR unit using 0-based or 1-based indexes, The IDE settings have nothing to do with that.
  22. (Low|High)(string) were introduced in XE3, the same version that introduced {$ZEROBASEDSTRINGS} and TStringHelper.
  23. That is easy enough to account for by using Low(String), eg: var s: String; c: Char; ... c := s.Chars[ZeroBasedIndex]; s[Low(s)+ZeroBasedIndex] := ...; Low(s) will be 0 for {$ZEROBASEDSTRINGS ON}, and 1 for {$ZEROBASEDSTRINGS OFF}.
  24. FYI, Embarcadero was considering making Delphi strings immutable in the NEXTGEN compilers, but they ultimately decided against that.
  25. You do know that is no longer necessary starting with RAD Studio 10.4, don't you? Embarcadero has moved mobile platforms away from ARC memory management for objects, and has done away with all of the NEXTGEN compiler features, which includes 0-based string indexing on mobile. Mobile now defaults to 1-based indexing, just like desktop platforms. If you really want to continue using 0-based string indexing, you can use {$ZEROBASEDSTRINGS ON}, or TStringHelper.
×