相同的C++代码有时也能工作,有时也不行。 我的C++代码工作了几次,执行很少,它突然停止工作,抛出异常,没有任何改变!我不知道为什么

相同的C++代码有时也能工作,有时也不行。 我的C++代码工作了几次,执行很少,它突然停止工作,抛出异常,没有任何改变!我不知道为什么,c++,winapi,C++,Winapi,这是代码中有问题的部分: STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); TCHAR *path; SHGetKnownFolderPath(FOLDERID_Startup, KF_FLAG_CREATE, NULL, &path); lstrcat(path, L"\\calc.ex

这是代码中有问题的部分:

STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
TCHAR *path;
SHGetKnownFolderPath(FOLDERID_Startup, KF_FLAG_CREATE, NULL, &path);
lstrcat(path, L"\\calc.exe");
if (CreateProcess(NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

}
执行几次之后,CreateProcess行上会抛出两个异常,第一个异常:

Unhandled exception at 0x779D8829 (ntdll.dll) in PS_Down.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77A15890).
第二点:

Exception thrown at 0x77946111 (ntdll.dll) in PS_Down.exe: 0xC0000005: Access violation reading location 0x00000069. 
这发生在我的一些其他项目中,这些项目不包括CreateProcess func,iv'e注意到,当涉及TCHAR和SHGetKnownFolderPath时,总是会发生这种情况。 如果您能帮助您了解如何解决此问题,我们将不胜感激,提前感谢

p.S-我不熟悉cpp编码,因此请尝试相应地解释

lstrcatpath,L\\calc.exe;将导致缓冲区溢出。path是指向数组的指针,该数组只能包含文件夹路径,仅此而已。您需要分配一个宽字符串,附加文件夹路径,然后是文件路径。此外,您还需要检查SHGetKnownFolderPath的结果,以确定该路径是否包含有效指针,然后通过调用CoTaskMemFree释放它。

lstrcatpath,L\\calc.exe;将导致缓冲区溢出。path是指向数组的指针,该数组只能包含文件夹路径,仅此而已。您需要分配一个宽字符串,附加文件夹路径,然后是文件路径。您还需要检查SHGetKnownFolderPath的结果,以确定该路径是否包含有效指针,然后通过调用CoTaskMemFree释放它。

SHGetKnownFolderPath为路径分配了固定长度,因此您无法将可执行文件直接连接到该路径。首先需要使用CoTaskMemRealloc来扩展空间。您还需要在使用完内存后释放内存。类似地,您需要关闭CreateProcess创建的句柄

因此,您可以创建支持类来自动处理资源:

#include "pch.h"
#include <iostream>

#include <windows.h>
#include <Shlobj.h>
#include <wchar.h>

// A wide string exception class. perhaps something like this already exist in VS?
class werror {
    std::wstring text;
public:
    werror(const wchar_t* Text) : text(Text) {}
    const wchar_t* what() const { return text.c_str(); }
};

class ConCatToKnownFolderPath {
    PWSTR m_path;
public:
    ConCatToKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, const WCHAR* AddToPath = nullptr) :
        m_path(nullptr)
    {
        if (SHGetKnownFolderPath(rfid, dwFlags, hToken, &m_path) != S_OK)
            throw werror(L"SHGetKnownFolderPath failed");

        if (AddToPath) {
            size_t newlen = wcslen(m_path) + wcslen(AddToPath) + sizeof(WCHAR); // place for \0
            size_t newbufsize = newlen * sizeof(WCHAR);

            auto newPtr = CoTaskMemRealloc(m_path, newbufsize);
            if (!newPtr) {
                CoTaskMemFree(m_path);
                throw werror(L"CoTaskMemRealloc failed");
            }
            m_path = reinterpret_cast<PWSTR>(newPtr);
            wcscat_s(m_path, newlen, AddToPath);
        }
    }
    // move works fine
    ConCatToKnownFolderPath(ConCatToKnownFolderPath&& other) noexcept :
        m_path(other.m_path)
    {
        other.m_path = nullptr;
    }
    ConCatToKnownFolderPath& operator=(ConCatToKnownFolderPath&& other) noexcept {
        if (m_path) CoTaskMemFree(m_path);
        m_path = other.m_path;
        other.m_path = nullptr;
        return *this;
    }
    // copy not supported (but could easily be added
    ConCatToKnownFolderPath(const ConCatToKnownFolderPath&) = delete;
    ConCatToKnownFolderPath& operator=(const ConCatToKnownFolderPath&) = delete;

    // automatic free when it goes out of scope
    ~ConCatToKnownFolderPath() {
        if (m_path) CoTaskMemFree(m_path);
    }

    PWSTR data() const { return m_path; }
    operator LPCWSTR () const { return m_path; }
};

