Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Visual C++/MFC/WTL > Доступ к директории исполняемого файла


Автор: xTr1m 18.1.2013, 12:53
Доброго времени суток. В корпоративном коде (назовем это так) директория, в которой находится запущенная программа определяется так

Код

CString GetExePath()
{
        CString sExePath;
        HMODULE hModule;
        char szModuleName[2048];
        
        hModule = GetModuleHandle(NULL);
        if(hModule!=NULL)
        {
                if(GetModuleFileName(hModule, szModuleName, 2048)>0)
                {
                        CString buf;
                        buf= szModuleName;
                        sExePath = buf.Left(buf.ReverseFind('\\')+1);
                }
        }

        return sExePath;
}


но  разбираясь  с  Win32, прочитал, что путь также всегда передается в
командной  строке первым параметром,  также нашел, что в студии есть переменные, которые
позволяют получить путь вот так:

Код

CString filePath;
if(__argc > 0)
{
          filePath = __targv[0];
          filePath = filePath.Left(filePath.ReverseFind('\\')+1);
}


Выглядит   проще,  работает  быстрее.  Есть  только  вопрос,  а  можно
полагаться на переменные   __argc и   __targv и всегда ли путь будет находиться первым параметром в командной строке? 

А может есть другие варианты? Заранее спасибо.

Автор: Albor 18.1.2013, 15:22
Можно так ещё
Код

CString target(GetCommandLine());
target=target.Mid(1,target.ReverseFind(_T('\\')));

Автор: volatile 18.1.2013, 18:50
Цитата(xTr1m @  18.1.2013,  12:53 Найти цитируемый пост)
всегда ли путь будет находиться первым параметром в командной строке? 

Нет, не всегда.
Если программа запущена например, из другой програаммы с помощью CreateProcess(), то там может быть вообще нулевая строка.
Есть и еще случаи. 
Кроме того там может находицца относительный путь. 

Цитата(Albor @  18.1.2013,  15:22 Найти цитируемый пост)
Можно так ещё
CString target(GetCommandLine());
target=target.Mid(1,target.ReverseFind(_T('\\')));

а так вобще делать нельзя. Надеятесь, что в дополнительных параметров накогда не встретицца '\\' ?
Кроме того, все что выше, относицца и к этому варианту.

Цитата(xTr1m @  18.1.2013,  12:53 Найти цитируемый пост)
 студии есть переменные, которые 

В студии есть уже готовая переменная с путем программы _pgmptr, и функция _get_pgmptr();
Ими особо не пользовался, поэтому сказать за них ничо не могу.

Цитата(xTr1m @  18.1.2013,  12:53 Найти цитируемый пост)
В корпоративном коде (назовем это так) директория, в которой находится запущенная программа определяется так

Есть золотое правило - "если код работает, не трогай его".  smile 
Еще есть - "premature optimization.." и все такое 
Единственное, что мне здесь не очень нравицца это захардкоденное 2048

Автор: Albor 19.1.2013, 22:07
Цитата(volatile @  18.1.2013,  17:50 Найти цитируемый пост)
а так вобще делать нельзя. Надеятесь, что в дополнительных параметров накогда не встретицца '\\' ?
Кроме того, все что выше, относицца и к этому варианту.

Слэш должен быть обязательно - получим путь без имени файла. В чём может быть проблема (если не считать случай запуска, когда командной строки может не быть)? 

Автор: volatile 19.1.2013, 23:18
Цитата(Albor @  19.1.2013,  22:07 Найти цитируемый пост)
 В чём может быть проблема 

Albor, GetCommandLine возвращает всю коммандную строку целиком.
например, при
Код

C:\path\mycopy.exe C:\tralala\file1.txt C:\blablabla\file2.txt

Ваш способ вернет:
Код

"C:\path\mycopy.exe C:\tralala\file1.txt C:\blablabla"

Это, не совсем 
Цитата(xTr1m @  18.1.2013,  12:53 Найти цитируемый пост)
директория, в которой находится запущенная программа 


Автор: Albor 20.1.2013, 10:57
Это понял, но программист наверняка знает как запускается его программа и может парсить строку как ему угодно. Кстати, озвученная вами  _pgmptr в MFC-проекте имеет значение NULL, соответственно вызов _get_pgmptr() заканчивается аварией. 

Автор: Albor 20.1.2013, 11:54
Цитата(volatile @  18.1.2013,  17:50 Найти цитируемый пост)
Единственное, что мне здесь не очень нравицца это захардкоденное 2048

MAX_PATH подошло бы больше, да и вызов GetModuleHandle(NULL) явно лишний, т.к. в GetModuleFileName() первым параметром можно передать NULL и получить тот же эффект.

Автор: volatile 20.1.2013, 19:37
Цитата(Albor @  20.1.2013,  10:57 Найти цитируемый пост)
Кстати, озвученная вами  _pgmptr в MFC-проекте имеет значение NULL

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

Автор: Albor 20.1.2013, 20:11
Цитата(volatile @  20.1.2013,  18:37 Найти цитируемый пост)
Albor, в MFC все прекрасно работает.
Озвучил я для того чтобы обратились к мсдн. там и плюсы и минусы.
(поищите на форуме про юникодный/неюникодный проекты, это грабли для новичков обсуждались тыщу раз, надоело уже.)

Да, работает, извиняюсь, мне нужно было обращаться к _wpgmptr.

