Преобразование UnicodeString в PAnsiChar в Delphi XE - PullRequest
2 голосов
/ 28 декабря 2010

В 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 как-то не работает. Я не вижу, что на самом деле получает басовая DLL, но с помощью точки останова в отладчике строка выглядит хорошо:

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

В этот момент строка s выглядит нормально, но функция BASS перестает работать, когда я передаю ее. Мой исходный код: PAnsiChar (url) хорошо работает с BASS, но выдает предупреждение.

Так, как правильно перейти от UnicodeString к PAnsiChar без предупреждения?

Ответы [ 2 ]

15 голосов
/ 28 декабря 2010

Я поражен, что

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)

- строка «test», заканчивающаяся нулевым символом (00).

Обновление

Я не экстрасенс (пока), но, может быть, библиотеке Басов действительно требуется указатель на UnicodeString в качестве первого аргумента этой функции? Это объяснило бы, почему казалось бы странным

PAnsiChar(url)

работает. Библиотека может сказать: «Эй, просто дай мне указатель на строку Unicode, и я сделаю некоторую ручную обработку с ней», и приведенный выше код делает именно это (указатель - это просто указатель (то есть 32-разрядный без знака целое число) ...). Но компилятор будет жаловаться, конечно, потому что простота библиотеки сказала компилятору, что для нее потребуется PAnsiChar, а обычно PAnsiChar(SomeUnicodeString) - это плохо. Если это так, то, конечно,

PAnsiChar(AnsiChar(url))

не работает. Библиотека ожидает / адрес / строки Юникода, но получает / адрес / строки 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});
1 голос
/ 10 октября 2011

Следующий фрагмент кода отлично работает на моем опыте.{$IFDEF UNICODE} или BASS_UNICODE {$ENDIF}

...