wsprintf() 使应用程序崩溃但成功

wsprintf() crashes application but is successful

为什么即使操作成功,以下代码 wsprintf() 也会使应用程序崩溃?

#include "Shlwapi.h"
#include <windows.h>
#include <shlobj.h>
#include <iostream>

#pragma comment( lib, "shlwapi.lib")

using namespace std;

int main()
{
    wchar_t buffer[MAX_PATH];
    GetModuleFileName(NULL, buffer, MAX_PATH);

    wchar_t* filepart = PathFindFileName(buffer);
    PathRemoveExtension(filepart);

    PWSTR path = NULL;
    HRESULT hres = SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &path);

    wsprintf(path, TEXT("%s\%s.bz2"), path, filepart);

    wcout << path << endl << endl;

    if (SUCCEEDED(hres))
        CoTaskMemFree(path);

    system("pause");
}

SHGetKnownFolderPath 返回的指针没有额外的 space 供您追加字符。您需要分配自己的缓冲区来写入完整的文件路径。

顺便说一句,对缓冲区执行 sprintf 类型的操作并将该缓冲区作为参数作为源操作数之一传递是未定义行为。而不是使用 wsprintf,您应该使用您 link 的文档页面中提到的替代函数之一。

SHGetKnownFolderPath 仅分配 SHGetKnownFolderPath 所需的内存量。但是在这一行

wsprintf(path, TEXT("%s\%s.bz2"), path, filepart);

您正在扩展字符串长度,访问未分配的内存,导致未定义的行为。如果删除此行,您的应用程序将不会在 CoTaskMemFree.

崩溃

您正在尝试将 wsprintf() 的输出写入内存缓冲区,但该缓冲区不足以容纳输出。您正在使用由 SHGetKnownFolderPath() 分配的缓冲区,该缓冲区大到足以仅容纳它 returns 的路径。您不能按原样附加到该缓冲区,您必须使用 CoTaskMemRealloc() 重新分配它以使其更大,例如:

#include "Shlwapi.h"
#include <windows.h>
#include <shlobj.h>
#include <iostream>

#pragma comment( lib, "shlwapi.lib")

using namespace std;

int main()
{
    wchar_t buffer[MAX_PATH];
    GetModuleFileName(NULL, buffer, MAX_PATH);

    wchar_t* filepart = PathFindFileName(buffer);
    PathRemoveExtension(filepart);

    PWSTR path = NULL;
    HRESULT hres = SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &path);

    if (SUCCEEDED(hres))
    {
        int len = lstrlenW(path);
        PWSTR newpath = (PWSTR) CoTaskMemRealloc(path, len + lstrlenW(filepart) + 6);
        if (newpath)
        {
            path = newpath;
            wsprintfW(path+len, TEXT("\%s.bz2"), filepart);
            wcout << path << endl << endl;
        }
        else
            wcout << L"CoTaskMemRealloc failed" << endl << endl;

        CoTaskMemFree(path);
    }
    else
        wcout << L"SHGetKnownFolderPath failed. Error " << (int)hres << endl << endl;

    system("pause");
}

尽管如此,您应该只分配自己的缓冲区并将各种子字符串复制到其中,例如:

#include "Shlwapi.h"
#include <windows.h>
#include <shlobj.h>
#include <iostream>

#pragma comment( lib, "shlwapi.lib")

using namespace std;

int main()
{
    wchar_t buffer[MAX_PATH];
    GetModuleFileNameW(NULL, buffer, MAX_PATH);

    wchar_t* filepart = PathFindFileNameW(buffer);
    PathRemoveExtensionW(filepart);

    PWSTR path = NULL;
    HRESULT hres = SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &path);

    if (SUCCEEDED(hres))
    {
        wchar_t fullPath = new wchar_t[lstrlenW(path) + lstrlenW(filepart) + 6];
        wsprintfW(fullPath, L"%s\%s.bz2", path, filepart);

        wcout << fullPath << endl << endl;

        delete[] fullPath;
        CoTaskMemFree(path);
    }
    else
        wcout << L"SHGetKnownFolderPath failed. Error " << (int)hres << endl << endl;

    system("pause");
}

或者,您可以去掉 wsprintf() 并将各种子字符串直接打印到 std::wcout,例如:

#include "Shlwapi.h"
#include <windows.h>
#include <shlobj.h>
#include <iostream>

#pragma comment( lib, "shlwapi.lib")

using namespace std;

int main()
{
    wchar_t buffer[MAX_PATH];
    GetModuleFileNameW(NULL, buffer, MAX_PATH);

    wchar_t* filepart = PathFindFileNameW(buffer);
    PathRemoveExtensionW(filepart);

    PWSTR path = NULL;
    HRESULT hres = SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &path);

    if (SUCCEEDED(hres))
    {
        wcout << path << L"\" << filepart << L".bz2" << endl << endl;
        CoTaskMemFree(path);
    }
    else
        wcout << L"SHGetKnownFolderPath failed. Error " << (int)hres << endl << endl;

    system("pause");
}

如果您需要格式化变量,请使用 std::wstring,例如来自 std::wostringstream,例如:

#include "Shlwapi.h"
#include <windows.h>
#include <shlobj.h>
#include <iostream>
#include <string>
#include <sstream>

#pragma comment( lib, "shlwapi.lib")

using namespace std;

int main()
{
    wchar_t buffer[MAX_PATH];
    GetModuleFileNameW(NULL, buffer, MAX_PATH);

    wchar_t* filepart = PathFindFileNameW(buffer);
    PathRemoveExtensionW(filepart);

    PWSTR path = NULL;
    HRESULT hres = SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &path);

    if (SUCCEEDED(hres))
    {
        wostringstream oss;

        oss << path << L"\" << filepart << L".bz2";
        CoTaskMemFree(path);
        
        wstring fullPath = oss.str();
        wcout << fullPath << endl << endl;
    }

    system("pause");
}

或者:

#include "Shlwapi.h"
#include <windows.h>
#include <shlobj.h>
#include <iostream>
#include <string>

#pragma comment( lib, "shlwapi.lib")

using namespace std;

int main()
{
    wchar_t buffer[MAX_PATH];
    GetModuleFileNameW(NULL, buffer, MAX_PATH);

    wchar_t* filepart = PathFindFileNameW(buffer);
    PathRemoveExtensionW(filepart);

    PWSTR path = NULL;
    HRESULT hres = SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &path);

    if (SUCCEEDED(hres))
    {
        wstring fullPath;
        fullPath.reserve(lstrlenW(path) + lstrlenW(filepart) + 5);

        fullPath += path;
        fullPath += L"\";
        fullPath += filepart;
        fullPath += L".bz2";

        CoTaskMemFree(path);
        
        wcout << fullPath << endl << endl;
    }

    system("pause");
}