Jump to content

Remy Lebeau

Members
  • Content Count

    2312
  • Joined

  • Last visited

  • Days Won

    94

Everything posted by Remy Lebeau

  1. That new snippet has nothing to do with your proposed VirtualTree property. So now I am completely lost as to what you really want. I'm out. Good luck.
  2. Correct, because a component event can have only 1 handler assigned at a time. If you need multiple handlers, you need to chain them together, ie have one handler call the other. After re-reading your original post, it is not clear to me what exactly you really want, so please clarify, preferably with an actual workflow example.
  3. Is this what you are looking for? type TMyComponent = class(...) private FVirtualTree: TBaseVirtualTree; procedure SetVirtualTree(AValue: TBaseVirtualTree); procedure VirtualTreeChange(Sender: TBaseVirtualTree; Node: PVirtualNode); protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; public destructor Destroy; override; published property VirtualTree: TBaseVirtualTree read FVirtualTree write SetVirtualTree; end; destructor TMyComponent.Destroy; begin SetVirtualTree(nil); inherited; end; procedure TMyComponent.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation = opRemove) and (AComponent = FVirtualTree) then FVirtualTree := nil; end; procedure TMyComponent.SetVirtualTree(AValue: TBaseVirtualTree); begin if AValue <> FVirtualTree then begin if FVirtualTree <> nil then begin FVirtualTree.OnChange := nil; FVirtualTree.RemoveFreeNotification(Self); end; FVirtualTree := AValue; if FVirtualTree <> nil then begin FVirtualTree.FreeNotification(Self); FVirtualTree.OnChange := VirtualTreeChange; end; end; end; procedure TMyComponent.VirtualTreeChange(Sender: TBaseVirtualTree; Node: PVirtualNode); begin // do something... end;
  4. Remy Lebeau

    TNetEncoding.URL.Decode question

    IntraWeb is built on top of Indy, and Indy has its own URL encoder/decoder (the TIdURI class).
  5. This is better handled using a waitable mutex or other synchronization object. The thread can wait on the object when it has nothing else to do, and another thread can signal the object to wake up the waiting thread.
  6. Remy Lebeau

    INI problem

    It is really not a good idea to store your data files in your program's installation folder, especially if it is installed under ProgramFiles. Windows has dedicated folders set aside for storing data files, you should create your own subfolder underneath them, such as %APPDATA%\MyApp, etc.
  7. Remy Lebeau

    LSP con C++ Builder 11.1

    Of course they do. New versions go through months of beta testing before release. Sometimes they do, sometimes they don't. It comes down to how they use the product, like anyone else.
  8. Indy supports thread-pooling in its TCP servers. I don't use IntraWeb, so I don't know if/how that can be enabled in the context of IntraWeb, though. But the code logic that is accessing a window handle across thread boundaries, without regard to the owning thread destroying the window at any time, needs to be fixed. That is certainly not an Indy/IntraWeb issue.
  9. Remy Lebeau

    Attachments to iPhones

    But, can you send a PDF from Outlook to iPhone and open it OK? If not, then this is an iPhone issue, otherwise this is an Indy issue. Just a few comments: You are (potentially) leaking the Indy objects (other than the MessageBuilder). You did not say which version of Delphi you are using, but I'm assuming it is an older version where FMX still relied on ARC memory management for objects on mobile platforms. That ARC system was removed in Delphi 10.4, so if you are (or ever will be) using 10.4 or later, you need to Free() unowned objects manually to avoid leaks. You are setting the TIdMessage.ContentType property 3 times, which is (obviously) redundant. Let the TIdMessageBuilderHtml populate the TIdMessage with the bulk of its settings as needed (including the ContentType), then you can provide the rest (Sender, Recipients, Subject, etc). The TIdMessageBuilderHtml.HtmlFiles property is meant for attachments that are referenced by the HTML (images, etc). So, unless you are displaying the PDF embedded in the HTML, non-HTML attachments that the user can download and save should be in the TIdMessageBuilderHtml.Attachments property instead. You don't need to set the SSLIOHandler's Destination, Host, or Port properties manually, Connnect() will handle that for you. I would suggest using the SSLIOHandler's SSLVersions property instead of its Method property. Not all servers require TLS 1.2 yet, so you should also enable TLS 1.1 at least. Connect() should be outside of the try/finally that calls Disconnect() Try this: procedure CreateIndyEmail( const xMsg: string; const xFirstName: string; xNameid: integer; const xEmail: string; const xSubject: string ); var DATA: TIdMessage; SMTP: TIdSMTP; SSL: TIdSSLIOHandlerSocketOpenSSL; begin DATA := TIdMessage.Create(nil); try with TIdMessageBuilderHtml.Create do try Html.Text := xMsg; //if gvbAttached then HtmlFiles.Add( gvAttachedFile ); if gvbAttached then Attachments.Add( gvAttachedFile ); FillMessage( DATA ); finally Free; end; DATA.Subject := xSubject; DATA.From.Address := 'Seniors@xyz.com'; DATA.Sender.Address := 'Seniors@xyz.com'; DATA.ReplyTo.EMailAddresses:= 'Seniors@xyz.com'; DATA.Recipients.EMailAddresses := xEmail; SMTP := TIdSMTP.Create(nil); try with SMTP do begin UseTLS := utUseExplicitTLS; Host := 'smtp.livemail.co.uk'; Port := 587; AuthType := satDefault; Username := 'Seniors@xyz.com'; Password := 'xyz'; end; IdOpenSSLSetLibPath( cLoc ); SSL := TIdSSLIOHandlerSocketOpenSSL.Create(SMTP); with SSL do begin SSLOptions.Mode := sslmClient; SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; SSLOptions.VerifyMode := []; SSLOptions.VerifyDepth := 0; end; SMTP.IOHandler := SSL; SMTP.Connect; try SMTP.Send( DATA ); finally SMTP.Disconnect; end; finally SMTP.Free; end; finally DATA.Free; end; end;
  10. Remy Lebeau

    INI problem

    That is actually incorrect in the case of TIniFile. Internally, it uses the Win32 PrivateProfile API, and if you don't specify a full path then it will use the Windows folder, not the calling process' working directory: This is not the case with TMemIniFile. Agreed.
  11. Remy Lebeau

    Problem with ClientToScreen() on Multiple Monitors

    Is there an error message? And you didn't answer my question - which version of Delphi are you using? Nothing in the code you have shown should be accessing any monitor-specific functionality, AFAIK. Yes.
  12. Remy Lebeau

    Problem with ClientToScreen() on Multiple Monitors

    Which version of Delphi are you using? TControl.ClientToScreen() does not take 2 integers as input, it takes either a TPoint or a TRect. But, fixing that, the rest of the code shown works just fine for me on my 2-monitor system. ClientToScreen() returns screen coordinates, and Popup() takes screen coordinates, and screen coordinates are within the Virtual Screen, not any particular monitor. Regardless of which monitor the PaintBox is displayed on, the PopupMenu appear wherever I click on the PaintBox. So, either you are doing something else wrong that we can't see, or this is a bug in your Delphi's VCL,
  13. Remy Lebeau

    Attachments to iPhones

    I agree, this does not sound like an Indy issue, especially since the PDFs can be opened on older iPhones without changing the Indy code. But, this does not necessarily negate the possibility that the emails might not be getting setup correctly to begin with, and perhaps newer iPhones are just less lenient on mistakes than older iPhones? Can you please show the actual code you are using? And what the raw data of the emails actually looks like? Also, are you having the same problem when sending the emails via Outlook to iPhone? If not, then I suggest you compare the raw data of Outlook's email to Indy's emails to see what is different between them.
  14. Remy Lebeau

    Tstringgrid grid index out of bounds

    Try something more like this: procedure Tmain.buildrackClick(Sender: TObject); var c, r, box : Integer; RackId : string; begin //ShowMessage('draw grid'); setgridrow := -1; setgridcol := -1; box := 0; RackId := ListBox1.Items[ListBox1.ItemIndex]; with StringGrid1 do begin ColCount := 1{FixedCols} + ddata.RackClientDataSet.FieldByName('c').AsInteger; RowCount := 1{FixedRows} + ddata.RackClientDataSet.FieldByName('r').AsInteger; for c := 1{FixedCols} to ColCount - 1 do begin Cells[c, 0{FixedRows-1}] := 'Column ' + IntToStr(c); end; for r := 1{FixedRows} to RowCount - 1 do begin Cells[0{FixedCols-1}, r] := 'Row ' + IntToStr(r); for c := 1{FixedCols} to ColCount - 1 do begin Inc(box); if ddata.TubClientDataSet.Locate('Rackid;Tubid', VarArrayOf([RackId, box]), []) and (ddata.TubClientDataSet.FieldByName('id').AsString <> '') then begin Cells[c, r] := 'Bin ' + IntToStr(box) + ' ' + ddata.TubClientDataSet.FieldByName('id').AsString; if (DBEdit17.Text = RackId) and (DBEdit18.Text = IntToStr(box)) then begin setgridrow := r; setgridcol := c; //ShowMessage('cell is row ' + IntToStr(r) + ' col ' + IntToStr(c)); end; end else begin Cells[c, r] := 'Bin ' + IntToStr(box); end; end; end; DefaultColWidth := Trunc((Width-56)/ColCount); DefaultRowHeight := Trunc((Height-45)/RowCount); ColWidths[0] := 56; end; end; ... if (setgridcol <> -1) and (setgridrow <> -1) then begin //StringGrid1.Refresh; ShowMessage(IntToStr(setgridcol) + ' is col ' + IntToStr(setgridrow) + ' is row - try to set cell selected.'); //StringGrid1.SetFocus; ShowMessage('grid focused'); StringGrid1.Col := setgridcol; ShowMessage('col set'); StringGrid1.Row := setgridrow; ShowMessage('row set'); end;
  15. I vote for this approach. Limiting the listener to run only when the number of connections is below a max threshold is exactly the kind of situation that semaphores are intended for. See Semaphore Objects for details.
  16. Yes. Depending on the hash algorithm used, the risk of hash collisions occurring is small, but not non-existant. In this situation, since you want strings as keys, I would probably opt to define a custom record type that holds a PChar and a length, and then use that record as the dictionary key. You would just need to provide a custom IEqualityComparer implementation to compare the keys. See https://stackoverflow.com/a/27820226/65863 as an example.
  17. Your example is checking for 4 bytes, not 8 bytes. And also, presumably @TokenStart should be FTokenStart, which is already a pointer. But, in any case, if the strings are greater than SizeOf(UInt64) in length, you are only using those first 8 bytes for the "key", so you might not end up with unique keys for different string values.
  18. Remy Lebeau

    Upload files from Delphi to PHP

    It should look like this: uses ..., IdMultipartFormDataStream; var PostData: TIdMultipartFormDataStream; begin PostData := TIdMultipartFormDataStream.Create; try PostData.AddFile('upfile', 'C:\path\filename'); IdHTTP.Post('https://server/UPLOAD.PHP', PostData); finally PostData.Free; end; end;
  19. Remy Lebeau

    MakeDWordIntoIPv4Address backwards result on Android

    GStack.LocalAddress is deprecated, you should be using GStack.GetLocalAddressList() instead. Indy does not currently use WifiManager, that operates at the Android Java layer, but since Indy operates at the native layer then it uses Linux APIs instead to enumerate local IPs, namely gethostname()+getaddrinfo() (there is a TODO in IdStackVCLPosix.pas to use getifaddrs() instead, or Android's NetworkInterface or WifiManager). MakeDWordIntoIPv4Address() is deprecated, use MakeUInt32IntoIPv4Address() instead. Alternatively, have a look at TIdIPAddress.IPv4(AsString) (which uses MakeUInt32IntoIPv4Address() internally). But in any case, the input value is expected to be in network byte order, aka big endian, but WifiInfo.getIpAddress() returns an IPv4 address in host byte order, which may be either little endian or big endian, depending on the device. You can use Integer.reverseBytes() to swap the IP's endian if ByteOrder.nativeOrder() is LITTLE_ENDIAN. Seems Indy's own use of MakeUInt32IntoIPv4Address() may be inconsistent, some places use host byte order, some don't. Also, WifiInfo.getIpAddress() is deprecated since Android 12. You should use LinkProperties now.
  20. Remy Lebeau

    Window /client size bug in 10.4

    Possibly. Embarcadero has been playing around with High-DPI support for a few versions now, which can cause weird side effects.
  21. I don't agree, since the failure is coming straight from the Win32 API itself. But there is definitely something fishy going on here. The only thing that makes sense to me is if maybe the window created by AllocateHWnd() was silently destroyed, but the FWinHandle variable wasn't updated to reflect that. What does IsWindow(FWinHandle) report after AllocateHWnd() returns, and when PostMessage() fails? Even if there were no message *pump* to process messages, there would still be a message *queue* (a thread's message *queue* is created the first time the thread calls any function in user32.dll) when the window is created. PostMessage() fails with error 1400 only if the provided HWND is not a valid window, not if the window is valid but no message pump is running. PostMessage() has no concept of whether a pump is running or how often, it only cares if the queue exists and is not full. And the failing code has nothing to do with IntraWeb itself, but rather with the Win32 API directly.
  22. Remy Lebeau

    Chain of two HttpProxyServer servers

    On the client side, I would strongly advise you NOT to bind the TIdTCPClient and TIdHTTPProxyServer to the same local IP/Port at all. Put the destination proxy server's IP/Port in the DEST command itself, and then have the receiving server extract the values from the command, not from the connected socket. Then you can drop the TIdTCPClient connection after sending the DEST command, and even run the TIdTCPClient on a different PC if you wanted to. constructor TRemoteServer.Create(IP: string; Port: Integer); begin IdHTTPProxyServer1 := TIdHTTPProxyServer.Create(nil); IdHTTPProxyServer1.ReuseSocket := rsTrue; IdHTTPProxyServer1.DefaultPort := Port; IdHTTPProxyServer1.OnHTTPBeforeCommand := HTTPBeforeCommandHandler; var handler := IdHTTPProxyServer1.CommandHandlers.Add; handler.Command := 'DEST'; handler.HelpSuperScript := 'Sets caller as a DESTINATION for remote support (usage DEST PIN IP PORT URL)'; handler.OnCommand := DEST_Handler; if (IP <> '') and (Port <> 0) then begin Writeln('Starting server, binding to: ' + IP + '-' + Port.ToString); var bind := IdHTTPProxyServer1.Bindings.Add; bind.IP := IP; bind.Port := Port; bind.ReuseSocket := rsTrue; end else Writeln('Starting server, default binding'); IdHTTPProxyServer1.Active := True; Writeln('Server running, listening on: ' + IdHTTPProxyServer1.Bindings[0].IP + ' - ' + IdHTTPProxyServer1.Bindings[0].Port.ToString); end; procedure TRemoteServer.DEST_Handler(ASender: TIdCommand); begin Writeln(ASender.RawLine); dest_pin := ASender.Params[0]; dest_ip := ASender.Params[1]; dest_port := ASender.Params[2]; dest_url := ASender.Params[3]; Writeln('Address: ' + dest_ip + ' - ' + dest_port.ToString); end; procedure TRemoteServer.HTTPBeforeCommandHandler(AContext: TIdHTTPProxyServerContext); begin if dest_ip <> '' then begin Writeln('Command redirected to DESTINATION: ' + dest_ip + ' - ' + dest_port.ToString); var tempIO := TIdIOHandlerStack.Create(AContext.OutboundClient); tempIO.ReuseSocket := rsTrue; // tempIO.BoundIP := '10.0.2.4'; //IdHTTPProxyServer1.Bindings[0].IP; tempIO.BoundPort := 443; // IdHTTPProxyServer1.Bindings[0].Port; var tempProxy := TIdConnectThroughHttpProxy.Create(AContext.OutboundClient); tempProxy.Enabled := True; tempProxy.Host := dest_ip; tempProxy.Port := dest_port; tempIO.TransparentProxy := tempProxy; AContext.OutboundClient.IOHandler := tempIO; end; end; procedure TForm1.ConnectToRemote(Connection: string); begin HTTPProxyServer.Active := False; HTTPProxyServer.Bindings.Clear; var bind := HTTPProxyServer.Bindings.Add; bind.IP := ...; bind.Port := ...; bind.ReuseSocket := rsTrue; HTTPProxyServer.Active := True; // Tell server we are there IdTCPClient1.Host := <SERVER IP/URL>; //Connection.Split([':'])[0]; IdTCPClient1.Port := <SERVER PORT>; // Connection.Split([':'])[1].ToInteger; IdTCPClient1.Connect; try IdTCPClient1.SendCmd('DEST 4444 ' + bind.IP + ' ' + bind.Port.ToString + ' https://abc.com'); finally IdTCPClient1.Disconnect; end; end;
  23. Remy Lebeau

    How to use tDateTimePicker for BOTH date and time

    Has that been reported as a bug yet? If not, it should be.
  24. Remy Lebeau

    Using COM to update Word docx files

    Try something like this: procedure update_toc(const docx_file: string); var word, doc: Variant; begin word := CreateOleObject('Word.Application'); doc := word.Documents.Open(WideString(docx_file)); doc.TablesOfContents.Item(1).Update; doc.Close(-1); word.Quit; end;
  25. Remy Lebeau

    quality.embarcadero.com not working?

    My usual QP credentials are not working right now.
×