| 
			| 
					
						| NeferSky 
								Постоялец    Offline 
								Бессмертный
								
								
								
								
								
							 | 
								|  | «  : 11-11-2016 09:36 »  |  | 
 
 Добрый день всем. Давно уже пишу на Делфях, но ничто не вечно. Это время пришло. Начал осваивать С++. Для примеров перевожу код своих проектов с Паскаля на С. Внезапно - запутался с указателями при использовании WinAPI. А именно - есть у меня функция. В Делфях работает, в BCB - нет. Хватаю AV на функции Move(). Чую, там дело в двойном указателе void **InfoPtr, но что я не так с ним делаю - не пойму. Разыменовываю, вроде, правильно... тип у него - тоже, как будто подходящий, и Move() ожидает там указатель... В чем может быть дело?Pascal code: C++ 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;
 
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 | 
								|  | « Ответ #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(zubr_In_      LPCTSTR lptstrFilename,
 _Out_opt_ LPDWORD lpdwHandle
 );
 ...
 lpdwHandle [out, optional]
 Type: LPDWORD
 A pointer to a variable that the function sets to zero.
 ...
 
  уже поправил, но всё же. Как понял второй аргумент требует передать адрес переменной, которая будет сброшена в ноль. (Зачем?) Под такую переменную память должна быть выделена, если просто передать указатель, то функция попытается сбросить в ноль не пойми что. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| 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 »   |  | 
 
 Ааа! То есть, это заполнение уже выделенного куска памяти? Тогда ясно. Ошибку понял, спасибо за пояснение. |  
						| 
								|  |  
								|  |  Записан | 
 
 Не тронь налаженный механизм, и он тебя не подведет.Делать надо хорошо, а плохо - само получится.
 |  |  | 
	|  |