【问题标题】:How to use SHFileOperation() with CString paths如何将 SHFileOperation() 与 CString 路径一起使用
【发布时间】:2018-09-21 09:48:06
【问题描述】:

我正在尝试将 CString 转换为 LPCWSTR 并且效果很好。但是在处理代码时出了点问题。

我想将一个目录复制到另一个路径,所以我使用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);

当我直接使用CString 时,复制操作不起作用,但是当我使用_TEXT() 宏(如在cmets 中)分配结构中的LPCWSTR 成员时,一切正常。

编辑 1

在源路径和目标路径的两种变体中,代码都会编译。

在这个变体中,代码编译并执行复制操作:

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");

在我实际需要的另一个变体中,代码也可以编译,但不会发生复制操作:

 s.pTo   = cstrTempDest;
 s.pFrom = cstrTemp;

【问题讨论】:

  • 这里可以正常工作,但你可以试试 cstrTempDest.GetBuffer(0)
  • @ServeLaurijssen 建议复制操作不起作用。在这种情况下,只有创建目标目录(如果它不存在)有效。这意味着,该结构可以读取目标路径“s.pTo”,但由于某种原因无法执行复制操作!
  • 这个项目是 UNICODE 还是 ANSI?您的 CString 是 wchar_t 变体吗?如果你有一个非 UNICODE 项目,你需要在这里MultiByteToWideChar
  • @ServeLaurijssen GetBuffer(0) 是错误的方法,因为它不能保证字符串以零结尾。请改用Data()
  • 你为什么首先使用 ANSI 文本?从修复它开始。

标签: c++ string winapi mfc null-terminated


【解决方案1】:

SHFILEOPSTRUCT 期望字符串以 两个 NUL 字符结尾,但根据定义,NUL 终止的字符串以 one 结尾,并且任何其他 NUL 字符都会被 CString 方法忽略不要采用显式长度参数。

您可以通过手动添加一个来强制双 NUL:

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 );

替代解决方案可以使用具有显式长度参数的方法:

CString cstrTempDest = firstPathDest + lastPartDest
                           + CString(_folderNameDest, _tcslen(_folderNameDest)+1);

 
如果您的项目配置为使用 unicode 字符集,请使用宽字符串调用 CString 构造函数:

CString _folderName(_T("a6_töüst-Oa5Z.OZS-CI5O5235")),
        firstPath(_T("C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\"))
        ...

CString 在 unicode 模式下会自动将窄字符串转换为宽字符串,但当运行时和开发代码页之间存在差异时,它可能会失败。如果您打算使用 Unicode 并且永不回头,请丢弃 _TEXTTEXT_T 宏,而只使用宽字面量:

CString _folderName( L"a6_töüst-Oa5Z.OZS-CI5O5235" ),
        firstPath( L"C:\\ORTIM-Daten\\a5Pc 2.0.3\\Temp\\" )
        ...

 
您还应该检查SHFileOperation 返回值。

【讨论】:

  • 谢谢,这是正确的解决方案和建设性的贡献
  • CString 可以很好地处理嵌入的空值,如您的示例所示。真正的问题是当将"a6_töüst-Oa5Z.OZS-CI5O5235\0" 按原样传递给_folderNameDest 时,它被视为以空值结尾的字符串,并在初始化_folderNameDest 时忽略了终止符。您可以通过传递字符串长度的第二个参数来避免包含嵌入式终止符,例如:LPCTSTR szFolderNameDest = TEXT("a6_töüst-Oa5Z.OZS-CI5O5235\0"); ... , _folderNameDest(szFolderNameDest, _tcslen(szFolderNameDest)+1); 然后您不需要将额外的空终止符作为单独的操作附加。
  • @RemyLebeau 我已将您的建议添加到回答中。
  • 这并没有解决使用 ANSI 文本的问题
  • @DavidHeffernan 建议一致使用宽字符串的扩展答案。
【解决方案2】:

creating double null ended CString 上用户 msp0815 的回答解决了您的问题。

// 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;

【讨论】:

  • 哇,我刚刚了解到有一个dedicated typedef 用于“双空终止”字符串。对于 UNICODE,它被声明为 typedef __nullnullterminated CONST WCHAR *PCZZWSTR;__nullnullterminatedSAL annotation 所以编译器可以做一些检查。
【解决方案3】:

我通常不怎么使用 LPCWSTR,但这是我的想法:

CString TestCSTR = "Hello world";
LPCWSTR TestLPC;

TestLPC = (LPCWSTR)_TEXT(TestCSTR.GetString());

它按预期工作,实际上变量 TestLPC 持有“Hello world”,或者更准确地说是一个指向它的长指针。应该可以删除 _TEXT 而不会产生任何后果,但我不确定,结果是一样的。

【讨论】:

  • 如果定义了_UNICODE,这将无法编译,如果没有定义,则会表现出未定义的行为。
  • 我很好奇,为什么它会产生未定义的行为?是不是因为它转换成一个可能导致无效的指针?
  • 这是因为您可能将char* 转换为wchar_t*
猜你喜欢
  • 2012-07-24
  • 1970-01-01
  • 1970-01-01
  • 2020-10-16
  • 2011-08-07
  • 1970-01-01
  • 1970-01-01
  • 2011-07-19
  • 1970-01-01
相关资源
最近更新 更多