Как сделать правильный вызов GetModuleFileName с помощью UnicodeStrings

GetModuleFileName не возвращает имя файла программы службы Windows.

Я уже определил переменные как AnsiString и UnicodeString, но, похоже, что-то связано с обработчиком событий, который я использую для получения этого значения.

AnsiString exefile = "", sIniFile = "", AppFile = "";

DWORD tamanho = MAX_PATH;
LPTSTR lpBuffer = exefile.c_str();

this->LogMessage("Iniciando serviço",EVENTLOG_INFORMATION_TYPE,0,0);

tamanho = GetModuleFileName( NULL, lpBuffer, tamanho );
if( tamanho > 0 ){ // retornou nome !
   exefile = String(lpBuffer);
}
else
{
     this->LogMessage("Impossível determinar pasta do  executável",EVENTLOG_ERROR_TYPE,0,0);
     Started = false;
     return;
 }

 sIniFile = ChangeFileExt( exefile, L".ini");
 if( !FileExists(sIniFile) )
 {
    this->LogMessage("Arquivo de inicialização não   encontrado",EVENTLOG_ERROR_TYPE,0,0);
    Started = false;
    return;
 }
 else
      this->LogMessage(sIniFile,EVENTLOG_INFORMATION_TYPE,0,0);
 /*
 // Debug
 Started = true;
 return;
 */

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

Я использовал тот же код в другом сервисном приложении, и он отлично работает.

Что я делаю неправильно, пожалуйста?

Есть ли какой-нибудь учебник, показывающий примеры работы с UnicodeStrings?

Вы проверили, возвращает ли GetModuleFileName правильную строку? А после ChangeFileExt?

Anders 22.05.2019 15:57

Похоже, вы не выделили памяти, в которую можно прочитать строку. И вы передаете буфер, который, как вы утверждаете, MAX_PATH длинный, но на самом деле он пуст. Если вы хотите использовать буфер фиксированной длины, объявите массив фиксированной длины wchar_t и передайте его.

David Heffernan 22.05.2019 16:39

Если вы сомневаетесь, прочтите документация, особенно часть о возвращаемом значении. Если буфер слишком мал для хранения имени модуля, строка усекается до символов nSize, включая завершающий нулевой символ функция возвращает nSize, и функция устанавливает последнюю ошибку как ERROR_INSUFFICIENT_BUFFER.. Вы не проверяете ошибку, просто проверяя возвращаемое значение > 0.

Ken White 22.05.2019 19:39

@ Андерс 3, Да, я проверил. Этот код отлично работает, когда я запускаю его в других службах Windows. Я попытался зарегистрировать сообщение, объединяющее строку и значение «exefile» сразу после возврата GetModuleFileName, и у меня есть строка с двумя конкатенированными значениями пути приложения, как это происходит, когда что-то не так.

Jayme Jeffman 22.05.2019 19:48

@ Дэвид Хеффернан, как вы можете видеть в месте ссылки на документацию, которую предложил Кен Уайт, параметр размера должен быть достаточным для удержания возврата функции, иначе он будет усечен. Параметр размера не имеет ничего общего с начальным размером буфера.

Jayme Jeffman 22.05.2019 19:55

Боюсь, это просто связано с тем, как я работаю со строками Unicode.

Jayme Jeffman 22.05.2019 19:57

Ты неправ. Вы сообщаете функции, что есть место для хранения символов MAX_PATH. Но буфер вообще не выделяется. В любом случае используйте массив символов фиксированной длины.

David Heffernan 22.05.2019 20:18

Ты не прав. Размер сообщает Windows, какой размер буфера у вас есть предварительно выделенный для получения содержимого, чего вы не сделали в своем коде выше. (По сути, вы предоставили API ложную информацию, поскольку вы сказали, что выделили MAX_PATH для буфера, когда вы этого не сделали.) Вам нужно снова прочитать эту документацию, особенно ту часть, которую я процитировал (и жирный для акцента) в моем предыдущем комментарии. . И успешно скомпилирован означает не что иное, как то, что вы выполнили требования синтаксиса; это не означает, что вы правильно вызвали функцию.

Ken White 23.05.2019 01:38
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
2
8
449
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы неправильно используете GetModuleFileName().

