Delphi 7中的宽字符串转换为字符串

6

我的应用程序是使用Delphi 7编写的非Unicode应用程序。

我想使用以下函数将Unicode字符串转换为ANSI:

function convertU(ws : widestring) : string;
begin
  result := string(ws);
end;

我也使用这段代码来设置正确的代码页进行转换。
initialization
  SetThreadLocale(GetSystemDefaultLCID);
  GetFormatSettings;

在VCL主线程中它运行得非常好,但在TThread中却不行,其中函数convertU的结果会出现一些问号'?'

为什么在TThread中不能正常工作?


首先,您不需要使用函数或类型转换来完成此操作;简单的stringVar := wideStringVar;就可以。其次,问题在于并非所有WideChar都可以直接转换为AnsiString;有些字符宽度超过一个字符,并且有些字符值无法用AnsiChar表示,有些字体不包含所有可能的Unicode值。如果你看到?,那就意味着你正在显示它们,这可能是第三个问题 - 线程不应该在没有使用Sychronize的情况下访问GUI控件。由于您没有发布显示代码,很难确定是否是这个问题。 - Ken White
问题是:为什么在TThread中使用时会出现问号? - user382591
2
我再说一遍:如果你看到了“?”,那么你正在显示文本。你没有提供任何关于如何显示它的代码或信息。 - Ken White
我使用调试器查看返回的值。 - user382591
一个 TThread 是否知道系统默认的 LCID? - user382591
当Unicode转换为MBCS的代码点失败时,您会得到一个问号。由于您正在讨论AnsiString的内容,并且由于?在常见的ASCII范围内,因此我们可以排除显示错误。 - David Heffernan
2个回答

6
据我所知,SetThreadLocale函数不会改变当前系统代码页,因此不会影响Delphi 7中的widestringansistring的转换,该转换依赖于GetACP API调用,即系统代码页。
系统代码页在控制面板中设置,例如在Windows Seven中,选择区域和语言/管理选项卡/非Unicode应用程序的代码页。这需要重新启动系统。
Delphi 7使用此系统代码页,在所有转换API调用中提供0。因此,据我所知,SetThreadLocale不会影响Delphi 7中的widestringansistring的转换。它将更改区域设置(例如日期/时间和货币格式),而不是系统用于其Ansi<->Unicode转换的代码页。
较新版本的Delphi具有SetMultiByteConversionCodePage()函数,能够设置用于所有AnsiString处理的代码页。
但是,API调用(即在Windows.pas中映射为...()的所有....A()函数)将使用此系统代码页。因此,如果要处理其他代码页,则必须在转换为Unicode后调用...W() wide API。也就是说,Delphi 7 VCL仅适用于系统代码页,而不是由SetThreadLocale指定的值。
在Delphi 7中,我的建议是:
  • 在所有地方使用WideString和特定的“Wide”API调用-有几组适用于Delphi 7的处理WideString的组件;
  • 使用自己的类型,具有专用字符集,但在使用VCL / RTL或“Ansi”API调用之前,您需要进行显式转换-例如MyString = type AnsiString(这是我们在mORMot中所做的,通过为内部UTF-8过程定义自定义RawUTF8类型)。
在Delphi 2009及更高版本中,这可以更好地处理,因为您可以为每个AnsiString类型指定代码页,并正确处理API调用或VCL进程的Unicode转换。

我只想处理默认的系统代码页。我注意到,对于Unicode<->Ansi转换,使用的代码页是默认用户LCID而不是默认系统LCID,这是一个问题。 - user382591

5
initialization块中调用SetThreadLocale()TThread没有影响。如果要设置线程的区域设置,您必须在TThread.Execute()方法中调用SetThreadLocale()
更好的选择是不依赖于SetThreadLocale()。通过直接调用WideCharToMultiByte()进行自己的转换,以便您可以指定要转换为的特定Ansi代码页。

如果您的应用程序中所有代码都是由您编写的,则显式调用WideCharToMultiByte是可行的选项。如果包含第三方代码,则线程范围内设置区域设置可能是最好的折衷方案。 - David Heffernan
是的,谢谢。我现在使用两种方法的混合:WideCharToMultiByte和SetThreadLocale()。 - user382591
因为我最初使用了WideCharToMultiByte,但我认为我将删除对该函数的调用,并仅为每个线程使用SetThreadLocale。 - user382591
据我所知,SetThreadLocale 不会更改当前系统代码页,因此不会影响 Delphi 7 中基于 GetACP API 调用的宽字符串到窄字符串的转换。 - Arnaud Bouchez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接