Моя 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'.). Возможно ли это?
Как я могу это решить?
Любая помощь будет очень признательна!