Jump to content

Remy Lebeau

Members
  • Content Count

    2684
  • Joined

  • Last visited

  • Days Won

    113

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Use TIdMessage to construct rfc 5322 message?

    Indy does not implement RFC 6854 (I didn't even know that existed). It does implement 822/2822, but I don't recall to what extent it implements 5322 (perhaps just aspects of it?). Most likely, yes, as long as you don't need to use some modern email features, like signing, etc. If you run into problems, let me know. Do be aware that, by default, TIdMessage saves its data to a TStream/file using a dot-transparency format (ie, lines that begin with a period are escaped with an extra period) that is intended for use with the SMTP and POP3 protocols but not with other protocols. So, you will likely need to utilize the class helper in the IdMessageHelper unit, which adds an AUsesDotTransparency parameter to the TIdMessage.SaveTo...() methods, then you can set that parameter to False.
  2. CW_USEDEFAULT is actually typed as 'int' in the Windows SDK (in winuser.h): #define CW_USEDEFAULT ((int)0x80000000)
  3. Indy allows that just fine - provided you adequately serialize access to the socket to avoid multiple threads overlapping their outgoing packets. Although multiple threads CAN send to the same socket provided they don't overlap, it is generally a better idea to handle all of the sends in a single thread instead.
  4. IMHO, just because the constant was DWord doesn't mean the variables should have been DWord.
  5. Did you setup your Connection Profile(s) for the target debugger(s) before invoking the "Attach to Process" dialog?
  6. Oh. I didn't realize that was you.
  7. I was referring to this code: Why were nLeft, etc declared as DWord to begin with and not as Integer, as the Win32 API expects?
  8. Why were your variables declared as DWORD to begin with? The Win32 API represents window positions and sizes as ints, not as dwords.
  9. Which makes sense, as that flag is used primarily with the CreateWindow/Ex() APIs and they accept it as an int, not a dword.
  10. Remy Lebeau

    #409: Add ValueFromIndex[] property to TIdHeaderList

    I appreciate the effort. I have added the info to the existing ticket.
  11. Remy Lebeau

    ListView

    In VCL, I would simply custom-draw an image of a "button" directly onto the ListView item, and then use mouse events on the ListView to detect a click on the "button" to invoke the dialog.
  12. I have opened a ticket on Indy's GitHub repo and will work on a fix: #523: EConvertError in TIdX509.notBefore and TIdX509.notAfter properties for 4-digit years UPDATE: I have now checked in a fix. Let me know if it works.
  13. And even if there was, critical sections are not named kernel objects, so they can't interfere with each other.
  14. Remy Lebeau

    Cannot download the Delphi Community Edition

    The CE installer requires the GetIt server to download various components during installation. But CE is still at version 11, and due to the recent server outages, GetIt has been restored for only RAD Studio 12 so far. They are still working on restoring GetIt for version 11 and earlier. So, it is not surprising that they would disable any downloads for older versions right now.
  15. Not yet, no. Only that the migration is in progress.
  16. Sorry, but you simply can't, as 10.4.2 CE is no longer available. The terms of the CE license require that you download and install the latest CE version every year. No exception. If you need to stick with 10.4.2 then you will have to pay for that license, you can't avoid it. Otherwise, you will have to address the problem that is preventing you from migrating the project to the latest CE version.
  17. Remy Lebeau

    Return value from other application

    Yes, it is possible. If it is just a simple integer, you can return it in the process exit code and then retrieve it via GetExitCodeProcess(). But for anything more complex, you can create an Inter-Process Communication channel between the two apps, such as with a pipe, socket, shared memory, etc and exchange data that way.
  18. Meaning, the server sends something to a client that it did not explicitly ask for. Such as a notification when another client connects/disconnects, or when a client sends a message to other client. Things which can happen at any time, outside of the normal request/response flow of "I want something" -> "Here you go" (solicited). "Oh, BTW, here is something else I have for you but you didn't ask for" (unsolicited). That contains a mix of solicited and unsolicited messaging. The new client connects, gets a greeting, says hello, and gets acknowledged. The client initiated an action and got a direct response before doing anything else. That is solicited messaging. Then, all of the other clients get notified of the new connection. They didn't have to send any requests to receive that event, they are just blindly told that it happened, when it happened. It happened in the background, and they have to passively watch out for it. That is unsolicited messaging. That is unsolicited messaging. Client1 sends a request to the server (solicited), but then the server notifies Client2/etc (unsolicited). Client2/etc did not send a request to receive that event, it/they are blindly told when the event happens. Same thing. All that extra messaging that the server does in the background needs to be handled properly. But if you code your server to just receive a request and send a response, you won't be able to handle that extra messaging at all. And right there is the problem. The code you showed doesn't handle that last part. You can't just write to a given client connection whenever and from wherever you want. You have to serialize your outgoing messaging. Think of what would happen if 2 clients happen to connect/disconnect at the same moment and thus need to notify the same target client. Or if multiple clients send private messages to the same target client at the same moment. You don't want multiple messages to that target client to overlap, that will corrupt your socket communications. So, you must provide some mechanism to make sure that subsequent messages wait their turn while an earlier message is still being written to the socket. That could as simple as putting a critical section or other exclusive lock around socket writes. But, that can cause unwanted blockages in threads that want to write to a socket, so that is where my earlier suggestion to use a per-client queue comes into play instead. That way, only the thread that actually writes the queue to the socket may block, while other threads are free to continue putting new messages into the queue. Well, that is certainly true, if you do the writing in the OnExecute event (as you usually should). But, if OnExecute never writes, only reads, then you can do the writing from another thread. I didn't say anything about a delay.
  19. Locality has nothing to do with it. You are opening the drive with just FILE_SHARE_READ specified by itself. You are telling the OS that you are willing to share access to the drive with other open handles only if they open the drive for read-only access. If anybody else already has the drive open for writing, then you won't be able to open it since you are not willing to share write access. And clearly someone does have the drive open for writing (you can verify this with a tool like SysInternals' Handle or Process Monitor utilities), which is why you are getting the "sharing violation" error. Try adding FILE_SHARE_WRITE for your sharing rights: Handle := CreateFile(..., FILE_SHARE_READ or FILE_SHARE_WRITE, ...); But be aware that this will allow other open handles to potentially write to the drive while you are reading from it. If that is not what you want, then omit FILE_SHARE_WRITE and simply warn your user that you can't gain read-only access to the drive.
  20. In a strictly request/response model, having the server send unsolicited notifications to the client will simply not work (unless they are delivered on a separate channel). When a client sends a request and is waiting for a response, it won't know to expect or handle notifications before the response arrives. And if the client is not waiting for a response, it won't be able to read in notifications at all until it sends its next request. So, you would have to either: cache the notifications per-client and deliver them only in solicited responses. But that means there is possible delay in notifications being delivered. make the client poll the server for the status of other clients periodically. Which is usually not desirable, especially with the sheer number of clients you are dealing with. Otherwise, you have to change your protocol to allow for clients to handle unsolicited notifications while waiting for solicited responses, and to handle unsolicited notifications while no request is pending. This typically requires: the client to read from the connection constantly. the client to include a unique ID in each request. the server to echo that unique ID back in the corresponding response. the server to format/identify notifications and responses in a way that allows the client to differentiate between them. Indy's write buffering is at the byte level when interacting directly with the socket connection. But you will likely need higher level message buffering at the business logic level instead. IOW, you will likely need a per-client thread-safe queue for outgoing messages, and then you can push your responses and notifications into that queue as needed. Then you can have the server's OnExecute event flush the calling client's queue to its connection when it is safe to do so. I've posted examples of that many times before in various forums.
  21. What does GetLastError() return when CreateFile() fails? Does it return ERROR_ACCESS_DENIED (5, ie you don't have rights to open the drive), ERROR_SHARING_VIOLATION (32, ie someone else has the drive open for non-read access, but you are sharing only read access), or something else? Handle := CreateFile(...); if Handle = INVALID_HANDLE_VALUE then begin ErrCode := GetLastError; // <-- what does this return? ... end
  22. You can't use {$R} to link a .manifest file directly like that, it can only link a precompiled .res file. But, what you can do is create an .rc file that references the .manifest file, and then tell {$R} to compile that .rc file into a .res file and link it, eg: // mymanifest.rc #define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 #define RT_MANIFEST 24 CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "myapp.manifest" {$R 'mymanifest.res' 'mymanifest.rc'} However, you do not need to resort to using {$R} in this manner at all, as the Project Options dialog has an option to let you specify a custom .manifest file: Customizing the Windows Application Manifest File And there is also a configurable option to specify an ExecutionLevel in a compiler-generated manifest: Application Options: Manifest File (Windows Only) That approach will not work, as your application's process is already running when you call SetEnvironmentVariable(), but elevation needs to occur BEFORE the process is created. To use __COMPAT_LAYER properly, you would have to create a separate .bat script that sets the __COMPAT_LAYER first and then runs your application so it inherits the updated environment. See: What does '__COMPAT_LAYER' actually do? Makes sense, as access to a physical drive requires admin rights (or, at least the SE_BACKUP_NAME privilege). But, that does not mean you have to run your entire application as an admin (unless you really need to). A better solution would be to isolate the disk access code into a separate process or COM object, and then run just that process/object elevated.
  23. Have a look at PostThreadMessage(). The email thread can run a mesaage loop looking for a custom message which the generator thread posts to it. The email thread can then post another message to the main UI thread when the grid should update. Alternatively, the email thread can run a loop waiting on a TEvent which the generator thread signals. The email thread can then post a message to the main UI thread. Or use TThread.Queue() instead. Alternatively, you can get rid of the email thread entirely. Wrap the email logic into a TTask which the generator thread creates for a batch of emails. When the TTask is done running, it can update the UI thread to display the results.
  24. Remy Lebeau

    IOHandler.Write() problems with SocketError # 10053 exception

    I'm having a really hard time understanding what this code is trying to accomplish in the first place, let alone why it is writing directly to the TCP connection instead of letting TIdHTTPServer handle all of the writing like it is designed to. In SIMPLE terms, can you please explain the goal of this code?
×