I need to format a string to be double null-terminated string in order to use SHFileOperation.
Interesting part is i found one of the following working, but not both:
// Example 1
CString szDir(_T("D:\\Test"));
szDir = szDir + _T('\0') + _T('\0');
// Example 2
CString szDir(_T("D:\\Test"));
szDir = szDir + _T("\0\0");
//Delete folder
SHFILEOPSTRUCT fileop;
fileop.hwnd = NULL; // no status display
fileop.wFunc = FO_DELETE; // delete operation
fileop.pFrom = szDir; // source file name as double null terminated string
fileop.pTo = NULL; // no destination needed
fileop.fFlags = FOF_NOCONFIRMATION|FOF_SILENT; // do not prompt the user
fileop.fAnyOperationsAborted = FALSE;
fileop.lpszProgressTitle = NULL;
fileop.hNameMappings = NULL;
int ret = SHFileOperation(&fileop);
Does anyone has idea on this?
Is there other way to append double-terminated string?
The CString class itself has no problem with a string containing a null character. The problem comes with putting null characters into the string in the first place. The first example works because it is appending a single character, not a string - it accepts the character as is without checking to see if it's null. The second example tries appending a typical C string, which by definition ends at the first null character - you're effectively appending an empty string.
You cannot use CString for this purpose. You will need to use your own char[] buffer:
char buf[100]; // or large enough
strcpy(buf, "string to use");
memcpy(buf + strlen(buf), "\0\0", 2);
Although you could do this by only copying one more NUL byte after the existing NUL terminator, I would prefer to copy two so that the source code more accurately reflects the intent of the programmer.
Related
I am trying to convert CString to LPCWSTR and it works well. But something went wrong in the processing of the code.
I want to copy a directory to another path so I am using SHFILEOPSTRUCT:
HWND console = GetConsoleWindow();
SHFILEOPSTRUCT s = { 0 };
s.hwnd = console;
s.wFunc = FO_COPY;
s.fFlags = FOF_SILENT;
CString _folderName("a6_töüst-Oa5Z.OZS-CI5O5235"),
firstPath("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\"),
lastPart("\\Documents\\*\0"),
firstPathDest("C:\\ORTIM-Daten\\a5Pc 2.0.3\\"),
lastPartDest("Documents\\"),
_folderNameDest("a6_töüst-Oa5Z.OZS-CI5O5235\0");
CString cstrTemp = firstPath + _folderName + lastPart,
cstrTempDest = firstPathDest + lastPartDest + _folderNameDest;
s.pTo = cstrTempDest /*_TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Documents\\a6_töüst-Oa5Z.OZS-CI5O5235\0")*/;
s.pFrom = cstrTemp /*_TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\a6_töüst-Oa5Z.OZS-CI5O5235\\Documents\\*\0")*/;
SHFileOperation(&s);
When I am using CString directly, the copy operation doesn't work, but when I use the _TEXT() macro (as in the comments) to assign the LPCWSTR members in the struct everything works.
EDIT 1
In both variants of source and destination paths the code compiles.
In this variant, the code compiles and does the copy operation:
s.pTo = _TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Documents\\a6_töüst-Oa5Z.OZS-CI5O5235\0");
s.pFrom = _TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\a6_töüst-Oa5Z.OZS-CI5O5235\\Documents\\*\0");
In the other variant, which I actually need, the code compiles too, but the copy operation doesn't take place:
s.pTo = cstrTempDest;
s.pFrom = cstrTemp;
SHFILEOPSTRUCT expects strings ending with two NUL characters, but NUL terminated strings by definition end with one and any additional NUL characters are ignored by CString methods that don't take explicit length argument.
You can force double NUL by adding one manually:
CString cstrTempDest = firstPathDest + lastPartDest + _folderNameDest;
// *** Add NUL manually ***
cstrTempDest.AppendChar( 0 );
s.pTo = cstrTempDest;
// For debuging - verify resulting string with example.
TCHAR* test = _TEXT("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Documents\\a6_töüst-Oa5Z.OZS-CI5O5235\0");
// +2 because we want to check two NULs at end.
ASSERT( memcmp( s.pTo, test, (_tcslen(test)+2)*sizeof(TCHAR) ) == 0 );
Alternative solution can use methods with explicit length argument:
CString cstrTempDest = firstPathDest + lastPartDest
+ CString(_folderNameDest, _tcslen(_folderNameDest)+1);
If your project is configured to use unicode character set, call CString constructors with wide strings:
CString _folderName(_T("a6_töüst-Oa5Z.OZS-CI5O5235")),
firstPath(_T("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\"))
...
CString in unicode mode automatically converts narrow strings to wide ones, but it can fail when threre is discrepancy between runtime and development codepages. If you plan to go Unicode and never look back, throw away _TEXT, TEXT and _T macros and just use wide literals:
CString _folderName( L"a6_töüst-Oa5Z.OZS-CI5O5235" ),
firstPath( L"C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\" )
...
You should also check SHFileOperation return value.
The answer of user msp0815 on creating double null ended CString solves your issue.
// strings must be double-null terminated
CString from(cstrTemp + (TCHAR)'\0');
PCZZTSTR szzFrom= from;
s.pFrom= szzFrom;
CString dest(cstrTempDest + (TCHAR)'\0');
PCZZTSTR szzDest= dest;
s.pTo= szzDest;
I generally don't use LPCWSTR that much but here is my idea:
CString TestCSTR = "Hello world";
LPCWSTR TestLPC;
TestLPC = (LPCWSTR)_TEXT(TestCSTR.GetString());
It works as expected in fact the variable TestLPC holds "Hello world" or to be more precise a long pointer to it. It should be possible to remove _TEXT without consequences but I'm not sure, the result is the same btw.
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();
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".
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');
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.
I'm trying to understand why a segmentation fault (SIGSEGV) occurs during the execution of this piece of code. This error occurs when testing the condition specified in the while instruction, but it does not occur at the first iteration, but at the second iteration.
LPTSTR arrayStr[STR_COUNT];
LPTSTR inputStr;
LPTSTR str;
// calls a function from external library
// in order to set the inputStr string
set_input_str(param1, (char*)&inputStr, param3);
str = inputStr;
while( *str != '\0' )
{
if( debug )
printf("String[%d]: %s\n", i, (char*)str);
arrayStr[i] = str;
str = str + strlen((char*)str) + 1;
i++;
}
After reading this answer, I have done some research on the internet and found this article, so I tried to modify the above code, using this piece of code read in this article (see below). However, this change did not solve the problem.
for (LPTSTR pszz = pszzStart; *pszz; pszz += lstrlen(pszz) + 1) {
... do something with pszz ...
}
As assumed in this answer, it seems that the code expects double null terminated arrays of string. Therefore, I wonder how I could check the contents of the inputStr string, in order to check if it actually contains only one null terminator char.
NOTE: the number of characters in the string printed from printf instruction is twice the value returned by the lstrlen(str) function call at the first iteration.
OK, now that you've included the rest of the code it is clear that it is indeed meant to parse a set of consecutive strings. The problem is that you're mixing narrow and wide string types. All you need to do to fix it is change the variable definitions (and remove the casts):
char *arrayStr[STR_COUNT];
char *inputStr;
char *str;
// calls a function from external library
// in order to set the inputStr string
set_input_str(param1, &inputStr, param3);
str = inputStr;
while( *str != '\0' )
{
if( debug )
printf("String[%d]: %s\n", i, str);
arrayStr[i] = str;
str = str + strlen(str) + 1;
i++;
}
Specifically, the issue was occurring on this line:
while( *str != '\0' )
since you hadn't cast str to char * the comparison was looking for a wide nul rather than a narrow nul.
str = str + strlen(str) + 1;
You go out of bounds, change to
str = str + 1;
or simply:
str++;
Of course you are inconsistently using TSTR and strlen, the latter assuming TCHAR = char
In any case, strlen returns the length of the string, which is the number of characters it contains not including the nul character.
Your arithmetic is out by one but you know you have to add one to the length of the string when you allocate the buffer.
Here however you are starting at position 0 and adding the length which means you are at position len which is the length of the string. Now the string runs from offset 0 to offset len - 1 and offset len holds the null character. Offset len + 1 is out of bounds.
Sometimes you might get away with reading it, if there is extra padding, but it is undefined behaviour and here you got a segfault.
This looks to me like code that expects double null terminated arrays of strings. I suspect that you are passing a single null terminated string.
So you are using something like this:
const char* inputStr = "blah";
but the code expects two null terminators. Such as:
const char* inputStr = "blah\0";
or perhaps an input value with multiple strings:
const char* inputStr = "foo\0bar\0";
Note that these final two strings are indeed double null terminated. Although only one null terminator is written explicitly at the end of the string, the compiler adds another one implicitly.
Your question edit throws a new spanner in the works? The cast in
strlen((char*)str)
is massively dubious. If you need to cast then the cast must be wrong. One wonders what LPTSTR expands to for you. Presumably it expands to wchar_t* since you added that cast to make the code compile. And if so, then the cast does no good. You are lying to the compiler (str is not char*) and lying to the compiler never ends well.
The reason for the segmentation fault is already given by Alter's answer. However, I'd like to add that the usual style of parsing a C-style string is more elegant and less verbose
while (char ch = *str++)
{
// other instructions
// ...
}
The scope of ch is only within in the body of the loop.
Aside: Either tag the question as C or C++ but not both, they're different languages.
We have StringCchCat function in Win32Api,
Concatenates one string to another string. The size of the destination
buffer is provided to the function to ensure that StringCchCat does
not write past the end of this buffer.
Ok so ..
StringCchCat(dirWPath, MAX_PATH, TEXT("\\*"));
I ll get : dirWPath + "\\*"
I look for get a :"\\*" + dirWPath
Anyone have a solution ?
std::string first = dirWPath, second = "\\*";
std::string result = second + first;
You can just pass the wildcard first:
char str1[MAX_PATH] = "\\*";
StringCchCat(str1, MAX_PATH, dirWPath);
I am using C Style strings for a project, and I am confusing myself a bit. I am checking strings to see what they are prepended with (zone_, player_, etc) then getting the rest of the string after that.
else if(strncmp(info, "zone_", 5) == 0)
{
int len = strlen(info);
char *zoneName = new char[len];
strncpy(zoneName, &info[5], len-5);
Msg("Zone Selected: %s\n", zoneName);
delete zoneName;
}
When I print out the zoneName variable though, it is correct except it is followed by a bunch of gibberish. What am I doing wrong? (I realize that the gibberish is the rest of the char array being empty, but I don't know a better way to do this)
See strncpy description :
No null-character is implicitly
appended to the end of destination, so
destination will only be
null-terminated if the length of the C
string in source is less than num.
You have to remember that C-style strings are terminated with a NUL character. You've allocated enough space in zoneName, but you only need len-5 plus one:
char *zoneName = new char[len - 5 + 1];
Then, you can actually use strcpy() to copy the tail of the string:
strcpy(zoneName, &info[5]);
You don't need to specify the length because the source string is NUL terminated.
C strings are zero terminated - so they occupy len bytes (chars to be precise) plus one more with value zero known as the 'zero terminator'. You need to allocate one more character, and either copy one more from the source (since it should be zero terminated) or just set the last char of the destination to 0.
int len = strlen(info);
char *zoneName = new char[len - 5 + 1];
strncpy(zoneName, &info[5], len - 5 + 1);
C-style strings has to be finished with a byte with zero value. You should modify your code like this:
char *zoneName = new char[len-5+1];
strncpy(zoneName, &info[5], len-5);
/* correct string ending */
zoneName[len]=0;
/* Now, it's safe to print */
Msg("Zone Selected: %s\n", zoneName);