Orientos
Orientos

Reputation: 151

How to use SHFileOperation() with CString paths

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;

Upvotes: 0

Views: 621

Answers (3)

Daniel Sęk
Daniel Sęk

Reputation: 2769

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.

Upvotes: 4

sergiol
sergiol

Reputation: 4335

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;

Upvotes: 1

Gioele Frapolli
Gioele Frapolli

Reputation: 11

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.

Upvotes: -3

Related Questions