-
Content Count
2633 -
Joined
-
Last visited
-
Days Won
109
Everything posted by Remy Lebeau
-
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.
-
That is odd, because the TIdHTTP.Request.AcceptLanguage property is empty by default, and according to the HTTP spec:
-
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.
-
Does application.processmessages behaviour differ between VCL and FMX?
Remy Lebeau replied to Incus J's topic in RTL and Delphi Object Pascal
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. -
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.
-
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.
-
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;
-
How to extract all resourcestrings from 3rd party components
Remy Lebeau replied to Tom Mueller's topic in General Help
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? -
FMX.WebBrowser + Rio 10.3.1 = net::ERR_CLEARTEXT_NOT_PERMITTED
Remy Lebeau replied to Magno's topic in FMX
More information: Android Developers Blog: Protecting against unintentional regressions to cleartext traffic in your Android apps Fix Cleartext Traffic Error in Android 9 Pie android:usesCleartextTraffic -
"Gotchas" when calling from a C DLL to a Delphi DLL?
Remy Lebeau replied to Dave Nottage's topic in General Help
There are only 2 other ways that I know of: a deadlock inside of the function, so it never even tries to exit. a corrupted call stack, ie the return address gets overwritten before the function tries to exit. -
"Gotchas" when calling from a C DLL to a Delphi DLL?
Remy Lebeau replied to Dave Nottage's topic in General Help
The exception handler itself is coded fine (once you fix the undefined behavior of your IntToStr() implementation), it just may not catch every possible type of exception that can be thrown. It can only handle SEH exceptions. But there is no guarantee that what you are experiencing is even related to exceptions. There may not even be an exception being thrown at all. -
"Gotchas" when calling from a C DLL to a Delphi DLL?
Remy Lebeau replied to Dave Nottage's topic in General Help
I would suggest writing your own test EXE that simply loads the C DLL directly and calls its DriverOpen() function. Then you can step into the DLL with the debugger to see how it calls into your DelphiDriverOpen() function, see how the parameter data gets passed back and forth, how the call stack is managed, etc. then it may be an environmental issue rather than a coding mistake. Maybe the offending machine(s) are simply using a different/newer API than what your DLLs use. Who knows. can't answer that seeing the actual code, so stepping through it with a debugger. if the exception handler is not being called, then either there is no exception being thrown at all, or it is not an SEH-based exception. -
"Gotchas" when calling from a C DLL to a Delphi DLL?
Remy Lebeau replied to Dave Nottage's topic in General Help
No, it is not. And besides, your IntToStr() has undefined behavior anyway. You should get rid of that function and just use sprintf() directly in your HandleException(). But that is beside the point. That implies to me that possibly either: there is a calling convention mismatch in how DelphiDriverOpen() is declared in both languages. the Delphi DLL is corrupting the call stack. an uncaught exception is being thrown. You will have to use the debugger to verify one way or the other. -
Blogged : Delphi Package Manager RFC
Remy Lebeau replied to Vincent Parrett's topic in Tips / Blogs / Tutorials / Videos
You SHOULD already be able to have per-project packages, in all versions of Delphi. The trick is to install the packages but NOT ENABLE them globally. With no project loaded, install the packages as needed, and then disable them so they don't appear in the Palettes when no project is loaded. Then, load a project, enable only the installed packages it actually needs, and close the project. Repeat as needed for other projects. Then, from now on, the IDE should load and unload packages on a per-project basis. At least, this is how it used to work years ago, I haven't tried it in recent years. Hopefully it still works. -
"Gotchas" when calling from a C DLL to a Delphi DLL?
Remy Lebeau replied to Dave Nottage's topic in General Help
That would imply that the Delphi DLL is not coded properly for the Citrix driver API, since Delphi can certainly produce C-compatible DLLs. Aside from that, the only thing I see wrong in your Delphi translation is that HND should be a Pointer instead of a THandle, and pLibMgrCallTable should be a Pointer instead of a ^DWORD. However, you did not show your translation of the PVD type, or of the DriverOpen() and other DLL function declarations. Since Citrix is complaining about an invalid handle, did you debug your C and Delphi DLLs to log the value of the handle that DriverOpen() actually returns, and the value of the handle that the other DLL functions receive? Where exactly does DriverOpen() return the handle to - the 'int' return value, or in the PVD parameter? -
Your translation is WAY off. DONT use String for char* parameters, use PAnsiChar instead. DONT use Integer for (D)WORD parameters, use (D)WORD instead. And your handle parameter needs to be declared as 'var' or 'out' since it is an output parameter. Try this instead: function MXEIO_E1K_Connect(szIP: PAnsiChar; wPort: WORD; dwTimeOut: DWORD; var hConnection: Integer; szPassword: PAnsiChar): Integer; stdcall; external 'MXIO.dll' name 'MXEIO_E1K_Connect'; Or this: function MXEIO_E1K_Connect(szIP: PAnsiChar; wPort: WORD; dwTimeOut: DWORD; hConnection: PInteger; szPassword: PAnsiChar): Integer; stdcall; external 'MXIO.dll' name 'MXEIO_E1K_Connect';
-
The DLL's documentation includes VB declarations. VB doesn't support cdecl at all, only stdcall. Also, I just found this, which clearly shows the DLL functions using CALLBACK for the calling convention. That is a preprocessor macro that maps to __stdcall.
-
In general, when creating a URL by hand, I suggest NOT using TIdURI.URLEncode() on the entire URL, but instead use TIdURI.PathEncode() and TIdURI.ParamsEncode() on individual components that actually need to be encoded. Also, the default encoding for TStringStream is the OS default charset, but JSON typically uses UTF-8 instead. If you know the actual charset the server uses for the response, you could hard-code it in the TStringStream constructor. But, it is generally better to just let TIdHTTP handle charset decoding of strings for you instead. Try something more like this: var http : TIdHTTP; sslIO : TIdSSLIOHandlerSocketOpenSSL; url, data : String; begin http := TIdHTTP.Create(nil); try http.HTTPOptions := http.HTTPOptions + [hoNoProtocolErrorException, hoWantProtocolErrorContent]; sslIO := TIdSSLIOHandlerSocketOpenSSL.Create(http); sslIO.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; sslIO.SSLOptions.Mode := sslmClient; sslIO.SSLOptions.VerifyMode := []; sslIO.SSLOptions.VerifyDepth := 0; http.IOHandler := sslIO; url := 'https://translate.googleapis.com/translate_a/single?client=gtx&sl=de&tl=ru&dt=t&q=' + TIdURI.ParamsEncode('Müller'); data := http.Get(url); finally http.Free; end; end;
-
Blogged : Delphi Package Manager RFC
Remy Lebeau replied to Vincent Parrett's topic in Tips / Blogs / Tutorials / Videos
FreePascal already has its own Online Package Manager built right into the Lazarus IDE. -
You can't, as TPaintBox is painted dynamically on an as-needed basis, so there is no persistent image to "capture". You need to change your drawing code to draw onto a TBitmap (or any arbitrary TCanvas) instead, and wrap that code into a function that you can call whenever needed. That way, when the TPaintBox needs to be painted, create a temp TBitmap, have the function draw onto it, and then draw that TBitmap onto the TPaintBox. When you want to do the "capture", create another temp TBitmap, have the function draw onto it, and then use the TBitmap as needed. This applies to both VCL and FMX, BTW.
-
TIdMessageBuilderHtml add background color
Remy Lebeau replied to azrael_11's topic in Network, Cloud and Web
That has nothing to do with Indy, and everything to do with your particular HTML/CSS, and whether the receiver supports what you are trying (for instance, not all email readers support background images - see The Ultimate Guide to Background Images in Email ). Indy does not care about the particular content of your HTML, it gets sent as-is (other than to encode it for transmission purposes only). I will say this, though: - in your 1st case, 'url("back,png")' is wrong for an embedded image, you need to use 'url("cid:back.png")' instead since "back.png" is the Content-ID you assigned to the attachment. - in your 2nd case, the 3rd call to Html.Add() appears to be broken. -
Does application.processmessages behaviour differ between VCL and FMX?
Remy Lebeau replied to Incus J's topic in RTL and Delphi Object Pascal
There is nothing wrong with using threads in general, you just have to understand WHEN and HOW to use them effectively. You can't write really high performant software without them. Especially on mobile platforms. I've been writing commercial software for 20 years as well, and have written many multi-threaded applications, just using Delphi's 'TThread' class. I don't use a version that has Embarcadero's "Parallel Processing Library" available (I would never trust it anyway, it has had too many bugs over the years!), and I don't use 3rd party threading libraries, like OmniThreadLibrary, etc (not that there is anything wrong with them, I just never got into them). -
Any chance to see NNTP version of the forum?
Remy Lebeau replied to AlexBelo's topic in Community Management
Very carefully! -
Conditional compilation for various Delphi versions
Remy Lebeau replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
Actually, since Delphi 6+, the preferred method is to use $IF with the CompilerVersion and/or RTLVersion constants, instead of using $IFDEF with the VERxxx conditionals.