Один и тот же код C ++ иногда работает, а иногда нет - PullRequest
0 голосов
/ 06 декабря 2018

мой код на 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 ()), и я заметил, что это всегда происходит, когда задействованы TCHAR и SHGetKnownFolderPath ().Любая помощь с пониманием того, как решить проблему, будет высоко оценена, спасибо заранее!

PS - Я новичок в кодировании в cpp, поэтому, пожалуйста, попробуйте объяснить соответственно

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

path выделяется с фиксированной длиной 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;
}
0 голосов
/ 06 декабря 2018

lstrcat(path, L"\\calc.exe"); вызовет переполнение буфера.path - указатель на массив, который может содержать только путь к папке, и ничего более.Вам нужно будет выделить широкую строку, добавить путь к папке, а затем путь к файлу.Также вам необходимо проверить результат SHGetKnownFolderPath, чтобы определить, содержит ли path правильный указатель, и освободить его позже, вызвав CoTaskMemFree.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...