arthur2 писал(а):SetCurrentDirectory, ChDrive и ChDir не помогли - они срабатывают, но у диалоговых окон бейсика, похоже, собственное представление о том, какая директория сейчас текущая.
The trick писал(а):Ну как "грязное" решение можно занулить DWORD по адресу 0059F29C, по которому хранится адрес строки с папкой проекта. После этого будет открываться текущая директория.
Хакер писал(а):Естественно, что путь этот тоже можно задать явно, и Common Dialog обращается к GetCurrentDirectory только если явно не было указано ничего.
lpstrInitialDir
Type: LPCTSTR
The initial directory. The algorithm for selecting the initial directory varies on different platforms.
Windows 7:Windows 2000/XP/Vista:
- If lpstrInitialDir has the same value as was passed the first time the application used an Open or Save As dialog box, the path most recently selected by the user is used as the initial directory.
- Otherwise, if lpstrFile contains a path, that path is the initial directory.
- Otherwise, if lpstrInitialDir is not NULL, it specifies the initial directory.
- If lpstrInitialDir is NULL and the current directory contains any files of the specified filter types, the initial directory is the current directory.
- Otherwise, the initial directory is the personal files directory of the current user.
- Otherwise, the initial directory is the Desktop folder.
- If lpstrFile contains a path, that path is the initial directory.
- Otherwise, lpstrInitialDir specifies the initial directory.
- Otherwise, if the application has used an Open or Save As dialog box in the past, the path most recently used is selected as the initial directory. However, if an application is not run for a long time, its saved selected path is discarded.
- If lpstrInitialDir is NULL and the current directory contains any files of the specified filter types, the initial directory is the current directory.
- Otherwise, the initial directory is the personal files directory of the current user.
- Otherwise, the initial directory is the Desktop folder.
bon818 писал(а):Не совсем понял, но я указываю в свойствах ярлыка к VB6.EXE "Рабочая папка: Рабочий стол"
А оконными сообщениями можно управлять этими диалоговыми окнами? Может как то через оконное сообщение можно путь установить?Хакер писал(а):Более стабильное и предсказуемое решение: перехватывать comdlg32-шную функцию GetOpenFileNameA и подменять значение поля структуры OPENFILENAMEA на нужный путь.
ger_kar писал(а):А оконными сообщениями можно управлять этими диалоговыми окнами? Может как то через оконное сообщение можно путь установить?
bon818 писал(а):Не совсем понял, но я указываю в свойствах ярлыка к VB6.EXE "Рабочая папка: Рабочий стол"
Да совет действительно классный. И самое интересное, что решение элементарное и лежало на поверхности, но тем не менее применить его голова не додумалась Я кстати раньше делал немного по другому. В папку VB98 добавлял ярлык, кликая который перемещался в нужную папку.Хакер писал(а):Отличный совет! Поменял себе на g:\dev и теперь буду делать меньше телодвижений.
Можно это делать их отдельного потока, или даже процесса. Т.е. можно предварительно запускать другое приложение (или скрипт), который будет ждать окно и путем оконного сообщения устанавливать нужный путь. Перехватывать функцию из VB6, то еще геммор. Для этого нужно будет отдельную Dll лепить. А со скриптом все гораздо проще. Если конечно оконными сообщениями можно путь подправить.Хакер писал(а):Зачем?
Кто будет слать оконное сообщение, если с момента, как диалог появился (и создалось окно) и до момента, когда окно перестанет существовать, управление уходит из нашего кода и выполняется оконный цикл внутри comdlg32.dll?
ger_kar писал(а):Можно это делать их отдельного потока, или даже процесса. Т.е. можно предварительно запускать другое приложение (или скрипт), который будет ждать окно и путем оконного сообщения устанавливать нужный путь.
ger_kar писал(а):Перехватывать функцию из VB6, то еще геммор. Для этого нужно будет отдельную Dll лепить.
Declare Function GetOpenFileNameA Lib "comdlg32.dll" (ByRef lpofn as OPENFILENAME) As Long
PutMem4 ptr_to_func_ptr, AddressOf GetOpenFileNameA_handler
Function GetOpenFileNameA_handler(ByRef lpofn as OPENFILENAME) As Long
lpofn.lpstrInitialDir = подмена
GetOpenFileNameA_handler = GetOpenFileNameA(lpofn)
End Function
Хакер писал(а):Занулять по жестко заданному адресу — выстрел вслепую.
C7 05 XX XX XX XX 12 C3 02 00 50 56 6A 01 56 56
56 56 56 56 68 XX XX XX XX 6A 64
Хакер писал(а):Более стабильное и предсказуемое решение: перехватывать comdlg32-шную функцию GetOpenFileNameA и подменять значение поля структуры OPENFILENAMEA на нужный путь.
The trick писал(а):Можно просто проанализировать билд по сигнатурам, и получать этот адрес (сорри у меня нет символов как у Хакера, поэтому почти голый асм):
The trick писал(а):Тут тоже не все так просто, к примеру в моем билде адреса получаются динамически и хранятся по фиксированному адресу. А если перехватывать внутри comdlg32 то там придется отслеживать откуда вызов пришел - мало ли кто вызывал GetOpenFileNameA с произвольными аргументами.
Declare Function GetOpenFileNameA Lib "comdlg32.dll" (ByRef lpofn as OPENFILENAME) As Long
Declare Function GetProcAddress Lib "kernel32.dll" (ByVal hMod as long, ByVal ProcOrOrd as Long) As Long
PutMem4 iat_cell_for_GPA, AddressOf GetProcAddress_handler
Function GetOpenFileNameA_handler(ByRef lpofn as OPENFILENAME) As Long
lpofn.lpstrInitialDir = подмена
GetOpenFileNameA_handler = GetOpenFileNameA(lpofn)
End Function
Function GetProcAddress_Handler(ByVal hMod as long, ByVal ProcOrOrd as Long) As Long
If hMod = GetModuleHandle("comdlg32.dll") And cbool(ProcOrOrd and &hFFFF0000) Then
If lstrcmp(ProcOrOrd, "GetOpenFileNameA") = 0 then
GetProcAddress_Handler = Val(AddressOf GetOpenFileNameA_handler)
Exit function
end if
End If
GetProcAddress_Handler = GetProcAddress(hMod, ProcOrOrd)
End Function
typedef struct
{
LPCSTR ProcNameOrOrd;
FARPROC* pFuncPointer;
} DYNALINKFUNC;
The trick писал(а):то там придется отслеживать откуда вызов пришел - мало ли кто вызывал GetOpenFileNameA с произвольными аргументами.
DWORD __stdcall EstablishDynalinks(
LPCSTR lpszDllName,
HMODULE * phmodLibrary,
DWORD dwErrOnFail,
BOOL fRememberErrOfFail,
unsigned int cDynafuncs,
DYNALINKFUNC * const Dynalinks)
{
DWORD err;
HMODULE hmod;
if(*phmodLibrary != NULL) return ERROR_SUCCESS;
hmod = LoadLibrary(lpszDllName);
err = GetLastError();
if(!hmod) goto error_handler;
for(UINT i = 0; i < cDynafuncs; i++)
{
FARPROC FuncAddr;
FuncAddr = GetProcAddress(hmod, Dynalinks[i].ProcNameOrOrd);
*(Dynalinks[i].pFuncPointer) = FuncAddr;
if(FuncAddr == NULL) goto error_handler;
}
*phmodLibrary = hmod;
return ERROR_SUCCESS;
error_handler:
if(fRememberErrOfFail) ErrorRemember(dwErrOnFail, lpszDllName);
if(hmod) FreeLibrary(hmod);
return dwErrOnFail;
}
static CHAR szComDlgDLL[] = "COMDLG32.DLL";
static CHAR szChooseFont[] = "ChooseFontA";
static CHAR szCommDlgExtendedError[] = "ComDlgExtendedError";
static CHAR szGetSaveFileName[] = "GetSaveFileNameA";
static CHAR szPrintDlg[] = "PringDlgA";
static CHAR szChooseColor[] = "ChooseColorA"
static CHAR szGetOpenFileName[] = "GetOpenFileNameA";
static DYNALINKFUNC rgComDlgDynalinks[] = {
{
{szChooseFont, &ChooseFontAPI },
{szCommDlgExtendedError, &CommDlgExtendedErrorAPI },
{szGetSaveFileName, &GetSaveFileNameAPI },
{szPrintDlg, &PrintDlgAPI },
{szChooseColor, &ChooseColorAPI },
{szGetOpenFileName, &GetOpenFileNameAPI },
};
DWORD LoadComDlgDLL()
{
return EstablishDynalinks(szComDlgDLL,
&Rby_hinstComDlg,
0x12A,
TRUE,
ARRAY_SIZE(rgComDlgDynalinks),
rgComDlgDynalinks);
}
Хакер писал(а):Будешь собирать все сигнатуры, включая всякие китайские билды VB6.EXE
The trick писал(а):то в целом элементарно: GetMem4(VarPtr(arg1) - 4) сравниваем с границами модуля VBA6.EXE в АП процесса
А если в отлаживаемом проекте будет GetOpenFileName, она, часом, тоже не перехватится?Хакер писал(а):Что в целом элементарно: GetMem4(VarPtr(arg1) - 4) сравниваем с границами модуля VBA6.EXE в АП процесса
Чтобы писать по этому адресу, нужно устанавливать какие-то разрешения?The trick писал(а):Ну как "грязное" решение можно занулить DWORD по адресу 0059F29C
The trick писал(а): но т.к. новых билдов VB6.EXE больше не будет, то это допустимый способ.
The trick писал(а):Ну так из VB6.exe тоже из нескольких мест вызывается GetOpenFileNameA (к примеру открытие изображений), придется еще анализировать дополнительно.
arthur2 писал(а):А если в отлаживаемом проекте будет GetOpenFileName, она, часом, тоже не перехватится?
arthur2 писал(а):Чтобы писать по этому адресу, нужно устанавливать какие-то разрешения?The trick писал(а):Ну как "грязное" решение можно занулить DWORD по адресу 0059F29C
Хакер писал(а):Могут быть неофициальные пересборки
Но вопрос в другом: чем работа с сигнатурой и ковыряние в приватных полях объекта класса CFileRep (что против принципа инкапсуляции) лучше, чем работа в с документированным интерфейсом (в лице API-функции GetOpenFileName и структурой OPENFILENAME)?
Но автор не сказал, как он хочет поступать со всеми возможными диалогами, точнее с каждым конкретным в отдельности. В любом случае, дифференциировать разные по назначению диалоги гораздо проще по фильтру (там где *.vbp — там делаем подмену, остальные не трогаем), а не по адресу возврата.
Код выглядит понятным... Вопрос, как найти эту ячейку? iat_cell_for_GPAХакер писал(а): у Add-in'а есть возможность перехватить GetProcAddress (правкой ячейки IAT — то есть в одну строчку) и подсунуть вместо GetOpenFileNameA свой адрес:
В общем, занулить не получилось. В тестовом проекте, прямо из него, зануление иногда срабатывало, а иногда почему-то нет. Из адина - вообще не сработало ни разу. Пришлось вместо зануления писать реальный адрес данных публичной строки, в которую предварительно вписан нужный путь, конвертированный ФромУникод Это сработало. Интересно, почему не сработало зануление?The trick писал(а):Ну как "грязное" решение можно занулить DWORD по адресу 0059F29C
arthur2 писал(а):В общем, занулить не получилось. В тестовом проекте, прямо из него, зануление иногда срабатывало, а иногда почему-то нет. Из адина - вообще не сработало ни разу. Пришлось вместо зануления писать реальный адрес данных публичной строки, в которую предварительно вписан нужный путь, конвертированный ФромУникод Это сработало. Интересно, почему не сработало зануление?
В том смысле, что не аддин. В модуле пишу публичную функцию, в ней меняю текущий диск, директорию и зануляю адрес. Затем просто прямо из Immediate запускаю эту функцию. Затем жму "открыть проект" и смотрю, сменилась ли папка. Иногда меняется (видимо, когда делаю это первый раз), иногда нет. Из аддина вообще не срабатывало ни разу. Подмена на реальный адрес вместо зануления работает всегда, так что, собственно, вопрос решен. Просто любопытно, почему не срабатывает зануление.The trick писал(а):А что за тестовый проект?
arthur2 писал(а):в ней меняю текущий диск, директорию и зануляю адрес.
arthur2 писал(а):Код выглядит понятным... Вопрос, как найти эту ячейку? iat_cell_for_GPA
'
' В скомпилированном файле выполнить перехват всех вызовов функции
' DllFunctionCall из текущего модуля легко: достаточно подменить
' значение ячейки таблицы импорта текущего модуля.
'
'
' К этоу моменту адрес ячейки IAT известен: перезаписываем, и этим
' выполняем модуле-локальный перехват.
'
VirtualProtect lpIATEntry, 4, PAGE_EXECUTE_READWRITE, i
PutMem4 lpIATEntry, AddressOf LocalResolver
VirtualProtect lpIATEntry, 4, i, i
arthur2 писал(а):стандартными ChDrive и ChDir
arthur2 писал(а):SetCurrentDirectory тоже пробовал с тем же эффектом - иногда срабатывает, а иногда (чаще) нет.
ElseIf 0 = lstrcmpiA(vaModuleName, _
Nm_RtlModuleName) = 0 Then
arthur2 писал(а):. Но в аддине-то перебирается импорт самого аддина, а там GetProcAddress нету
Если App.hInstance возвращается для самого аддина, как теперь считать смещения для бейсика?
Кстати, у тебя в коде опечатка:
Сейчас этот форум просматривают: SemrushBot и гости: 51