Jump to content

Remy Lebeau

Members
  • Content Count

    3053
  • Joined

  • Last visited

  • Days Won

    139

Posts posted by Remy Lebeau


  1. I have some issues with your implementation:

    • IPv6 networks are not supported.
    • The client request is needlessly wordy.
    • The client broadcasts to 255.255.255.255 instead of to the subnet's actual broadcast IP. Some routers block traffic to 255.255.255.255 by default.
    • In case the client is run on a machine with multiple networks installed (VPN, etc), there is no option to let the user decide which network to broadcast to.
    • The client is using separate UDP sockets to send the request and read replies. You are forcing the client to receive on a specific listening port. The server is not sending its reply back to the real port that actually sent the request. Most routers will block this traffic if port forwarding is used.  On the server side, always reply to the sending port. On the client side, either 1) let TIdUDPClient read replies on its sending port, or 2) let TIdUDPServer send the request on its listening binding. Use one or the other, not both.
    • Doesn't use Indy's TIdStack.GetLocalAddressList() method to discover local networks (at least on Windows, as it may have issues on Android).

     

    Since your goal is to allow a TCP client to discover a server, I would opt to get rid of the TIdUDPServer on the client side, and just use TIdUDPClient by itself.  Send a request, read a response, done.  You don't need to make that process asynchronous with TIdUDPServer in most use cases.  Using TIdUDPServer on the client side makes more sense if you want to discover multiple servers and then let the user choose between them, or to leave it open so servers can just broadcast their presence over time.

    • Like 3

  2. 22 hours ago, david berneda said:

    In the same process (the ide at designtime).

    21 hours ago, david berneda said:

    Yep ! but I need a cross platform vcl  / fmx way

    Why? You said you are creating IDE design-time packages. So they run in the IDE process, and the IDE is not itself a cross-platform application, it is a Windows-only VCL application.

     

    What am I missing? Can you provide more exact details of what you are actually trying to accomplish?


  3. 12 minutes ago, david berneda said:

    Do you know if there is some global variable in the RTL that can be used to "register" a class or something? Or some neat trick? 😂

    System.Classes.RegisterClass()?  It is not clear what you are actually looking for.  Please clarify.  Can you provide an example of what you want to accomplish?

    12 minutes ago, david berneda said:

    The need is, unit A needs to "talk" to unit B, but they cannot use them, and cannot use any unit C that could act as a common base.

    Maybe using custom window messages, or System.Messaging.TMessageManager ?

    12 minutes ago, david berneda said:

    Main reason is A and B are units that form part of completely different products, different packages that cannot depend between them.

    Are they separate packages in the same process, or are they in separate processes?  It makes a big difference depending on whether you have to cross the process boundary or not.

    12 minutes ago, david berneda said:

    Firemonkey has a nice "service" global registration via interfaces, I'm asking about the same concept in the RTL.

    There is nothing like that framework in the common RTL.  And it wouldn't make sense anyway if your two units can't share a common unit that defines what they need to share.


  4. If I'm reading your codes correctly, you are both assuming that ReturnAddress() itself doesn't have a stack frame, and it is returning the address stored in its caller's stack frame. Which means it won't work correctly if its caller has no stack frame, or if it is called from inside of an inline function.


  5. 7 hours ago, LennyKrost said:

    I'm sorry. CleanTalk always repeat me that all code i write here is seen as SPAM and does not post my replies.

    Never heard of CleanTalk.

    Quote

    How can I send code snippets?

    Are you putting your code snippets in code blocks (the `</>` button on the editor toolbar)?

    7 hours ago, LennyKrost said:

    This is the function that creates the UDPServerSocket

    I'm guessing you did not read the ICS documentation. It says:

    https://wiki.overbyte.eu/wiki/index.php/TWSocketServer

    Quote

    Note that TWSocketServer is only usable for incoming TCP connections. Use TWSocket if you need to use UDP.

    7 hours ago, LennyKrost said:

    And this is the code of Execute method of the thread.

    On a side note: `while PeekMessage() = False do WaitMessage();` is the exact same as calling `GetMessage()` without a loop.

    Quote

    This was full working with ICS V7. Now with ICS 8.71 the OnDataAvailable event is not triggered anymore.

    Makes sense, if you are creating a TCP server socket expecting it to receive UDP traffic. You need a UDP server socket.


  6. 3 hours ago, DelphiUdIT said:

    The only useful tidbits I get from that page are that:

    • the Start Menu doesn't display two shortcuts pointing to the same target
    • the Start Menu tries to block showing Uninstallers.

    Good to know.  Seems to be on track with what I'm seeing.


  7. It turns out that I had a separate "RAD Studio 12" shortcut in the "%APPDATA%\Microsoft\Windows\Start Menu\Programs" folder from an earlier installation.  So the Start Menu was displaying "RAD Studio 12" underneath "R" instead of underneath "Embarcadero RAD Studio 12".  When I deleted the rogue shortcut, "RAD Studio 12" now appears under "Embarcadero RAD Studio 12" as expected.

     

    But, the "Uninstall" shortcut is still missing.  And I have several other programs with "Uninstall" shortcuts that are also missing.

     

    Apparently, the Start Menu doesn't like displaying multiple shortcuts with duplicate names, even though they are in different folders.  Nice one, Microsoft.


  8. I just installed RAD Studio 12.3 on Windows 10.  On my Start Menu, underneath "Embarcadero RAD Studio 12", I see the following shortcuts:

     

    C++Builder 12

    C++Builder 12 (DPI Unaware)

    Delphi 12

    Delphi 12 (DPI Unaware)

    Migration Tool

    RAD Studio 12 (64-bit Initial Release)

    RAD Studio 12 (64-bit Initial Release) (DPI Unaware)

    RAD Studio 12

    RAD Studio 12 (DPI Unaware)

    RAD Studio Command Prompt

    RAD Studio Command Prompt (x64)

    Uninstall

     

    Note the two highlighted items are missing.

     

    However, in the "%ProgramData%\Microsoft\Windows\Start Menu\Programs\Embarcadero RAD Studio 12" folder, I see the following shortcuts:

     

    C++Builder 12

    C++Builder 12 (DPI Unaware)
    Delphi 12
    Delphi 12 (DPI Unaware)

    Migration Tool

    RAD Studio 12 (64-bit Initial Release)
    RAD Studio 12 (64-bit Initial Release) (DPI Unaware)
    RAD Studio 12

    RAD Studio 12 (DPI Unaware)

    RAD Studio Command Prompt

    RAD Studio Command Prompt (x64)
    Uninstall

     

    So, why are these two items not appearing on my Start Menu?  Anyone else ever see this happen?


  9. 4 hours ago, Anders Melander said:

    Another is the PostMessage trick Peter mentioned.

    Or TThread.ForceQueue(), eg:

    procedure TMyForm.Edit1Exit(Sender: TObject);
    begin
      TThread.ForceQueue(nil,
        procedure
        begin
          ShowDialog('It works!');
        end
      );
    end;

     


  10. 24 minutes ago, dummzeuch said:

    Is there a way to call a 64 bit dll from 32 bit Delphi code?

    In a word, no. Certainly not directly, anyway.

    24 minutes ago, dummzeuch said:

    I am aware of this Article on Process Interoperability on Learn Microsoft.com.

    Basically. Load the DLL into a 64bit process, and then use some kind of IPC with the 32bit process.

    24 minutes ago, dummzeuch said:

    The article is using C#, but the technique is not limited to C#. A COM out-of-proc server is aits own EXE process, and can be instantiated by 32bit and 64bit clients. So, you can write a 64bit server that wraps the 64bit DLL (or use COM's own DllSurrogate if the DLL is itself a COM object) , and then a 32bit client can use the server's COM object.

    24 minutes ago, dummzeuch said:

    and  WoW64Injection: Loading a 64-bit DLL in a 32-bit process which probably won't help at all.

    I'm not familiar with that. It looks interesting but I wouldn't trust it in production code.

    • Like 1

  11. 4 hours ago, Freeeee said:

    code was from ? 2000,.  Worked then.

    There wasn't a reserved word "Result"

    so you  used the function name at the end of the code set equal to whatever variable 

    you were using to get the results. 

    The Result variable (enabled by default via the {$EXTENDEDSYNTAX ON} / {$X+} directive) has been available for a very very long time, much earlier than 2000. But even so, assigning values to the function's name is still an allowed option, too. 

    • Like 2

  12. Obviously, I'm aware of the existence of class procedures/functions, but I've never seen a "procedure of class" declaration before. I didn't know that was a possibility.

     

    In any case, a "class" procedure/function still has a hidden Self parameter, it simply points at the class type instead of an object instance.  Your code is not taking that parameter into account.  You have to declare a "class" procedure/function as "static" in order to remove its Self parameter.


  13. 5 hours ago, Freeeee said:

     

    Is there some reason for using Non-Static as opposed to Active or Dynamic when talking about classes?

    There's no such thing as an "active" class method.

     

    Non-static, dynamic, virtual methods - these all have a hidden implicit Self pointer to the object instance they are called on.

    Quote

    What I'm understanding is any Procedure code generated from the Tform by some action (like a mouse clik) is in the TForm class.

    so as long as you do ALL of your coding inside one of those procedures you don't have to qualify back to the TForm.

    Yes, because they will all be using the Self pointer. For example

    procedure TForm5.FormClick(Sender: TObject);
    begin
      ABCycle.Visible := True;
    end;

    Is really this:

    procedure TForm5.FormClick(Sender: TObject);
    begin
      Self.ABCycle.Visible := True;
    end;

    Quote

    But if you call something like a WriteRecord procedure that was NOT generated by an action in./of/on the form you do have to qualify because you;ve gone outsife of the scope of the TForm class.   

    If such a procedure is trying to access members of the Form of object, then yes.

    Quote

    or are the nuances ?? like hierarchy rules.    Is it "You know when your outside of the class scope when you have to qualify".  or is there an easier way?

    I suggest you brush up on your fundamentals. For instance:

     

    https://docwiki.embarcadero.com/RADStudio/en/Procedures_and_Functions_Index

     

    https://docwiki.embarcadero.com/RADStudio/en/Classes_and_Objects_Index

     

    https://docwiki.embarcadero.com/RADStudio/en/Methods_(Delphi)

    • Like 1

  14. 2 hours ago, Freeeee said:

    thank you Remy

    the errors are

    E2023 Function needs result type at line 415 (415:10)                     there - is  -  a"    : LongInt;  "    after the closing paren

    E2029 Declaration expected but '(' found at line 415 (415:22)         that's the opening paren for Mo, yr: LongInt

    E2003 Undeclared identifier: 'Mo" at line 417 (417:11)                     Mo was declared in the input args

    E2993 Undeclared identifier: DaysInMonth at line 418(418:22)        ??? It's the name of the function.

    Since we can't see the rest of your code, we can't help you with these errors. The sole function you have shown only had one mistake in it. So clearly these other errors are caused by other problems (likely other syntax mistakes) higher up in your code that we can't see.

    2 hours ago, Freeeee said:

    I want to know WHY the compiler 

    accepts the routine without error in at least two other UNITS earlier but NOT THIS ONE?

    Because clearly THIS unit has other mistakes in it that are breaking its compilation.

    2 hours ago, Freeeee said:

    Is there some major flaw in the code above the routine that confuses the compiler so badly it starts flagging errors where there are none??

    Obviously yes, but what that flaw could be specifically is anybody guess without seeing the rest of the code in question.

    2 hours ago, Freeeee said:

    By the way,  Why would I need to qualify a variable on a form when it's the ONLY form in the program?

    example (   ABCycle.Visible  ) is a E2003    -     but if I put in  (    Form5.ABCycle.Visible  )   in the code it's accepted.

    That behavior happens when the failing code is NOT inside a non-static method of the TForm5 class, thus requires an explicit object reference to reach the ABCycle member.

    2 hours ago, Freeeee said:

    And Form5 IS the only form in this particular program

    It doesn't matter if you have 1 Form or 100. You need a valid TForm5 object reference in order to access its ABCycle member. Inside of a method of the TForm5 class, that reference can be the implicit 'Self' pointer. But code outside of TForm5's methods will need an explicit pointer to the Form5 object. 

    2 hours ago, Freeeee said:

    Same program as the DaysInMonth problem

    That behavior would happen if DaysInMonth() is a non-static method of the TForm5 class, and thus must be called on a valid Form5 object reference, but the calling code does not have such a reference without qualifying it. 


  15. FYI, the SysUtils unit has an IsLeapYear() function, and a MonthDays[] array, eg:

    uses
      ..., SysUtils;
    
    function DaysInMonth(Mo, Yr : Word): Word;
    begin
      Result := MonthDays[IsLeapYear(Yr), Mo];
    end;

    Alternatively, the DateUtils unit has its own DaysInAMonth() function, eg:

    uses
      ..., DateUtils;
    
    function DaysInMonth(Mo, Yr : Word): Word;
    begin
      Result := DateUtils.DaysInAMonth(Yr, Mo);
    end;

    No need to reinvent the wheel here...

    • Like 1

  16. 1 hour ago, jesu said:

    I had supposed that Screen would return the size of the monitor where the program is shown.

    The Width/Height properties of the global Screen object return the size of the primary monitor only.  If you want the size of the monitor that your program is displayed on, use the Width/Height or DisplayRect properties of the TForm.Monitor property.

    1 hour ago, jesu said:

    It seems that at least I should do something like:

    That code assumes all monitors are positioned only side-by-side horizontally, it does not account for monitors being stacked vertically.

     

    To get the total Width/Height of the virtual screen, use the global Screen object's DesktopWidth/DesktopHeight or DesktopRect properties.


  17. 9 hours ago, Mark- said:

    Also testing shows, if all the dlls are in the exe path, all works as expected.

    As it should. That is one of the pre-defined places where the DLL loader looks for dependant DLLs.

    9 hours ago, Mark- said:

    Setting all functions to delayed and calling SetDllDirectory before any function is called, it fails, on the first call to the main dll

    That means the DLL could not be loaded. Either you used the wrong path, or a dependency could not be found, etc.

    9 hours ago, Mark- said:

    I tried several path options, no joy.

    Did you try using a Delay Load failure hook to find out exactly what is failing? See the example in Delphi's documentation

    • Like 1

  18. 39 minutes ago, Mark- said:

    I also wondered if I declared one function from each of the three dlls, (required by the "main" dll) and, used static linking for the the three dlls, if that would work.

    No, it will not work.

     

    Although it will load the DLLs at process startup, you have no control over the order in which static-linked DLLs are loaded.  More importantly, you CANNOT specify the paths where to search for static-linked DLLs, The resulting IMPORTS table simply does not contain paths, only filenames.  Statically-linked DLLs are always searched for in system-defined paths only, see: Dynamic-link library search order. So, if your app needs to control the paths where DLLs are loaded from, then you MUST use dynamic/delayed loading.


  19. 2 hours ago, Freeeee said:

    procedure TForm1.Button1Click(Sender: TObject);
    begin     // FROM LABELS  this does not work reliabley
    Memo1.lines.delete (0);    // will cleaning help?
    Memo1.Lines.Delete (1);    // nope each B1 clk removed
    memo1.lines.Delete (2);    // another line.
    Memo1.lines [0] := Label1.caption;
    Memo1.lines [1] := Label2.Caption;
    Memo1.lines [2] := Label3.caption;
    end;

    Why are your DELETING lines you are trying to WRITE to? Also, you are not taking into account that the line indexes will CHANGE when you delete lines, so you are actually deleting the 1st 3rd 5th lines, not the 1st 2nd 3rd lines. 

     

    Simply don't do this. You don't need the Delete() calls. 

     

    procedure TForm1.Button1Click(Sender: TObject);

    begin

      Memo1.Lines [0] := Label1.Caption;

      Memo1.Lines [1] := Label2.Caption;

      Memo1.Lines [2] := Label3.Caption;

    end;

     

    procedure TForm1.Button2Click(Sender: TObject);

    begin

      Label1.Caption := Memo1.Lines [0];

      Label2.Caption := Memo1.Lines [1];

      Label3.Caption := Memo1.Lines [2];

    end;

     

    But, if you must delete lines, then you can simply Insert() them back in, eg:

     

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Memo1.Lines.Delete (0);
      Memo1.Lines.Delete (0);
      Memo1.Lines.Delete (0);
      Memo1.Lines.Insert(0, Label1.Caption);
      Memo1.Lines.Insert(1, Label2.Caption);
      Memo1.Lines.Insert(2, Label3.Caption);
    end;

     

    But, I still stand by my earlier comment that this is really the wrong UI choice to use in the first place.

×