Вы не передаете буфер выделенный в GetModuleFileName(), поэтому ему негде хранить свои выходные данные. Строковый метод c_str() никогда не возвращает указатель NULL, поэтому, если строка пуста, c_str() возвращает указатель на нулевой символ, хранящийся в статической памяти. Вы говорите GetModuleFileName(), что вы выделили память для буфера, который вы ему предоставили, но на самом деле это не так, поэтому, когда GetModuleFileName() пытается записать в ваш нераспределенный буфер, он либо уничтожает память, либо просто просто падает.

Вместо этого используйте что-то вроде этого:

this->LogMessage(_D("Iniciando serviço"), EVENTLOG_INFORMATION_TYPE, 0, 0);

WCHAR szBuffer[MAX_PATH];
DWORD tamanho = GetModuleFileNameW(NULL, szBuffer, MAX_PATH);

if( tamanho == 0 ){ // retornou nome !
{
    this->LogMessage(_D("Impossível determinar pasta do  executável"), EVENTLOG_ERROR_TYPE, 0, 0);
    Started = false;
    return;
}

String exefile(szBuffer, tamanho);

String sIniFile = ChangeFileExt(exefile, _D(".ini"));
if (!FileExists(sIniFile))
{
    this->LogMessage(_D("Arquivo de inicialização não encontrado"), EVENTLOG_ERROR_TYPE, 0, 0);
    Started = false;
    return;
}

this->LogMessage(sIniFile, EVENTLOG_INFORMATION_TYPE, 0, 0);

/*
// Debug
Started = true;
return;
*/

Или вы можете сделать это:

this->LogMessage(_D("Iniciando serviço"), EVENTLOG_INFORMATION_TYPE, 0, 0);

UnicodeString exefile;
exefile.SetLength(MAX_PATH);

DWORD tamanho = GetModuleFileNameW(NULL, &exefile[1], MAX_PATH);

if( tamanho == 0 ){ // retornou nome !
{
    this->LogMessage(_D("Impossível determinar pasta do  executável"), EVENTLOG_ERROR_TYPE, 0, 0);
    Started = false;
    return;
}

exefile.SetLength(tamanho);

String sIniFile = ChangeFileExt(exefile, _D(".ini"));
if (!FileExists(sIniFile))
{
    this->LogMessage(_D("Arquivo de inicialização não encontrado"), EVENTLOG_ERROR_TYPE, 0, 0);
    Started = false;
    return;
}

this->LogMessage(sIniFile, EVENTLOG_INFORMATION_TYPE, 0, 0);

/*
// Debug
Started = true;
return;
*/

При этом вам вообще не нужно вызывать GetModuleFileName() напрямую. Вместо этого вы можете использовать функцию RTL ParamStr()1. Когда его параметр Index равен 0, он возвращает путь и имя файла вызывающего процесса (т. е. он вызывает GetModuleFileName() внутренне для вас), например:

this->LogMessage(_D("Iniciando serviço"), EVENTLOG_INFORMATION_TYPE, 0, 0);

String exefile = ParamStr(0);

String sIniFile = ChangeFileExt(exefile, _D(".ini"));
if (!FileExists(sIniFile))
{
    this->LogMessage(_D("Arquivo de inicialização não encontrado"), EVENTLOG_ERROR_TYPE, 0, 0);
    Started = false;
    return;
}

this->LogMessage(sIniFile, EVENTLOG_INFORMATION_TYPE, 0, 0);

/*
// Debug
Started = true;
return;
*/

1: In a VCL Forms Application, the Application->ExeName property simply returns ParamStr(0).

Большое спасибо @Remy Lebeau за ответ на мой вопрос. Кстати, что делает _D(String)?

Jayme Jeffman 23.05.2019 22:53

В C++Builder 2009 System::Char изменилось с char на wchar_t, а System::String изменилось с AnsiString на UnicodeString. UnicodeString — это UTF-16 на всех платформах, но wchar_t используется только в Windows, на других платформах используется char16_t. _D() — это RTL-эквивалент Microsoft TEXT() в Win32 API. _D() сопоставляет входной символьный/строковый литерал с System::Char. Лучше использовать _D("literal") вместо L"literal", если вам нужно кодировать для платформ, отличных от Windows, или если вы хотите быть совместимым с предыдущими версиями, если System::Char когда-либо снова изменится тип в будущем.

Remy Lebeau 23.05.2019 23:05

Другие вопросы по теме