Как сменить текущую директорию?

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Re: Как сменить текущую директорию?

Сообщение arthur2 » 03.07.2019 (Ср) 23:12

Хакер писал(а):А зачем ты перебираешь импорт самого эддина?
Ну как было в коде через App.hInstance, так и пытался. Про GetModuleHandle может не помнил, а может и не знал, думал, что всё равно от App.hInstance как-то считать придется.

Всё, функция поймалась! Круто! Спасибо!!!

Теперь вопрос: я сейчас подменю указатель на строку с нужным путём. И чем это будет принципиально отличаться от предыдущей ситуации в плане утечки? Старая-то строка не обнулится. А запоминать адрес и возвращать его тоже не получится - диалог может строку переписать. Или если все текстовые поля в структуре объявить строками, то бейсик их корректно будет переписывать?
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Как сменить текущую директорию?

Сообщение Хакер » 04.07.2019 (Чт) 0:21

arthur2 писал(а):Теперь вопрос: я сейчас подменю указатель на строку с нужным путём. И чем это будет принципиально отличаться от предыдущей ситуации в плане утечки? Старая-то строка не обнулится. А запоминать адрес и возвращать его тоже не получится - диалог может строку переписать. Или если все текстовые поля в структуре объявить строками, то бейсик их корректно будет переписывать?


Вот ты не хочешь пытаться думать. Окей, допустим, никакого перехвата нет. Вызывающая сторона передала одну строку, диалог переписал строку, как ты сказал. Кто удалит старую строку? Всё? Утечка?'

Или оно и без всякого перехвата и подмены устроено так криво, что всё равно даёт утечку (в случае, если диалог перезаписал строку), и тогда за что ты переживаешь? Или же оно работает попросту не так, как ты себе это представляешь, и тогда твой вопрос и твои опасения не имеют смысла.

Чем это отличается от ситуации с перезаписью сомнительной ячейки в памяти процесса? Отличается всем. Давай разбираться. В предыдущем сценарии есть ячейка памяти хранящая указатель на блок памяти, содержащий строку. О всяком ресурсе (любой сущности, которая выделяется и уничтожается) есть владелец. Владелец может быть постоянным. Или же владелец может быть переменным: если ресурс является переходящим, то можно выделить поставщика и потребителя ресурса.

В первом случае кто является владельцем блока памяти со строчкой? Это класс CFileRep. Он же поставщик, он же потребитель. Он выделяет блок, он им пользуется, он его уничтожает. И ты вклиниваешься в этот жизненный цикл самым неожиданным образом.

А вот когда происходит передача ресурса (любого — строки, блока, массива, объекта) между двумя разными функциями, да ещё живущих в двух совершенно разных модулях, написанных разными людьми, обычно ресурс является переходящим и в момент вызова владельцем ресурса становится другая сторона. Но не обязательно.

Поэтому в подобных случаях всегда устанавливаются правила игры, контракт: если вызывающая сторона породила ресурс и передаёт его в пользование другой стороне, то обязанность по уничтожению ресурса либо будет за вызываемой стороной, либо же вызывающая сторона должна дождаться возврата и всё-таки сама уничтожить свой ресурс (или не уничтожить, а использовать как-то дальше).

Правила игры (кто создаёт, а кто уничтожает, кто выделяет, а кто освобождает) обычно указанны в документации либо явно, либо просто понятны исходя из текста.

И если почитать документацию на структуру OPENFILENAME, то всё становится ясным.
Вызывающая сторона передаёт функции доступ к буферу в виде указателя и размера буфера. Вызываемая сторона получает возможность читать и писать в буфер (не выходя за его пределы), но владельцем буфера вызываемая сторона не становится. Хозяином буфера остаётся вызывающая сторона. Вызываемая сторона не удаляет, не увеличивает, не переразмещает буфер. Она только меняет содержимое буфера, но никак не указатель на буфер в структуре.

