在Delphi XE中将UnicodeString转换为PAnsiChar

时间:2010-12-28 01:32:38

标签: delphi pointers delphi-xe unicode-string ansistring

在Delphi XE中,我使用的是包含此功能的BASS audio library

function BASS_StreamCreateURL(url: PAnsiChar; offset: DWORD; flags: DWORD; 
    proc: DOWNLOADPROC; user: Pointer):HSTREAM; stdcall; external bassdll;

'url'参数的类型为PAnsiChar,所以在我的代码中我做了一个演员:

FStreamHandle := BASS_StreamCreateURL(PAnsiChar( url ) [...]

编译器在此行上发出警告:“对PAnsiChar的字符串的可疑类型转换”。在尝试消除警告时,我found that推荐的方法是使用双重投射:

FStreamHandle := BASS_StreamCreateURL(PAnsiChar( AnsiString( url )) [...]

这确实消除了警告,但是BASS函数现在返回错误代码2(“无法打开文件”),这告诉我它收到的URL字符串以某种方式被破坏。我无法看到低音DLL实际接收到的内容,但在调试器中使用断点时字符串看起来很好:

var
  s : PAnsiChar;
begin
  s := PAnsiChar( AnsiString( url ));

此时字符串s显示正常,但是当我通过它时BASS功能失败。我的初始代码:PAnsiChar(url)适用于BASS,但会发出警告。

那么在没有警告的情况下从UnicodeString到PAnsiChar的正确方法是什么?

2 个答案:

答案 0 :(得分:15)

我很惊讶

BASS_StreamCreateURL(PAnsiChar( url ) [...]

的工作原理。如果url是unicode字符串,则每个字符将占用两个字节。例如,如果字符串是test,则会读取

7400 6500 7300 7400 0000                t e s t #0                     (Unicode)

在记忆中。请注意,字符串以空字符结尾(0000)当您执行

PAnsiChar(url)

你会告诉编译器这个地址的内存应该被认为是AnsiString。但是如果考虑上面的字节序列,在这种情况下你只会发现“t”。实际上,作为AnsiString,序列应该被解释为

74 00 65 00 73 00 74 00 00 00           t #0 e #0 s #0 t #0 #0 #0      (Ansi)

这是字符串“t”,以空字符(00)结尾。

PAnsiChar(AnsiString(url))
另一方面,

会首先将unicode字符串转换为ansi字符串,也就是说,你将获得

74 65 73 74 00                           t e s t #0                    (Ansi)

这是以空字符(00)结尾的字符串“test”。

更新

我不是通灵者(但是),但也许Bass库实际上确实需要指向UnicodeString的指针作为此函数的第一个参数?这可以解释为什么看似奇怪的

PAnsiChar(url)

的工作原理。库可能会说“嘿,只要给我指向unicode字符串的指针,我就用它做一些手动处理”,上面的代码就是这样(指针只是一个指针(也就是说,一个无符号的32位)整数)...)。但是编译器会抱怨,当然,因为库明确告诉编译器它需要PAnsiChar,而且通常PAnsiChar(SomeUnicodeString)是坏的。如果是这种情况,那么,

PAnsiChar(AnsiChar(url))

不起作用。库需要/一个unicode字符串的地址,但是得到/一个ansi字符串的地址。

更新2

如果我在 Update 1 中的假设是正确的,则声明

function BASS_StreamCreateURL(url: PWideChar; offset: DWORD; flags: DWORD; 
    proc: DOWNLOADPROC; user: Pointer):HSTREAM; stdcall; external bassdll;

会再次使天空变蓝。

更新3

来自文档:

  

注意:Delphi 2009用户应尽可能使用BASS_UNICODE标志

我敢打赌,如果您在拨打BASS_StreamCreateURL时指定此标志,问题就会消失!

来自样本单元Main.pas

Channel := BASS_StreamCreateFile(FALSE, PChar(OpenDialog.FileName), 0, 0,
    0 {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});

答案 1 :(得分:1)

以下代码段在我的经验中非常有用。 {$IFDEF UNICODE}BASS_UNICODE {$ENDIF}