Предоставление буфера для получения значения Out LPWSTR - PullRequest
1 голос
/ 30 октября 2019

Я редко использую Windows API напрямую, но у меня есть ситуация, когда это неизбежно, и я не уверен, как с этим справиться.

Документация MS API относится к параметру вызова APIкак

out Something LPWSTR

, поэтому я, очевидно, должен предоставить буфер для его получения, но как мне узнать, какой размер буфера предоставить? Я полагаю, что я мог бы попытаться вызвать StrLen для Something, но это, кажется, означает, что мне нужно будет вызывать API дважды, один раз, чтобы получить длину, и второй раз, чтобы фактически получить результат, и длина может измениться за это время.

OTOH, я мог бы предварительно выделить буфер (например, как локальный массив WideChar), но это кажется немного непрактичным, потому что возвращаемая строка может иметь любую произвольную длину (?).

Итак, мой вопрос, как правильно получить значение Out LPWSTR? И, в качестве дополнения, как мне найти его кодировку (или Win API всегда использует одну и ту же кодировку, и если да, то какая?)

Я спрашиваю о версиях Delphi после Unicode.

Ответы [ 2 ]

6 голосов
/ 30 октября 2019

В ожидании вашего ответа на комментарии: рассмотрим, фи, GetTempPathW() функция. задокументировано следующим образом

DWORD GetTempPathW(
  DWORD  nBufferLength,
  LPWSTR lpBuffer
);

nBufferLength

Размер буфера строк, идентифицируемого lpBuffer, в TCHAR.

lpBuffer

Указатель на строковый буфер, который получает строку с нулевым символом в конце, указывающую путь к временному файлу.

Возвращаемое значение

Если функция завершается успешно, возвращаемое значение представляет собой длину в TCHAR строки, скопированной в lpBuffer, не включая завершающий нулевой символ. Если возвращаемое значение больше, чем nBufferLength, возвращаемое значение представляет собой длину в TCHAR буфера, необходимого для хранения пути.

Так что да, если вы хотите выделить буфер с правильнымдлина вам нужно сначала назвать его с nBufferLength = 0, или достаточно мало. Затем используйте возвращаемое значение, чтобы выделить буфер и снова вызвать функцию.

4 голосов
/ 30 октября 2019

Трудно ответить на этот вопрос однозначно, поскольку вы не упомянули точную функцию API, которую хотите вызвать. Но в целом в Win32 API используются две модели управления памятью:

  • , вызывающая сторона выделяет буфер достаточного размера, а затем API заполняет буфер данными.

  • API распределяет и заполняет буфер данными, а затем возвращает их вызывающей стороне, которая затем должна их освободить.

Ответ Тома даетпример первого случая, поэтому я приведу пример второго случая.

В Delphi out ParamName: LPWSTR означает, что параметр является ссылкой только для вывода на тип указателя. Это будет LPWSTR* (wchar_t**) в C и LPWSTR& (wchar_t*&) в C ++. И поскольку это ссылка на тип указателя, функция, вероятно, выделяет необходимую память и возвращает ее вызывающей стороне через ссылку, а затем ожидается, что вызывающая сторона впоследствии освободит ее.

Поскольку вы упомянули API файловой системыдавайте рассмотрим в качестве примера IShellItem.GetDisplayName() API, который имеет такой параметр:

HRESULT GetDisplayName(
  SIGDN sigdnName,
  LPWSTR *ppszName
);

Параметры

sigdnName
Тип: SIGDN
Одно из значений SIGDN, указывающее, как должно выглядеть имя.

ppszName
Тип: LPWSTR *
Значение, которое при успешном возврате этой функции получает адрес указателя на найденное отображаемое имя.

...

Примечания

Ответственный за освобождениестрока, на которую указывает ppszName, когда она больше не нужна. Вызовите CoTaskMemFree для *ppszName, чтобы освободить память.

Итак, в Delphi этот метод GetDisplayName() может быть объявлен и использован одним из двух способов:

type
  IShellitem = interface
    ['{43826d1e-e718-42ee-bc55-a1e261c37bfe}']
    ...
    function GetDisplayName(sigdnName: SIGDN, out ppszName: LPWSTR): HRESULT; stdcall;
    ...
  end;

...

var
  Item: IShellItem;
  Path: PWideChar;
begin
  // obtain Item as needed, then...
  OleCheck(Item.GetDisplayName(SIGDN_FILESYSPATH, Path));
  try
    // use Path as needed...
  finally
    CoTaskMemFree(Path);
  end;
end;
type
  IShellitem = interface
    ['{43826d1e-e718-42ee-bc55-a1e261c37bfe}']
    ...
    function GetDisplayName(sigdnName: SIGDN): LPWSTR; safecall;
    ...
  end;

...

var
  Item: IShellItem;
  Path: PWideChar;
begin
  // obtain Item as needed, then...
  Path := Item.GetDisplayName(SIGDN_FILESYSPATH);
  try
    // use Path as needed...
  finally
    CoTaskMemFree(Path);
  end;
end;

SHGetKnownFolderPath() - еще один пример API файловой системы с похожим выходным параметром:

HRESULT SHGetKnownFolderPath(
  REFKNOWNFOLDERID rfid,
  DWORD dwFlags,
  HANDLE hToken,
  PWSTR *ppszPath
);

...

ppszPath
Тип: PWSTR *
Когда этот метод возвращает, содержит адрес указателя на строку Unicode с нулевым символом в конце, которая указывает путь к известной папке. Вызывающий процесс отвечает за освобождение этого ресурса, если он больше не нужен, вызывая CoTaskMemFree. Возвращенный путь не включает в себя завершающий обратный слеш. Например, возвращается «C: \ Users», а не «C: \ Users \».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...