Все указатели в структуре после вызова остаются неизменными. Меняется только содержимое буферов.
Буферы выделяет вызывающая сторона, и обязанность по уничтожение буферов остаётся за нею же. Поэтому никакой утечки от того, что диалог что-то изменил, быть не может в принципе. А сам подход позволяет буферы выделять не из кучи, а размещать прямо на стеке, используя локальную переменную (массив достаточной длины).

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

Соответственно, для замены текста, передаваемого функции, можно пользоваться двумя подходами:
  • Не трогать адрес в поле lpstrXxxxxxx, а пойти и записать новые символы по этому адресу (не выходя за границы буфера). То есть попросту заменить содержимое в буфере, любезно предоставленном вызывающей стороной для функции OpenFileName (и перепавшем нам).
  • Выделить свой буфер (внутри функции-перехватчика) и в структуре OPENFILE поставить указатель на него (и новый размер), а старый указатель и размер запомнить. Перед возвратом из функции-перехватчика вернуть на место старый указатель и размер.

Логика подсказывает, что первый подход надо применять для всех параметров, которые вызываемая сторона может (и должна) заменить, а второй подход — для всех параметров, которые вызывающая сторона не должна менять.

На практике, если для всех параметров использовать первый подход, или если во втором подходе не запоминать и не возвращать старую пару адрес:размер, то ничего страшного не произойдёт. Но такой поход попахивает. Откуда мы можем быть на 100 процентов уверенными, что в вызываемой стороне ничего не сломается от того, что мы вдруг решим поменять содержимое буфера, которое по мнению вызывающей стороны ни при каких обстоятельствах вызываемая сторона менять не должна?

В конце-концов, буфер для параметра, который CommonDialog-функция никогда не меняет (а только читает) может лежать в памяти, доступной только для чтения (выделенной с таким атрибутом доступа или же просто в секции PE-файла, доступной только для чтения). И если мы для такого параметра и такого буфера применим первый подход, у нас приложение рухнет с исключением C0000005.

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

Понять, какой буфер явным образом подразумевает доступ «только для чтения» можно легко, взглянув на описание структуры: часть указателей имеют тип LPSTR, а часть — LPCSTR. Вот там, где C (а это не что иное как CONSTANT) — там мы не имеем права менять что-то в буфере, предоставленном вызывающей стороной, а должны выделить свой.

Например это касается lpstrInitialDir и lpstrTitle — логично, что диалоговая функция ни при каких обстоятельствах не будет пытаться поменять буфер, хранящий заголовок для диалогового окна. И буфером под эту строку может оказаться участок секции кода (для которой действует запрет на запись).
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Re: Как сменить текущую директорию?

Сообщение arthur2 » 04.07.2019 (Чт) 20:24

В общем, да - диалог оказывается не меняет поле InitialDir, так что вполне можно подсунуть адрес другой строки, а потом просто вернуть старый.

Вообще, те поля, которые могут меняться - LPSTR -, задаются адресом и размером, а те поля, которые не меняются - LPCSTR - задаются только адресом.

В целом, казалось так грандиозно, а на практике даже флаги поменять не могу. Хотел добавить OFN_ENABLESIZING - вроде сработало, стало можно растягивать диалоги... Но стартовое окно, будь оно не ладно... В него тоже вмурован диалог открытия файла, так вот на его показе бейсик дохнет. Пришлось убирать флаг.
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Как сменить текущую директорию?

Сообщение Хакер » 05.07.2019 (Пт) 4:13

arthur2 писал(а):В целом, казалось так грандиозно, а на практике даже флаги поменять не могу. Хотел добавить OFN_ENABLESIZING - вроде сработало, стало можно растягивать диалоги... Но стартовое окно, будь оно не ладно... В него тоже вмурован диалог открытия файла, так вот на его показе бейсик дохнет. Пришлось убирать флаг.

