-
Content Count
2914 -
Joined
-
Last visited
-
Days Won
130
Everything posted by Remy Lebeau
-
Runtime create new "fields" with RTTI on a tComponent
Remy Lebeau replied to microtronx's topic in RTL and Delphi Object Pascal
All of that applies to T(Object)Dictionary, as well. If the TComponent or associated object is freed, you have to manually remove them from the dictionary to avoid dangling pointers. TDictionary doesn't support object management at all. TObjectDictionary, on the other hand, simply lets you specify whether the dictionary owns the objects stored as Keys/Values, in that if an entry is removed/altered in the dictionary, the associate Key/Value objects are freed automatically as needed. But, if those same objects are freed external to the dictionary, the dictionary entry that refers to them is not removed automatically. You still have to handle that manually. The likelihood is reduced, but not eliminated. It is still possible to have invalid references, if you are not careful. In your code, perhaps, but not the overhead needed to manage the dictionary elements, hash tables, etc. You are actually adding runtime overhead to the program just to gain writing simpler code. But whatever. It is up to each user to decide whatever they are comfortable using. -
Runtime create new "fields" with RTTI on a tComponent
Remy Lebeau replied to microtronx's topic in RTL and Delphi Object Pascal
There is nothing wrong with using the Tag property for pointers. It is specifically intended to hold user-defined data, and intentionally pointer-sized because storing pointers in it is very common practice. It is even documented that way So, there is no misuse here. That is your choice, and it will certainly work. I prefer to use the Tag instead, it offers a 1:1 relationship without any overhead of a lookup table. -
Batch Reading Emails from Windows Explorer
Remy Lebeau replied to Mark Williams's topic in General Help
It is not enough to just add the file to the folder, you would have to actually edit the IndyProtocols package to reference the file. Because it is still being used by the IDE. You would have to remove Indy from the IDE first. Have you read the installation instructions yet? -
Runtime create new "fields" with RTTI on a tComponent
Remy Lebeau replied to microtronx's topic in RTL and Delphi Object Pascal
That is not possible. Delphi is a compiled language, you can't alter object layouts at runtime. The best you could do is store your data in another class/record object that you create at runtime, and then use the component's Tag property to point at that object. -
Batch Reading Emails from Windows Explorer
Remy Lebeau replied to Mark Williams's topic in General Help
Then you don't have your project setup to include that folder in its search paths. Simply adding the unit to the Protocols folder won't magically add it to Indy, since it is already compile. That would require adding the unit to Indy's Protocols package, and then recompiling and reinstalling Indy. -
Binary compatible, in that they have the same memory layout, yes. But you can't pass a TBytes to a 'var TIdBytes' function parameter, and vice versa, unless you use a type-cast, eg: type PIdBytes = ^TIdBytes; var Bytes: TBytes; SetLength(Bytes, 6 + Buffersize); CopyTIdString('Audio1', PIdBytes(@Bytes)^, 0); CopyTIdBytes(RawToBytes(Buffer^, Buffersize), 0, PIdBytes(@Bytes)^, 6, Buffersize); Or maybe just: var Bytes: TBytes; SetLength(Bytes, 6 + Buffersize); CopyTIdString('Audio1', TIdBytes(Bytes), 0); CopyTIdBytes(RawToBytes(Buffer^, Buffersize), 0, TIdBytes(Bytes), 6, Buffersize);
-
CopyTIdBytes() is just a Move() from one TIdBytes to another, using starting source/target indexes and a byte length: procedure CopyTIdBytes(const ASource: TIdBytes; const ASourceIndex: Integer; var VDest: TIdBytes; const ADestIndex: Integer; const ALength: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF} begin {$IFDEF DOTNET} System.array.Copy(ASource, ASourceIndex, VDest, ADestIndex, ALength); {$ELSE} //if these asserts fail, then it indicates an attempted buffer overrun. Assert(ASourceIndex >= 0); Assert((ASourceIndex+ALength) <= Length(ASource)); Move(ASource[ASourceIndex], VDest[ADestIndex], ALength); {$ENDIF} end; CopyTIdString(), on the other hand, is a little more involved, as it first has to convert the input String to bytes in a specified encoding, and then it moves those bytes into the target TIdBytes where specified: procedure CopyTIdString(const ASource: String; var VDest: TIdBytes; const ADestIndex: Integer; const ALength: Integer = -1; ADestEncoding: IIdTextEncoding = nil {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} ); overload; {$IFDEF USE_INLINE}inline;{$ENDIF} begin CopyTIdString(ASource, 1, VDest, ADestIndex, ALength, ADestEncoding {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF} ); end; procedure CopyTIdString(const ASource: String; const ASourceIndex: Integer; var VDest: TIdBytes; const ADestIndex: Integer; const ALength: Integer = -1; ADestEncoding: IIdTextEncoding = nil {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} ); overload; {$IFDEF USE_INLINE}inline;{$ENDIF} var LLength: Integer; {$IFDEF STRING_IS_ANSI} LTmp: TIdWideChars; {$ENDIF} begin {$IFDEF STRING_IS_ANSI} LTmp := nil; // keep the compiler happy {$ENDIF} LLength := IndyLength(ASource, ALength, ASourceIndex); if LLength > 0 then begin EnsureEncoding(ADestEncoding); {$IFDEF STRING_IS_UNICODE} ADestEncoding.GetBytes(ASource, ASourceIndex, LLength, VDest, ADestIndex); {$ELSE} EnsureEncoding(ASrcEncoding, encOSDefault); LTmp := ASrcEncoding.GetChars(RawToBytes(ASource[ASourceIndex], LLength)); // convert to Unicode ADestEncoding.GetBytes(LTmp, 0, Length(LTmp), VDest, ADestIndex); {$ENDIF} end; end; So, given your example, the simplest translation to TBytes would look like this: var Bytes: TBytes; SetLength(Bytes, 6 + Buffersize); TEncoding.Default.GetBytes('Audio1', 1, 6, Bytes, 0); Move(Buffer^, Bytes[6], Buffersize);
-
Batch Reading Emails from Windows Explorer
Remy Lebeau replied to Mark Williams's topic in General Help
Like I said, the IdCoderTNEF unit has been in the Protocols folder for years. But if you are using the version of Indy that shipped pre-installed with each IDE, and they don't have the unit present, then Embarcadero must have chosen not to distribute it. The unit is not actually referenced by the IndyProtocols package, as it is not part of the main Indy packages, just a standalone utility unit, so Embarcadero may have just skipped it as a non-dependency. In which case, you will have to download it from Indy's GitHub repo instead (https://github.com/IndySockets/Indy/blob/master/Lib/Protocols/IdCoderTNEF.pas). -
Batch Reading Emails from Windows Explorer
Remy Lebeau replied to Mark Williams's topic in General Help
Which version of Indy are you looking at? TIdCoderTNEF was introduced in Indy 10, are you looking at Indy 9, perhaps? TIdCoderTNEF is not documented in the help files. No, TIdCoderTNEF still exists in Indy 10. TIdCoderTNEF has been in Indy 10 for over a decade. It is in the IndyProtocols package. There is no documentation available for TIdCoderTNEF, other than comments in the unit itself. I think it was created after the last time the documentation was updated, which was sadly a very long time ago. In a nutshell, you would simply extract a TNEF attachment from a normal email, and pass that attachment to TIdCoderTNEF.Parse(), which will then populate a TIdMessage with the relevant message data (headers, body, attachments, etc) from the TNEF. -
Batch Reading Emails from Windows Explorer
Remy Lebeau replied to Mark Williams's topic in General Help
Since the MSG format is publicly documented, it would be feasible to add support for it in Indy, in a manner that does not depend on Outlook APIs. Similar to how Indy already does for TNEF (winmail.dat) attachments via the TIdCoderTNEF class. But having the TIME to make such an implementation is an issue, for me at least. -
Messages are used, yes (LB_ADDSTRING, etc). But not the message queue, no. The messages in question would be SENT directly to the ListBox's window procedure for immediate processing, not POSTED to the message queue of the thread that owns the ListBox awaiting dispatching to the ListBox's window procedure.
-
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().