-
Content Count
2349 -
Joined
-
Last visited
-
Days Won
95
Everything posted by Remy Lebeau
-
Batch Reading Emails from Windows Explorer
Remy Lebeau replied to Mark Williams's topic in General Help
No, it does not, only emails in RFC822 format, like .EML files. -
Why do these 2 statments produce different results ?
Remy Lebeau replied to AndrewHoward's topic in Delphi IDE and APIs
'#$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. -
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
-
Subnet broadcasting is supported only in IPv4, not in IPv6, you have to use multicasting instead.
-
IOTAProcess.ReadProcessMemory / .WriteProcessMemory
Remy Lebeau replied to dummzeuch's topic in Delphi IDE and APIs
That parameter is what I was referring to. -
My custom component becomes NI>
Remy Lebeau replied to alogrep's topic in RTL and Delphi Object Pascal
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. -
IOTAProcess.ReadProcessMemory / .WriteProcessMemory
Remy Lebeau replied to dummzeuch's topic in Delphi IDE and APIs
That would be my assumption as well, since the underlying Win32 API (Read|Write)ProcessMemory() functions return the number of bytes actually read/written. -
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.
-
Did you post this same issue on StackOverflow? https://stackoverflow.com/questions/64581935/
-
And your point of showing that code is .... what, exactly?
-
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.
-
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.
-
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().
-
Showing secondary form from the main form OnShow event
Remy Lebeau replied to Mowafaq's topic in Cross-platform
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); -
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.
-
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;
-
Showing secondary form from the main form OnShow event
Remy Lebeau replied to Mowafaq's topic in Cross-platform
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; -
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.
-
Random unsigned 64 bit integers
Remy Lebeau replied to julkas's topic in Algorithms, Data Structures and Class Design
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); -
Is the missing System.TStringHelper SetChars in Chars property on purpose ?
Remy Lebeau replied to Rollo62's topic in RTL and Delphi Object Pascal
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. -
Is the missing System.TStringHelper SetChars in Chars property on purpose ?
Remy Lebeau replied to Rollo62's topic in RTL and Delphi Object Pascal
(Low|High)(string) were introduced in XE3, the same version that introduced {$ZEROBASEDSTRINGS} and TStringHelper. -
Is the missing System.TStringHelper SetChars in Chars property on purpose ?
Remy Lebeau replied to Rollo62's topic in RTL and Delphi Object Pascal
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}. -
Is the missing System.TStringHelper SetChars in Chars property on purpose ?
Remy Lebeau replied to Rollo62's topic in RTL and Delphi Object Pascal
FYI, Embarcadero was considering making Delphi strings immutable in the NEXTGEN compilers, but they ultimately decided against that. -
Is the missing System.TStringHelper SetChars in Chars property on purpose ?
Remy Lebeau replied to Rollo62's topic in RTL and Delphi Object Pascal
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.