locked
GetWindowText and SetWindowText using std::wstring RRS feed

  • Question

  • I realize I'm doing something wrong, but can't figure out what it is.

    This function compiles without an error but always returns a blank string when I try to get the main window's caption:

    std::wstring gettext(HWND hwnd)
    {
         std::wstring stxt;
         GetWindowText(hwnd,(LPTSTR) stxt.c_str(),32768);
         return stxt;
    }

    Yet the same code in reverse successfully changes that same caption text:

    void settext(HWND hctl,std::wstring stxt)
    {
         SetWindowText(hctl,stxt.c_str());
    }

    Any ideas?


    • Edited by TallGuy63 Wednesday, February 25, 2015 5:50 PM
    Wednesday, February 25, 2015 5:48 PM

Answers

  • On 2/25/2015 1:20 PM, TallGuy63 wrote:

    Then how are you supposed to get any window text ?

    You pass a pointer to a memory buffer that's N characters long as the second parameter of GetWindowText, and N as the third parameter. There are many way to obtain such a pointer; wstring::c_str() is not one of them.

    For example:

    int len = GetWindowTextLength(hwnd) + 1;
    vector<wchar_t> buf(len);
    GetWindowText(hwnd, &buf[0], len);
    wstring stxt = &buf[0];

    Igor Tandetnik
    • Marked as answer by TallGuy63 Wednesday, February 25, 2015 7:43 PM
    Wednesday, February 25, 2015 6:54 PM

