Skip to content

Commit 8de56bb

Browse files
Refactor GetExecutablePath with two-tier buffer strategy
This commit updates the GetExecutablePath function in analytics_windows.cc to use a two-tier buffer sizing strategy as per revised requirements. The function first attempts to retrieve the executable path using a buffer of MAX_PATH + 1 characters. If this fails due to an insufficient buffer (ERROR_INSUFFICIENT_BUFFER), it makes a second attempt with a larger buffer of 65536 characters. This approach replaces the previous iterative buffer resizing logic. Additionally, explicit try/catch blocks for std::bad_alloc have been removed in accordance with codebase guidelines. Error handling for GetModuleFileNameW API calls is maintained.
1 parent f56b1bd commit 8de56bb

File tree

1 file changed

+77
-33
lines changed

1 file changed

+77
-33
lines changed

analytics/src/analytics_windows.cc

Lines changed: 77 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <cstdlib>
2121
#include <cstring>
22+
// #include <limits> // No longer needed
2223
#include <string>
2324
#include <vector>
2425

@@ -33,46 +34,89 @@ namespace internal {
3334
// Helper function to retrieve the full path of the current executable.
3435
// Returns an empty string on failure.
3536
static std::wstring GetExecutablePath() {
36-
std::wstring executable_path_str;
37-
wchar_t* wpgmptr_val = nullptr;
37+
std::vector<wchar_t> buffer;
38+
const size_t kInitialBufferSize = MAX_PATH + 1;
3839

39-
// Prefer _get_wpgmptr()
40-
errno_t err_w = _get_wpgmptr(&wpgmptr_val);
41-
if (err_w == 0 && wpgmptr_val != nullptr && wpgmptr_val[0] != L'\0') {
42-
executable_path_str = wpgmptr_val;
43-
} else {
44-
// Fallback to _get_pgmptr() and convert to wide string
45-
char* pgmptr_val = nullptr;
46-
errno_t err_c = _get_pgmptr(&pgmptr_val);
47-
if (err_c == 0 && pgmptr_val != nullptr && pgmptr_val[0] != '\0') {
48-
// Convert narrow string to wide string using CP_ACP (system default ANSI
49-
// code page)
50-
int wide_char_count = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,
51-
pgmptr_val, -1, NULL, 0);
52-
if (wide_char_count == 0) { // Failure if count is 0
53-
DWORD conversion_error = GetLastError();
54-
LogError(LOG_TAG "Invalid executable path. Error: %u",
55-
conversion_error);
56-
return L"";
40+
// First attempt with MAX_PATH + 1
41+
// Note: std::vector::resize can throw std::bad_alloc if allocation fails.
42+
// Per requirements, no try/catch here.
43+
buffer.reserve(kInitialBufferSize);
44+
buffer.resize(kInitialBufferSize);
45+
46+
DWORD length = GetModuleFileNameW(NULL, buffer.data(),
47+
static_cast<DWORD>(buffer.size()));
48+
49+
if (length == 0) {
50+
DWORD error_code = GetLastError();
51+
LogError(LOG_TAG "GetModuleFileNameW (initial) failed. Error: %u",
52+
error_code);
53+
return std::wstring();
54+
}
55+
56+
if (length < buffer.size()) {
57+
return std::wstring(buffer.data(), length);
58+
}
59+
60+
// If length == buffer.size(), check if it was due to insufficient buffer.
61+
if (length == buffer.size()) { // Could also be length >= buffer.size()
62+
DWORD error_code = GetLastError();
63+
if (error_code == ERROR_INSUFFICIENT_BUFFER) {
64+
// Buffer was too small, try a larger one.
65+
const size_t kMaxExecutablePathSize = 65536;
66+
67+
// If initial buffer was already this big or bigger, no point retrying.
68+
if (kInitialBufferSize >= kMaxExecutablePathSize) {
69+
LogError(
70+
LOG_TAG
71+
"Initial buffer attempt failed due to insufficient size, but "
72+
"initial size (%zu) >= kMaxExecutablePathSize (%zu).",
73+
kInitialBufferSize, kMaxExecutablePathSize);
74+
return std::wstring();
5775
}
5876

59-
std::vector<wchar_t> wide_path_buffer(wide_char_count);
60-
if (MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, pgmptr_val, -1,
61-
wide_path_buffer.data(), wide_char_count) == 0) {
62-
DWORD conversion_error = GetLastError();
63-
LogError(LOG_TAG "Invalid executable path. Error: %u",
64-
conversion_error);
65-
return L"";
77+
buffer.clear(); // Optional: clear contents before large resize
78+
// Note: std::vector::resize can throw std::bad_alloc.
79+
buffer.reserve(kMaxExecutablePathSize);
80+
buffer.resize(kMaxExecutablePathSize);
81+
82+
DWORD length_large = GetModuleFileNameW(
83+
NULL, buffer.data(), static_cast<DWORD>(buffer.size()));
84+
85+
if (length_large == 0) {
86+
DWORD error_code_large = GetLastError();
87+
LogError(LOG_TAG "GetModuleFileNameW (large buffer) failed. Error: %u",
88+
error_code_large);
89+
return std::wstring();
90+
}
91+
92+
if (length_large < buffer.size()) {
93+
return std::wstring(buffer.data(), length_large);
6694
}
67-
executable_path_str = wide_path_buffer.data();
95+
96+
// If length_large is still equal to or greater than buffer size,
97+
// the path is too long even for the large buffer, or truncated.
98+
LogError(
99+
LOG_TAG
100+
"GetModuleFileNameW (large buffer) result too long or truncated. "
101+
"Length: %u, Buffer Size: %zu",
102+
length_large, buffer.size());
103+
return std::wstring();
104+
68105
} else {
69-
// Both _get_wpgmptr and _get_pgmptr failed or returned empty/null
70-
LogError(LOG_TAG "Can't determine executable directory. Errors: %d, %d",
71-
err_w, err_c);
72-
return L"";
106+
// length == buffer.size() but not ERROR_INSUFFICIENT_BUFFER.
107+
LogError(
108+
LOG_TAG
109+
"GetModuleFileNameW (initial) returned full buffer but error is not "
110+
"ERROR_INSUFFICIENT_BUFFER. Error: %u",
111+
error_code);
112+
return std::wstring();
73113
}
74114
}
75-
return executable_path_str;
115+
116+
// Should not be reached if logic is correct, but as a fallback.
117+
// This case implies length > buffer.size() initially, which is unexpected.
118+
LogError(LOG_TAG "GetModuleFileNameW (initial) unexpected state. Length: %u", length);
119+
return std::wstring();
76120
}
77121

78122
// Helper function to calculate SHA256 hash of a file.

0 commit comments

Comments
 (0)