Jump to content

Remy Lebeau

Members
  • Content Count

    2337
  • Joined

  • Last visited

  • Days Won

    95

Everything posted by Remy Lebeau

  1. Remy Lebeau

    TListView filled by Thread = Freeze

    I always advise people NOT to rely on the TThread destructor terminating and waiting on the running thread, as doing so has been the source of many headaches in the past. Terminating and waiting on the running thread should always be an explicit operation before destroying the TThread object, IMHO.
  2. Remy Lebeau

    TListView filled by Thread = Freeze

    Yes. Get rid of the use of TThread.CurrentThread when calling TThread.Queue(), pass nil instead, like I described earlier. Chances are that the thread is terminating before the Queue() requests are processed by the main thread, so they are getting cancelled. I would also suggest getting rid of the final call to TThread.Queue() after the loop, use the thread's OnTerminate event instead. Try this: procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin StopThread; end; procedure TForm1.StopThread; begin if Assigned(FMyThread) then begin FMyThread.Terminate; FMyThread.WaitFor; end; end; procedure TForm1.Button2Click(Sender: TObject); begin FMyThread := TThread.CreateAnonymousThread( procedure var I: Integer; begin for I := 1 to MaxValue do begin TThread.Queue (nil, procedure begin ListView1.Items.Add.Text := 'Th: ' + I.ToString; Button2.Text := I.ToString; end ); if TThread.CurrentThread.Terminated or Application.Terminated then Exit; end; end ); FMyThread.FreeOnTerminate := False; FMyThread.OnTerminate := ThreadFinished; FMyThread.Start; AniIndicator1.Visible := True; AniIndicator1.Enabled := True; ListView1.Enabled := False; ListView1.BeginUpdate; end; procedure TForm1.ThreadFinished(Sender: TObject); begin TThread.ForceQueue(nil, FMyThread.Free); FMyThread := nil; NotifyComplete; end; procedure TForm1.NotifyComplete; begin ListView1.EndUpdate; ListView1.Enabled := True; AniIndicator1.Enabled := False; AniIndicator1.Visible := False; end;
  3. Remy Lebeau

    TListView filled by Thread = Freeze

    Note that passing a TThread object in the 1st parameter of TThread.Queue() will associate the request with that thread. If the thread terminates before the request is processed by the main thread, the request will be cancelled. The reason for this is to handle cases where a request accesses data/objects that exist inside the thread, you don't want the thread termination to invalidate that data before the request can use it. But, if the request does not depend on the thread, ie the anonymous procedure is able to capture everything it needs to run independantly, then you should set the 1st parameter to nil instead.
  4. Remy Lebeau

    TListView filled by Thread = Freeze

    It is OK to wait on the thread if you use TThread.WaitFor() instead of WaitForSingleObject() directly. When TThread.WaitFor() is called in the main thread, it dispatches pending TThread.Synchronize()/TThread.Queue() requests while waiting for the thread to terminate. Just don't use TThread.WaitFor() with TThread.FreeOnTerminate=True, or else TThread.WaitFor() will crash when the thread actually terminates. If you do use WaitForSingleObject() directly, you can call it in a loop with a timeout so that you can call CheckSynchronize() periodically. Or, you can use (Msg)WaitForMultipleObjects() instead, waiting on both the thread and the global SyncEvent (and the message queue) at the same time. In this particular example, yes, since the thread does not depend on the results of the sync requests. But that is not always the case, sometimes Synchronize() is actually needed.
  5. The mouse could have moved between the time it took the button click to be queued and processed and the time the dialog is displayed. If the DLL is being called in the context of the click handler, then GetMessagePos() would make more sense. But again, the button could have been invoked by the keyboard (or even programmably) instead of by the mouse. This is why I think it is better to hook and intercept the original click event itself so you can detect which button HWND is triggering the event in the first place.
  6. Since you are not in control of the UI that hosts the buttons you are interested in, then you don't have direct access to know which button is being clicked on. Period. However, there may be a way to determine this indirectly. If the buttons in question are standard Win32 BUTTON controls, then you might try using SetWindowsHookEx() to catch their BN_CLICKED notifications, which carry each clicked button's HWND. Or, you might try using SetWinEventHook() to catch EVENT_OBJECT_INVOKED events, which also carry each clicked button's HWND.
  7. Remy Lebeau

    AllocHwnd + TTimer = lag?

    Yes, and not only that, but it allocates its own window internally to handle those messages. So it doesn't really matter if you create your own window or not, each window will handle only its own messages, but everything is going through a single message queue. Creating your own window will not cause such issues for TTimer. And your own window will not pick up any messages that are not specifically targeting that window. It is, however, possible that your own messages can cause delays in TTimer, because WM_TIMER is a low-priority synthesized message, so it will not be generated until the message queue is idle, unless you force it. So don't overwhelm the message queue unnecessarily, regardless of which windows are involved.
  8. Did you try GetFocus(), like I suggested?
  9. Is your routine being called inside of the button click handler? Or, is the routine inside of the DLL that the button handler calls into? Are you handling the button click event directly? If so, then it tells you the button that was clicked. Otherwise, if the button in question took input focus when it was clicked (most buttons do) then trying using GetFocus() instead of GetActiveWindow().
  10. Remy Lebeau

    How to connect to office 365 using proxy server

    Does the proxy require the client to use HTTPS rather than HTTP? TIdConnectThroughHttpProxy has no concept of HTTPS. If the proxy requires HTTPS, you could try using a TIdSSLIOHandlerSocketBase-derived component, such as TIdSSLIOHandlerSocketOpenSSL, instead of TIdIOHandlerStack, and set the IOHandler's PassThrough property to false before connecting. Though, I think that will also affect TIdIMAP4, too. Does the 403 error contain any content on the wire (TIdConnectThroughHttpProxy does not expose access to this) to explain why the connection is being rejected?
  11. Remy Lebeau

    How to connect to office 365 using proxy server

    Does the proxy in question require its own authentication? If so, does it support BASIC authentication, or does it require a different authentication?
  12. Have you read Delphi's JSON documentation yet? Particularly the "Reading a JSON With Different Frameworks" examples in the Readers and Writers JSON Framework section. Also see Readers and Writers JSON Framework: Reading JSON Objects
  13. Remy Lebeau

    Error in latest IdIOHandler.

    Fixed
  14. If you don't want to rely on 3rd party wrappers, it is not very overly complicated to redirect the output and read it manually. Just create a pipe, assign it to the STARTUPINFO passed to CreateProcess(), and then read from the pipe. MSDN documents this task: Creating a Child Process with Redirected Input and Output
  15. Remy Lebeau

    SysUtils, AnsiString

    In that regard, Indy's IIdTextEncoding interface has a public GetBytes() method that would be useful in this example, eg: uses ..., IdGlobal; var patData : TPatData; SurName : String; begin FillChar(@patData, sizeof(patData), 0); SurName := ...; IndyTextEncoding_UTF8.GetBytes(PChar(SurName), Length(SurName), PByte(@patData.SurName[0]), SizeOf(patData.SurName)); ... end;
  16. Remy Lebeau

    SysUtils, AnsiString

    The main problem with TEncoding.GetBytes() is that its 'public' overloads all require the output to be a TBytes array, which you would then have to copy into your record afterwards, eg: var patData : TPatData; SurName : String; Bytes: TBytes; begin FillChar(@patData, sizeof(patData), 0); SurName := ...; Bytes := TEncoding.UTF8.GetBytes(SurName); Move(PBytes(Bytes)^, patData.SurName[0], Math.Min(SizeOf(patData.SurName), Length(Bytes))); ... end; The one overload of GetBytes() that would actually let you output directly into your record without using TBytes is declared as 'strict protected', which means you can't use it without involving some hacks, eg: type TEncodingHelper = class(TEncoding) public function GetBytes(const S: string; Bytes: PBytes; ByteCount: Integer): Integer; end; function TEncodingHelper.GetBytes(const S: string; Bytes: PBytes; ByteCount: Integer): Integer; begin Result := GetBytes(PChar(S), Length(S), Bytes, ByteCount); end; var patData : TPatData; SurName : String; begin FillChar(@patData, sizeof(patData), 0); SurName := ...; TEncodingHelper(TEncoding.UTF8).GetBytes(S, PByte(@patData.SurName[0]), SizeOf(patData.SurName)); ... end; Or: type TEncodingHelper = class helper for TEncoding public function GetBytes(const S: string; Bytes: PByte; ByteCount: Integer): Integer; end; function TEncodingHelper.GetBytes(const S: string; Bytes: PByte; ByteCount: Integer): Integer; begin Result := Self.GetBytes(PChar(S), Length(S), Bytes, ByteCount); end; var patData : TPatData; SurName : String; begin FillChar(@patData, sizeof(patData), 0); SurName := ...; TEncoding.UTF8.GetBytes(S, PByte(@patData.SurName[0]), SizeOf(patData.SurName)); ... end; The alternative would be to use System.LocaleCharsFromUnicode() instead, eg: var patData : TPatData; SurName : String; begin FillChar(@patData, sizeof(patData), 0); SurName := ...; LocaleCharsFromUnicode(CP_UTF8, 0, PChar(SurName), Length(SurName), @patData.SurName[0], sizeof(patData.SurName), nil, nil); ... end;
  17. I don't understand what you mean. Please clarify. Is the event assigned a handler? If so, is the handler being called? If so, is the provided TIdSocketHandle object filled in with the correct port information? I don't see any port number being assigned in that code. So, unless the port number is assigned elsewhere after TTetheringNetworkServerCommUDP.Create() exits and before TTetheringNetworkServerCommUDP.DoStartServer() is called, then the UDP server will end up binding to a random port for each entry created in FUDPServer.Bindings. Really? uses ..., IdSocketHandle, IdUDPSerrver; ... var UDPServer: TIdUDPServer; ... UDPServer := TIdUDPServer.Create; UDPServer.ThreadedEvent := True; UDPServer.OnRead := DoUDPRead; UDPServer.OnException := DoUDPException; UDPServer.IPVersion := Id_IPv4; with UDPServer.Bindings.Add do begin IP := '192.168.10.30; Port := 2020; end; UDPServer.Active := True; ... procedure TMyForm.DoUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); begin ... end; procedure TMyForm.DoUDPException(AThread: TIdUDPListenerThread; ABinding: TIdSocketHandle; const AMessage : String; const AExceptionClass : TClass); begin ... end;
  18. Interesting. I'm surprised that no error is being raised if TIdUDPServer is having trouble opening port 2020. Makes me wonder if it is even attempting to open the port in the first place. I don't know how Tethering uses Indy, can you show the actual code that sets up TIdUDPServer? Do you have the same problem if you use TIdUDPServer directly? Do you get an OnBeforeBind event fired for the Binding assigned to port 2020? Well, that makes sense, if there is no port being opened to accept the packets.
  19. No. But, on the other hand, what you provided in your last reply didn't really answer my questions at all. Did you VERIFY (ie, via netstat or lsof) that the IP/Port your server listens on is actually open successfully? Did you VERIFY (ie, with a packet sniffer) that network traffic is actually reaching your listening IP/Port? I'm not a Mac developer. I have no way to test this. But BSD-style socket APIs, like the one Indy uses internally, are largely similar/consistent across different platforms.
  20. Remy Lebeau

    Interfacing Unicode string to DLL PAnsiChar

    Oh, I didn't notice that directive. I don't know if it will work, though. I rarely ever use inlining.
  21. Remy Lebeau

    Interfacing Unicode string to DLL PAnsiChar

    Your 1st approach using StringToAnsiCPointer() will never work, since the AnsiString will go out of scope and be destroyed before the PAnsiChar pointer can be passed to the DLL. Your second approach using an AnsiString typecast at the call site will work fine, though you don't actually need the Length() check at all since an AnsiString consists of a nil pointer when empty, so you can just use that inner pointer as-is, by type-casting the AnsiString to a Pointer before casting to PAnsiChar, eg: function CompilerEncryptFile(const TextFileName: string; Key: TCNCEncryptKey; const EncryptedFileName: string): Longint; begin Result := _CompilerEncryptFile ( PAnsiChar(Pointer(AnsiString(TextFileName))), @Key, PAnsiChar(Pointer(AnsiString(EncryptedFileName))) ); end;
  22. Remy Lebeau

    Range check error.

    Yes, per the documentation:
  23. Remy Lebeau

    Convert C# function to delphi

    That was one of the formats I had tested, but it didn't get the same hash as the decoded base64 from the site's example.
  24. Remy Lebeau

    64 bit compiler problem

    Kind of hard to answer that without seeing your actual Delphi code.
  25. Did you verify that your UDP listening port is actually open, and that inbound data is actually reaching that port? If Select() returns false, it means the underlying OS reported that no data was available before the specified timeout had elapsed. What is your AcceptWait set to exactly?
×