From 3ff2d70a15fc198f1f5b986d3e738c2150b9595c Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 16 Nov 2016 13:26:11 +0100 Subject: Rework qAppFileName() on MS-Win to handle problems gracefully Using a stack buffer and then a growing realloc() buffer can be tidily packaged by using a QVLA. Passing the reported name length to QString::fromWCharArray() saves the need for 0-termination, even if it has been missed. This also degrades gracefully to produce an empty QString when GetModuleFileName fails (returning 0). Change-Id: If58f5ad8505f66fb67e59572af1ceb60a068f0ab Reviewed-by: Timur Pocheptsov Reviewed-by: Jesus Fernandez --- src/corelib/kernel/qcoreapplication_win.cpp | 60 ++++++++++++----------------- 1 file changed, 25 insertions(+), 35 deletions(-) (limited to 'src/corelib/kernel/qcoreapplication_win.cpp') diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp index 50888dd0aa..00548365d2 100644 --- a/src/corelib/kernel/qcoreapplication_win.cpp +++ b/src/corelib/kernel/qcoreapplication_win.cpp @@ -67,43 +67,33 @@ int appCmdShow = 0; Q_CORE_EXPORT QString qAppFileName() // get application file name { - // We do MAX_PATH + 2 here, and request with MAX_PATH + 1, so we can handle all paths - // up to, and including MAX_PATH size perfectly fine with string termination, as well - // as easily detect if the file path is indeed larger than MAX_PATH, in which case we - // need to use the heap instead. This is a work-around, since contrary to what the - // MSDN documentation states, GetModuleFileName sometimes doesn't set the - // ERROR_INSUFFICIENT_BUFFER error number, and we thus cannot rely on this value if - // GetModuleFileName(0, buffer, MAX_PATH) == MAX_PATH. - // GetModuleFileName(0, buffer, MAX_PATH + 1) == MAX_PATH just means we hit the normal - // file path limit, and we handle it normally, if the result is MAX_PATH + 1, we use - // heap (even if the result _might_ be exactly MAX_PATH + 1, but that's ok). - wchar_t buffer[MAX_PATH + 2]; - DWORD v = GetModuleFileName(0, buffer, MAX_PATH + 1); - buffer[MAX_PATH + 1] = 0; - - if (v == 0) - return QString(); - else if (v <= MAX_PATH) - return QString::fromWCharArray(buffer); - - // MAX_PATH sized buffer wasn't large enough to contain the full path, use heap - wchar_t *b = 0; - int i = 1; - size_t size; + /* + GetModuleFileName() returns the length of the module name, when it has + space to store it and 0-terminate; this return is necessarily smaller than + the buffer size, as it doesn't include the terminator. When it lacks + space, the function returns the full size of the buffer and fills the + buffer, truncating the full path to make it fit. We have reports that + GetModuleFileName sometimes doesn't set the error number to + ERROR_INSUFFICIENT_BUFFER, as claimed by the MSDN documentation; so we + only trust the answer when the return is actually less than the buffer + size we pass in. (When truncating, except on XP, it does so by enough to + still have space to 0-terminate; in either case, it fills the claimed + space and returns the size of the space. While XP might thus give us the + full name, without a 0 terminator, and return its actual length, we can + never be sure that's what's happened until a later call with bigger buffer + confirms it by returning less than its buffer size.) + */ + // Full path may be longer than MAX_PATH - expand until we have enough space: + QVarLengthArray space; + DWORD v; + size_t size = 1; do { - ++i; - size = MAX_PATH * i; - b = reinterpret_cast(realloc(b, (size + 1) * sizeof(wchar_t))); - if (b) - v = GetModuleFileName(NULL, b, DWORD(size)); - } while (b && v == size); - - if (b) - *(b + size) = 0; - QString res = QString::fromWCharArray(b); - free(b); + size += MAX_PATH; + space.resize(int(size)); + v = GetModuleFileName(NULL, space.data(), DWORD(space.size())); + } while (Q_UNLIKELY(v >= size)); - return res; + return QString::fromWCharArray(space.data(), v); } QString QCoreApplicationPrivate::appName() const -- cgit v1.2.3