C++ LPSTR and string trouble with zero-terminated strings

人盡茶涼 提交于 2019-12-25 18:37:08

问题


I'm using GetOpenFileName function from Winapi, and I'm applying filter to the select file dialog.

THIS works perfectly:

LPSTR mfilter = "Filter\0*.PDF\0";
ofn.lpstrFilter = mfilter;

if(GetOpenFileName(&ofn)){
...

THIS fails (dialog opens but no filters apply):

string mfilter = "Filter\0*.PDF\0";
ofn.lpstrFilter = mfilter.c_str();

if(GetOpenFileName(&ofn)){
...

I need to use std:string because I'm getting the file extension via parameters and this type facilitates the concatenation but I'm getting incompatibility issues...

This would be my code if it worked as expected (IT FAILS the same as previous example):

const char * ext = &(4:); //Ampersand parameter (from CA Plex) It contains "PDF"
string mfilter = "Filter\0*." + ext + "\0"; //Final string: Filter\0*.PDF\0;
ofn.lpstrFilter = mfilter.c_str();

When I use this method, I'm getting runtime exception:

string mf;
mf.append("Filter")
.append('\0')
.append("*.pdf")
.append('\0');

ofn.lpstrFilter = mf.c_str();


回答1:


The GetOpenFileName function uses TCHARs, and TCHARs become WCHARs in case of UNICODE character set is used.

Here's an example:

std::wstring getOpenFileName(HWND hWnd, const std::wstring& sFilter)
{
    wchar_t buffer[MAX_PATH] = L"";

    OPENFILENAMEW ofn = {0};

    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = hWnd;
    ofn.lpstrFilter = sFilter.c_str();
    ofn.nFilterIndex = 1;
    ofn.lpstrFile = buffer;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;

    if( !::GetOpenFileNameW( &ofn ) )
        return L"";

    return buffer;
}

If you want to parametrize lpstrFilter based on std::wstring you can just use wstring::c_str() to get LPCTSTR which is const wchar* in case of UNICODE.

IMPORTANT: The problem is that the std::wstring constructor that takes a const wchar* assumes the input is a C string. C strings are '\0' terminated and thus parsing stops when it reaches the '\0' character. To compensate for this you need to use the constructor that takes two parameters a pointer to the char array and a length. You can also use string::push_back() method to append NULLs.

std::wstring sFilter = L"PDF Files";
sFilter.push_back('\0');
sFilter.append(L"*.pdf");
sFilter.push_back('\0');



回答2:


With

string mfilter = "Filter\0*.PDF\0";

you are calling an std::string contructor, which terminates the string at the first \0.

The following code:

string mfilter = "Filter\0*.PDF\0";
cout << "string:" << mfilter << "   len: " << mfilter.length() << endl;

prints

string: Filter   len: 6

The string is only constructed until the first \0 terminator. Do the string is only composed of the word "Filter".




回答3:


string mfilter = "Filter\0*.PDF\0";

This calls a std::basic_string constructor that uses a null-terminated string. It will stop parsing the string literal at "Filter".

Try this one instead:

string mfilter( "Filter\0*.PDF", 13 ); // need double null at end

This calls a std::basic_string constructor that uses "the first count characters of character string pointed to by s. s can contain null characters."

You have to either count the characters yourself, or write wrapper code if you encounter this problem more often.


Related: std::basic_string constructors.


As for your runtime error:

string mf;
mf.append("Filter")
.append('\0')
.append("*.pdf")
.append('\0');

append() does not have an overload for a single character type. You are probably hitting the const CharT* s overload, with a null pointer.

Use either append( 1, '\0' ) or append( "", 1 ), either of which should append a null byte.



来源:https://stackoverflow.com/questions/34201213/c-lpstr-and-string-trouble-with-zero-terminated-strings

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!