相同的C ++代码有时有效,有时无效

时间:2018-12-06 10:37:37

标签: c++ winapi

我的c ++代码可以连续工作几次,执行几次后,它突然停止工作并抛出异常(没有任何更改!),我不知道为什么。

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

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()行上抛出2个异常,第一个:

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()函数)中发生了这种情况,并且iv'e注意到,总是在涉及TCHAR和SHGetKnownFolderPath()时发生。 非常感谢您对如何解决此问题的任何帮助,在此先感谢!

P.S-我是cpp编码的新手,请尝试相应地解释

2 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:1)

pathSHGetKnownFolderPath分配了固定的长度,因此您不能直接将可执行文件连接到它。您需要先使用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;
}