1 /*****************************************************************************\
3 * Name : check_version *
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 1996-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include "version_checker.h"
17 #include <application/windoze_helper.h>
18 #include <basis/astring.h>
19 #include <basis/environment.h>
20 #include <basis/functions.h>
21 #include <basis/guards.h>
22 #include <basis/utf_conversion.h>
23 #include <configuration/application_configuration.h>
24 #include <filesystem/filename.h>
25 #include <loggers/critical_events.h>
29 using namespace basis;
30 using namespace configuration;
31 using namespace loggers;
32 using namespace structures;
34 #ifndef BOOT_STRAPPING
35 // pull in the version specified for this build.
36 #include <__build_version.h>
37 //why was this include "on hold"?
39 // plug in a fake version for our bootstrapping process.
40 #define __build_FILE_VERSION "108.420.1024.10008"
50 // ensures that we handle the data properly regardless of unicode settings.
52 #define render_ptr(ptr) from_unicode_temp( (UTF16 *) ptr)
54 #define render_ptr(ptr) ( (char *) ptr)
62 version_checker::version_checker(const astring &library_file_name,
63 const version &expected_version, const astring &version_complaint)
64 : _library_file_name(new astring(library_file_name)),
65 _expected_version(new version(expected_version)),
66 _version_complaint(new astring(version_complaint))
69 version_checker::~version_checker()
71 WHACK(_library_file_name);
72 WHACK(_expected_version);
73 WHACK(_version_complaint);
76 astring version_checker::text_form() const
78 return astring(class_name()) + ": library_file_name=" + *_library_file_name
79 + ", expected_version=" + _expected_version->text_form()
80 + ", complaint_message=" + *_version_complaint;
83 bool version_checker::good_version() const
85 astring version_disabler = environment::TMP();
86 version_disabler += "/no_version_check.txt";
87 FILE *always_okay = fopen(version_disabler.s(), "r");
93 version version_found = retrieve_version(*_library_file_name);
94 if (version_found.compatible(*_expected_version)) return true; // success.
95 complain_wrong_version(*_library_file_name, *_expected_version,
100 bool version_checker::loaded(const astring &library_file_name)
103 #if defined(_MSC_VER)
104 return bool(get_handle(library_file_name) != 0);
107 return true || library_file_name.t();
111 void *version_checker::get_handle(const astring &library_file_name)
114 #if defined(_MSC_VER)
115 return GetModuleHandle(to_unicode_temp(library_file_name));
117 if (library_file_name.t()) return NULL_POINTER; else return NULL_POINTER;
121 astring version_checker::module_name(const void *module_handle)
123 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
124 if (module_handle) {}
125 return application_configuration::application_name();
126 #elif defined(_MSC_VER)
127 //#elif defined(__WIN32__)
128 flexichar low_buff[MAX_ABS_PATH + 1];
129 GetModuleFileName((HMODULE)module_handle, low_buff, MAX_ABS_PATH - 1);
130 astring buff = from_unicode_temp(low_buff);
134 #pragma message("module_name unknown for this operating system.")
135 return application_configuration::application_name();
139 astring version_checker::get_name(const void *to_find)
140 { return module_name(to_find); }
142 bool version_checker::retrieve_version_info(const astring &filename,
145 to_fill.reset(); // clear the buffer.
147 // determine the required size of the version info buffer.
149 #if defined(_MSC_VER)
151 un_long module_handle; // filled with the dll or exe handle.
152 required_size = GetFileVersionInfoSize(to_unicode_temp(filename), &module_handle);
154 required_size = 0 && filename.t();
156 if (!required_size) return false;
157 to_fill.reset(required_size); // resize the buffer.
159 // read the version info into our buffer.
160 bool success = false;
161 #if defined(_MSC_VER)
163 success = GetFileVersionInfo(to_unicode_temp(filename), module_handle,
164 required_size, to_fill.access());
171 bool version_checker::get_language(byte_array &version_chunk,
172 basis::un_short &high, basis::un_short &low)
176 #if defined(_MSC_VER)
178 // determine the language that the version's written in.
179 basis::un_int data_size;
180 void *pointer_to_language_structure;
181 // query the information from the version blob.
182 if (!VerQueryValue(version_chunk.access(),
183 to_unicode_temp("\\VarFileInfo\\Translation"),
184 &pointer_to_language_structure, &data_size))
186 // get the low & high shorts of the structure.
187 high = LOWORD(*(unsigned int *)pointer_to_language_structure);
188 low = HIWORD(*(unsigned int *)pointer_to_language_structure);
190 high = 0 && version_chunk.length();
197 version version_checker::retrieve_version(const astring &filename)
200 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
202 // totally bogus stand-in; this just returns the version we were built with
203 // rather than the version that's actually tagged on the file.
205 //hmmm: fix this! get the version header back.
206 // return version(__build_FILE_VERSION);
211 byte_array version_info_found(0, NULL_POINTER);
212 if (!retrieve_version_info(filename, version_info_found))
213 return version(0, 0, 0, 0);
215 basis::un_short high, low; // holds the language of the version data.
216 if (!get_language(version_info_found, high, low))
217 return version(0, 0, 0, 0);
219 // retrieve the file version from version info using the appropriate
221 astring root_key(astring::SPRINTF, "\\StringFileInfo\\%04x%04x", high, low);
222 astring file_version_key(root_key + astring("\\FileVersion"));
224 astring version_string;
226 abyte *file_version_pointer;
227 basis::un_int data_size;
228 if (!VerQueryValue(version_info_found.access(),
229 to_unicode_temp(file_version_key),
230 (LPVOID *)&file_version_pointer, &data_size))
231 return version(0, 0, 0, 0);
232 version_string = render_ptr(file_version_pointer);
233 // clean any spaces out of the string; people sometimes format these
235 for (int i = 0; i < version_string.length(); i++) {
236 if (version_string[i] == ' ') {
237 version_string.zap(i, i);
238 i--; // skip back a beat.
242 return version(0, 0, 0, 0);
245 return version::from_text(version_string);
248 bool version_checker::get_record(const astring &filename,
249 version_record &to_fill)
251 to_fill = version_record();
252 byte_array version_info_found(0, NULL_POINTER);
253 if (!retrieve_version_info(filename, version_info_found))
256 basis::un_short high, low; // holds the language of the version data.
257 if (!get_language(version_info_found, high, low))
260 // set the root key for all accesses of the version chunk.
261 astring root_key(astring::SPRINTF, "\\StringFileInfo\\%04x%04x", high, low);
263 // reports whether all lookups succeeded or not.
264 bool total_success = true;
266 // the various version pieces are retrieved...
270 basis::un_int data_size;
274 if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
275 + astring("\\FileVersion")), &data_pointer, &data_size))
276 total_success = false;
278 to_fill.file_version = version::from_text(render_ptr(data_pointer));
281 if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
282 + astring("\\CompanyName")), &data_pointer, &data_size))
283 total_success = false;
285 to_fill.company_name = render_ptr(data_pointer);
288 if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
289 + astring("\\FileDescription")), &data_pointer, &data_size))
290 total_success = false;
292 to_fill.description = render_ptr(data_pointer);
295 if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
296 + astring("\\InternalName")), &data_pointer, &data_size))
297 total_success = false;
299 to_fill.internal_name = render_ptr(data_pointer);
302 if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
303 + astring("\\LegalCopyright")), &data_pointer, &data_size))
304 total_success = false;
306 to_fill.copyright = render_ptr(data_pointer);
309 if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
310 + astring("\\LegalTrademarks")), &data_pointer, &data_size))
311 total_success = false;
313 to_fill.trademarks = render_ptr(data_pointer);
315 // original file name.
316 if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
317 + astring("\\OriginalFilename")), &data_pointer, &data_size))
318 total_success = false;
320 to_fill.original_name = render_ptr(data_pointer);
323 if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
324 + astring("\\ProductName")), &data_pointer, &data_size))
325 total_success = false;
327 to_fill.product_name = render_ptr(data_pointer);
330 if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
331 + astring("\\ProductVersion")), &data_pointer, &data_size))
332 total_success = false;
334 to_fill.product_version = version::from_text(render_ptr(data_pointer));
336 // hmmm: chunks missing in version check.
339 return total_success;
342 void version_checker::complain_wrong_version(const astring &library_file_name,
343 const version &expected_version, const version &version_found) const
345 astring to_show("There has been a Version Mismatch: The module \"");
346 // use embedded module handle to retrieve name of dll or exe.
347 astring module_name = get_name(version::__global_module_handle());
348 if (!module_name) module_name = "Unknown";
349 to_show += module_name;
350 to_show += astring("\" cannot load. This is because the file \"");
351 to_show += library_file_name;
352 to_show += astring("\" was expected to have a version of [");
354 to_show += expected_version.flex_text_form(version::DOTS);
356 to_show += astring("] but it instead had a version of [");
357 to_show += version_found.flex_text_form(version::DOTS);
359 to_show += astring("]. ");
360 to_show += *_version_complaint;
362 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
363 continuable_error("version checking", "failure", to_show.s());
364 #elif defined(_MSC_VER)
365 MessageBox(0, to_unicode_temp(to_show),
366 to_unicode_temp("version_checking::failure"), MB_OK);
370 void version_checker::complain_cannot_load(const astring &lib_file) const
372 astring to_show("There has been a failure in Version Checking: The file \"");
374 to_show += astring("\" could not be loaded or found. ");
375 to_show += *_version_complaint;
376 continuable_error("version checking", "loading dll", to_show.s());