Jump to content

Remy Lebeau

Members
  • Content Count

    2319
  • Joined

  • Last visited

  • Days Won

    94

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Compiling code originally for VS in C++Builder

    Works fine for me. The extern "C" has nothing to do with the exporting itself, that is handled by the __declspec. The extern "C" simply disables name mangling on the exported name. I've never had to resort to that. But then again, I've never tried linking to 3rd party static .lib files that are not already compatible with C++Builder, and import .lib files for DLLs are easy to regenerate using C++Builder's implib.exe.
  2. Remy Lebeau

    Using dxgettext on Windows 10

    I can think of one reason: System.UTF8ToUnicode(), and its companion System.UnicodeToUTF8(), were broken or more accurately incomplete in Delphi 6-2007, as they did not support 4-byte UTF-8 sequences (Unicode codepoints outside of the BMP), only 1-3 byte sequences (Unicode codepoints in the BMP) . That was not fixed until Delphi 2009, when they were rewritten to use platform conversions instead of manual conversions. Now granted, at the time, 4-byte UTF-8 sequences were pretty rare, typically only seen in strings using Eastern Asian languages. But in modern Unicode, they are much more common now, especially with the popularity of emojis on the rise, most of which use high codepoint values outside the BMP.
  3. UTF8String was first introduced in Delphi 6 (but it did not become a true UTF-8 string until Delphi 2009). Delphi 6 has the following UTF8 <-> UTF16 functions in the System unit: function UnicodeToUtf8(Dest: PChar; Source: PWideChar; MaxBytes: Integer): Integer; overload; deprecated; function Utf8ToUnicode(Dest: PWideChar; Source: PChar; MaxChars: Integer): Integer; overload; deprecated; function UnicodeToUtf8(Dest: PChar; MaxDestBytes: Cardinal; Source: PWideChar; SourceChars: Cardinal): Cardinal; overload; function Utf8ToUnicode(Dest: PWideChar; MaxDestChars: Cardinal; Source: PChar; SourceBytes: Cardinal): Cardinal; overload; function UTF8Encode(const WS: WideString): UTF8String; function UTF8Decode(const S: UTF8String): WideString; Do note, however, that these functions did not support 4-byte UTF-8 sequences (Unicode codepoints outside of the BMP), only 1-3 byte sequences (Unicode codepoints in the BMP). That was not fixed until Delphi 2009, when they were rewritten to use platform conversions instead of manual conversions. Now granted, at the time, 4-byte UTF-8 sequences were pretty rare, typically only seen in strings using Eastern Asian languages. But in modern Unicode, they are much more common now, especially with the popularity of emojis on the rise, most of which use high codepoint values outside the BMP.
  4. Like the events of most other components, the events of Indy components expect class methods, not standalone functions. You could go the way that others have recommended, where creating a TDataModule at design-time and using it at run-time would be the easiest. But, even if you were to simply write a class in code to wrap the events, note that you don't actually need to instantiate such a class at run-time, you can declare its methods with the class directive instead, eg: program IndyConsoleApp; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, IdContext, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, IdGlobal; var IdTCPServer1: TIdTCPServer; procedure ShowStartServerdMessage; begin WriteLn('START SERVER @' + TimeToStr(now)); end; procedure StopStartServerdMessage; begin WriteLn('STOP SERVER @' + TimeToStr(now)); end; type TCPServerEvents = class class procedure OnExecute(AContext: TIdContext); end; class procedure TCPServerEvents.OnExecute(AContext: TIdContext); var LLine: String; begin LLine := AContext.Connection.IOHandler.ReadLn(); writeln(LLine); AContext.Connection.IOHandler.WriteLn('OK'); end; begin try IdTCPServer1 := TIdTCPServer.Create; try with IdTCPServer1.Bindings.Add do begin IP := '127.0.0.1'; Port := 6000; end; IdTCPServer1.OnExecute := TCPServerEvents.OnExecute; IdTCPServer1.Active := True; try ShowStartServerdMessage; Readln; finally IdTCPServer1.Active := False; StopStartServerdMessage; end; finally IdTCPServer1.Free; end; except on E: Exception do WriteLn(E.ClassName, ': ', E.Message); end; end. However, there IS actually a way to use a standalone function instead, but it involves a little trickery using Delphi's TMethod record: program IndyConsoleApp; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, IdContext, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, IdGlobal; var IdTCPServer1: TIdTCPServer; procedure ShowStartServerdMessage; begin WriteLn('START SERVER @' + TimeToStr(now)); end; procedure StopStartServerdMessage; begin WriteLn('STOP SERVER @' + TimeToStr(now)); end; procedure TCPServerExecute(ASelf: Pointer; AContext: TIdContext); // NOTE THE EXTRA PARAMETER! var LLine: String; begin LLine := AContext.Connection.IOHandler.ReadLn(); WriteLn(LLine); AContext.Connection.IOHandler.WriteLn('OK'); end; var ExecuteFct: TMethod; begin try IdTCPServer1 := TIdTCPServer.Create; try with IdTCPServer1.Bindings.Add do begin IP := '127.0.0.1'; Port := 6000; end; ExecuteFct.Data := nil; // or anything you want to pass to the ASelf parameter... ExecuteFct.Code := @TCPServerExecute; IdTCPServer1.OnExecute := TIdServerThreadEvent(ExecuteFct); IdTCPServer1.Active := True; try ShowStartServerdMessage; Readln; finally IdTCPServer1.Active := False; StopStartServerdMessage; end; finally IdTCPServer1.Free; end; except on E: Exception do WriteLn(E.ClassName, ': ', E.Message); end; end.
  5. Remy Lebeau

    Who is doing MacOS app with RadStudio for clients ?

    Yes. The OSX 64bit compiler is still in development and is scheduled to be released sometime in a future 10.3.x update.
  6. Remy Lebeau

    best practise sharing : 2 buttons with mrOK on a form

    There is no way to differentiate which control set the ModalResult if they both set it to the same value. ShowModal() will close the Form when any non-zero value is assigned to ModalResult, you are not limited to the handful of constants that the RTL defines, use whatever values you want.
  7. Remy Lebeau

    Compiling code originally for VS in C++Builder

    You appear to be mixing C/C++ and Delphi syntaxes. It should be: extern "C" __declspec(dllexport) int Load(PDLLLINK pLink); Or: extern "C" int __declspec(dllexport) Load(PDLLLINK pLink);
  8. Remy Lebeau

    Compiling code originally for VS in C++Builder

    Those modifiers can be applied directly to the function declaration. But, are you referring to a VS static library that you want to link inside your C++Builder DLL? Or are you referring to the import lib that the C++Builder compiler outputs in addition to the DLL? I'm confused about your setup and what you are trying to accomplish exactly.
  9. Remy Lebeau

    Compiling code originally for VS in C++Builder

    I rarely use .DEF files in my C++Builder DLLs, I prefer to use 'extern "C"' and __declspec(dllexport) in the DLL's source code instead. .DEF files are good when you need custom control over how exports are name-mangled and/or assigned ordinal values. In most cases, that is not necessary. I have never seen that error before, and the only reference to it I find online is this: https://stackoverflow.com/questions/5707046/ However, I have never upgraded my C++Builder to a version that is based on MSBuild, either. I still use C++Builder 6.
  10. Remy Lebeau

    Compiling code originally for VS in C++Builder

    bcc32c is the CLang-based Windows 32bit compiler. What you are seeing is a CLang error message. See Stricter C++ Compilers (Clang-enhanced C++ Compilers): Token Pasting in Embarcadero's documentation. Also, I don't think __VA_ARGS__ will work in this situation, as that would pass FuncName as the first parameter to FuncName(). I'm sure Hpc_Trace_AddClassId() does not expect Hpc_Trace_AddClassId as its first parameter value. In the Project Options, in the C++ Compiler section, there is a "Use 'classic' Borland compiler" option.
  11. Remy Lebeau

    Delphi inline and function evaluation

    Why does that surprise you? ANY compiler worth its salt will perform optimizations, especially when dealing with inline functions, and would be smart enough to recognize when basic arithmetic operations are being performed on compile-time literals and just output code for the result of those operations at compile-time instead of generating code to perform those operations at runtime. "5 > 9" is ALWAYS false, so the compiler can just output False instead of outputting code to compare 5 to 9. And "if False Then" will ALWAYS skip the IF block and jump to the ELSE block. so the compiler can ignore any of the code in the IF block. As such, your call to Max(5, 9) gets optimized to simply "Result := 9" when inlined, and so the compiler can simply replace the entire call to Max(5, 9) with just the literal 9.
  12. Remy Lebeau

    Compiling code originally for VS in C++Builder

    What kind of errors exactly? Also, which version of C++Builder are you using? Which of its many C++ compilers specifically are you using? Are you using a compiler that is compatible with the C++ version that the VS code is targeting? That would imply that the .lib file is probably a static library instead of an import lib for a DLL. If the VS project is for a static library rather than a DLL, then compiling it as a DLL may not be helpful since its code would likely not be setup to export symbols that IMPLIB could find.
  13. Remy Lebeau

    OverbyteIcscryptuiapi.h what is it for?

    I think such an HPPEMIT statement should not be needed at all. Delphi .PAS files output .HPP files, not .H files, when compiled for C++Builder, and there is no need for a .PAS file to ever HPPEMIT a reference to its own .HPP file.
  14. I can confirm the semicolons are required, in ALL C++ versions and compilers. Typedef statements need to be terminated by a semicolon, just like any other statement in C++.
  15. Indy has a similar issue, having dozens of packages that have to be updated whenever units are added/removed, and new packages that have to be created when new compilers are released. To help with this task, we created an internal project that has a database of active units and flags to dictate their behavior, and it can generate the various package files, resource files, etc for each compiler version we support. This way, when we do make changes to the units, we simply update the database and regenerate the packages. And when a new compiler is released, we can generate new package files for it. Indy works in C++Builder, but doesn't have any C++ project files, only Delphi project files. For C++, we simply compile the Delphi projects configured with C++ output enabled.
  16. Remy Lebeau

    Communication between processes (IPC)

    Another alternative for IPC on a single computer is the WM_COPYDATA message, see Using Data Copy. Network DDE across multiple computers is no longer supported, but Local DDE on a single computer is still supported.
  17. Remy Lebeau

    TIdSSLIOHandlerSocketOpenSSL and TLS 1.3 ?

    The link that Lars provided is a year old, but the answer is still the same. Indy does not yet support OpenSSL 1.1+, and so does not yet support TLS 1.3. It is in the planning stage, no code has been written yet: https://github.com/IndySockets/Indy/issues/183
  18. Remy Lebeau

    CreateProcess[A] only returns Chinese

    Typically, a fixed AnsiChar[] array can be passed to a PAnsiChar pointer, but apparently that only works when the low index is 0, whereas yours is 1 instead. From the documentation: Yes, that will work, and is what you need to do, unless you change your Buf array to use 0-based indexing.
  19. Remy Lebeau

    Request to Google Translate API

    That is odd, because the TIdHTTP.Request.AcceptLanguage property is empty by default, and according to the HTTP spec:
  20. Remy Lebeau

    Request to Google Translate API

    In what way exactly does it not work? Please show the expected and actual results. Have you tried sniffing the HTTP traffic to see exactly what is different between TIdHTTP's traffic vs a web browser's traffic? To see the browser's traffic (since you are using HTTPS), you can use a debugging proxy like Fiddler, or your web browser's own built-in debugger. To see TIdHTTP's traffic, you can use a debugging proxy, or assign one of Indy's TIdLog... components to the TIdHTTP.Intercept property.
  21. Then you are probably using Synchronize() ineffectively, or your synced code is doing too much work in the UI thread. Hard to say without seeing your actual code. ANYTHING that must be serialized with the main thread needs to be synced. That is USUALLY only the UI, but there may be other things, too. Again, it really depends on your particular code. File I/O is not one of them. Though, if you have multiple threads accessing the same file, they need to coordinate with each other, not with the main UI thread (unless the main UI thread is one of the threads accessing the file). FMX's TBitmap was not thread-safe prior to 10.2 Tokyo, it could not be used safely outside of the main UI thread. But that was changed in Tokyo, which you say you are using. Which CreateBitmapFromFile() function are you using exactly? I don't see any such function in FMX.
  22. Remy Lebeau

    CreateProcess[A] only returns Chinese

    Only the string input parameters that are passed directly to CreateProcess() itself are converted, not the data that gets sent over pipes afterwards. It needs to occur at the point where the data first enters your app, before you pass it up to other areas of your code. In general, you should use a single encoding inside your app (ie, always use Delphi's native encoding), and then convert data at the last possible moment when it leaves your app, and convert it at the earliest possible moment when it enters your app.
  23. Remy Lebeau

    CreateProcess[A] only returns Chinese

    Essentially, yes. Because the "dir" process is outputting its data in ANSI, but the TRedirectedConsole code is expecting the data to be in Unicode when compiled in D2009+. CMD.EXE (not COMMAND.EXE) outputs the result of its internal commands (those built into CMD.EXE itself, like "dir") in OEM/ANSI unless you explicitly ask it to output in Unicode via the "/u" parameter. To apply that to "dir" in your example, you would have to run CMD.EXE directly, executing "dir" via the "/c" parameter, then you can add the "/u" parameter, eg: "cmd.exe /c /u dir c:\" Note that this only works for internal commands. The "/u" parameter does not effect external processes run from CMD.EXE. They are free to output in whatever codepage they want, or in the calling console's current codepage. ReadFile() operates on raw bytes only. Like I said, it has no concept of strings or character encodings. So it will read whatever raw data is sent to the output pipes. In your situation, "dir" is outputting ANSI and you are reading it into a Unicode buffer, thus the mismatch leading to the mojibake text you see. Not easily, short of buffering the raw data as-is and then analyzing it to guess what encoding it may be using, and then convert it to the desired encoding if/when needed. That would be an external process outside of CMD.EXE, so you would be subject to whatever character encoding it decides to send its output as. You will have to test it and see what it sends,and then code for it accordingly. I think I've already covered that.
  24. Remy Lebeau

    CreateProcess[A] only returns Chinese

    The problem is not with CreateProcess[A] itself, but in how the output of the spawned process is [mis]interpreted by TRedirectedConsole when converted to a string format. What you describe is commonly known as "mojibake", and it can happen when 7/8-bit ANSI character data is mis-interpreted as 16/32-bit Unicode data, thus producing bad "characters" that fall within, in this case, the Chinese language, for instance. If you look at TRedirectedConsole's internal logic more carefully (specifically, in its ReadHandle() method), you will see that it reads the spawned process's output using the Win32 API ReadFile() function (which has no concept of string characters), stores the raw bytes as-is into a Char[] array, and then copies that array into a native String. That worked just fine in 2004 when Delphi's native String type was still AnsiString, and the native Char type was still AnsiChar. But remember that in Delphi 2009, the native String type was changed to UnicodeString, and the native Char type was changed to WideChar. As such, TRedirectedConsole's current reading logic is no longer valid under a Unicode environment. It needs to be updated to account for the fact that the 8-bit ANSI encoding of the spawned process's output is now different than the 16-bit Unicode encoding of Delphi's native String type. One way to handle this would be to tweak TRedirectedConsole to make it explicitly use AnsiString and AnsiChar everywhere, instead of using String and Char, respectively. This is commonly known as "Ansifying" your code, which is generally shunned upon, but this is a common use case for it. Also, note that AnsiString is now codepage-aware, so you would have to use the RTL's SetCodePage() function to assign a proper codepage identifier to any AnsiString you give to the user, so the ANSI data will be converted properly when assigned to other strings, such as a UnicodeString when adding to the TMemo (since the entire VCL uses UnicodeString now). Unless the spawned process is manipulating its output to use a specific encoding (like UTF-8), you can generally use the Win32 API GetACP() function for the codepage identifier, or the RTL's global DefaultSystemCodePage . For example: //============================================================== function TRedirectedConsole.ReadHandle(h: THandle; var s: AnsiString): integer; //============================================================== var BytesWaiting: DWORD; Buf: Array[1..BufSize] of AnsiChar; BytesRead: {$IFDEF VER100}Integer{$ELSE}DWORD{$ENDIF}; begin Result := 0; PeekNamedPipe(h, nil, 0, nil, @BytesWaiting, nil); if BytesWaiting > 0 then begin if BytesWaiting > BufSize then BytesWaiting := BufSize; ReadFile(h, Buf[1], BytesWaiting, BytesRead, nil); SetString(s, Buf, BytesRead); {$IFDEF CONDITIONALEXPRESSIONS} {$IF CompilerVersion >= 12} // D2009+ SetCodePage(PRawByteString(@s)^, GetACP(), False); {$IFEND} {$ENDIF} Result := BytesRead; end; end; ... //============================================================== procedure TRedirectedConsole.Run( working_dir : string ); //============================================================== var s: AnsiString; // not String! ... begin ... end; However, other areas of TRedirectedConsole would also have to be ansified, and I can see some issues with doing that, so I would suggest instead to have TRedirectedConsole continue to use the native String type everywhere, and just re-write the ReadHandle() method to explicitly convert the spawned process's output from ANSI to UTF-16, such as with the RTL's UnicodeFromLocaleChars() function (using the same codepage as above): {$IFDEF CONDITIONALEXPRESSIONS} {$IF CompilerVersion >= 12} {$DEFINE STRING_IS_UNICODESTRING} {$IFEND} {$ENDIF} //============================================================== function TRedirectedConsole.ReadHandle(h: THandle; var s: String): integer; //============================================================== var BytesWaiting: DWORD; Buf: Array[1..BufSize] of AnsiChar; BytesRead: {$IFDEF VER100}Integer{$ELSE}DWORD{$ENDIF}; begin Result := 0; if PeekNamedPipe(h, nil, 0, nil, @BytesWaiting, nil) then begin if BytesWaiting > 0 then begin if BytesWaiting > BufSize then BytesWaiting := BufSize; ReadFile(h, Buf[1], BytesWaiting, BytesRead, nil); {$IFDEF STRING_IS_UNICODESTRING} SetLength(s, UnicodeFromLocaleChars(GetACP(), 0, Buf, BytesRead, nil, 0)); UnicodeFromLocaleChars(GetACP(), 0, Buf, BytesRead, PChar(s), Length(s))); {$ELSE} SetString(s, Buf, BytesRead); {$IFEND} Result := BytesRead; end; end; end; //============================================================== procedure TRedirectedConsole.Run( working_dir : string ); //============================================================== var ... begin ... {$IFDEF UNICODE} // this is important to prevent a crash in CreateProcessW()... UniqueString(fCmdLine); {$ENDIF} // this raises an exception if anything happens Win32Check(CreateProcess(nil, PChar(fCmdLine), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, wd, fSI, fPI)); ... end;
  25. That makes sense, as an executable can have up to 65536 max exported names/ordinals, and up to 65536 max resource strings. It is a limitation of the EXPORTS and STRINGTABLE formats. That ticket discusses exported names, not string resources. Two different things. I would expect it to work for an application, as application's don't usually export very many names. Resource strings, that is a different matter. Just how many resource strings are you dealing with?
×