В многопоточности C# вызывает ошибку функции C ++ DLL - PullRequest
0 голосов
/ 03 августа 2020

Моя C# программа должна вызывать функции C ++, расположенные во внешней DLL.

Я обнаружил, что она отлично работает в однопоточном режиме, но ошибка в многопоточности.

Код C ++:


enum ProgramExitType:int {
    ET_ERROR,
    ET_SUCCEED,
    ET_TLE,
    ET_MLE
};
enum ProgramErrorType :int {
    PE_UNKNOWN,
    PE_INPUT,
    PE_OUTPUT,
    PE_ERRPUT,
    PE_CREATEPROCESS,
    PE_WAIT,

};
struct ProgramExitInfo {
    ProgramExitType type;
    ProgramErrorType err_type;
    long long memory_usage;
    int time;
    unsigned int exit_code;
};

#define ___RR_COMBINE_(x,y) x##y
#define __RR_COMBINE(x,y) ___RR_COMBINE_(x,y)
#define UNIQUESYM __RR_COMBINE(__RRLibrary__unique_symbol_,__COUNTER__)
#define AUTOCLOSE(h) AutoClose UNIQUESYM(h)
#define AUTOCLOSEREF(h) AutoCloseRef UNIQUESYM(h)
class AutoClose {
    HANDLE h;
public:
    AutoClose(HANDLE h) :h(h) {}
    ~AutoClose() { CloseHandle(h);/* LOGW(L"AutoClose\n"); */ }
};
class AutoCloseRef {
    HANDLE* h;
public:
    AutoCloseRef(HANDLE* h) :h(h) {}
    ~AutoCloseRef() { CloseHandle(*h); /* LOGW(L"AutoCloseRef\n");*/ }
};
ProgramExitInfo RunProgram(PCWSTR path, PCWSTR param, PCWSTR input, PCWSTR output, PCWSTR errput, int time_limit, long long memory_limit) {
    //MessageBox(0, L"RunProgram Start", L"", MB_ICONINFORMATION);
    ProgramExitInfo ret;
    ZeroMemory(&ret, sizeof(ProgramExitInfo));
    HANDLE hin = INVALID_HANDLE_VALUE, hout = INVALID_HANDLE_VALUE, herr = INVALID_HANDLE_VALUE;
    AUTOCLOSEREF(&hin); AUTOCLOSEREF(&hout); AUTOCLOSEREF(&herr);
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;

    if (input != NULL && *input != 0) {
        //MessageBox(0, input, L"Load Input", MB_ICONINFORMATION);
        hin = CreateFile(input, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
        if (hin == 0 || hin == INVALID_HANDLE_VALUE) {
            ret.type = ET_ERROR;
            ret.err_type = PE_INPUT;
            return ret;
        }
    }
    if (output != NULL && *output != 0) {
        //MessageBox(0, input, L"Load Output", MB_ICONINFORMATION);
        hout = CreateFile(output, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
        if (hout == 0 || hout == INVALID_HANDLE_VALUE) {
            ret.type = ET_ERROR;
            ret.err_type = PE_OUTPUT;
            return ret;
        }
    }
    if (errput != NULL && *errput != 0) {
        //MessageBox(0, input, L"Load Errput", MB_ICONINFORMATION);
        herr = CreateFile(errput, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
        if (herr == 0 || herr == INVALID_HANDLE_VALUE) {
            ret.type = ET_ERROR;
            ret.err_type = PE_ERRPUT;
            return ret;
        }
    }

    STARTUPINFO si = { sizeof(si) };
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.wShowWindow = SW_HIDE;
    si.hStdOutput = hout;
    si.hStdError = herr;
    si.hStdInput = hin;
    PROCESS_INFORMATION pi = { 0 };
    wchar_t* param_buff = 0;
    if (param != NULL) {
        param_buff = new wchar_t[lstrlenW(param) + 1];
        lstrcpyW(param_buff, param);
    }
    BOOL b = CreateProcess(path, NULL,
        NULL,
        NULL,
        TRUE,
        NULL,
        NULL,
        NULL,
        &si, &pi);
    if (param_buff != 0)
        delete[] param_buff;
    AUTOCLOSE(pi.hProcess); AUTOCLOSE(pi.hThread);
    if (b == FALSE) {
        ret.type = ET_ERROR;
        ret.err_type = PE_CREATEPROCESS;
        return ret;
    }
    int ti = GetTickCount();
    DWORD dw = WaitForSingleObject(pi.hProcess, time_limit);
    ret.time = GetTickCount() - ti;
    if (dw != WAIT_OBJECT_0)
        TerminateProcess(pi.hProcess, -1);
    GetExitCodeProcess(pi.hProcess, (LPDWORD)&ret.exit_code);

    switch (dw) {
    case WAIT_OBJECT_0: {
        break;
    }
    case WAIT_TIMEOUT: {
        ret.type = ET_TLE;
        break;
    }
    default: {
        ret.type = ET_ERROR;
        ret.err_type = PE_WAIT;
        return ret;
    }
    }
    PROCESS_MEMORY_COUNTERS pmc = { 0 };
    GetProcessMemoryInfo(pi.hProcess, &pmc, sizeof(pmc));
    ret.memory_usage = (long long)pmc.PeakWorkingSetSize + pmc.PeakPagefileUsage;
    if (ret.memory_usage > memory_limit) {
        ret.type = ET_MLE;
        return ret;
    }
    ret.type = ET_SUCCEED;
    return ret;
}

C# код:

[DllImport("ProgramRunningHelper.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ProgramExitInfo RunProgram([MarshalAs(UnmanagedType.LPWStr)]string path, [MarshalAs(UnmanagedType.LPWStr)]string param, [MarshalAs(UnmanagedType.LPWStr)]string input, [MarshalAs(UnmanagedType.LPWStr)]string output, [MarshalAs(UnmanagedType.LPWStr)]string errput, int time_limit, long memory_limit);

Я использую RunProgram запускает другой файл .exe с разными перенаправлениями ввода / вывода в разных потоках.

Но функция возвращает PE_INPUT или PE_OUTPUT, что означает, что она не может создать входной / выходной файл.

Я вызвал FindLastError() и получил 32 «Другая программа использует этот файл, и процесс не может получить к нему доступ. "

Но если есть только один поток, никаких ошибок нет.

Я думаю, это может быть из-за того, что параметры, передаваемые разными потоками, перепутаны (например, два потока передают параметры 'a' и 'b' соответственно, но CLR считает, что они оба прошли 'a'.). Возможно ли это?

Как я могу это решить?

Любая помощь будет очень признательна!

...