struct WProcessWithInfo : PROCESS_INFORMATION {
    WProcessWithInfo(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPCWSTR lpCurrentDirectory) {
        STARTUPINFOW si;
        ZeroMemory(&si, sizeof(STARTUPINFOW));
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_SHOWNORMAL;

        if (!CreateProcessW(lpApplicationName, lpCommandLine, NULL, NULL, FALSE, 0, NULL, lpCurrentDirectory, &si, *this))
            throw werror(L"CreateProcessWCreateProcessW failed");
        CloseHandle(hThread);
    }
    WProcessWithInfo(const WProcessWithInfo&) = delete;
    WProcessWithInfo(WProcessWithInfo&&) = delete;
    WProcessWithInfo& operator=(const WProcessWithInfo&) = delete;
    WProcessWithInfo& operator=(WProcessWithInfo&&) = delete;
    ~WProcessWithInfo() {
        CloseHandle(hProcess);
    }
    DWORD Wait(DWORD  dwMilliseconds=INFINITE) {
        return WaitForSingleObject(*this, dwMilliseconds);
    }

    operator HANDLE () { return hProcess; }
    operator LPPROCESS_INFORMATION () { return this; }
};


int main() {
    try {
        ConCatToKnownFolderPath path(FOLDERID_System, KF_FLAG_CREATE, NULL, L"\\calc.exe");
        std::wcout << L"Starting " << path.data() << L"\n";
        WProcessWithInfo proc(path, NULL, NULL);
        std::wcout << L"Process started\n";
        proc.Wait();
        std::wcout << L"Process done\n";
    }
    catch (const werror& ex) {
        std::wcerr << L"Exception: " << ex.what() << L"\n";
    }
    return 0;
}
SHGetKnownFolderPath为路径分配了固定长度,因此您无法将可执行文件直接连接到该路径。首先需要使用CoTaskMemRealloc来扩展空间。您还需要在使用完内存后释放内存。类似地,您需要关闭CreateProcess创建的句柄

因此,您可以创建支持类来自动处理资源:

#include "pch.h"
#include <iostream>

#include <windows.h>
#include <Shlobj.h>
#include <wchar.h>

// A wide string exception class. perhaps something like this already exist in VS?
class werror {
    std::wstring text;
public:
    werror(const wchar_t* Text) : text(Text) {}
    const wchar_t* what() const { return text.c_str(); }
};

class ConCatToKnownFolderPath {
    PWSTR m_path;
public:
    ConCatToKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, const WCHAR* AddToPath = nullptr) :
        m_path(nullptr)
    {
        if (SHGetKnownFolderPath(rfid, dwFlags, hToken, &m_path) != S_OK)
            throw werror(L"SHGetKnownFolderPath failed");

        if (AddToPath) {
            size_t newlen = wcslen(m_path) + wcslen(AddToPath) + sizeof(WCHAR); // place for \0
            size_t newbufsize = newlen * sizeof(WCHAR);

            auto newPtr = CoTaskMemRealloc(m_path, newbufsize);
            if (!newPtr) {
                CoTaskMemFree(m_path);
                throw werror(L"CoTaskMemRealloc failed");
            }
            m_path = reinterpret_cast<PWSTR>(newPtr);
            wcscat_s(m_path, newlen, AddToPath);
        }
    }
    // move works fine
    ConCatToKnownFolderPath(ConCatToKnownFolderPath&& other) noexcept :
        m_path(other.m_path)
    {
        other.m_path = nullptr;
    }
    ConCatToKnownFolderPath& operator=(ConCatToKnownFolderPath&& other) noexcept {
        if (m_path) CoTaskMemFree(m_path);
        m_path = other.m_path;
        other.m_path = nullptr;
        return *this;
    }
    // copy not supported (but could easily be added
    ConCatToKnownFolderPath(const ConCatToKnownFolderPath&) = delete;
    ConCatToKnownFolderPath& operator=(const ConCatToKnownFolderPath&) = delete;

    // automatic free when it goes out of scope
    ~ConCatToKnownFolderPath() {
        if (m_path) CoTaskMemFree(m_path);
    }

    PWSTR data() const { return m_path; }
    operator LPCWSTR () const { return m_path; }
};