Сдаётся мне, что ты что-то просто делаешь неправильно. Стало интересно: каким боком добавление OFN_ENABLESIZING может что-то сломать в VB? Решил посмотреть, что именно ломается. Выставил этот флаг. И оказалось, что всё работает. Диалог растягивается. Конечно, VB-шные прибамбасы (вкладки, картинки) не масштабируются, потому что они не знают, что размер диалога может меняться — выглядит это некрасиво. Но никакого падения нет.


И со стартовым диалогом, и с нестартовым диалогом пробовал. И даже рискнул впихнуть ноль в флаговое поле — все VB-шные прибамбасы отвалились, появился обычный диалог (explorer-style) с боковой панелью.

Возможно у тебя структура неправильно объявлены и ты портишь какие-то другие данные? В любом случае:
  • Цель же была не в OFN_ENABLESIZING, а в замене дефолтного пути. Она достигнута?
  • Всегда можно дифференциировать, какой диалог сейчас хочет показать среда, и для диалога нового проекта не трогать флаги (хотя я и не нашёл, что вообще замена флагов может сломать)
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Как сменить текущую директорию?

Сообщение Хакер » 05.07.2019 (Пт) 4:17

Дополнительно показываю, что именно для стартового диалога ни добавление OFN_ENABLESIZING, ни сброс флагов в ноль (что убирает картинку и вкладки полностью) не ломает среду:
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Re: Как сменить текущую директорию?

Сообщение arthur2 » 05.07.2019 (Пт) 7:22

Хакер писал(а):Цель же была не в OFN_ENABLESIZING, а в замене дефолтного пути. Она достигнута?
Да, эта цель достигнута.

Но сама возможность поймать функцию казалась такой заманчивой:
arthur2 писал(а):Идея перехватить GetOpenFileName мне нравится - открывается перспектива заменить неказистые и тесные диалоги бейсика на нормальные.


Да, это не сам флаг так действует - мне тоже удалось дождаться запуска стартового диалога без крушения. Но что-то - не могу понять, что - всё же ломает бейсик, если изменить флаг именно на стартовом окне.

Зато, оказывается, можно вообще подавить появление стартового диалога, чего я тоже хотел. Нужно только понять, что это именно стартовый диалог.

Хакер писал(а):Всегда можно дифференциировать, какой диалог сейчас хочет показать среда
Пробовал посмотреть поле TemplateName - для всех диалогов оно оказалось одинаковым - FF4 Кстати, посмотрел ковырятелем ресурсов - там под этим номером вообще шаблон какой-то не похожий.
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Как сменить текущую директорию?

Сообщение Хакер » 05.07.2019 (Пт) 8:16

arthur2 писал(а):Но сама возможность поймать функцию казалась такой заманчивой:

Почему казалась? Она и есть такая. Можно менять что угодно в этих диалогах, раз ты поймал функцию. Даже заменить их на совершенно свои. Абсолютно любой каприз — до тех пор, пока ты не нарушишь контракт (то, как по мнению VB6 должна вести себя функция).

arthur2 писал(а):Но что-то - не могу понять, что - всё же ломает бейсик, если изменить флаг именно на стартовом окне.

Какая-нибудь ошибка в коде. Выкладывай проект.

arthur2 писал(а):Пробовал посмотреть поле TemplateName - для всех диалогов оно оказалось одинаковым - FF4 Кстати, посмотрел ковырятелем ресурсов - там под этим номером вообще шаблон какой-то не похожий.

Прежде чем смотреть поле TemplateName, нужно посмотреть какое смещение имеет TemplateName, и какое значение имеет первое поле структуры, и как эти две величины коррелируют.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Re: Как сменить текущую директорию?

Сообщение arthur2 » 05.07.2019 (Пт) 23:17

В общем, косяк был совсем в другом - просто заметил после того, как флаг выставил, так уж совпало. Начал собирать проект, чтобы выложить - и сразу вычленил ошибку.

Теперь всё работает!

А как узнать стартовый диалог? Собственно, можно по полю title, но только это не универсально.
Артур
 
   

Пред.

Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 45

    TopList  
cron