NeferSky
Постоялец
Offline
Бессмертный
|
|
« : 11-11-2016 09:36 » |
|
Добрый день всем. Давно уже пишу на Делфях, но ничто не вечно. Это время пришло. Начал осваивать С++. Для примеров перевожу код своих проектов с Паскаля на С. Внезапно - запутался с указателями при использовании WinAPI. А именно - есть у меня функция. В Делфях работает, в BCB - нет. Хватаю AV на функции Move(). Чую, там дело в двойном указателе void **InfoPtr, но что я не так с ним делаю - не пойму. Разыменовываю, вроде, правильно... тип у него - тоже, как будто подходящий, и Move() ожидает там указатель... В чем может быть дело? Pascal code:function GetFullFileVersion(const FileName: string): string; var InfoSize, puLen: DWORD; Pt, InfoPtr: Pointer; VerInfo: TVSFixedFileInfo; begin InfoSize := GetFileVersionInfoSize(PChar(FileName), puLen); FillChar(VerInfo, SizeOf(TVSFixedFileInfo), 0);
if InfoSize > 0 then begin GetMem(Pt, InfoSize); GetFileVersionInfo(PChar(FileName), 0, InfoSize, Pt);
VerQueryValue(Pt, '\', InfoPtr, puLen); Move(InfoPtr^, VerInfo, SizeOf(TVSFixedFileInfo)); FreeMem(Pt);
Result := Format('%u.%u.%u.%u', [HiWord(VerInfo.dwProductVersionMS), LoWord(VerInfo.dwProductVersionMS), HiWord(VerInfo.dwProductVersionLS), LoWord(VerInfo.dwProductVersionLS)]); end else Result := ''; end;
C++ code:String __fastcall TNsUtils::GetFullFileVersion(const String FileName) { unsigned long *puLenLong, InfoSize; unsigned int *puLenInt; void *Pt; void **InfoPtr; TVSFixedFileInfo *VerInfo;
InfoSize = GetFileVersionInfoSize(FileName.c_str(), puLenLong);
if (InfoSize > 0) { Pt = malloc(InfoSize); GetFileVersionInfo(FileName.c_str(), 0, InfoSize, Pt);
memset(VerInfo, sizeof(TVSFixedFileInfo), 0);
VerQueryValue(Pt, "\\", InfoPtr, puLenInt); Move(*InfoPtr, VerInfo, sizeof(TVSFixedFileInfo)); // Здесь падает с AV free(Pt);
TVarRec args[4] = {HIWORD(VerInfo->dwProductVersionMS), LOWORD(VerInfo->dwProductVersionMS), HIWORD(VerInfo->dwProductVersionLS), LOWORD(VerInfo->dwProductVersionLS)}; String Result = Format("%u.%u.%u.%u", args, 3); return Result; } else { return ""; } }
|
|
|
Записан
|
Не тронь налаженный механизм, и он тебя не подведет. Делать надо хорошо, а плохо - само получится.
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #1 : 11-11-2016 10:24 » |
|
Давно уже пишу на Делфях, но ничто не вечно. Это время пришло. Начал осваивать С++. Для примеров перевожу код своих проектов с Паскаля на С.
Досконально не разбирался, но по-моему, скорее всего, должно быть что-то типа такого: String __fastcall TNsUtils::GetFullFileVersion(const String FileName) { unsigned long *puLenLong, InfoSize; unsigned int *puLenInt; void *Pt; void *InfoPtr; TVSFixedFileInfo *VerInfo;
InfoSize = GetFileVersionInfoSize(FileName.c_str(), puLenLong);
if (InfoSize > 0) { Pt = malloc(InfoSize); GetFileVersionInfo(FileName.c_str(), 0, InfoSize, Pt);
memset(VerInfo, sizeof(TVSFixedFileInfo), 0);
VerQueryValue(Pt, "\\", &InfoPtr, puLenInt); Move(InfoPtr, VerInfo, sizeof(TVSFixedFileInfo)); // Здесь не должно уже падать с AV free(Pt);
TVarRec args[4] = {HIWORD(VerInfo->dwProductVersionMS), LOWORD(VerInfo->dwProductVersionMS), HIWORD(VerInfo->dwProductVersionLS), LOWORD(VerInfo->dwProductVersionLS)}; String Result = Format("%u.%u.%u.%u", args, 3); return Result; } else { return ""; } }
|
|
|
Записан
|
|
|
|
NeferSky
Постоялец
Offline
Бессмертный
|
|
« Ответ #2 : 11-11-2016 10:41 » |
|
darkelf, все равно AV. Я, пожалуй, дома на другой версии билдера еще попробую перекомпилить, чтобы исключить какие-нибудь настройки самой IDE.
|
|
|
Записан
|
Не тронь налаженный механизм, и он тебя не подведет. Делать надо хорошо, а плохо - само получится.
|
|
|
Aether
|
|
« Ответ #3 : 11-11-2016 10:45 » |
|
https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx
BOOL WINAPI VerQueryValue( _In_ LPCVOID pBlock, _In_ LPCTSTR lpSubBlock, _Out_ LPVOID *lplpBuffer, _Out_ PUINT puLen );
... void *InfoPtr; ... VerQueryValue(Pt, "\\", &InfoPtr, puLenInt); ... Move(InfoPtr, VerInfo, sizeof(TVSFixedFileInfo)); ...
void ** InfoPtr, вероятно было бы корректнее. Прототип Move откуда брать непонятно. Лучше протестировать на каком этапе что и где получается. И, код выявления ошибок после malloc и прочих функций добавить.
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #4 : 11-11-2016 10:52 » |
|
NeferSky, никакая версия не спасет от незнания языка. Включи больше варнингов. Выполни проход функции пошагово: 1) найди, где падает 2) посмотри, что ты передаешь И да, Win32 API писали уроды. С этим надо свыкнуться. LPVOID *lplpBuffer LPVOID - это тип указателя из дебрей винды, эквивалентный void*. LPVOID* эквивалентен void**.
|
|
« Последнее редактирование: 11-11-2016 10:55 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
zubr
Гость
|
|
« Ответ #5 : 11-11-2016 10:54 » |
|
TVSFixedFileInfo *VerInfo; Где выделение памяти под структуру, раз она объявлена как указатель. Или тогда: TVSFixedFileInfo VerInfo; String __fastcall TNsUtils::GetFullFileVersion(const String FileName) { unsigned long puLenLong, InfoSize; unsigned int puLenInt; void *Pt; void *InfoPtr; TVSFixedFileInfo VerInfo;
InfoSize = GetFileVersionInfoSize(FileName.c_str(), &puLenLong);
if (InfoSize > 0) { Pt = malloc(InfoSize); GetFileVersionInfo(FileName.c_str(), 0, InfoSize, Pt);
memset(&VerInfo, sizeof(TVSFixedFileInfo), 0);
VerQueryValue(Pt, "\\", &InfoPtr, &puLenInt); Move(InfoPtr, &VerInfo, sizeof(TVSFixedFileInfo));
|
|
« Последнее редактирование: 11-11-2016 10:56 от zubr »
|
Записан
|
|
|
|
Aether
|
|
« Ответ #6 : 11-11-2016 11:02 » |
|
https://msdn.microsoft.com/en-us/library/windows/desktop/ms647005(v=vs.85).aspxDWORD WINAPI GetFileVersionInfoSize( _In_ LPCTSTR lptstrFilename, _Out_opt_ LPDWORD lpdwHandle ); ... lpdwHandle [out, optional] Type: LPDWORD A pointer to a variable that the function sets to zero. ...
zubr уже поправил, но всё же. Как понял второй аргумент требует передать адрес переменной, которая будет сброшена в ноль. (Зачем?) Под такую переменную память должна быть выделена, если просто передать указатель, то функция попытается сбросить в ноль не пойми что.
|
|
|
Записан
|
|
|
|
zubr
Гость
|
|
« Ответ #7 : 11-11-2016 11:40 » |
|
Aether, 1. Проблема у топикстартера не в этой функции, а в том, что он в функции Move использует указатель на невыделенный блок памяти. 2. Касаемо GetFileVersionInfoSize, мне тоже не совсем понятно назначение параметра lpdwHandle. Наверно разаработчики что то предусматривали на будущее. Но пока данный параметр, бессмыслен там, имхо. Что касается выделения памяти под данную переменную, так если объявлена переменная типа DWORD - то память в стеке 4 байта под нее уже выделена, а функции указывается указатель на ячейку стека, где эта память выделена.
|
|
|
Записан
|
|
|
|
NeferSky
Постоялец
Offline
Бессмертный
|
|
« Ответ #8 : 11-11-2016 11:41 » |
|
BOOL WINAPI VerQueryValue( _In_ LPCVOID pBlock, _In_ LPCTSTR lpSubBlock, _Out_ LPVOID *lplpBuffer, _Out_ PUINT puLen );
Видел, спасибо. void ** InfoPtr, вероятно было бы корректнее.
void ** InfoPtr мне тоже кажется корректнее. Я так и сделал сразу, если обратили внимание. Прототип Move откуда брать непонятно.
Move(const * void Source, void * Dest, int Count), как предлагает BCB... Move(const Source, var Dest, count: Integer), как предлагает Delphi. Лучше протестировать на каком этапе что и где получается. И, код выявления ошибок после malloc и прочих функций добавить.
Пошаговым выполнением смотрел - все, вроде, ровно было. Только этот двойной указатель отследить не смог. NeferSky, никакая версия не спасет от незнания языка.
Ну так и говорю - переучиваюсь с Делфей. Точнее, С++ вторым языком осваивать начал. LPVOID - это тип указателя из дебрей винды, эквивалентный void*. LPVOID* эквивалентен void**.
Видел, спасибо. TVSFixedFileInfo *VerInfo; Где выделение памяти под структуру, раз она объявлена как указатель. Или тогда: TVSFixedFileInfo VerInfo;
Так эээ... memset(VerInfo, sizeof(TVSFixedFileInfo), 0) не выделение памяти? Ваш код помог, спасибо.
|
|
|
Записан
|
Не тронь налаженный механизм, и он тебя не подведет. Делать надо хорошо, а плохо - само получится.
|
|
|
zubr
Гость
|
|
« Ответ #9 : 11-11-2016 11:59 » |
|
memset - это не выделение памяти, а инициализация ее опреленными байтами, в твоем случае 0.
|
|
|
Записан
|
|
|
|
NeferSky
Постоялец
Offline
Бессмертный
|
|
« Ответ #10 : 11-11-2016 12:14 » |
|
Ааа! То есть, это заполнение уже выделенного куска памяти? Тогда ясно. Ошибку понял, спасибо за пояснение.
|
|
|
Записан
|
Не тронь налаженный механизм, и он тебя не подведет. Делать надо хорошо, а плохо - само получится.
|
|
|
|