在Delphi XE中将UnicodeString转换为PAnsiChar

3
在Delphi XE中,我正在使用BASS音频库,其中包含此函数:
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类型”。在尝试消除警告时,我发现推荐的方法是使用双重转换:
FStreamHandle := BASS_StreamCreateURL(PAnsiChar( AnsiString( url )) [...]

这样做可以消除警告,但是BASS函数现在返回错误代码2(“无法打开文件”),这告诉我它接收到的URL字符串有些问题。我看不到bass DLL实际接收到什么,但是使用调试器中的断点,字符串看起来很好:
var
  s : PAnsiChar;
begin
  s := PAnsiChar( AnsiString( url ));

在这一点上,字符串s看起来很好,但是当我传递它时,BASS函数失败了。我的初始代码:PAnsiChar(url)与BASS配合良好,但会发出警告。
那么,从UnicodeString到PAnsiChar的正确方法是什么,而不会有警告呢?

我刚刚使用了AnsiString AnsiString:我的URL; myURL:='http://...' BASS_StreamCreateURL(PAnsiChar(myURL),0,[...] 并且在Delphi XE2上工作。 - vhanla
2个回答

16
我很惊讶
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)

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

PAnsiChar(AnsiString(url))

另一方面,它会先将Unicode字符串转换为ANSI字符串,即您将获得

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

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

更新

我还不是能预知未来的先知,但也许Bass库实际上需要一个指向UnicodeString的指针作为该函数的第一个参数?这可能解释了为什么看起来很奇怪的

PAnsiChar(url)

在 IT 技术中,有一个库可能会这样说:“嘿,把指向 Unicode 字符串的指针给我,然后我会用它来做一些手动处理”,而上面的代码也正是如此(指针只是一个指针(即,一个无符号 32 位整数)……)。但编译器当然会报错,因为该库明确告诉编译器它需要一个 PAnsiChar,而通常情况下 PAnsiChar(SomeUnicodeString) 是不好的。如果出现这种情况,那么……

PAnsiChar(AnsiChar(url))

无法正常工作。这个库期望一个Unicode字符串的地址,但是得到了一个ANSI字符串的地址。

更新2

如果我在更新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});

我完全同意您的分析。然而,“可疑”的类型转换有效,而应该正确的转换却会导致BASS出错。在Delphi XE和2009中行为相同。(BASS随附的演示之一也使用了从字符串到PAnsiChar的简单转换。) - Marek Jedliński
2
关于您的更新3:BASS_UNICODE标志确实存在于我的代码中。而且您之前的直觉可能是正确的。由于指定了该标志,即使声明不是这样,BASS也会期望一个PWideChar。非常感谢,Andreas! - Marek Jedliński

1
以下代码片段在我的经验中表现很好。 {$IFDEF UNICODE}BASS_UNICODE {$ENDIF}

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