struct WProcessWithInfo : PROCESS_INFORMATION {
    WProcessWithInfo(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPCWSTR lpCurrentDirectory) {
        STARTUPINFOW si;
        ZeroMemory(&si, sizeof(STARTUPINFOW));
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_SHOWNORMAL;

        if (!CreateProcessW(lpApplicationName, lpCommandLine, NULL, NULL, FALSE, 0, NULL, lpCurrentDirectory, &si, *this))
            throw werror(L"CreateProcessWCreateProcessW failed");
        CloseHandle(hThread);
    }
    WProcessWithInfo(const WProcessWithInfo&) = delete;
    WProcessWithInfo(WProcessWithInfo&&) = delete;
    WProcessWithInfo& operator=(const WProcessWithInfo&) = delete;
    WProcessWithInfo& operator=(WProcessWithInfo&&) = delete;
    ~WProcessWithInfo() {
        CloseHandle(hProcess);
    }
    DWORD Wait(DWORD  dwMilliseconds=INFINITE) {
        return WaitForSingleObject(*this, dwMilliseconds);
    }

    operator HANDLE () { return hProcess; }
    operator LPPROCESS_INFORMATION () { return this; }
};


int main() {
    try {
        ConCatToKnownFolderPath path(FOLDERID_System, KF_FLAG_CREATE, NULL, L"\\calc.exe");
        std::wcout << L"Starting " << path.data() << L"\n";
        WProcessWithInfo proc(path, NULL, NULL);
        std::wcout << L"Process started\n";
        proc.Wait();
        std::wcout << L"Process done\n";
    }
    catch (const werror& ex) {
        std::wcerr << L"Exception: " << ex.what() << L"\n";
    }
    return 0;
}

这是一个未定义行为的教科书案例。因为问题可能出在其他地方,所以要做出决定。您还需要检查当前未检查的地方是否存在错误。e、 如何知道路径有效?SHGetKnownFolderPath如何处理&path参数?传入空ptr时是否保留内存;如果参数不为NULL,它会使用它作为缓冲区吗?这是一个未定义行为的教科书案例。因为问题可能出在其他地方,所以要做出决定。您还需要检查当前未检查的地方是否存在错误。e、 如何知道路径有效?SHGetKnownFolderPath如何处理&path参数?传入空ptr时是否保留内存;如果参数不为NULL,它会使用它作为缓冲区吗?谢谢你的回答,但是你介意在我使用完它后,再详细说明一下如何生成完整字符串和释放路径的内存吗?@Apex类似::std::wstring full_path{};full_path.appendpath;完整路径append\\calc.exe;和::CoTaskMemFreepath@apexOr更简单:std::wstring full_path{path};完整路径。append\\calc.exe;::CoTaskMemFreepath@VTT当我这样做并在CreateProcess func中将path更改为full_path时,我得到一个错误:不存在从'std::wstring'到'LPWSTR'的适当转换,如何在func中包含full_path?@Jabberwocky谢谢,你知道如何解决我在执行此操作后遇到的问题吗?谢谢你的回答,但是,当我处理完完整字符串和空闲路径时,您是否介意详细说明一下如何生成完整字符串和空闲路径的内存?@Apex类似::std::wstring full_path{};full_path.appendpath;完整路径append\\calc.exe;和::CoTaskMemFreepath@apexOr更简单:std::wstring full_path{path};完整路径。append\\calc.exe;::CoTaskMemFreepath@VTT当我这样做并在CreateProcess func中将path更改为full_path时,我得到一个错误:不存在从'std::wstring'到'LPWSTR'的适当转换,我如何在func中包含full_path?@Jabberwocky谢谢,你知道如何解决我执行此操作后遇到的问题吗?现在它正在工作。我有一只愚蠢的虫子。请注意,我已经将路径作为CreateProcessW的第一个参数,以避免必须围绕程序开始…是的,计算器对我来说启动很好。我改为FOLDERID_系统,但您最初使用FOLDERID_启动。你们试过改变它吗?是的,我注意到并改变了它,但计算器仍然不能为我启动。然而,我注意到,如果我改为使用CLI exe,它在主控制台OK下工作正常,它会写异常吗?我已经更新了上面的代码以打印出异常,这样你就可以看到它失败的地方。是的,它与notepad.exe一起工作很好,那么calc.exe是问题所在吗?现在它工作了。我得了一个sil
小虫子。请注意,我已经将路径作为CreateProcessW的第一个参数,以避免必须围绕程序开始…是的,计算器对我来说启动很好。我改为FOLDERID_系统,但您最初使用FOLDERID_启动。你们试过改变它吗?是的,我注意到并改变了它,但计算器仍然不能为我启动。然而,我注意到,如果我改为使用CLI exe,它在主控制台OK下工作正常,它会写异常吗?我已经更新了上面的代码以打印出异常,这样您就可以看到它失败的地方。是的,它与notepad.exe一起工作很好,那么calc.exe是问题所在吗?