Автор: xTr1m 21.1.2013, 14:18
Спасибо большое. Получается, что буду писать так, как есть сейчас. Этот код не подходит, так как первым параметром не всегда идет
Код

CString filePath;
if(__argc > 0)
{
          filePath = __targv[0];
          filePath = filePath.Left(filePath.ReverseFind('\\')+1);
}


а этот, так как если будет два параметра, то нужен чуть более сложный разбор
Код

char *path = 0;
 _get_pgmptr(&path);

CString filePath(path);
filePath = filePath.Left(filePath.ReverseFind('\\')+1);

Автор: volatile 21.1.2013, 17:48
Цитата(xTr1m @  21.1.2013,  14:18 Найти цитируемый пост)
нужен чуть более сложный разбор

xTr1m, да не нужен будет с _get_pgmptr()  разбор. (это я про совсем другое говорил.)
Просто у меня есть сильноее подозрение, что он работает точно также, извлекая из arg[0];
но вопрос сей подробно не изучал, так как
Цитата(volatile @  18.1.2013,  18:50 Найти цитируемый пост)
особо не пользовался, поэтому сказать за них ничо не могу.


Используйте ваш первый вариант. Имхо нормальный!
Единственно, теоретически, путь может быть длиной до 32К. 
И в этом случае и 2048 и тем более MAX_PATH обломаюцца.

Добавлено через 11 минут и 36 секунд
Кстати, на сверх длинных путях, обламываюцца многие программы, если вас это утешит.  smile 
ну или делайте с расчетом на 32К. 

Автор: Albor 22.1.2013, 08:10
Можно (если очень осторожно) использовать http://msdn.microsoft.com/ru-ru/library/windows/desktop/aa364934.aspx (внимательно читаем ремарку).
А это по http://msdn.microsoft.com/ru-ru/library/vstudio/930f87yf(v=vs.100).aspx, можно найти ещё кучу статей по решению проблем длинного пути. Но, надеюсь, что у xTr1m данной проблемы нет.

Автор: volatile 22.1.2013, 09:47
Цитата(Albor @  22.1.2013,  08:10 Найти цитируемый пост)
использовать GetCurrentDirectory() 

smile
вообще не из той оперы

Автор: Albor 22.1.2013, 12:01
volatile, если, допустим, что моя программа не многопоточная и я ни где не устанавливаю текущую директорию,то почему 
Цитата(volatile @  22.1.2013,  08:47 Найти цитируемый пост)
вообще не из той оперы

http://www.radikal.ru

Автор: Albor 3.2.2013, 09:17
Решил всё же окончательно разобраться с данным вопросом. Итак, что мы имеем при запуске нашей программы? Я решил посмотреть в сторону  CreateProcess(): в неё передаётся (из того что нас интересует)
1. имя приложения и командная строка - данные параметры используются довольно хотрО, то есть мы можем и не передавать полный путь к исполняемому файлу - система сама будет искать файл по определённому алгоритму и, если найдёт, то запустит. Из этого следует то, что уже обсуждалось - через командную строку можно и не получить требуемое.
2. текущая директория - может передаваться, а может и нет,  смотря кто и как запускает процесс, то есть, вызовом GetCurrentDirectory() мы можем получить текущую директорию родительского процесса либо явно указываемую  при создании процесса, либо установленную вызовом SetCurrentDirectory() - опять-таки мы не получаем ни какой гарантии что текущая директория то что нам нужно.
Выходит, что входные параметры не дают 100 процентной информации для решения задачи, но... создатель процесса что-то ещё и делает - внутри функции путь к исполняемому файлу известен (если файл найден конечно), вот здесь, по моему предположению, и присваивается значение переменной _pgmptr, либо её UNICODE - версии _wpgmptr, которая позиционируется как полный путь и имя исполняемого файла
Теперь заглянем в русскую редакцию 2009г книги Рихтера "WINDIWS via c/c++", и посмотрим его мнение на счёт этой и некоторых других глобальных переменных
Цитата

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

Соответствующей функцией, по указанию этого же источника, является GetModuleFileName(). Выходит, что данная функция гарантированно даст нам правильный результат.
Остался вопрос с MAX_PATH. MSDN прямо указывает, что путь не должен превышать данного значения, но тут же делается оговорка для NTFS. Рихтер тоже однозначно не высказывается по этому вопросу - он говорит примерно то же самое и делает замечание, что размер, определённый в MAX_PATH можно обойти, вот только для чего и кому это нужно, для читателя остаётся загадкой. Для себя я сделал такой вывод, что если не хочешь проблем, то не нужно именовать файлы и каталоги своей программы так, чтобы они превышали MAX_PATH, а пользователю не следует пихать программу туда, от куда её будет тяжело достать (кстати, в некоторых статьях по решению проблемы длинных путей, Microsoft предлагает переименовать файлы и каталоги более короткими именами).

Автор: HANDLE 9.4.2013, 14:39
Все можно сократить до безобразия, и сверхдлинные пути не помеха (32768 - предел), а MAX_PATH можно преодолеть путем перетаскивания файлов из папки в папку в пределах одного диска. 

Код

CString GetExePath()
{
        CString sExePath;
        GetModuleFileName(NULL, sExePath.GetBuffer(32768), 32768);
        sExePath.ReleaseBuffer(sExePath.ReverseFind('\\')+1);
        return sExePath;
}

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)