Jump to content

Remy Lebeau

Members
  • Content Count

    2684
  • Joined

  • Last visited

  • Days Won

    113

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Overview of the generated files after build/compile

    There is no such list generated by the IDE/compiler. And the only documentation that I know of which talks about generated files is: File Types Index File Extensions of Files Generated by RAD Studio
  2. Remy Lebeau

    How can I make TTimer run in the main tread ?

    Then TTimer is not a good choice, as it is not a real-time timer, it is a message-based timer, and so is subject to the speed of the message queue. And also, the WM_TIMER message is low-priority and only generated when there are no other messages pending. You should use an actual multimedia timer instead. And don't use PlaySound() when high performance is needed. Pre-prepare the audio sample ahead of time and use a multimedia API to play the sample when needed. Such as waveOutWrite(), or more modern audio APIs like WASAPI, DirectSound/XAudio2, etc.
  3. Remy Lebeau

    D11.3 Surfaces a Bug in 8-Year-Old Code that reads DBF file...

    @Steve Maughan you might want to read: Converting 32-bit Delphi Applications to 64-bit Windows since the original code was doing things that were not meant to be done in 64bit builds to begin with.
  4. Remy Lebeau

    Delphi 12 IDE: unchecking a component is NOT SAVED

    You can update the "HKCU\SOFTWARE\Embarcadero\BDS\23.0\Known Packages" Registry key. Find the entry for the package, and either: modify the entry's Data value to prefix it with an underscore ("_"). remove the entry (optionally move it to the "HKCU\SOFTWARE\Embarcadero\BDS\23.0\Disabled Packages" key)
  5. Remy Lebeau

    GetWindowHandle + Ctrl V

    If your app actually needs admin rights, then you should add an application manifest to your project to specify its requestedExecutionLevel at compile-time, not change the properties of the EXE file after the compile is finished. Modern Delphi versions even have a project setting for that very purpose: http://docwiki.embarcadero.com/RADStudio/en/Manifest_File http://docwiki.embarcadero.com/RADStudio/en/Application_Manifest Basically, what you did for your app, but for the IDE instead, so it runs as an admin, and then any project it runs will also run as an admin.
  6. You should NEVER stream pointers from one process' address space into another process' address space, unless both processes are running concurrently and one process needs to directly access the other process's memory via the ReadProcessMemory() and/or WriteProcessMemory() APIs (using shared memory would be better, though). Otherwise, just don't do it! Stream offsets instead. And in the example given, a linked list can certainly be streamed using offsets instead of pointers. The actual pointers would only be meaningful in the memory of the process that originally creates the list, and in the process that loads the stream into a new list in it own memory. Pointers are fairly meaningless when passed around from one process to another.
  7. Remy Lebeau

    GetWindowHandle + Ctrl V

    Do not use keybd_event(), use SendInput() instead. Only if you have permission to access the other app. Into a simple app like Notepad, yes. But given that Kaspersky is a security app, I doubt they want people to automate it like this, so the approach you are attempting will likely never work with it.
  8. Remy Lebeau

    indy https error 10061

    Are the client and server on the same LAN? If not, then there could be other AVs/Firewalls present in between the client and server as the connection jumps from one network to another. The only thing that makes sense given what you have described is if there is a local AV/Firewall running on the client machine that is blocking the connections from TIdHTTP directly, but has an exception which allows Fiddler to make connections. You say your TIdHTTP code is in a DLL, what app is loading that DLL?
  9. Remy Lebeau

    indy https error 10061

    Rather than using the ProxyParams (which affects TIdHTTP's internal logic in how it handles HTTP/S requests), I would suggest using a packet sniffer like Wireshark to see the actual IP/Port that TIdHTTP is trying to connect to for a normal non-proxy request, and see whether that connection is actually going out over the network. The main causes of error 10061 are: the target IP is reachable by the client, but there is no TCP server running on the target Port there is a TCP server running on the target IP/Port, but it has too many pending connections in its backlog the connection is being blocking by an AV/firewall that is located anywhere on the network(s) between your client and target server. Unfortunately, there is no way to know on the client side what the root cause of the error actually is, so the only thing the client can do is catch the error and retry the request, preferably after a short delay, in case the error is temporary.
  10. Remy Lebeau

    How do I execute FFMPEG from inside a VCL App?

    I would not suggest putting it all on the OS to run that entire command in one go. You should break it up into steps and then use appropriate APIs to run each step directly in your code, eg: uses ..., System.SysUtils; procedure TForm1.act_MUSIC_CONVERSION_FLACStoMP3Execute(Sender: TObject); var dir: string; sr: TSearchRec; begin dir := IncludeTrailingPathDelimiter(FileListBox1.Directory); if FindFirst(dir + '*.flac', faAnyFile, sr) <> 0 then try repeat if (sr.Attr and faDirectory) = 0 then begin ShellExecute(0, nil, 'ffmpeg.exe', PChar('-i "' + sr.Name + '" -ab 320k -map_metadata 0 -id3v2_version 3 "' + sr.Name + '.mp3"'), PChar(dir), SW_HIDE); end; until FindNext(sr) <> 0; finally FindClose(sr); end; end; Alternatively: uses ..., System.SysUtils, System.IOUtils; procedure TForm1.act_MUSIC_CONVERSION_FLACStoMP3Execute(Sender: TObject); var dir, fileName: string; begin dir := IncludeTrailingPathDelimiter(FileListBox1.Directory); for fileName in TDirectory.GetFiles(dir, '*.flac') do ShellExecute(0, nil, 'ffmpeg.exe', PChar('-i "' + fileName + '" -ab 320k -map_metadata 0 -id3v2_version 3 "' + fileName + '.mp3"'), PChar(dir), SW_HIDE); end; Yes, its' more work, but it gives you much more control.
  11. There is nowhere you can "put the app" that will make the OS run your app during sleeps/wakeups. Your app needs to already be running in order to catch those events in real-time in your app's code. As for Startup specifically, you could use the "Startup" group in the Start Menu, or the "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" key in the Registry, to run your app during startup/logon. There are also startup/shutdown and logon/logoff policy scripts that you can edit to run your app: Working with startup, shutdown, logon, and logoff scripts using the Local Group Policy Editor
  12. Also, because SetDialogParams() is taking a TTaskDialogParams by value, a copy is made, which will have its own TStringList object, so you must implement the Assign() operator as well to copy the source record's TStringList data to the copied record after it is Initialized: class operator TTaskDialogParams.Assign(var Dest: TTaskDialogParams; const [ref] Src: TTaskDialogParams); begin Dest.CustomButtons.Assign(Src.CustomButtons); end; class operator TTaskDialogParams.Finalize(var Dest: TTaskDialogParams); begin Dest.CustomButtons.Free; end; class operator TTaskDialogParams.Initialize(out Dest: TTaskDialogParams); begin Dest.CustomButtons := TStringList.Create; end; Otherwise, if you don't do this, the default assignment behavior will copy the object pointer instead, causing the copied record to lose its own TStringList object (leaking it) and end up pointing to the original TStringList object, so when the copied record is Finalized then the original TStringList object gets freed, corrupting the original record that was copied from. That being said, there is no good reason to pass the record by value in the code shown. SetDialogParams() should take the record by const reference instead, then no copy is made. Read the documentation, especially the section on "Passing Managed Records as Parameters" : https://docwiki.embarcadero.com/RADStudio/en/Custom_Managed_Records
  13. You did not setup your C++Builder code correctly to match the functionality of the Delphi code. Here is what your C++ code would need to look like instead to directly translate the Delphi code: //--------------------------------------------------------------------------- #ifndef examplecomponentcppH #define examplecomponentcppH //--------------------------------------------------------------------------- #include <System.Classes.hpp> #include <Vcl.Controls.hpp> #include <Winapi.Messages.hpp> class PACKAGE TExampleCpp : public TCustomControl { protected: MESSAGE void __fastcall WMMouseMove(TWMMouseMove& Message); public: __fastcall TExampleCpp(TComponent *Owner); __fastcall virtual ~TExampleCpp(); BEGIN_MESSAGE_MAP VCL_MESSAGE_HANDLER(WM_MOUSEMOVE, TWMMouseMove, WMMouseMove) END_MESSAGE_MAP(TCustomControl) }; #endif //--------------------------------------------------------------------------- #pragma hdrstop #include "examplecomponentcpp.h" //--------------------------------------------------------------------------- #pragma package(smart_init) __fastcall TExampleCpp::TExampleCpp(TComponent *Owner) : TCustomControl(Owner) { } __fastcall TExampleCpp::~TExampleCpp() { } void __fastcall TExampleCpp::WMMouseMove(TWMMouseMove& Message) { //... TCustomControl::Dispatch(&Message); } namespace Examplecomponentcpp { void __fastcall PACKAGE Register() { TMetaClass* classes[1] = {__classid(TExampleCpp)}; RegisterComponents("Samples", classes, 0); } } See Declaring a New Message-handling Method in Embarcadero's documentation. Note the inclusion of the MESSAGE_MAP, that is what actually hooks up the WM_MOUSEMOVE message to the WMMouseMove() method. As I mentioned earlier, an alternative would be to override the virtual TWinControl::WndProc() method instead of using a MESSAGE_MAP, eg: //--------------------------------------------------------------------------- #ifndef examplecomponentcppH #define examplecomponentcppH //--------------------------------------------------------------------------- #include <System.Classes.hpp> #include <Vcl.Controls.hpp> #include <Winapi.Messages.hpp> class PACKAGE TExampleCpp : public TCustomControl { protected: virtual void __fastcall WndProc(TMessage& Message); public: __fastcall TExampleCpp(TComponent *Owner); __fastcall virtual ~TExampleCpp(); }; #endif //--------------------------------------------------------------------------- #pragma hdrstop #include "examplecomponentcpp.h" //--------------------------------------------------------------------------- #pragma package(smart_init) __fastcall TExampleCpp::TExampleCpp(TComponent *Owner) : TCustomControl(Owner) { } __fastcall TExampleCpp::~TExampleCpp() { } void __fastcall TExampleCpp::WndProc(TMessage& Message) { if (Message.Msg == WM_MOUSEMOVE) { //... } TCustomControl::WndProc(Message); } namespace Examplecomponentcpp { void __fastcall PACKAGE Register() { TMetaClass* classes[1] = {__classid(TExampleCpp)}; RegisterComponents("Samples", classes, 0); } } But, that being said, in this particular situation you really should not be handling the WM_MOUSEMOVE message directly at all. Override the virtual TControl::MouseMove() method instead, eg: //--------------------------------------------------------------------------- #ifndef examplecomponentcppH #define examplecomponentcppH //--------------------------------------------------------------------------- #include <System.Classes.hpp> #include <Vcl.Controls.hpp> class PACKAGE TExampleCpp : public TCustomControl { protected: DYNAMIC void __fastcall MouseMove(TShiftState Shift, int X, int Y); public: __fastcall TExampleCpp(TComponent *Owner); __fastcall virtual ~TExampleCpp(); }; #endif //--------------------------------------------------------------------------- #pragma hdrstop #include "examplecomponentcpp.h" //--------------------------------------------------------------------------- #pragma package(smart_init) __fastcall TExampleCpp::TExampleCpp(TComponent *Owner) : TCustomControl(Owner) { } __fastcall TExampleCpp::~TExampleCpp() { } void __fastcall TExampleCpp::MouseMove(TShiftState Shift, int X, int Y) { //... TCustomControl::MouseMove(Shift, X, Y); } namespace Examplecomponentcpp { void __fastcall PACKAGE Register() { TMetaClass* classes[1] = {__classid(TExampleCpp)}; RegisterComponents("Samples", classes, 0); } } As I stated earlier, there is no equivalent to "inherited" in C++Builder. And there is no __super in C++Builder, either. Read that post again, it does not mention __super in relation to Borland/Embarcadero compilers at all, only in relation to Microsoft compilers. your only option in C++ is to specify the base class method directly. "inherited" in a Delphi message handler has no concept of public/private at all. Note that both the base class WMMouseMove() method in TControl and the derived class WMMouseMove() method in TExample are marked with the "message" directive (see Message Methods in Embarcadero's documentation). That means they are both stored in a message dispatch table that the compiler creates for every class that has "message" handlers. Calling "inherited" in a message handler will simply call the corresponding base class method in the dispatch table. It doesn't matter whether the methods are public or private. The table is just a bunch of method pointers. As it should be, because you are doing something COMPLETELY DIFFERENT than what the Delphi code is doing. There is no message dispatch table in C++. You are trying to directly call a base class method, and such a method call is subject to the standard public/private restrictions outlined in the C++ language. In the Delphi code, that is not the case. Because it can't be. C++ and Delphi are completely different languages, with their own set of rules and restrictions. Not everything translates across the language boundary, and "inherited" is one of them. There is nothing like "inherited" in C++. No, of course not. Plenty of components are written in C++Builder. I've written several myself. For the most part, it can. But certain methodologies may differ due to language differences. For instance, in this case the message dispatch happens to be different between Delphi and C++. Notice the MESSAGE_MAP in my example? That overrides the virtual TObject::Dispatch() method, and is why you have to call the base class Dispatch() method if you want to pass the message to a base class handler in C++. You are comparing apples and oranges here. You can't rationalize why one language doesn't behave the same as another language.
  14. Your declaration of WTSQuerySessionInformation() is wrong. It should be like this instead: function WTSQuerySessionInformation( hServer: THandle; SessionId: DWORD; WTSInfoClass: WTS_INFO_CLASS; var ppBuffer: LPWSTR; var pBytesReturned: DWORD): BOOL; stdcall; external 'Wtsapi32.dll' name {$IFDEF UNICODE}'WTSQuerySessionInformationW'{$ELSE}'WTSQuerySessionInformationA'{$ENDIF}; And technically, both WTSEnumerateSessions() and WTSQuerySessionInformation() should be using 'out' parameters instead of 'var' parameters. But that is not important here. You are also leaking the memory that WTSQuerySessionInformation() returns. Try this instead: function GetSessionInfo(id: DWORD; var codeErr: Integer): TSessionInfo; var wpSessionInfo: PWTS_INFO; SessionInfo: LPWSTR; BytesReturned: DWORD; idErr: Integer; begin Result.ID := -1; try if WTSQuerySessionInformation ( WTS_CURRENT_SERVER_HANDLE, id, WTS_INFO_CLASS.WTSSessionInfo, SessionInfo, BytesReturned) then begin try Result.ID := id; wpSessionInfo := PWTS_INFO(SessionInfo); //user name Result.user := wpSessionInfo.UserName; //station name Result.StationName := wpSessionInfo.WinStationName; //domain name Result.domain := wpSessionInfo.Domain; //connection status Result.ConnectState := wpSessionInfo.State; finally WTSFreeMemory(SessionInfo); end; end else begin idErr := GetLastError; GV_TxtFileLog.Log('[GetSessionInfo] id ' + IntToStr(id) + ' ERR : ' + intToStr(idErr) + ' ' + SysErrorMessage(idErr), True); end; except on e: Exception do GV_TxtFileLog.Log('[GetSessionInfo] id ' + IntToStr(id) + ' EX : ' + e.Message, True); end; end; function SF_GetOpenedSessions(var codeErr: Integer): TSessionsInfo; var Sessions, Session: PWTS_SESSION_INFO; NumSessions, I: DWORD; SessionInfo: TSessionInfo; SessionCount: Integer; begin Result := nil; codeErr := 0; try if WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, Sessions, NumSessions) then begin try if NumSessions > 0 then begin SessionCount := 0; SetLength(Result, NumSessions); try Session := Sessions; for i := 0 to NumSessions-1 do begin if Session.SessionId <> 0 then begin GV_TxtFileLog.Log('[SF_GetOpenedSessions] GetSessionInfo '+ intToStr(Session.SessionId)); SessionInfo := GetSessionInfo(Session.SessionId); if SessionInfo.ID <> -1 then begin Result[SessionCount] := SessionInfo; Inc(SessionCount); end; end; Inc(Session); end; finally SetLength(Result, SessionCount); end; end; finally WTSFreeMemory(Sessions); end; end else begin codeErr := GetLastError; GV_TxtFileLog.Log('[SF_GetOpenedSessions] WTSEnumerateSessions err : ' + IntToStr(codeErr) + ' ' + SysErrorMessage(codeErr), True); end; except on e: Exception do GV_TxtFileLog.Log('[SF_GetOpenedSessions] EX : ' + e.Message, True); end; end;
  15. There is no "inherited" in C++. You need to call the base class Dispatch() method. Or override the virtual WndProc() method instead of using a MESSAGE_MAP.
  16. Remy Lebeau

    How to keep hdc alive

    This is an English forum, please. I'm not familiar with the ScriptStringAnalyze API or how it works, so I can't help you further with this. I don't understand what you are trying to accomplish. Good luck with your journey.
  17. There is a simpler way that doesn't involve keeping a second list. You can simply read in the INI values into a temporary unsorted TStringList first, and then you can loop through that list adding each entry to the live MRU normally, where the MRU insertion function looks for a duplicate in the live list and if found then removes it before then adding the new entry to the top of the list. I have updated my earlier example to demonstrate this.
  18. It is commonly recommended that you should obtain a Network Named User license instead, so that you can then manage your own licensing locally without having to contact Embarcadero. You would just have to download and install the License Server onto a machine in your local network, and then you can issue/revoke your own licenses as needed.
  19. Remy Lebeau

    Help with Query at run time

    You should not use TDirectory.GetCurrentDirectory() to get the EXE path, as the CWD is dynamic. It is subject to whatever path the launcher of the EXE wants to use, and it can also change value during the EXE's runtime. Instead, use TApplication.ExeName or ParamStr(0) to get the full path to the EXE file, and then strip off the filename with ExtractFilePath()/ExtractFileDir() or TPath.GetDirectoryName(). Though, you really should not be storing ANY mutable application data in the EXE's folder to begin with.
  20. I suppose so. For example: procedure TMyForm.FormCreate(Sender: TObject); begin LoadMRUFromINI; end; procedure TMyForm.LoadMRUFromINI; var Ini: TIniFile; tempMRU: TStringList; I: Integer; begin Ini := TIniFile.Create('C:\path\settings.ini'); try tempMRU := TStringList.Create; try Ini.ReadSectionValues('mru' tempMRU); for I := 0 to tempMRU.Count-1 do begin AddPathToMRU(tempMRU[I], False); end; finally tempMRU.Free; end; finally Ini.Free; end; end; procedure TMyForm.SaveMRUToINI; var Ini: TIniFile; I: Integer; begin Ini := TIniFile.Create('C:\path\settings.ini'); try Ini.EraseSection('mru'); for I := 0 to PathsListBox.Items.Count-1 do begin Ini.WriteString('mru', 'path'+IntToStr(I+1), PathsListBox.Items[I]); end; Ini.UpdateFile; finally Ini.Free; end; end; procedure TMyForm.AddPathToMRU(const APath: string; AUpdateINI: Boolean = True); const MaxMRUItems = 5; var Idx: Integer; begin while PathsListBox.Items.Count >= MaxMRUItems do begin PathsListBox.Items.Delete(PathsListBox.Items.Count-1); end; Idx := PathsListBox.Items.IndexOf(APath); if Idx <> -1 then begin PathsListBox.Items.Delete(Idx); end; PathsListBox.Items.Insert(0, APath); if AUpdateINI then SaveMRUToINI; end; procedure TMyForm.DoSomething; var LPath: string; ... begin LPath := ...; ... AddPathToMRU(LPath); ... end;
  21. I would do something like this: [mru] count=N path1=... path2=... ... pathN=... Keep adding new paths to the front of the list until the desired count is reached, then remove the oldest path from the end of the list each time a new path is added.
  22. Remy Lebeau

    TCP ping

    There is no such as a "TCP ping". Ping (ICMP) operates at a different protocol level than TCP. A ping can only tell you whether a machine/device is reachable on the LAN (assuming it even implements ICMP, and that ICMP is not being blocked by routers/firewalls, etc). But it cannot tell you whether there is an app actually listening on any TCP port on that machine/device. You would have to actually connect to a port to verify connectivity. That is what port scanners do.
  23. Did you verify that DateTimeToStr() is actually producing a String that uses U+202F instead of U+0020? I can't imagine the RTL ever using U+202F on purpose, so it is likely coming from the OS configuration when the RTL reads in the OS's locale info. All the more reason NOT to rely on OS locales when saving your data. Use a more reliable format, like ISO-8601.
  24. When I look at that .ini file, there is a Unicode character U+202F (Narrow No-Break Space) in between "11:15:12" and "AM". I think TryStrToDateTime() only supports the ASCII space character U+0020, so likely can't handle that other type of space character and so fails the parse.
  25. The problem with that approach is that you may not get a decoding error, so you wouldn't really know which encoding "works" right away. You might just end up with garbage when reading data. And what do you consider to be "valid"? Because different formats can produce results without errors, but just won't be what you are expecting. Can you parse the string first to make an educated guess about its likely format before you then try to decode it as a TDateTime? Yes, and more than that, its the BOM for UTF-16LE specifically. So you can use TEncoding.Unicode when loading the file. Actually, internally TMemIniFile reads the file into a TStringList first before parsing it, and TStringList is smart enough to look for a BOM when no TEncoding is specified, so in theory TMemIniFile should be able to load your file in the correct encoding provided a BOM is always present and TMemIniFile.Encoding is nil. However... The file you have provided has a UTF-8 BOM, not a UTF-16 BOM. So your example code would need to use TEncoding.UTF8 instead of TEncoding.Unicode (or, don't specify the encoding at all and let TMemIniFile figure it out for you).
×