RokWeb 0 Posted October 1, 2023 Hello. Users of my application began to complain about an error in the application after updating to iOS 17 (screenshot attached). The server is written in Delphi 7 and sends data in CP1251 encoding (it is not possible to change this). Before the iOS 17 update, the application worked and no errors occurred. The exception occurs in the code section of the idGlobal.pas module: else FMaxCharSize := LocaleCharsFromUnicode(FCodePage, FWCharToMBFlags, @cValue[0], 2, nil, 0, nil, nil); //returns less than 1 and then throws an exception RSInvalidCodePage if FMaxCharSize < 1 then begin raise EIdException.CreateResFmt(@RSInvalidCodePage, [FCodePage]); end; From the information that I was able to find, a relatively similar problem was discussed here: https://youtrack.jetbrains.com/issue/KT-59124/kmm-production-sample-iOS-app-crashes-after-start-on-iOS17-Beta If I understand correctly, the problem was solved by updating XCode to 15 Beta 6 and using the current version of the iOS SDK. Tell me, please, has anyone encountered this problem? Are there any solutions (I can’t update to the latest versions of XCode and iOS SDK)? Share this post Link to post
Dave Nottage 557 Posted October 1, 2023 3 hours ago, RokWeb said: If I understand correctly, the problem was solved by updating XCode to 15 Beta 6 and using the current version of the iOS SDK. If I understand correctly, the current beta of Xcode is relevant only to those developing for Vision OS - it is otherwise the same as the released version. 3 hours ago, RokWeb said: The exception occurs in the code section of the idGlobal.pas module Can you provide evidence that this is the case? Regardless, I'm not sure why Indy chooses to use its own function over the one in System.pas - the code for it is executed differently on iOS than on Windows. Share this post Link to post
Remy Lebeau 1396 Posted October 2, 2023 10 hours ago, Dave Nottage said: Can you provide evidence that this is the case? Clearly it is, since the user is seeing an Indy error message. 10 hours ago, Dave Nottage said: Regardless, I'm not sure why Indy chooses to use its own function over the one in System.pas - the code for it is executed differently on iOS than on Windows. Which function are you referring to? The failing code is calling LocaleCharsFromUnicode(), which is an RTL system function. Share this post Link to post
Remy Lebeau 1396 Posted October 2, 2023 14 hours ago, RokWeb said: Users of my application began to complain about an error in the application after updating to iOS 17 (screenshot attached). The server is written in Delphi 7 and sends data in CP1251 encoding (it is not possible to change this). Before the iOS 17 update, the application worked and no errors occurred. What does the call stack look like when the error occurs? What is your code doing that it is trying to instantiate an Indy encoding that is not working correctly with CP1251? Share this post Link to post
RokWeb 0 Posted October 2, 2023 (edited) 16 minutes ago, Remy Lebeau said: Как выглядит стек вызовов в случае возникновения ошибки? Что делает ваш код, который пытается создать экземпляр кодировки Indy, которая не работает правильно с CP1251? I can't check because I have an iPhone with iOS 15.6 and updating to iOS 17 is scary since Delphi 11.3 doesn't fully support iOS 17 (for example, as far as I know, debugging doesn't work) constructor TClientThread.Create(AHost: String; APort: Integer); begin inherited Create(True); FClient := TIdTCPClient.Create; FClient.ReadTimeout := 100; FClient.ConnectTimeout := 1000; FHost := AHost; FClient.Host := FHost; FClient.Port := APort; FClient.CreateIOHandler(); FClient.IOHandler.DefStringEncoding := IndyTextEncoding(1251); end; And the Execute method of the data flow: try try while not Terminated do begin Sleep(1); FClient.IOHandler.CheckForDisconnect(true,true); if FClient.IOHandler.InputBufferIsEmpty then begin if not FClient.IOHandler.CheckForDataOnSource(10) then begin CSForData.Enter; try if (Length(FSendData) > 0) then FClient.IOHandler.Write(FSendData, IndyTextEncoding(1251)); FSendData := ''; finally CSForData.Leave; end; Continue; end; end; {$IFDEF IOS} FData := FData + FClient.IOHandler.InputBufferAsString(IndyTextEncoding(1251)); {$ENDIF} if Length(FData) > 0 then begin Synchronize(nil,DoData); SetLength(FData, 0); end; CSForData.Enter; try if (Length(FSendData) > 0) then FClient.IOHandler.Write(FSendData, IndyTextEncoding(1251)); FSendData := ''; finally CSForData.Leave; end; end; except on E: Exception do DoError(E); end; finally FClient.Disconnect(False); if FClient.IOHandler <> nil then FClient.IOHandler.InputBuffer.Clear; Synchronize(DoDisconnected); end; Edited October 2, 2023 by RokWeb Share this post Link to post
Dave Nottage 557 Posted October 2, 2023 24 minutes ago, Remy Lebeau said: Which function are you referring to? I was looking at the wrong part of IdGlobal, i.e. line 2865 (in Delphi 11.3) Share this post Link to post
Remy Lebeau 1396 Posted October 2, 2023 5 hours ago, RokWeb said: I can't check because I have an iPhone with iOS 15.6 and updating to iOS 17 is scary since Delphi 11.3 doesn't fully support iOS 17 Ok 5 hours ago, RokWeb said: for example, as far as I know, debugging doesn't work FYI, the whole point of setting the IOHandler.DefStringEncoding property is so you don't need to keep specifying an encoding on every subsequent read/write string operation on the IOHandler, like you are doing. That said, where is your call to Connect()? You don't need to call CreateIOHandler() if you set the DefStringEncoding after Connect() exits successfully. In any case, the code shown is fine as far as encodings are concerned, so the error has to be caused by an underlying failure of the newer OS, or a bug in the RTL. Either way, try using the charset name via IndyTextEncoding('Windows-1251') or GetCharsetEncoding('Windows-1251') instead of using the codepage number via IndyTextEncoding(1251). If those still fail, then the latter one provides a GIdEncodingNeeded event so you can provide Indy with your own class that implements the IIdTextEncoding interface, so you can do your own encoding/decoding of the CP1251 data (which is fairly trivial using a simple lookup table via https://en.m.wikipedia.org/wiki/Windows-1251) 5 hours ago, RokWeb said: constructor TClientThread.Create(AHost: String; APort: Integer); begin inherited Create(True); FClient := TIdTCPClient.Create; FClient.ReadTimeout := 100; FClient.ConnectTimeout := 1000; FHost := AHost; FClient.Host := FHost; FClient.Port := APort; FClient.CreateIOHandler(); FClient.IOHandler.DefStringEncoding := IndyTextEncoding(1251); end; And the Execute method of the data flow: try try while not Terminated do begin Sleep(1); FClient.IOHandler.CheckForDisconnect(true,true); if FClient.IOHandler.InputBufferIsEmpty then begin if not FClient.IOHandler.CheckForDataOnSource(10) then begin CSForData.Enter; try if (Length(FSendData) > 0) then FClient.IOHandler.Write(FSendData, IndyTextEncoding(1251)); FSendData := ''; finally CSForData.Leave; end; Continue; end; end; {$IFDEF IOS} FData := FData + FClient.IOHandler.InputBufferAsString(IndyTextEncoding(1251)); {$ENDIF} if Length(FData) > 0 then begin Synchronize(nil,DoData); SetLength(FData, 0); end; CSForData.Enter; try if (Length(FSendData) > 0) then FClient.IOHandler.Write(FSendData, IndyTextEncoding(1251)); FSendData := ''; finally CSForData.Leave; end; end; except on E: Exception do DoError(E); end; finally FClient.Disconnect(False); if FClient.IOHandler <> nil then FClient.IOHandler.InputBuffer.Clear; Synchronize(DoDisconnected); end; Share this post Link to post