diff --git a/impl/getcomputername.cpp b/impl/getcomputername.cpp index c403cfe6d..801c4000b 100644 --- a/impl/getcomputername.cpp +++ b/impl/getcomputername.cpp @@ -1,33 +1,74 @@ -#include "getcomputername.h" +//! @file getcomputername.cpp +//! @author Aaron Katz +//! @brief Implements GetComputerName Win32 API + #include #include +#include #include #include -#include +#include +#include +#include +#include +#include "getcomputername.h" - -// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724432(v=vs.85).aspx -// Sets errno to: -// ERROR_INVALID_PARAMETER - parameter is not valid -// ERROR_BAD_ENVIRONMENT - locale is not UTF-8 -// ERROR_TOO_MANY_OPEN_FILES - already have the maximum allowed number of open files -// ERROR_NO_ASSOCIATION - calling process has no controlling terminal -// ERROR_INSUFFICIENT_BUFFER - buffer not large enough to hold username string -// ERROR_NO_SUCH_USER - there was no corresponding entry in the utmp-file -// ERROR_OUTOFMEMORY - insufficient memory to allocate passwd structure -// ERROR_NO_ASSOCIATION - standard input didn't refer to a terminal -// ERROR_INVALID_FUNCTION - getlogin_r() returned an unrecognized error code -// -// Returns: -// 1 - succeeded -// 0 - failed -BOOL GetComputerNameW(LPTSTR nameOfComputer, LPDWORD len) +//! @brief GetComputerName retrieves the name of the host associated with +//! the current thread. +//! +//! GetComputerNameW is the Unicode variation. See [MSDN documentation]. +//! +//! @param[out] lpBuffer +//! @parblock +//! A pointer to the buffer to receive the hosts's +//! name. If this buffer is not large enough to contain the +//! entire user hosts name, the function fails. +//! +//! WCHAR_T* is a Unicode [LPTSTR]. +//! @endparblock +//! +//! @param[in, out] lpnSize +//! @parblock +//! On input, this variable specifies the size of the lpBuffer buffer, +//! in TCHARs. On output, the variable receives the number of TCHARs +//! copied to the buffer, including the terminating null character. +//! +//! TCHAR is a Unicode 16-bit [WCHAR]. +//! +//! If lpBuffer is too small, the function fails and GetLastError +//! returns ERROR_INSUFFICIENT_BUFFER. This parameter receives the +//! required buffer size, including the terminating null character. +//! @endparblock +//! +//! @exception errno Passes these errors via errno to GetLastError: +//! - ERROR_INVALID_PARAMETER: parameter is not valid +//! - ERROR_BAD_ENVIRONMENT: locale is not UTF-8 +//! - ERROR_TOO_MANY_OPEN_FILES: already have the maximum allowed number of open files +//! - ERROR_NO_ASSOCIATION: calling process has no controlling terminal +//! - ERROR_INSUFFICIENT_BUFFER: buffer not large enough to hold username string +//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file +//! - ERROR_OUTOFMEMORY: insufficient memory to allocate passwd structure +//! - ERROR_NO_ASSOCIATION: standard input didn't refer to a terminal +//! - ERROR_INVALID_FUNCTION: getlogin_r() returned an unrecognized error code +//! +//! @retval 1 If the function succeeds, the return value is a nonzero +//! value, and the variable pointed to by lpnSize contains the number +//! of TCHARs copied to the buffer specified by lpBuffer, including +//! the terminating null character. +//! +//! @retval 0 If the function fails, the return value is zero. To get +//! extended error information, call GetLastError. +//! +//! [MSDN documentation]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724295%28v=vs.85%29.aspx +//! [WCHAR]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx#WCHAR +//! [LPTSTR]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx#LPTSTR +BOOL GetComputerNameW(WCHAR_T* lpBuffer, LPDWORD lpnSize) { const std::string utf8 = "UTF-8"; errno = 0; // Check parameters - if (!nameOfComputer || !len) { + if (!lpBuffer || !lpnSize) { errno = ERROR_INVALID_PARAMETER; return 0; } @@ -41,8 +82,8 @@ BOOL GetComputerNameW(LPTSTR nameOfComputer, LPDWORD len) } // Get computername from system in a thread-safe manner - std::string computerName(HOST_MAX_NAME, '\0'); - int err = gethostname(&computerName[0], computerName.size()); + std::string computername(HOST_NAME_MAX, '\0'); + int err = gethostname(&computername[0], computername.size()); // Map errno to Win32 Error Codes if (err != 0) { switch (errno) { @@ -72,21 +113,31 @@ BOOL GetComputerNameW(LPTSTR nameOfComputer, LPDWORD len) return 0; } - //Resize the string to not inlcude extra buffer space - const DWORD length = computerName.find_first_of('\0') + 1; - computerName.resize(length); + // Convert to char* to WCHAR_T* (UTF-8 to UTF-16 LE w/o BOM) + std::basic_string computername16(HOST_NAME_MAX + 1, 0); + icu::UnicodeString computername8(computername.c_str(), "UTF-8"); + int32_t targetSize = computername8.extract(0, computername8.length(), + reinterpret_cast(&computername16[0]), + (computername16.size()-1)*sizeof(char16_t), + "UTF-16LE"); + // Number of characters including null + computername16.resize(targetSize/sizeof(char16_t)+1); + + // Size in WCHARs including null + const DWORD size = computername16.length(); //Check if parameters passed enough buffer space - if (length > *len) { + if (size > *lpnSize) { errno = ERROR_INSUFFICIENT_BUFFER; // Set lpnSize if buffer is too small to inform user // of necessary size - *len= length; + *lpnSize= size; return 0; } - memcpy(nameOfComputer, &computerName[0], length); - *len = length; + // Copy bytes from string to buffer + memcpy(lpBuffer, &computername16[0], size*sizeof(char16_t)); + *lpnSize = size; return 1; } diff --git a/impl/getcomputername.h b/impl/getcomputername.h index 5e0d5be2b..8d39d3f9f 100644 --- a/impl/getcomputername.h +++ b/impl/getcomputername.h @@ -4,6 +4,6 @@ PAL_BEGIN_EXTERNC -BOOL GetComputerNameW(LPTSTR nameOfComputer, LPDWORD len); +BOOL GetComputerNameW(WCHAR_T* lpBuffer, LPDWORD lpnSize); PAL_END_EXTERNC \ No newline at end of file diff --git a/impl/pal.h b/impl/pal.h index a6034f0eb..17e58d409 100644 --- a/impl/pal.h +++ b/impl/pal.h @@ -80,7 +80,6 @@ #define ERROR_NO_SUCH_USER 0x00000525 #define ERROR_INVALID_FUNCTION 0x00000001 #define MAX_PATH 0x00000104 - #define HOST_MAX_NAME 256 typedef unsigned long long uint64; #endif diff --git a/tests/test-getcomputername.cpp b/tests/test-getcomputername.cpp index b91730553..93cbfe71d 100644 --- a/tests/test-getcomputername.cpp +++ b/tests/test-getcomputername.cpp @@ -1,36 +1,65 @@ -#include -#include "getcomputername.h" -#include +//! @file test-getcomputername.cpp +//! @author Aaron Katz +//! @brief Unit tests for GetComputerNameW +#include +#include +#include +#include +#include +#include +#include +#include +#include "getcomputername.h" + +//! Test fixture for GetComputerNameTest class GetComputerNameTest : public ::testing::Test { protected: DWORD lpnSize; + std::vector lpBuffer; BOOL result; std::string expectedComputerName; - std::string computerName; + DWORD expectedSize; //Get expected result from using linux call GetComputerNameTest(){ - char hostname[HOST_MAX_NAME]; + char hostname[HOST_NAME_MAX]; BOOL host = gethostname(hostname, sizeof hostname); expectedComputerName = hostname; + expectedSize = expectedComputerName.length() + 1; } void TestWithSize(DWORD size) { lpnSize = size; // allocate a DWORD buffer to receive computername - computerName.assign(lpnSize, '\0'); - result = GetComputerNameW(&computerName[0], &lpnSize); + lpBuffer.assign(lpnSize, '\0'); + result = GetComputerNameW(&lpBuffer[0], &lpnSize); } void TestSuccess() { SCOPED_TRACE(""); - // returns 1 on success - EXPECT_EQ(1, result); - //Resize to cut off '\0' - computerName.resize(expectedComputerName.length()); - ASSERT_EQ(expectedComputerName.size()+1, lpnSize); - EXPECT_EQ(computerName, expectedComputerName); + + //! Returns TRUE on success. + EXPECT_EQ(TRUE, result); + + //! Sets lpnSize to number of WCHARs including null. + ASSERT_EQ(expectedSize, lpnSize); + + // setup for conversion from UTF-16LE + const char* begin = reinterpret_cast(&lpBuffer[0]); + // multiply to get number of bytes + icu::UnicodeString usercomputer16(begin, lpnSize*sizeof(char16_t), "UTF-16LE"); + // username16 length includes null and is number of characters + ASSERT_EQ(expectedSize, usercomputer16.length()); + + // convert (minus null) to UTF-8 for comparison + std::string computername(lpnSize-1, 0); + ASSERT_EQ(expectedComputerName.length(), computername.length()); + usercomputer16.extract(0, computername.length(), + reinterpret_cast(&computername[0]), "UTF-8"); + + //! Returned computername(after conversion) is what was expected. + EXPECT_EQ(expectedComputerName, computername); } void TestInvalidParameter() { @@ -66,14 +95,14 @@ TEST_F(GetComputerNameTest, BufferAsNullButNotBufferSize) { } TEST_F(GetComputerNameTest, BufferSizeAsNullButNotBuffer) { - computerName.push_back('\0'); - result = GetComputerNameW(&computerName[0], NULL); + lpBuffer.push_back('\0'); + result = GetComputerNameW(&lpBuffer[0], NULL); TestInvalidParameter(); } TEST_F(GetComputerNameTest, BufferSizeAsZero) { TestWithSize(0); - EXPECT_EQ(errno, ERROR_INSUFFICIENT_BUFFER); + EXPECT_EQ(errno, ERROR_INVALID_PARAMETER); } TEST_F(GetComputerNameTest, BufferSizeAsUserNameMinusOne) { @@ -90,6 +119,6 @@ TEST_F(GetComputerNameTest, BufferSizeAsUserNamePlusOne) { TEST_F(GetComputerNameTest, BufferSizeAsLoginNameMax) { // the buffer larger than needed - TestWithSize(HOST_MAX_NAME); + TestWithSize(HOST_NAME_MAX); TestSuccess(); } \ No newline at end of file