All replies

  • On 2/25/2015 12:48 PM, TallGuy63 wrote:

          std::wstring stxt;
          GetWindowText(hwnd,(LPTSTR) stxt.c_str(),32768);

    What makes you believe (LPTSTR) stxt.c_str() points to a writable buffer 32768 characters long? Or if that's not what you believe, then why are you lying to GetWindowText?

    wstring::c_str() returns const wchar_t* for a reason. You shouldn't attempt to write to it. Your program exhibits undefined behavior.


    Igor Tandetnik
    Wednesday, February 25, 2015 5:58 PM
  • OK, but that doesn't answer the question really...

    Then how are you supposed to get any window text ?

    the WM_GETTEXT needs a pointer to an LPTSTR too !

    Wednesday, February 25, 2015 6:20 PM
  •      std::wstring stxt;
         GetWindowText(hwnd,(LPTSTR) stxt,32768);

    doesn't work... it only compiled if you add the .c_str()

    What I need to know then is how to get string text as a std::wstring from a Windows control or window!
    • Edited by TallGuy63 Wednesday, February 25, 2015 6:24 PM
    Wednesday, February 25, 2015 6:21 PM
  •      std::wstring stxt;
         GetWindowText(hwnd,(LPTSTR) stxt,32768);

    doesn't work... it only compiled if you add the .c_str()

    What I need to know then is how to get string text as a std::wstring from a Windows control or window!

    May be you can try something like

            CString str;
    ::GetWindowText(m_hWnd,(LPTSTR) (LPCTSTR)str,100);
    std::wstring wstr = str;

    Thanks


    Rupesh Shukla

    Wednesday, February 25, 2015 6:38 PM
  • Thanks for the suggestion...

    Only problem is that although it does compile, it also returns a blank string!

    std::wstring gettext(HWND hwnd)
    {
         CString str;
         GetWindowText(hwnd,(LPTSTR)(LPCTSTR)str,8192);
         std::wstring stxt=str;
         return stxt;
    }

    Wednesday, February 25, 2015 6:43 PM
  • Check for hwnd . Is it a valid handle or not. Please check the scope of stxt variable as well.

    Thanks


    Rupesh Shukla


    • Edited by Pintu Shukla Wednesday, February 25, 2015 6:49 PM
    Wednesday, February 25, 2015 6:46 PM
  • On 2/25/2015 1:20 PM, TallGuy63 wrote:

    Then how are you supposed to get any window text ?

    You pass a pointer to a memory buffer that's N characters long as the second parameter of GetWindowText, and N as the third parameter. There are many way to obtain such a pointer; wstring::c_str() is not one of them.

    For example:

    int len = GetWindowTextLength(hwnd) + 1;
    vector<wchar_t> buf(len);
    GetWindowText(hwnd, &buf[0], len);
    wstring stxt = &buf[0];

    Igor Tandetnik
    • Marked as answer by TallGuy63 Wednesday, February 25, 2015 7:43 PM
    Wednesday, February 25, 2015 6:54 PM
  •    

    Rupesh:

          The reason I know it is a legitimate handle is the statement before changed the text successfully

                 stxt = TEXT("Change To This");
                 settext(hwnd, stxt);

                 stxt=gettext(hwnd);
                 showtextmessage(stxt);

    As you can see the very next line calls the exact same handle for the std::wstring


    • Edited by TallGuy63 Wednesday, February 25, 2015 7:29 PM
    Wednesday, February 25, 2015 7:28 PM
  • Igor: Thanks for the example!

    It didn't compile for me, though. The <vector> came across as invalid...

    Do I need to add another library to use a "vector" ?

    Wednesday, February 25, 2015 7:32 PM
  • On 2/25/2015 2:32 PM, TallGuy63 wrote:

    Do I need to add another library to use a "vector" ?

    #include <vector>

    and make it std::vector.


    Igor Tandetnik
    Wednesday, February 25, 2015 7:38 PM
  • Thanks Igor! It is now working correctly!

    std::wstring gettext(HWND hctl)
    {
         int lngth=GetWindowTextLength(hctl)+1;
         std::vector<wchar_t> buf(lngth);
         GetWindowText(hctl,&buf[0],lngth);
         std::wstring stxt=&buf[0];
         return stxt;
    }

    Wednesday, February 25, 2015 7:43 PM
  •    

    Rupesh:

          The reason I know it is a legitimate handle is the statement before changed the text successfully

                 stxt = TEXT("Change To This");
                 settext(hwnd, stxt);

                 stxt=gettext(hwnd);
                 showtextmessage(stxt);

    As you can see the very next line calls the exact same handle for the std::wstring


    I have No idea what you are doing in your code . But one thing i am pretty sure that if you are using proper handle to window then all the mentioned suggestion is going to work else nothing is going to work.And i am not sure what window are you talking might be you can try looking with main Window try using m_hWnd instead of hwnd.One more thing look how igor calculated length . i Will suggest you to perform same operation before calling GetWindowText() and pass the actual length to the function.

    Thanks


    Rupesh Shukla


    • Edited by Pintu Shukla Wednesday, February 25, 2015 7:46 PM
    Wednesday, February 25, 2015 7:44 PM
  • I have No idea what you are doing in your code . But one thing i am pretty sure that if you are using proper handle to window then all the mentioned suggestion is going to work else nothing is going to work.

    Actually

    CString str;
    ::GetWindowText(m_hWnd,(LPTSTR) (LPCTSTR)str,100);
    std::wstring wstr = str;
    

    is not going to work, because the CString is empty and in any case you should not cast the internal buffer to a LPTSTR, because using it will corrupt the CString. To manipulate the internal CString buffer you must use GetBuffer/ReleaseBuffer, but using vector<char> as shown by Igor is much cleaner/simpler.


    David Wilkinson | Visual C++ MVP

    Wednesday, February 25, 2015 8:45 PM
  • Actually

    CString str;
    ::GetWindowText(m_hWnd,(LPTSTR) (LPCTSTR)str,100);
    std::wstring wstr = str;

    is not going to work, because the CString is empty and in any case you should not cast the internal buffer to a LPTSTR, because using it will corrupt the CString. To manipulate the internal CString buffer you must use GetBuffer/ReleaseBuffer, but using vector<char> as shown by Igor is much cleaner/simpler.


    David Wilkinson | Visual C++ MVP

    Hi Dave,

                  Make sense logically but it will work for sure . Because When you are making a call to GetWindowText() it is going with 

    GetWindowTextA(
        __in HWND hWnd,
        __out_ecount(nMaxCount) LPSTR lpString,
        __in int nMaxCount);

     . So i am not sure how you think that it will not work.One more thing to perform operation on string interbnal buffer for sure  you can use GetBuffer()/ReleaseBuffer() along with GetLength() for this is not true for empty string .

    Thanks


    Rupesh Shukla


    • Edited by Pintu Shukla Wednesday, February 25, 2015 10:23 PM
    Wednesday, February 25, 2015 10:20 PM
  • On 2/25/2015 5:20 PM, "Pintu Shukla" wrote:

      . So i am not sure how you think that it will not work.

    If will not work because (LPTSTR) (LPCTSTR)str does not point to a writable memory buffer 100 characters large.


    Igor Tandetnik
    Wednesday, February 25, 2015 10:33 PM
  • I am not sure igor as on 2008 and 2010 on both it is working fine and i believe str is a writable buffer as it's type is CString . Am i missing something here . See i am not denying Dave point logically we shouldn't do such things. Initially i just wrote it for wstring conversion etc. But now i am really thinking how this is invalid.

    Thanks


    Rupesh Shukla



    • Edited by Pintu Shukla Wednesday, February 25, 2015 10:40 PM
    Wednesday, February 25, 2015 10:39 PM
  • On 2/25/2015 5:39 PM, "Pintu Shukla" wrote:

    I am not sure igor as on 2008 and 2010 on both it is working fine and i believe str is a writable buffer as it's type is CString . Am i missing something here .

    How large is the buffer that (LPTSTR) (LPCTSTR)str points to, in your opinion? You promise to GetWindowText that it is at least large enough to hold 100 characters - how do you know that this is true? Do you expect str to read your mind and magically resize its internal storage to just the right size?


    Igor Tandetnik
    Wednesday, February 25, 2015 10:52 PM
  •    I have No idea what you are doing in your code . But one thing i am pretty sure that if you are using proper handle to window then all the mentioned suggestion is going to work else nothing is going to work.And i am not sure what window are you talking might be you can try looking with main Window try using m_hWnd instead of hwnd.One more thing look how igor calculated length . i Will suggest you to perform same operation before calling GetWindowText() and pass the actual length to the function.

    Thanks


    Rupesh Shukla


    Hello Igor , I think you answered the question without even reading my previous post .i am aware about buffer length . That's why i already mentioned to OP that follow IGOR suggestion to get the buffer length and use it inside your code. But David question was different .

    Thanks


    Rupesh Shukla


    • Edited by Pintu Shukla Wednesday, February 25, 2015 11:01 PM
    Wednesday, February 25, 2015 10:59 PM
  • Hi Dave,

                  Make sense logically but it will work for sure . Because When you are making a call to GetWindowText() it is going with

    GetWindowTextA(
        __in HWND hWnd,
        __out_ecount(nMaxCount) LPSTR lpString,
        __in int nMaxCount);

    . So i am not sure how you think that it will not work.One more thing to perform operation on string interbnal buffer for sure  you can use GetBuffer()/ReleaseBuffer() along with GetLength() for this is not true for empty string .

    Thanks


    Rupesh Shukla

    It will compile for sure, but it will only work reliably if the internal buffer length of a default-constructed CString is greater than 100. If this is true, it is an implementation detail subject to change.


    David Wilkinson | Visual C++ MVP

    Wednesday, February 25, 2015 11:01 PM
  • On 25/02/2015 19:20, TallGuy63 wrote:


    Then how are you supposed to get any window text ?

    An option would be to make enough room inside the string for storing text in it.

    Something like this:

    int len = GetWindowTextLength(hwnd);
    wstring str;
    str.resize(len); // make enough room in string
    GetWindowText(hwnd, &str[0], len+1);
    

    Note that the length value returned by GetWindowTextLength() excludes the terminating NUL.
    However, you have to pass the whole destination buffer length (*including* the terminating NUL) to GetWindowText() as third parameter.

    This approach is more efficient than having a separate std::vector for buffer allocation, with a separate dynamic memory allocation, and then a deep-copy into the std::wstring.

    However, note that overwriting the NUL terminator in STL strings with another NUL terminator seems to be "undefined behavior", at least according to this discussion on Stack"http://stackoverflow.com/questions/12740403/legal-to-overwrite-stdstrings-null-terminator" target="_blank">http://stackoverflow.com/questions/12740403/legal-to-overwrite-stdstrings-null-terminator

    However, it seems to work just fine in Visual Studio (tested on both 2010 and 2013).
    Now, since your C++ code is already Windows-specific, that should not be a problem.

    And, anyway, IMO the standard should be fixed to allow something like overwriting the terminating NUL with another NUL a valid well-defined operation.

    If you want to be 100% standard compliant and not trigger "undefined behavior", you should first allocate a string of len+1, and then resize it to len, as indicated here: http://stackoverflow.com/a/12742517

    Giovanni

    Monday, March 2, 2015 11:32 AM