2021-02-25 10:14:22 -08:00
|
|
|
#define INITKNOWNFOLDERS
|
|
|
|
#include <guiddef.h>
|
|
|
|
#include <KnownFolders.h>
|
|
|
|
#undef INITKNOWNFOLDERS
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
#pragma warning(disable:4065)
|
|
|
|
#pragma warning(disable:4996)
|
|
|
|
|
|
|
|
#include "Common.h"
|
|
|
|
|
|
|
|
#ifndef BF_NO_BFAPP
|
|
|
|
#include "BFApp.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <mmsystem.h>
|
|
|
|
#include <shellapi.h>
|
|
|
|
#include <Objbase.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <ntsecapi.h> // UNICODE_STRING
|
|
|
|
#include <psapi.h>
|
|
|
|
#include <shlobj.h>
|
|
|
|
#include "../PlatformInterface.h"
|
|
|
|
#include "../PlatformHelper.h"
|
|
|
|
#include "CrashCatcher.h"
|
|
|
|
#include "../util/CritSect.h"
|
|
|
|
#include "../util/Dictionary.h"
|
|
|
|
#include "../util/HashSet.h"
|
|
|
|
|
|
|
|
#include "util/AllocDebug.h"
|
|
|
|
|
|
|
|
#pragma comment(lib, "ole32.lib")
|
|
|
|
#pragma comment(lib, "shell32.lib")
|
|
|
|
#pragma comment(lib, "user32.lib")
|
|
|
|
#pragma comment(lib, "gdi32.lib")
|
|
|
|
#pragma comment(lib, "rpcrt4.lib")
|
|
|
|
|
|
|
|
#define NOT_IMPL BF_FATAL("Not implemented")
|
|
|
|
|
|
|
|
USING_NS_BF;
|
|
|
|
|
|
|
|
static bool gTimerInitialized = false;
|
|
|
|
static int gTimerDivisor = 0;
|
|
|
|
CritSect gBfpCritSect;
|
|
|
|
|
|
|
|
struct WindowsSharedInfo
|
|
|
|
{
|
|
|
|
int mThreadAcc;
|
|
|
|
uint32 mTickStart;
|
|
|
|
};
|
|
|
|
|
|
|
|
static WindowsSharedInfo* gGlobalPlatformInfo = NULL;
|
|
|
|
static HANDLE gGlobalMutex = 0;
|
|
|
|
|
|
|
|
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
|
|
|
|
typedef LONG KPRIORITY;
|
|
|
|
|
|
|
|
enum SYSTEM_INFORMATION_CLASS
|
|
|
|
{
|
|
|
|
SystemProcessInformation = 5
|
|
|
|
}; // SYSTEM_INFORMATION_CLASS
|
|
|
|
|
|
|
|
struct CLIENT_ID
|
|
|
|
{
|
|
|
|
HANDLE UniqueProcess;
|
|
|
|
HANDLE UniqueThread;
|
|
|
|
}; // struct CLIENT_ID
|
|
|
|
|
|
|
|
enum THREAD_STATE
|
|
|
|
{
|
|
|
|
StateInitialized,
|
|
|
|
StateReady,
|
|
|
|
StateRunning,
|
|
|
|
StateStandby,
|
|
|
|
StateTerminated,
|
|
|
|
StateWait,
|
|
|
|
StateTransition,
|
|
|
|
StateUnknown
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VM_COUNTERS
|
|
|
|
{
|
|
|
|
SIZE_T PeakVirtualSize;
|
|
|
|
SIZE_T VirtualSize;
|
|
|
|
ULONG PageFaultCount;
|
|
|
|
SIZE_T PeakWorkingSetSize;
|
|
|
|
SIZE_T WorkingSetSize;
|
|
|
|
SIZE_T QuotaPeakPagedPoolUsage;
|
|
|
|
SIZE_T QuotaPagedPoolUsage;
|
|
|
|
SIZE_T QuotaPeakNonPagedPoolUsage;
|
|
|
|
SIZE_T QuotaNonPagedPoolUsage;
|
|
|
|
SIZE_T PagefileUsage;
|
|
|
|
SIZE_T PeakPagefileUsage;
|
|
|
|
SIZE_T PrivatePageCount;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SYSTEM_THREAD {
|
|
|
|
LARGE_INTEGER KernelTime;
|
|
|
|
LARGE_INTEGER UserTime;
|
|
|
|
LARGE_INTEGER CreateTime;
|
|
|
|
ULONG WaitTime;
|
|
|
|
LPVOID StartAddress;
|
|
|
|
CLIENT_ID ClientId;
|
|
|
|
DWORD Priority;
|
|
|
|
LONG BasePriority;
|
|
|
|
ULONG ContextSwitchCount;
|
|
|
|
THREAD_STATE State;
|
|
|
|
ULONG WaitReason;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SYSTEM_PROCESS_INFORMATION
|
|
|
|
{
|
|
|
|
ULONG NextEntryOffset;
|
|
|
|
ULONG NumberOfThreads;
|
|
|
|
LARGE_INTEGER Reserved[3];
|
|
|
|
LARGE_INTEGER CreateTime;
|
|
|
|
LARGE_INTEGER UserTime;
|
|
|
|
LARGE_INTEGER KernelTime;
|
|
|
|
UNICODE_STRING ImageName;
|
|
|
|
KPRIORITY BasePriority;
|
|
|
|
HANDLE ProcessId;
|
|
|
|
HANDLE InheritedFromProcessId;
|
|
|
|
ULONG HandleCount;
|
|
|
|
ULONG Reserved2[2];
|
|
|
|
ULONG PrivatePageCount;
|
|
|
|
VM_COUNTERS VirtualMemoryCounters;
|
|
|
|
IO_COUNTERS IoCounters;
|
|
|
|
SYSTEM_THREAD Threads[1];
|
|
|
|
};
|
|
|
|
|
|
|
|
class BfpManager
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
BfpManager* mNext;
|
|
|
|
|
|
|
|
BfpManager()
|
|
|
|
{
|
|
|
|
mNext = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~BfpManager()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
BfpManager* gManagerTail;
|
|
|
|
|
|
|
|
static void BfpRecordManager(BfpManager* manager)
|
|
|
|
{
|
|
|
|
AutoCrit autoCrit(gBfpCritSect);
|
|
|
|
manager->mNext = gManagerTail;
|
|
|
|
gManagerTail = manager;
|
|
|
|
}
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
typedef NTSTATUS(NTAPI* NtQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
|
2019-08-23 11:56:54 -07:00
|
|
|
static NtQuerySystemInformation_t gNtQuerySystemInformation = NULL;
|
|
|
|
static HMODULE gNTDll = NULL;
|
|
|
|
|
|
|
|
static void ImportNTDll()
|
|
|
|
{
|
|
|
|
if (gNTDll != NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
WCHAR path[MAX_PATH];
|
|
|
|
GetSystemDirectory(path, MAX_PATH);
|
|
|
|
wcscat(path, L"\\ntdll.dll");
|
|
|
|
gNTDll = GetModuleHandle(path);
|
|
|
|
if (gNTDll == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gNtQuerySystemInformation = (NtQuerySystemInformation_t)GetProcAddress(gNTDll, "NtQuerySystemInformation");
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef HRESULT(WINAPI* SetThreadDescription_t)(HANDLE hThread, PCWSTR lpThreadDescription);
|
|
|
|
typedef HRESULT(WINAPI* GetThreadDescription_t)(HANDLE hThread, PWSTR* lpThreadDescription);
|
|
|
|
static SetThreadDescription_t gSetThreadDescription = NULL;
|
|
|
|
static GetThreadDescription_t gGetThreadDescription = NULL;
|
|
|
|
static HMODULE gKernelDll = NULL;
|
|
|
|
|
|
|
|
static void ImportKernel()
|
|
|
|
{
|
|
|
|
if (gKernelDll != NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
WCHAR path[MAX_PATH];
|
|
|
|
GetSystemDirectory(path, MAX_PATH);
|
|
|
|
wcscat(path, L"\\kernel32.dll");
|
|
|
|
gKernelDll = GetModuleHandle(path);
|
|
|
|
if (gKernelDll == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSetThreadDescription = (SetThreadDescription_t)GetProcAddress(gKernelDll, "SetThreadDescription");
|
|
|
|
gGetThreadDescription = (GetThreadDescription_t)GetProcAddress(gKernelDll, "GetThreadDescription");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef BF_MINGW
|
|
|
|
#define timeGetTime GetTickCount
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static bool IsHandleValid(HANDLE handle)
|
|
|
|
{
|
|
|
|
return (handle != NULL) && (handle != INVALID_HANDLE_VALUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
WindowsSharedInfo* GetSharedInfo()
|
|
|
|
{
|
|
|
|
if (gGlobalPlatformInfo != NULL)
|
|
|
|
return gGlobalPlatformInfo;
|
|
|
|
|
|
|
|
String sharedName = StrFormat("BfpSharedInfo_%d", GetCurrentProcessId());
|
|
|
|
|
|
|
|
bool created = false;
|
|
|
|
HANDLE sharedFileMapping = ::OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, sharedName.c_str());
|
|
|
|
|
|
|
|
if (sharedFileMapping == NULL)
|
|
|
|
{
|
|
|
|
sharedFileMapping = ::CreateFileMappingA(
|
2022-01-03 11:24:45 -05:00
|
|
|
INVALID_HANDLE_VALUE,
|
|
|
|
NULL,
|
|
|
|
PAGE_READWRITE,
|
|
|
|
0,
|
|
|
|
sizeof(WindowsSharedInfo),
|
|
|
|
sharedName.c_str());
|
2019-08-23 11:56:54 -07:00
|
|
|
created = true;
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
BF_ASSERT(sharedFileMapping != NULL);
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
gGlobalPlatformInfo = (WindowsSharedInfo*)MapViewOfFile(sharedFileMapping,
|
|
|
|
FILE_MAP_READ | FILE_MAP_WRITE,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
sizeof(WindowsSharedInfo));
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (created)
|
|
|
|
{
|
|
|
|
memset(gGlobalPlatformInfo, 0, sizeof(WindowsSharedInfo));
|
|
|
|
gGlobalPlatformInfo->mTickStart = timeGetTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
timeBeginPeriod(1);
|
|
|
|
|
|
|
|
return gGlobalPlatformInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 Beefy::BFTickCount()
|
|
|
|
{
|
|
|
|
return timeGetTime() - GetSharedInfo()->mTickStart;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*#ifdef BF_MINGW
|
|
|
|
uint64 __rdtsc( )
|
|
|
|
{
|
|
|
|
#if defined i386
|
|
|
|
long long a;
|
|
|
|
asm volatile("rdtsc":"=A" (a));
|
|
|
|
return a;
|
|
|
|
#elif defined __x86_64
|
|
|
|
unsigned int _hi,_lo;
|
|
|
|
asm volatile("rdtsc":"=a"(_lo),"=d"(_hi));
|
|
|
|
return ((unsigned long long int)_hi << 32) | _lo;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif*/
|
|
|
|
|
|
|
|
#ifdef BF_MINGW
|
|
|
|
#define ALL_PROCESSOR_GROUPS 0xffff
|
|
|
|
WINBASEAPI DWORD WINAPI GetActiveProcessorCount(WORD GroupNumber);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int IntCompare(const void* a, const void* b)
|
|
|
|
{
|
|
|
|
return *(int*)a - *(int*)b;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64 Beefy::BFGetTickCountMicroFast()
|
|
|
|
{
|
|
|
|
if (!gTimerInitialized)
|
|
|
|
{
|
|
|
|
WindowsSharedInfo* windowsSharedInfo = GetSharedInfo();
|
|
|
|
windowsSharedInfo->mThreadAcc++;
|
|
|
|
|
|
|
|
#ifdef BF_MINGW
|
|
|
|
//TODO: Fix (needs new libkernel32.a)
|
|
|
|
int processorCount = 4;
|
|
|
|
#else
|
|
|
|
int processorCount = (int) ::GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
|
|
|
|
#endif
|
2022-01-03 11:24:45 -05:00
|
|
|
|
|
|
|
::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
|
|
|
::SetThreadAffinityMask(::GetCurrentThread(), (int64)1 << (windowsSharedInfo->mThreadAcc % processorCount));
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
uint64 deltaMicro = 0;
|
|
|
|
|
|
|
|
uint64 startMicroA = __rdtsc();
|
|
|
|
uint32 outStartMS = timeGetTime();
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
int timingSet[30];
|
|
|
|
|
|
|
|
uint32 prevQPFMicro = 0;
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
LARGE_INTEGER frequency = { 0, 1 };
|
2019-08-23 11:56:54 -07:00
|
|
|
QueryPerformanceFrequency(&frequency);
|
|
|
|
uint64 startMicro = __rdtsc();
|
|
|
|
|
|
|
|
for (int i = 0; i < BF_ARRAY_COUNT(timingSet); i++)
|
|
|
|
{
|
|
|
|
uint32 qPFMicro;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
LARGE_INTEGER timeNow;
|
|
|
|
QueryPerformanceCounter(&timeNow);
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
qPFMicro = (uint32)((timeNow.QuadPart * 100000000) / frequency.QuadPart);
|
2019-08-23 11:56:54 -07:00
|
|
|
} while (qPFMicro - prevQPFMicro < 100000);
|
|
|
|
prevQPFMicro = qPFMicro;
|
|
|
|
|
|
|
|
int64 curMicro = __rdtsc();
|
2022-01-03 11:24:45 -05:00
|
|
|
int aDivisor = (int)(curMicro - startMicro);
|
2019-08-23 11:56:54 -07:00
|
|
|
startMicro = curMicro;
|
|
|
|
timingSet[i] = aDivisor;
|
|
|
|
}
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
qsort(timingSet, BF_ARRAY_COUNT(timingSet), sizeof(timingSet[0]), IntCompare);
|
|
|
|
gTimerDivisor = timingSet[BF_ARRAY_COUNT(timingSet) / 3];
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
//gTimerDivisor = *gTimingSet.rbegin();
|
|
|
|
OutputDebugStrF("BFGetTickCountMicro divisor: %d\n", gTimerDivisor);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
|
|
|
|
|
|
|
uint32 outEndMS = timeGetTime();
|
2022-01-03 11:24:45 -05:00
|
|
|
uint64 endMicroA = __rdtsc();
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
// It's possible we can run this test in multiple threads at once, we could wrap a CritSect around it but
|
|
|
|
// at least this fence will avoid the case where we have a zero gTimerDivisor
|
|
|
|
BF_FULL_MEMORY_FENCE();
|
|
|
|
gTimerInitialized = true;
|
|
|
|
}
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
uint64 clock = __rdtsc();
|
2019-08-23 11:56:54 -07:00
|
|
|
return (clock * 1000) / gTimerDivisor;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64 Beefy::BFGetTickCountMicro()
|
|
|
|
{
|
|
|
|
static LARGE_INTEGER freq;
|
|
|
|
static UINT64 startTime;
|
|
|
|
UINT64 curTime;
|
|
|
|
LARGE_INTEGER value;
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
if (!freq.QuadPart)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
if (!QueryPerformanceFrequency(&freq))
|
|
|
|
{
|
|
|
|
ULARGE_INTEGER fileTime;
|
|
|
|
GetSystemTimeAsFileTime((FILETIME*)&fileTime);
|
|
|
|
return fileTime.QuadPart / 10;
|
|
|
|
}
|
|
|
|
QueryPerformanceCounter(&value);
|
|
|
|
startTime = value.QuadPart;
|
|
|
|
}
|
|
|
|
QueryPerformanceCounter(&value);
|
|
|
|
curTime = value.QuadPart;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
|
|
|
return (int64)((curTime - startTime) * (double)1000000 / freq.QuadPart);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint64 WinConvertFILETIME(const FILETIME& ft)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
LONGLONG ll = (int64)ft.dwHighDateTime << 32;
|
|
|
|
ll = ll + ft.dwLowDateTime - 116444736000000000;
|
|
|
|
return (uint64)(ll / 10000000);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64 Beefy::GetFileTimeWrite(const StringImpl& path)
|
|
|
|
{
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA data;
|
|
|
|
if (GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &data))
|
2022-01-03 11:24:45 -05:00
|
|
|
return (int64)WinConvertFILETIME(data.ftLastWriteTime);
|
|
|
|
else
|
2019-08-23 11:56:54 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Beefy::FileExists(const StringImpl& path, String* outActualName)
|
|
|
|
{
|
|
|
|
WIN32_FIND_DATAW findData;
|
|
|
|
HANDLE handleVal = FindFirstFileW(UTF8Decode(path).c_str(), &findData);
|
|
|
|
if (handleVal == INVALID_HANDLE_VALUE)
|
|
|
|
return false;
|
|
|
|
FindClose(handleVal);
|
|
|
|
if (outActualName != NULL)
|
|
|
|
*outActualName = UTF8Encode(findData.cFileName);
|
|
|
|
return (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Beefy::DirectoryExists(const StringImpl& path, String* outActualName)
|
|
|
|
{
|
|
|
|
WIN32_FIND_DATAW findData;
|
|
|
|
HANDLE handleVal = FindFirstFileW(UTF8Decode(path).c_str(), &findData);
|
|
|
|
if (handleVal == INVALID_HANDLE_VALUE)
|
|
|
|
return false;
|
|
|
|
FindClose(handleVal);
|
|
|
|
if (outActualName != NULL)
|
|
|
|
*outActualName = UTF8Encode(findData.cFileName);
|
|
|
|
return (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Beefy::BFFatalError(const StringImpl& message, const StringImpl& file, int line)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
#ifndef BF_NO_BFAPP
|
2022-01-03 11:24:45 -05:00
|
|
|
if (gBFApp != NULL)
|
2019-08-23 11:56:54 -07:00
|
|
|
gBFApp->mSysDialogCnt++;
|
|
|
|
#endif
|
2022-01-03 11:24:45 -05:00
|
|
|
|
|
|
|
String failMsg = StrFormat("%s in %s:%d", message.c_str(), file.c_str(), line);
|
2020-05-13 12:30:25 -07:00
|
|
|
BfpSystem_FatalError(failMsg.c_str(), "FATAL ERROR");
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
#ifndef BF_NO_BFAPP
|
|
|
|
if (gBFApp != NULL)
|
2022-01-03 11:24:45 -05:00
|
|
|
gBFApp->mSysDialogCnt--;
|
2019-08-23 11:56:54 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static bool GetFileInfo(const char* path, BfpTimeStamp* lastWriteTime, uint32* fileAttributes)
|
|
|
|
{
|
|
|
|
if (lastWriteTime != NULL)
|
|
|
|
*lastWriteTime = 0;
|
|
|
|
if (fileAttributes != NULL)
|
|
|
|
*fileAttributes = 0;
|
|
|
|
|
|
|
|
// Fast Path
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA data = { 0 };
|
|
|
|
if (::GetFileAttributesExW(UTF8Decode(path).c_str(), GetFileExInfoStandard, &data))
|
|
|
|
{
|
|
|
|
if (lastWriteTime != NULL)
|
|
|
|
*lastWriteTime = *(BfpTimeStamp*)&data.ftLastWriteTime;
|
|
|
|
if (fileAttributes != NULL)
|
|
|
|
*fileAttributes = data.dwFileAttributes;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int error = ::GetLastError();
|
|
|
|
if ((error == ERROR_FILE_NOT_FOUND) ||
|
|
|
|
(error == ERROR_PATH_NOT_FOUND) ||
|
|
|
|
(error == ERROR_NOT_READY))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slow Path- This case is in case someone latched onto the file. In this case, GetFileAttributes will fail but
|
|
|
|
// FindFirstFile will not (though it is slower)
|
|
|
|
WIN32_FIND_DATAW findData = { 0 };
|
|
|
|
HANDLE findHandleFrom = ::FindFirstFileW(UTF8Decode(path).c_str(), &findData);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (!IsHandleValid(findHandleFrom))
|
|
|
|
return false;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (lastWriteTime != NULL)
|
|
|
|
*lastWriteTime = *(BfpTimeStamp*)&findData.ftLastWriteTime;
|
|
|
|
if (fileAttributes != NULL)
|
|
|
|
*fileAttributes = findData.dwFileAttributes;
|
|
|
|
|
|
|
|
::FindClose(findHandleFrom);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BfpFileAttributes FileAttributes_WinToBFP(uint32 fileAttributes)
|
|
|
|
{
|
|
|
|
BfpFileAttributes attrs = BfpFileAttribute_None;
|
|
|
|
if ((fileAttributes & FILE_ATTRIBUTE_NORMAL) != 0)
|
|
|
|
attrs = (BfpFileAttributes)(attrs | BfpFileAttribute_Normal);
|
|
|
|
if ((fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
|
|
|
attrs = (BfpFileAttributes)(attrs | BfpFileAttribute_Directory);
|
|
|
|
if ((fileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)
|
|
|
|
attrs = (BfpFileAttributes)(attrs | BfpFileAttribute_Hidden);
|
|
|
|
if ((fileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0)
|
|
|
|
attrs = (BfpFileAttributes)(attrs | BfpFileAttribute_System);
|
|
|
|
if ((fileAttributes & FILE_ATTRIBUTE_TEMPORARY) != 0)
|
|
|
|
attrs = (BfpFileAttributes)(attrs | BfpFileAttribute_Temporary);
|
|
|
|
if ((fileAttributes & FILE_ATTRIBUTE_OFFLINE) != 0)
|
|
|
|
attrs = (BfpFileAttributes)(attrs | BfpFileAttribute_Offline);
|
|
|
|
if ((fileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0)
|
|
|
|
attrs = (BfpFileAttributes)(attrs | BfpFileAttribute_Encrypted);
|
|
|
|
if ((fileAttributes & FILE_ATTRIBUTE_ARCHIVE) != 0)
|
|
|
|
attrs = (BfpFileAttributes)(attrs | BfpFileAttribute_Archive);
|
|
|
|
if ((fileAttributes & FILE_ATTRIBUTE_READONLY) != 0)
|
|
|
|
attrs = (BfpFileAttributes)(attrs | BfpFileAttribute_ReadOnly);
|
|
|
|
return attrs;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32 FileAttributes_BFPToWin(BfpFileAttributes fileAttributes)
|
|
|
|
{
|
|
|
|
uint32 attributes = 0;
|
|
|
|
if ((fileAttributes & BfpFileAttribute_ReadOnly) != 0)
|
|
|
|
attributes |= FILE_ATTRIBUTE_READONLY;
|
|
|
|
if ((fileAttributes & BfpFileAttribute_Hidden) != 0)
|
|
|
|
attributes |= FILE_ATTRIBUTE_HIDDEN;
|
|
|
|
if ((fileAttributes & BfpFileAttribute_System) != 0)
|
|
|
|
attributes |= FILE_ATTRIBUTE_SYSTEM;
|
|
|
|
if ((fileAttributes & BfpFileAttribute_Temporary) != 0)
|
|
|
|
attributes |= FILE_ATTRIBUTE_TEMPORARY;
|
|
|
|
if ((fileAttributes & BfpFileAttribute_Offline) != 0)
|
|
|
|
attributes |= FILE_ATTRIBUTE_OFFLINE;
|
|
|
|
if ((fileAttributes & BfpFileAttribute_Encrypted) != 0)
|
|
|
|
attributes |= FILE_ATTRIBUTE_ENCRYPTED;
|
|
|
|
if ((fileAttributes & BfpFileAttribute_Archive) != 0)
|
|
|
|
attributes |= FILE_ATTRIBUTE_ARCHIVE;
|
|
|
|
if ((fileAttributes & BfpFileAttribute_Normal) != 0)
|
|
|
|
attributes |= FILE_ATTRIBUTE_NORMAL;
|
|
|
|
return attributes;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum BfpOverlappedKind
|
|
|
|
{
|
|
|
|
BfpOverlappedKind_FileWatcher,
|
|
|
|
BfpOverlappedKind_File
|
|
|
|
};
|
|
|
|
|
|
|
|
struct BfpOverlapped
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
BfpOverlappedKind mKind;
|
2019-08-23 11:56:54 -07:00
|
|
|
volatile HANDLE mHandle;
|
|
|
|
OVERLAPPED mOverlapped;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
virtual ~BfpOverlapped()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Completed(int errorCode, int numBytes, OVERLAPPED* overlapped)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct BfpOverlappedFile : BfpOverlapped
|
|
|
|
{
|
|
|
|
Array<uint8> mRecvBuffer;
|
|
|
|
|
|
|
|
BfpOverlappedFile()
|
|
|
|
{
|
|
|
|
mKind = BfpOverlappedKind_File;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct BfpAsyncData
|
|
|
|
{
|
|
|
|
HANDLE mEvent;
|
|
|
|
|
|
|
|
BfpAsyncData()
|
|
|
|
{
|
|
|
|
mEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
~BfpAsyncData()
|
|
|
|
{
|
|
|
|
::CloseHandle(mEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetEvent()
|
|
|
|
{
|
|
|
|
::SetEvent(mEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitAndResetEvent(int timeoutMS)
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
auto result = ::WaitForSingleObjectEx(mEvent, (DWORD)timeoutMS, TRUE);
|
|
|
|
if (result == STATUS_USER_APC)
|
|
|
|
continue; // Retry
|
|
|
|
if (result == WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
::ResetEvent(mEvent);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct BfpFile
|
|
|
|
{
|
|
|
|
HANDLE mHandle;
|
|
|
|
BfpAsyncData* mAsyncData;
|
|
|
|
bool mIsPipe;
|
|
|
|
bool mIsStd;
|
|
|
|
|
|
|
|
BfpFile(HANDLE handle)
|
|
|
|
{
|
|
|
|
mHandle = handle;
|
|
|
|
mAsyncData = NULL;
|
|
|
|
mIsPipe = false;
|
|
|
|
mIsStd = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
BfpFile()
|
|
|
|
{
|
|
|
|
mHandle = 0;
|
|
|
|
mAsyncData = NULL;
|
|
|
|
mIsPipe = false;
|
|
|
|
mIsStd = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
~BfpFile()
|
|
|
|
{
|
|
|
|
delete mAsyncData;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct BfpFileWatcher : public BfpOverlapped
|
|
|
|
{
|
|
|
|
String mPath;
|
|
|
|
BfpDirectoryChangeFunc mDirectoryChangeFunc;
|
|
|
|
BfpFileWatcherFlags mFlags;
|
2022-01-03 11:24:45 -05:00
|
|
|
void* mUserData;
|
|
|
|
char mBuffer[0x10000];
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
void Monitor()
|
|
|
|
{
|
|
|
|
mOverlapped = { 0 };
|
|
|
|
|
|
|
|
DWORD filter = FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY;
|
|
|
|
|
|
|
|
DWORD size;
|
|
|
|
DWORD success = ::ReadDirectoryChangesW(mHandle,
|
|
|
|
mBuffer, 0x10000,
|
|
|
|
(mFlags & BfpFileWatcherFlag_IncludeSubdirectories) != 0,
|
|
|
|
filter,
|
|
|
|
&size,
|
|
|
|
&mOverlapped,
|
|
|
|
NULL);
|
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
mDirectoryChangeFunc(this, mUserData, BfpFileChangeKind_Failed, mPath.c_str(), NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Restart()
|
|
|
|
{
|
|
|
|
mDirectoryChangeFunc(this, mUserData, BfpFileChangeKind_Failed, mPath.c_str(), NULL, NULL);
|
|
|
|
OutputDebugStrF("Restarting directory monitor for %s\n", mPath.c_str());
|
|
|
|
Monitor();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Completed(int errorCode, int numBytes, OVERLAPPED* overlapped) override
|
|
|
|
{
|
|
|
|
if (errorCode != 0)
|
|
|
|
{
|
|
|
|
if (errorCode == 995 /* ERROR_OPERATION_ABORTED */)
|
|
|
|
{
|
|
|
|
// Win2000 inside a service the first completion status is false?
|
|
|
|
// Restart file monitoring?
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
mDirectoryChangeFunc(this, mUserData, BfpFileChangeKind_Failed, mPath.c_str(), NULL, NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (numBytes == 0)
|
|
|
|
{
|
|
|
|
mDirectoryChangeFunc(this, mUserData, BfpFileChangeKind_Failed, mPath.c_str(), NULL, NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE_NOTIFY_INFORMATION* fileNotifyInformation = (FILE_NOTIFY_INFORMATION*)mBuffer;
|
|
|
|
String oldName;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
String fileName = UTF8Encode(UTF16String(fileNotifyInformation->FileName, fileNotifyInformation->FileNameLength / 2));
|
|
|
|
if (fileNotifyInformation->Action == FILE_ACTION_RENAMED_OLD_NAME)
|
|
|
|
oldName = fileName;
|
|
|
|
else if (fileNotifyInformation->Action == FILE_ACTION_RENAMED_NEW_NAME)
|
|
|
|
mDirectoryChangeFunc(this, mUserData, BfpFileChangeKind_Renamed, mPath.c_str(), oldName.c_str(), fileName.c_str());
|
|
|
|
else if (fileNotifyInformation->Action == FILE_ACTION_ADDED)
|
|
|
|
mDirectoryChangeFunc(this, mUserData, BfpFileChangeKind_Added, mPath.c_str(), fileName.c_str(), NULL);
|
|
|
|
else if (fileNotifyInformation->Action == FILE_ACTION_REMOVED)
|
|
|
|
mDirectoryChangeFunc(this, mUserData, BfpFileChangeKind_Removed, mPath.c_str(), fileName.c_str(), NULL);
|
|
|
|
else if (fileNotifyInformation->Action == FILE_ACTION_MODIFIED)
|
|
|
|
mDirectoryChangeFunc(this, mUserData, BfpFileChangeKind_Modified, mPath.c_str(), fileName.c_str(), NULL);
|
|
|
|
|
|
|
|
if (fileNotifyInformation->NextEntryOffset == 0)
|
|
|
|
break;
|
|
|
|
fileNotifyInformation = (FILE_NOTIFY_INFORMATION*)((uint8*)fileNotifyInformation + fileNotifyInformation->NextEntryOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
Monitor();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class IOCPManager;
|
|
|
|
static IOCPManager* gIOCPManager;
|
|
|
|
class IOCPManager : public BfpManager
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
HANDLE mIOCompletionPort;
|
|
|
|
|
|
|
|
Array<HANDLE> mWorkerThreads;
|
|
|
|
CritSect mCritSect;
|
|
|
|
bool mShuttingDown;
|
|
|
|
|
|
|
|
public:
|
|
|
|
IOCPManager()
|
|
|
|
{
|
|
|
|
mIOCompletionPort = NULL;
|
|
|
|
mShuttingDown = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
~IOCPManager()
|
|
|
|
{
|
|
|
|
Shutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Shutdown()
|
|
|
|
{
|
|
|
|
OutputDebugStrF("IOCP.Shutdown Start\n");
|
|
|
|
|
|
|
|
//
|
|
|
|
{
|
|
|
|
AutoCrit autoCrit(mCritSect);
|
|
|
|
|
|
|
|
if (mShuttingDown)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mShuttingDown = true;
|
|
|
|
|
|
|
|
if (!IsHandleValid(mIOCompletionPort))
|
|
|
|
return;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
for (int i = 0; i < (int)mWorkerThreads.size(); i++)
|
|
|
|
::PostQueuedCompletionStatus(mIOCompletionPort, 0, (ULONG_PTR)1, NULL);
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)mWorkerThreads.size(); i++)
|
|
|
|
{
|
|
|
|
::WaitForSingleObject(mWorkerThreads[i], INFINITE);
|
|
|
|
::CloseHandle(mWorkerThreads[i]);
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
::CloseHandle(mIOCompletionPort);
|
2022-01-03 11:24:45 -05:00
|
|
|
mIOCompletionPort = 0;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
OutputDebugStrF("IOCP.Shutdown Done\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddWorkerThread()
|
|
|
|
{
|
|
|
|
AutoCrit autoCrit(mCritSect);
|
|
|
|
|
|
|
|
if (mShuttingDown)
|
|
|
|
return;
|
|
|
|
|
|
|
|
DWORD threadId;
|
|
|
|
HANDLE handle = ::CreateThread(NULL, 0, WorkerProcThunk, (void*)this, 0, &threadId);
|
|
|
|
mWorkerThreads.Add(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EnsureInitialized()
|
|
|
|
{
|
|
|
|
AutoCrit autoCrit(mCritSect);
|
|
|
|
|
|
|
|
if ((mShuttingDown) || (mIOCompletionPort != NULL))
|
|
|
|
return;
|
|
|
|
|
|
|
|
mIOCompletionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
|
|
|
|
|
|
|
int workerThreads = 1;
|
|
|
|
|
|
|
|
for (int i = 0; i < workerThreads; i++)
|
|
|
|
AddWorkerThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Add(BfpFileWatcher* fileWatcher)
|
|
|
|
{
|
|
|
|
//OutputDebugStrF("IOCP.Add %@\n", fileWatcher);
|
|
|
|
|
|
|
|
EnsureInitialized();
|
|
|
|
auto handle = ::CreateIoCompletionPort(fileWatcher->mHandle, mIOCompletionPort, (ULONG_PTR)fileWatcher, 0);
|
|
|
|
BF_ASSERT(handle == mIOCompletionPort);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Kill(BfpFileWatcher* fileWatcher)
|
|
|
|
{
|
|
|
|
//OutputDebugStrF("IOCP.Kill %@\n", fileWatcher);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
BF_ASSERT(fileWatcher->mHandle == NULL);
|
|
|
|
delete fileWatcher;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Remove(BfpOverlapped* bfpOverlapped)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
/*if (mShuttingDown)
|
|
|
|
{
|
|
|
|
Kill(fileWatcher);
|
|
|
|
return;
|
|
|
|
} */
|
|
|
|
|
|
|
|
//QueueRemove(fileWatcher);
|
|
|
|
|
|
|
|
//OutputDebugStrF("IOCP.Remove %@\n", fileWatcher);
|
|
|
|
|
|
|
|
AutoCrit autoCrit(mCritSect);
|
|
|
|
auto handle = bfpOverlapped->mHandle;
|
|
|
|
bfpOverlapped->mHandle = NULL;
|
|
|
|
BF_FULL_MEMORY_FENCE();
|
2022-01-03 11:24:45 -05:00
|
|
|
::CloseHandle(handle);
|
2019-08-23 11:56:54 -07:00
|
|
|
::PostQueuedCompletionStatus(mIOCompletionPort, 0, (ULONG_PTR)bfpOverlapped | 1, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorkerProc()
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
DWORD numBytes;
|
|
|
|
ULONG_PTR completionKey;
|
|
|
|
OVERLAPPED* overlapped;
|
|
|
|
int error = 0;
|
|
|
|
if (!::GetQueuedCompletionStatus(mIOCompletionPort, &numBytes, &completionKey, &overlapped, INFINITE))
|
|
|
|
{
|
|
|
|
error = GetLastError();
|
|
|
|
if ((error == ERROR_INVALID_HANDLE) || (error == ERROR_ABANDONED_WAIT_0))
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
int32 keyFlags = (int32)completionKey & 3;
|
|
|
|
BfpOverlapped* bfpOverlapped = (BfpOverlapped*)(completionKey & ~3);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (keyFlags == 0) // Normal
|
|
|
|
{
|
|
|
|
// Check to see if we have released this
|
|
|
|
{
|
|
|
|
AutoCrit autoCrit(mCritSect);
|
|
|
|
if (bfpOverlapped->mHandle == NULL)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (numBytes == 0)
|
|
|
|
{
|
|
|
|
if (bfpOverlapped->mKind == BfpOverlappedKind_FileWatcher)
|
|
|
|
{
|
|
|
|
auto fileWatcher = (BfpFileWatcher*)bfpOverlapped;
|
|
|
|
if (fileWatcher->mHandle != NULL)
|
2022-01-03 11:24:45 -05:00
|
|
|
fileWatcher->Restart();
|
2019-08-23 11:56:54 -07:00
|
|
|
else
|
|
|
|
Kill(fileWatcher);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
|
|
|
bfpOverlapped->Completed(error, numBytes, overlapped);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
else if (keyFlags == 1)
|
|
|
|
{
|
|
|
|
if (bfpOverlapped == NULL)
|
|
|
|
break; // Normal shutdown
|
|
|
|
|
|
|
|
if (bfpOverlapped->mKind == BfpOverlappedKind_FileWatcher)
|
|
|
|
Kill((BfpFileWatcher*)bfpOverlapped);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static DWORD __stdcall WorkerProcThunk(void* _this)
|
|
|
|
{
|
|
|
|
BfpThread_SetName(NULL, "IOCPManager", NULL);
|
|
|
|
((IOCPManager*)_this)->WorkerProc();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static IOCPManager* Get()
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
AutoCrit autoCrit(gBfpCritSect);
|
|
|
|
if (gIOCPManager == NULL)
|
|
|
|
{
|
|
|
|
gIOCPManager = new IOCPManager();
|
|
|
|
BfpRecordManager(gIOCPManager);
|
|
|
|
}
|
|
|
|
return gIOCPManager;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void __cdecl HandlePureVirtualFunctionCall()
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
BfpSystem_FatalError("Pure virtual function call", NULL);
|
|
|
|
}
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
static void __cdecl HandleInvalidParameter(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
BfpSystem_FatalError("Invalid parameter", NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __cdecl AbortHandler(int)
|
|
|
|
{
|
|
|
|
BfpSystem_FatalError("Abort handler", NULL);
|
|
|
|
}
|
|
|
|
|
2019-11-19 09:58:35 -08:00
|
|
|
static int64 gCPUFreq = -1;
|
|
|
|
static int64 gStartCPUTick = -1;
|
|
|
|
static int64 gStartQPF = -1;
|
|
|
|
|
|
|
|
static void InitCPUFreq()
|
|
|
|
{
|
|
|
|
if (gStartCPUTick == -1)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-11-19 09:58:35 -08:00
|
|
|
gStartCPUTick = __rdtsc();
|
|
|
|
LARGE_INTEGER largeVal = { 0 };
|
|
|
|
QueryPerformanceCounter(&largeVal);
|
|
|
|
gStartQPF = largeVal.QuadPart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-15 22:11:56 -08:00
|
|
|
static void(*sOldSIGABRTHandler)(int signal) = nullptr;
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_Init(int version, BfpSystemInitFlags flags)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-11-19 09:58:35 -08:00
|
|
|
InitCPUFreq();
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
::_set_error_mode(_OUT_TO_STDERR);
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
::_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
|
|
::SetErrorMode(SEM_FAILCRITICALERRORS);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (version != BFP_VERSION)
|
|
|
|
{
|
|
|
|
BfpSystem_FatalError(StrFormat("Bfp build version '%d' does not match requested version '%d'", BFP_VERSION, version).c_str(), "BFP FATAL ERROR");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & BfpSystemInitFlag_InstallCrashCatcher) != 0)
|
|
|
|
{
|
|
|
|
// Set up a handler for pure virtual function calls
|
|
|
|
_set_purecall_handler(HandlePureVirtualFunctionCall);
|
|
|
|
// Set up a handler for invalid parameters such as printf(NULL);
|
|
|
|
_set_invalid_parameter_handler(HandleInvalidParameter);
|
|
|
|
|
|
|
|
// Set up a handler for abort(). This is done in two steps.
|
|
|
|
// First we ask the CRT to call an abort handler. This is the
|
|
|
|
// default, but explicitly setting it seems like a good idea.
|
|
|
|
_set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT);
|
|
|
|
// Then we install our abort handler.
|
2022-02-15 22:11:56 -08:00
|
|
|
sOldSIGABRTHandler = signal(SIGABRT, &AbortHandler);
|
|
|
|
if (sOldSIGABRTHandler == SIG_ERR)
|
|
|
|
sOldSIGABRTHandler = nullptr;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
CrashCatcher::Get()->Init();
|
2019-08-23 11:56:54 -07:00
|
|
|
if ((flags & BfpSystemInitFlag_SilentCrash) != 0)
|
2020-05-28 07:25:25 -07:00
|
|
|
CrashCatcher::Get()->SetCrashReportKind(BfpCrashReportKind_None);
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCommandLine(int argc, char** argv)
|
|
|
|
{
|
|
|
|
// This isn't required on Windows, but it is on Linux
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCrashReportKind(BfpCrashReportKind crashReportKind)
|
|
|
|
{
|
2020-05-28 07:25:25 -07:00
|
|
|
CrashCatcher::Get()->SetCrashReportKind(crashReportKind);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfoFunc(BfpCrashInfoFunc crashInfoFunc)
|
|
|
|
{
|
2020-05-28 07:25:25 -07:00
|
|
|
CrashCatcher::Get()->AddCrashInfoFunc(crashInfoFunc);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfo(const char* str)
|
|
|
|
{
|
2020-05-28 07:25:25 -07:00
|
|
|
CrashCatcher::Get()->AddInfo(str);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2020-09-17 14:15:38 -07:00
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCrashRelaunchCmd(const char* str)
|
|
|
|
{
|
|
|
|
CrashCatcher::Get()->SetRelaunchCmd(str);
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_Shutdown()
|
|
|
|
{
|
|
|
|
while (gManagerTail != NULL)
|
|
|
|
{
|
|
|
|
auto next = gManagerTail->mNext;
|
|
|
|
delete gManagerTail;
|
|
|
|
gManagerTail = next;
|
|
|
|
}
|
2022-02-15 22:11:56 -08:00
|
|
|
|
|
|
|
if (CrashCatcher::Shutdown())
|
|
|
|
{
|
|
|
|
_set_purecall_handler(nullptr);
|
|
|
|
_set_invalid_parameter_handler(nullptr);
|
|
|
|
signal(SIGABRT, sOldSIGABRTHandler);
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_TickCount()
|
|
|
|
{
|
|
|
|
return BFTickCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpSystem_GetTimeStamp()
|
|
|
|
{
|
|
|
|
BfpTimeStamp timeStamp = 0;
|
|
|
|
GetSystemTimeAsFileTime((FILETIME*)&timeStamp);
|
|
|
|
return timeStamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint16 BFP_CALLTYPE BfpSystem_EndianSwap16(uint16 val)
|
|
|
|
{
|
|
|
|
return _byteswap_ushort(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_EndianSwap32(uint32 val)
|
|
|
|
{
|
|
|
|
return _byteswap_ulong(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_EndianSwap64(uint64 val)
|
|
|
|
{
|
|
|
|
return _byteswap_uint64(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint8 BFP_CALLTYPE BfpSystem_InterlockedExchange8(uint8* ptr, uint8 val) // Returns the initial value in 'ptr'
|
|
|
|
{
|
|
|
|
return ::InterlockedExchange8((volatile char*)ptr, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint16 BFP_CALLTYPE BfpSystem_InterlockedExchange16(uint16* ptr, uint16 val) // Returns the initial value in 'ptr'
|
|
|
|
{
|
|
|
|
return ::InterlockedExchange16((volatile short*)ptr, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchange32(uint32* ptr, uint32 val) // Returns the initial value in 'ptr'
|
|
|
|
{
|
|
|
|
return ::InterlockedExchange((volatile uint32*)ptr, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchange64(uint64* ptr, uint64 val)
|
|
|
|
{
|
|
|
|
return ::InterlockedExchange((volatile uint64*)ptr, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd8(uint8* ptr, uint8 val) // Returns the initial value in 'ptr'
|
|
|
|
{
|
|
|
|
return ::InterlockedExchangeAdd8((volatile char*)ptr, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint16 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd16(uint16* ptr, uint16 val) // Returns the initial value in 'ptr'
|
|
|
|
{
|
|
|
|
return ::_InterlockedExchangeAdd16((volatile short*)ptr, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd32(uint32* ptr, uint32 val) // Returns the initial value in 'ptr'
|
|
|
|
{
|
|
|
|
return ::InterlockedExchangeAdd((volatile uint32*)ptr, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd64(uint64* ptr, uint64 val)
|
|
|
|
{
|
|
|
|
return ::InterlockedExchangeAdd((volatile uint64*)ptr, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint8 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange8(uint8* ptr, uint8 oldVal, uint8 newVal)
|
|
|
|
{
|
|
|
|
return ::_InterlockedCompareExchange8((volatile char*)ptr, newVal, oldVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint16 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange16(uint16* ptr, uint16 oldVal, uint16 newVal)
|
|
|
|
{
|
|
|
|
return ::_InterlockedCompareExchange16((volatile short*)ptr, newVal, oldVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange32(uint32* ptr, uint32 oldVal, uint32 newVal)
|
|
|
|
{
|
|
|
|
return ::InterlockedCompareExchange((volatile uint32*)ptr, newVal, oldVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange64(uint64* ptr, uint64 oldVal, uint64 newVal)
|
|
|
|
{
|
|
|
|
return ::InterlockedCompareExchange64((volatile int64*)ptr, newVal, oldVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_FatalError(const char* error, const char* title)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
if (title != NULL)
|
2020-05-28 07:25:25 -07:00
|
|
|
CrashCatcher::Get()->Crash(String(title) + "\n" + String(error));
|
2019-08-23 11:56:54 -07:00
|
|
|
else
|
2020-05-28 07:25:25 -07:00
|
|
|
CrashCatcher::Get()->Crash(error);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetCommandLine(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
|
|
|
|
{
|
|
|
|
WCHAR* ptr = ::GetCommandLineW();
|
|
|
|
|
|
|
|
UTF16String wString(ptr);
|
2022-01-03 11:24:45 -05:00
|
|
|
String env = UTF8Encode(wString);
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
TryStringOut(env, outStr, inOutStrSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetExecutablePath(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
|
|
|
|
{
|
|
|
|
WCHAR path[MAX_PATH];
|
|
|
|
GetModuleFileNameW(NULL, path, MAX_PATH);
|
|
|
|
|
|
|
|
String env = UTF8Encode(path);
|
|
|
|
TryStringOut(env, outStr, inOutStrSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetEnvironmentStrings(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
|
|
|
|
{
|
|
|
|
WCHAR* ptr = ::GetEnvironmentStringsW();
|
|
|
|
WCHAR* endPtr = ptr;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if ((endPtr[0] == 0) && (endPtr[1] == 0))
|
|
|
|
break;
|
|
|
|
endPtr++;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
UTF16String wString(ptr, (int)(endPtr - ptr + 2));
|
|
|
|
String env = UTF8Encode(wString);
|
|
|
|
::FreeEnvironmentStringsW(ptr);
|
|
|
|
|
|
|
|
TryStringOut(env, outStr, inOutStrSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT int BFP_CALLTYPE BfpSystem_GetNumLogicalCPUs(BfpSystemResult* outResult)
|
|
|
|
{
|
|
|
|
SYSTEM_INFO sysInfo;
|
2022-01-03 11:24:45 -05:00
|
|
|
GetSystemInfo(&sysInfo);
|
2019-08-23 11:56:54 -07:00
|
|
|
OUTRESULT(BfpSystemResult_Ok);
|
|
|
|
return sysInfo.dwNumberOfProcessors;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTick()
|
|
|
|
{
|
|
|
|
return __rdtsc();
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTickFreq()
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-11-19 09:58:35 -08:00
|
|
|
LARGE_INTEGER largeVal = { 0 };
|
|
|
|
QueryPerformanceFrequency(&largeVal);
|
|
|
|
int64 qpfFreq = largeVal.QuadPart;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-11-19 09:58:35 -08:00
|
|
|
if (gStartCPUTick == -1)
|
|
|
|
{
|
|
|
|
InitCPUFreq();
|
|
|
|
Sleep(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64 cpuTick1 = __rdtsc();
|
|
|
|
QueryPerformanceCounter(&largeVal);
|
|
|
|
int64 slowTick1 = largeVal.QuadPart;
|
|
|
|
|
|
|
|
int64 cpuElapsed = cpuTick1 - gStartCPUTick;
|
|
|
|
int64 slowElapsed = slowTick1 - gStartQPF;
|
|
|
|
double elapsedSeconds = slowElapsed / (double)qpfFreq;
|
|
|
|
int64 freq = (int64)(cpuElapsed / elapsedSeconds);
|
|
|
|
gCPUFreq = freq;
|
|
|
|
|
|
|
|
return gCPUFreq;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_CreateGUID(BfpGUID* outGuid)
|
|
|
|
{
|
|
|
|
memset(outGuid, 0, sizeof(BfpGUID));
|
|
|
|
UuidCreate((UUID*)outGuid);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetComputerName(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
|
|
|
|
{
|
|
|
|
char computerName[MAX_COMPUTERNAME_LENGTH + 1] = { 0 };
|
|
|
|
DWORD computerNameSize = MAX_COMPUTERNAME_LENGTH;
|
|
|
|
GetComputerNameA(computerName, &computerNameSize);
|
|
|
|
TryStringOut(computerName, outStr, inOutStrSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// BfpFileWatcher
|
|
|
|
|
|
|
|
BFP_EXPORT BfpFileWatcher* BFP_CALLTYPE BfpFileWatcher_WatchDirectory(const char* path, BfpDirectoryChangeFunc callback, BfpFileWatcherFlags flags, void* userData, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
HANDLE directoryHandle = CreateFileW(
|
|
|
|
UTF8Decode(path).c_str(), // Directory name
|
|
|
|
FILE_LIST_DIRECTORY, // access (read-write) mode
|
|
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
|
|
NULL, // security descriptor
|
|
|
|
OPEN_EXISTING,
|
|
|
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // file attributes
|
|
|
|
NULL // file with attributes to copy
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!IsHandleValid(directoryHandle))
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
BfpFileWatcher* fileWatcher = new BfpFileWatcher();
|
2022-01-03 11:24:45 -05:00
|
|
|
fileWatcher->mKind = BfpOverlappedKind_FileWatcher;
|
2019-08-23 11:56:54 -07:00
|
|
|
fileWatcher->mPath = path;
|
|
|
|
fileWatcher->mDirectoryChangeFunc = callback;
|
2022-01-03 11:24:45 -05:00
|
|
|
fileWatcher->mHandle = directoryHandle;
|
2019-08-23 11:56:54 -07:00
|
|
|
fileWatcher->mFlags = flags;
|
|
|
|
fileWatcher->mUserData = userData;
|
|
|
|
IOCPManager::Get()->Add(fileWatcher);
|
|
|
|
fileWatcher->Monitor();
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
return fileWatcher;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFileWatcher_Release(BfpFileWatcher* fileWatcher)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
IOCPManager::Get()->Remove(fileWatcher);
|
|
|
|
//::CloseHandle(fileWatcher->mFileHandle);
|
|
|
|
//fileWatcher->mFileHandle = NULL;
|
|
|
|
|
|
|
|
//delete fileWatcher;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// BfpProcess
|
|
|
|
|
|
|
|
BFP_EXPORT intptr BFP_CALLTYPE BfpProcess_GetCurrentId()
|
|
|
|
{
|
|
|
|
return ::GetCurrentProcessId();
|
|
|
|
}
|
|
|
|
|
|
|
|
struct BfpProcess
|
|
|
|
{
|
|
|
|
int mProcessId;
|
2022-01-03 11:24:45 -05:00
|
|
|
SYSTEM_PROCESS_INFORMATION* mInfo;
|
2019-08-23 11:56:54 -07:00
|
|
|
String mImageName;
|
|
|
|
};
|
|
|
|
|
|
|
|
BFP_EXPORT bool BFP_CALLTYPE BfpProcess_IsRemoteMachine(const char* machineName)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpProcess* BFP_CALLTYPE BfpProcess_GetById(const char* machineName, int processId, BfpProcessResult* outResult)
|
|
|
|
{
|
|
|
|
BfpProcess* process = new BfpProcess();
|
|
|
|
process->mProcessId = processId;
|
|
|
|
process->mInfo = NULL;
|
|
|
|
return process;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpProcess_Enumerate(const char* machineName, BfpProcess** outProcesses, int* inOutProcessesSize, BfpProcessResult* outResult)
|
|
|
|
{
|
|
|
|
ImportNTDll();
|
|
|
|
if (gNtQuerySystemInformation == NULL)
|
|
|
|
{
|
|
|
|
*inOutProcessesSize = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint allocSize = 1024;
|
|
|
|
uint8* data = NULL;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
data = new uint8[allocSize];
|
|
|
|
|
|
|
|
ULONG wantSize = 0;
|
|
|
|
NTSTATUS status = gNtQuerySystemInformation(SystemProcessInformation, data, allocSize, &wantSize);
|
|
|
|
|
|
|
|
if (status != STATUS_INFO_LENGTH_MISMATCH)
|
|
|
|
{
|
|
|
|
auto processInfo = (SYSTEM_PROCESS_INFORMATION*)data;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
auto curProcessInfo = processInfo;
|
|
|
|
int count = 0;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (curProcessInfo == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
if (curProcessInfo->NextEntryOffset == 0)
|
|
|
|
break;
|
|
|
|
curProcessInfo = (SYSTEM_PROCESS_INFORMATION*)((intptr)curProcessInfo + curProcessInfo->NextEntryOffset);
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (count > *inOutProcessesSize)
|
|
|
|
{
|
|
|
|
*inOutProcessesSize = count;
|
2022-01-03 11:24:45 -05:00
|
|
|
OUTRESULT(BfpProcessResult_InsufficientBuffer);
|
2019-08-23 11:56:54 -07:00
|
|
|
delete data;
|
|
|
|
return;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
curProcessInfo = processInfo;
|
2022-01-03 11:24:45 -05:00
|
|
|
count = 0;
|
2019-08-23 11:56:54 -07:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (curProcessInfo == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (curProcessInfo->ProcessId != 0)
|
|
|
|
{
|
|
|
|
BfpProcess* process = new BfpProcess();
|
|
|
|
int dataSize = sizeof(SYSTEM_PROCESS_INFORMATION) + sizeof(SYSTEM_THREAD) * (curProcessInfo->NumberOfThreads - 1);
|
|
|
|
process->mProcessId = (int)(intptr)curProcessInfo->ProcessId;
|
|
|
|
process->mInfo = (SYSTEM_PROCESS_INFORMATION*)malloc(dataSize);
|
|
|
|
memcpy(process->mInfo, curProcessInfo, dataSize);
|
|
|
|
|
|
|
|
UTF16String utf16;
|
2022-01-03 11:24:45 -05:00
|
|
|
utf16.Set(curProcessInfo->ImageName.Buffer, curProcessInfo->ImageName.Length / 2);
|
2019-08-23 11:56:54 -07:00
|
|
|
process->mImageName = UTF8Encode(utf16);
|
|
|
|
|
|
|
|
outProcesses[count++] = process;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (curProcessInfo->NextEntryOffset == 0)
|
|
|
|
break;
|
|
|
|
curProcessInfo = (SYSTEM_PROCESS_INFORMATION*)((intptr)curProcessInfo + curProcessInfo->NextEntryOffset);
|
|
|
|
}
|
|
|
|
*inOutProcessesSize = count;
|
|
|
|
|
|
|
|
OUTRESULT(BfpProcessResult_Ok);
|
|
|
|
delete data;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
allocSize = wantSize + 4096;
|
|
|
|
delete data;
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpProcess_Release(BfpProcess* process)
|
|
|
|
{
|
|
|
|
if (process->mInfo != NULL)
|
|
|
|
free(process->mInfo);
|
|
|
|
delete process;
|
|
|
|
}
|
|
|
|
|
|
|
|
//struct EnumWndData
|
|
|
|
//{
|
|
|
|
// HWND mBestHandle;
|
|
|
|
// uint32 mProcessId;
|
|
|
|
//};
|
|
|
|
|
|
|
|
struct MainWindowCache
|
|
|
|
{
|
|
|
|
Dictionary<uint32, HWND> mMap;
|
2022-01-03 11:24:45 -05:00
|
|
|
uint32 mLastUpdate;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
MainWindowCache()
|
|
|
|
{
|
|
|
|
mLastUpdate = 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static MainWindowCache gMainWindowCache;
|
|
|
|
|
|
|
|
static BOOL CALLBACK EnumWndProc(HWND hWnd, LPARAM param)
|
|
|
|
{
|
|
|
|
//EnumWndData* enumWndData = (EnumWndData*)param;
|
|
|
|
|
|
|
|
DWORD processId = 0;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
::GetWindowThreadProcessId(hWnd, &processId);
|
|
|
|
|
|
|
|
if (gMainWindowCache.mMap.ContainsKey(processId))
|
|
|
|
{
|
|
|
|
//if (processId == 6720)
|
|
|
|
//OutputDebugStrF("Bailed\n");
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((::IsWindowVisible(hWnd)) && (::GetWindow(hWnd, GW_OWNER) == NULL))
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
//if (processId == 6720)
|
|
|
|
//OutputDebugStrF("Set\n");
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
gMainWindowCache.mMap[processId] = hWnd;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetMainWindowTitle(BfpProcess* process, char* outTitle, int* inOutTitleSize, BfpProcessResult* outResult)
|
|
|
|
{
|
|
|
|
HWND mainWindow = NULL;
|
|
|
|
|
|
|
|
//
|
|
|
|
{
|
|
|
|
AutoCrit autoCrit(gBfpCritSect);
|
|
|
|
|
|
|
|
if ((gMainWindowCache.mLastUpdate == 0) || (BFTickCount() - gMainWindowCache.mLastUpdate > 100))
|
|
|
|
{
|
|
|
|
gMainWindowCache.mMap.Clear();
|
|
|
|
::EnumWindows(EnumWndProc, 0);
|
|
|
|
gMainWindowCache.mLastUpdate = BFTickCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
gMainWindowCache.mMap.TryGetValue(process->mProcessId, &mainWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*EnumWndData enumWndData;
|
|
|
|
enumWndData.mBestHandle = NULL;
|
|
|
|
enumWndData.mProcessId = process->mProcessId;*/
|
|
|
|
|
|
|
|
String title;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (mainWindow != NULL)
|
|
|
|
{
|
|
|
|
int wantSize = 128;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
WCHAR* str = new WCHAR[wantSize];
|
|
|
|
|
|
|
|
int size = ::GetWindowTextW(mainWindow, str, wantSize) + 1;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (size <= 0)
|
|
|
|
{
|
|
|
|
delete str;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size <= wantSize)
|
|
|
|
{
|
|
|
|
title = UTF8Encode(str);
|
|
|
|
delete str;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete str;
|
|
|
|
wantSize = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TryStringOut(title, outTitle, inOutTitleSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetProcessName(BfpProcess* process, char* outName, int* inOutNameSize, BfpProcessResult* outResult)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
if (process->mImageName.IsEmpty())
|
|
|
|
{
|
|
|
|
HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process->mProcessId);
|
|
|
|
if (hProc == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpProcessResult_UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
WCHAR wName[MAX_PATH];
|
|
|
|
::GetModuleFileNameExW(hProc, NULL, wName, MAX_PATH);
|
2022-01-03 11:24:45 -05:00
|
|
|
::CloseHandle(hProc);
|
2019-08-23 11:56:54 -07:00
|
|
|
String name = UTF8Encode(wName);
|
|
|
|
process->mImageName = name;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
TryStringOut(process->mImageName, outName, inOutNameSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT int BFP_CALLTYPE BfpProcess_GetProcessId(BfpProcess* process)
|
|
|
|
{
|
|
|
|
return process->mProcessId;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT int BFP_CALLTYPE BfpProcess_GetRunningTime(BfpProcess* process)
|
|
|
|
{
|
|
|
|
if (process->mInfo == NULL)
|
|
|
|
return -1;
|
|
|
|
return (int)(process->mInfo->CreateTime.QuadPart / 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// BfpSpawn
|
|
|
|
|
|
|
|
struct BfpSpawn
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
String mArgsParamFilePath;
|
|
|
|
HANDLE mHProcess;
|
|
|
|
DWORD mProcessId;
|
|
|
|
HANDLE mStandardInputWritePipeHandle;
|
|
|
|
HANDLE mStandardOutputReadPipeHandle;
|
|
|
|
HANDLE mStandardErrorReadPipeHandle;
|
|
|
|
bool mIsDone;
|
|
|
|
|
|
|
|
public:
|
|
|
|
BfpSpawn()
|
|
|
|
{
|
|
|
|
mHProcess = 0;
|
|
|
|
mProcessId = 0;
|
|
|
|
mStandardInputWritePipeHandle = 0;
|
|
|
|
mStandardOutputReadPipeHandle = 0;
|
|
|
|
mStandardErrorReadPipeHandle = 0;
|
|
|
|
mIsDone = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
~BfpSpawn()
|
|
|
|
{
|
|
|
|
if (IsHandleValid(mHProcess))
|
|
|
|
::CloseHandle(mHProcess);
|
|
|
|
|
|
|
|
if (IsHandleValid(mStandardInputWritePipeHandle))
|
|
|
|
::CloseHandle(mStandardInputWritePipeHandle);
|
|
|
|
if (IsHandleValid(mStandardOutputReadPipeHandle))
|
|
|
|
::CloseHandle(mStandardOutputReadPipeHandle);
|
|
|
|
if (IsHandleValid(mStandardErrorReadPipeHandle))
|
|
|
|
::CloseHandle(mStandardErrorReadPipeHandle);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool CreatePipeWithSecurityAttributes(HANDLE& hReadPipe, HANDLE& hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes, int32 nSize)
|
|
|
|
{
|
|
|
|
hReadPipe = 0;
|
|
|
|
hWritePipe = 0;
|
|
|
|
bool ret = ::CreatePipe(&hReadPipe, &hWritePipe, lpPipeAttributes, nSize);
|
|
|
|
if (!ret || (hReadPipe == INVALID_HANDLE_VALUE) || (hWritePipe == INVALID_HANDLE_VALUE))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Using synchronous Anonymous pipes for process input/output redirection means we would end up
|
|
|
|
// wasting a worker threadpool thread per pipe instance. Overlapped pipe IO is desirable, since
|
|
|
|
// it will take advantage of the NT IO completion port infrastructure. But we can't really use
|
|
|
|
// Overlapped I/O for process input/output as it would break Console apps (managed Console class
|
|
|
|
// methods such as WriteLine as well as native CRT functions like printf) which are making an
|
|
|
|
// assumption that the console standard handles (obtained via GetStdHandle()) are opened
|
|
|
|
// for synchronous I/O and hence they can work fine with ReadFile/WriteFile synchrnously!
|
|
|
|
bool CreatePipe(HANDLE& parentHandle, HANDLE& childHandle, bool parentInputs)
|
|
|
|
{
|
|
|
|
SECURITY_ATTRIBUTES securityAttributesParent = { 0 };
|
|
|
|
securityAttributesParent.bInheritHandle = 1;
|
|
|
|
|
|
|
|
HANDLE hTmp = INVALID_HANDLE_VALUE;
|
|
|
|
if (parentInputs)
|
|
|
|
CreatePipeWithSecurityAttributes(childHandle, hTmp, &securityAttributesParent, 0);
|
|
|
|
else
|
|
|
|
CreatePipeWithSecurityAttributes(hTmp, childHandle, &securityAttributesParent, 0);
|
|
|
|
|
|
|
|
HANDLE dupHandle = 0;
|
|
|
|
|
|
|
|
// Duplicate the parent handle to be non-inheritable so that the child process
|
|
|
|
// doesn't have access. This is done for correctness sake, exact reason is unclear.
|
|
|
|
// One potential theory is that child process can do something brain dead like
|
|
|
|
// closing the parent end of the pipe and there by getting into a blocking situation
|
|
|
|
// as parent will not be draining the pipe at the other end anymore.
|
|
|
|
if (!::DuplicateHandle(GetCurrentProcess(), hTmp,
|
|
|
|
GetCurrentProcess(), &dupHandle,
|
|
|
|
0, false, DUPLICATE_SAME_ACCESS))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
parentHandle = dupHandle;
|
|
|
|
|
|
|
|
if (hTmp != INVALID_HANDLE_VALUE)
|
|
|
|
::CloseHandle(hTmp);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool StartWithCreateProcess(const char* targetPath, const char* args, const char* workingDir, const char* env, BfpSpawnFlags flags, BfpSpawnResult* outResult)
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
String fileName = targetPath;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
String commandLine;
|
|
|
|
|
|
|
|
if ((flags & BfpSpawnFlag_ArgsIncludesTarget) == 0)
|
|
|
|
{
|
|
|
|
if ((!fileName.StartsWith("\"")) && (!fileName.EndsWith("\"")))
|
|
|
|
{
|
|
|
|
bool needsQuoting = false;
|
|
|
|
for (int i = 0; i < (int)fileName.length(); i++)
|
|
|
|
{
|
|
|
|
char c = fileName[i];
|
|
|
|
if (c == ' ')
|
|
|
|
needsQuoting = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (needsQuoting)
|
|
|
|
{
|
|
|
|
commandLine.Append('\"');
|
|
|
|
commandLine.Append(fileName);
|
|
|
|
commandLine.Append('\"');
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
commandLine.Append(fileName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
commandLine.Append(fileName);
|
|
|
|
}
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if ((args != NULL) && (args[0] != '0'))
|
|
|
|
{
|
|
|
|
if (!commandLine.IsEmpty())
|
|
|
|
commandLine.Append(' ');
|
|
|
|
commandLine.Append(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
STARTUPINFOW startupInfo = { 0 };
|
|
|
|
PROCESS_INFORMATION processInfo = { 0 };
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
bool retVal;
|
|
|
|
int32 errorCode = 0;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
//
|
|
|
|
{
|
|
|
|
// set up the streams
|
|
|
|
if ((flags & (BfpSpawnFlag_RedirectStdInput | BfpSpawnFlag_RedirectStdOutput | BfpSpawnFlag_RedirectStdError)) != 0)
|
|
|
|
{
|
|
|
|
if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
|
|
|
|
CreatePipe(mStandardInputWritePipeHandle, startupInfo.hStdInput, true);
|
|
|
|
else if (::GetConsoleWindow() != NULL)
|
|
|
|
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
else
|
2022-01-03 11:24:45 -05:00
|
|
|
startupInfo.hStdInput = INVALID_HANDLE_VALUE;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
|
2021-12-31 10:26:32 -05:00
|
|
|
CreatePipe(mStandardOutputReadPipeHandle, startupInfo.hStdOutput, false);
|
2022-01-03 11:24:45 -05:00
|
|
|
else
|
|
|
|
startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
|
|
|
|
CreatePipe(mStandardErrorReadPipeHandle, startupInfo.hStdError, false);
|
|
|
|
else
|
|
|
|
startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
startupInfo.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set up the creation flags paramater
|
|
|
|
int32 creationFlags = 0;
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
if ((flags & BfpSpawnFlag_NoWindow) != 0)
|
2019-08-23 11:56:54 -07:00
|
|
|
creationFlags |= CREATE_NO_WINDOW;
|
|
|
|
// set up the environment block parameter
|
|
|
|
|
|
|
|
WCHAR* targetStrPtr = NULL;
|
|
|
|
UTF16String targetStrW;
|
|
|
|
if ((flags & BfpSpawnFlag_ArgsIncludesTarget) != 0)
|
|
|
|
{
|
|
|
|
// If we already included the target in the args then the actual target may be different, so we need to explicitly pass that
|
|
|
|
targetStrW = UTF8Decode(targetPath);
|
|
|
|
targetStrPtr = (WCHAR*)targetStrW.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
WCHAR* dirStrPtr = NULL;
|
|
|
|
UTF16String dirStrW;
|
|
|
|
if ((workingDir != NULL) && (workingDir[0] != 0))
|
|
|
|
{
|
|
|
|
dirStrW = UTF8Decode(workingDir);
|
|
|
|
dirStrPtr = (WCHAR*)dirStrW.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
UTF16String envW;
|
2021-01-03 07:04:33 -08:00
|
|
|
void* envVoidPtr = NULL;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if ((env != NULL) && (env[0] != 0))
|
|
|
|
{
|
2021-01-03 07:04:33 -08:00
|
|
|
bool useUnicodeEnv = false;
|
|
|
|
if (useUnicodeEnv)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2021-01-03 07:04:33 -08:00
|
|
|
const char* envPtr = env;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if ((envPtr[0] == 0) && (envPtr[1] == 0))
|
|
|
|
break;
|
|
|
|
envPtr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
int envSize = (int)(envPtr - env) + 2;
|
|
|
|
String str8(env, envSize);
|
|
|
|
envW = UTF8Decode(str8);
|
|
|
|
envVoidPtr = (void*)envW.c_str();
|
|
|
|
startupInfo.dwFlags |= CREATE_UNICODE_ENVIRONMENT;
|
|
|
|
}
|
|
|
|
else
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2021-01-03 07:04:33 -08:00
|
|
|
envVoidPtr = (void*)env;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
retVal = ::CreateProcessW(
|
2022-01-03 11:24:45 -05:00
|
|
|
targetStrPtr,
|
2019-08-23 11:56:54 -07:00
|
|
|
(WCHAR*)UTF8Decode(commandLine).c_str(), // pointer to the command line string
|
|
|
|
NULL, // pointer to process security attributes, we don't need to inherit the handle
|
|
|
|
NULL, // pointer to thread security attributes
|
|
|
|
true, // handle inheritance flag
|
|
|
|
creationFlags, // creation flags
|
2021-01-03 07:04:33 -08:00
|
|
|
envVoidPtr, // pointer to new environment block
|
2019-08-23 11:56:54 -07:00
|
|
|
dirStrPtr, // pointer to current directory name
|
|
|
|
&startupInfo, // pointer to STARTUPINFO
|
|
|
|
&processInfo // pointer to PROCESS_INFORMATION
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!retVal)
|
|
|
|
errorCode = ::GetLastError();
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (processInfo.hThread != INVALID_HANDLE_VALUE)
|
|
|
|
::CloseHandle(processInfo.hThread);
|
|
|
|
|
|
|
|
if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
|
|
|
|
::CloseHandle(startupInfo.hStdInput);
|
|
|
|
if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
|
|
|
|
::CloseHandle(startupInfo.hStdOutput);
|
|
|
|
if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
|
|
|
|
::CloseHandle(startupInfo.hStdError);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (!retVal)
|
|
|
|
{
|
|
|
|
if (IsHandleValid(mStandardInputWritePipeHandle))
|
|
|
|
{
|
|
|
|
::CloseHandle(mStandardInputWritePipeHandle);
|
|
|
|
mStandardInputWritePipeHandle = 0;
|
|
|
|
}
|
|
|
|
if (IsHandleValid(mStandardOutputReadPipeHandle))
|
|
|
|
{
|
|
|
|
::CloseHandle(mStandardOutputReadPipeHandle);
|
|
|
|
mStandardOutputReadPipeHandle = 0;
|
|
|
|
}
|
|
|
|
if (IsHandleValid(mStandardErrorReadPipeHandle))
|
|
|
|
{
|
|
|
|
::CloseHandle(mStandardErrorReadPipeHandle);
|
|
|
|
mStandardErrorReadPipeHandle = 0;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
OUTRESULT(BfpSpawnResult_UnknownError);
|
|
|
|
if (errorCode == ERROR_BAD_EXE_FORMAT || errorCode == ERROR_EXE_MACHINE_TYPE_MISMATCH)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return false;
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
bool ret = false;
|
2019-08-23 11:56:54 -07:00
|
|
|
if (IsHandleValid(processInfo.hProcess))
|
|
|
|
{
|
|
|
|
mHProcess = processInfo.hProcess;
|
2022-01-03 11:24:45 -05:00
|
|
|
mProcessId = processInfo.dwProcessId;
|
2019-08-23 11:56:54 -07:00
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpSpawnResult_UnknownError);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool StartWithShellExecute(const char* targetPath, const char* args, const char* workingDir, const char* env, BfpSpawnFlags flags, BfpSpawnResult* outResult)
|
|
|
|
{
|
|
|
|
SHELLEXECUTEINFOW shellExecuteInfo = { 0 };
|
|
|
|
shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
|
|
|
|
shellExecuteInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
|
|
if ((flags & BfpSpawnFlag_ErrorDialog) != 0)
|
|
|
|
{
|
|
|
|
//shellExecuteInfo.hwnd = startInfo.ErrorDialogParentHandle;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
shellExecuteInfo.fMask |= SEE_MASK_FLAG_NO_UI;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & BfpSpawnFlag_NoWindow) != 0)
|
|
|
|
shellExecuteInfo.nShow = SW_HIDE;
|
|
|
|
else
|
|
|
|
shellExecuteInfo.nShow = SW_SHOWNORMAL;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
UTF16String fileW;
|
|
|
|
UTF16String verbW;
|
|
|
|
UTF16String argsW;
|
|
|
|
UTF16String dirW;
|
|
|
|
|
|
|
|
String target = targetPath;
|
|
|
|
int barPos = (int)target.IndexOf('|');
|
|
|
|
if (barPos != -1)
|
|
|
|
{
|
|
|
|
fileW = UTF8Decode(target.Substring(0, barPos));
|
|
|
|
shellExecuteInfo.lpFile = fileW.c_str();
|
|
|
|
|
|
|
|
verbW = UTF8Decode(target.Substring(barPos + 1));
|
|
|
|
shellExecuteInfo.lpVerb = verbW.c_str();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fileW = UTF8Decode(target);
|
|
|
|
shellExecuteInfo.lpFile = fileW.c_str();
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if ((args != NULL) && (args[0] != 0))
|
|
|
|
{
|
|
|
|
argsW = UTF8Decode(args);
|
|
|
|
shellExecuteInfo.lpParameters = argsW.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((workingDir != NULL) && (workingDir[0] != 0))
|
|
|
|
{
|
|
|
|
dirW = UTF8Decode(workingDir);
|
|
|
|
shellExecuteInfo.lpDirectory = dirW.c_str();
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
shellExecuteInfo.fMask |= SEE_MASK_FLAG_DDEWAIT;
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
BOOL success = ::ShellExecuteExW(&shellExecuteInfo);
|
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
int lastError = ::GetLastError();
|
|
|
|
OUTRESULT(BfpSpawnResult_UnknownError);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mHProcess = shellExecuteInfo.hProcess;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
BFP_EXPORT BfpSpawn* BFP_CALLTYPE BfpSpawn_Create(const char* targetPath, const char* args, const char* workingDir, const char* env, BfpSpawnFlags flags, BfpSpawnResult* outResult)
|
|
|
|
{
|
|
|
|
String newArgs;
|
|
|
|
String tempFileName;
|
|
|
|
|
|
|
|
if ((flags & BfpSpawnFlag_UseArgsFile) != 0)
|
|
|
|
{
|
|
|
|
char tempPathStr[MAX_PATH];
|
|
|
|
::GetTempPathA(MAX_PATH, tempPathStr);
|
|
|
|
|
|
|
|
char tempFileNameStr[MAX_PATH];
|
|
|
|
::GetTempFileNameA(tempPathStr, "BFP", 0, tempFileNameStr);
|
|
|
|
|
|
|
|
tempFileName = tempFileNameStr;
|
|
|
|
|
|
|
|
BfpFileResult fileResult;
|
|
|
|
BfpFile* file = BfpFile_Create(tempFileNameStr, BfpFileCreateKind_CreateAlways, BfpFileCreateFlag_Write, BfpFileAttribute_Normal, &fileResult);
|
|
|
|
if (file == NULL)
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpSpawnResult_TempFileError);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & BfpSpawnFlag_UseArgsFile_Native) != 0)
|
|
|
|
{
|
|
|
|
UTF16String wStr = UTF8Decode(args);
|
|
|
|
|
|
|
|
if ((flags & BfpSpawnFlag_UseArgsFile_BOM) != 0)
|
|
|
|
{
|
|
|
|
uint8 bom[2] = { 0xFF, 0xFE };
|
|
|
|
BfpFile_Write(file, bom, 2, -1, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
BfpFile_Write(file, wStr.c_str(), wStr.length() * 2, -1, NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
BfpFile_Write(file, args, strlen(args), -1, NULL);
|
|
|
|
BfpFile_Release(file);
|
|
|
|
|
|
|
|
newArgs.Append("@");
|
|
|
|
newArgs.Append(tempFileName);
|
|
|
|
if (newArgs.Contains(' '))
|
|
|
|
{
|
|
|
|
newArgs.Insert(0, '\"');
|
|
|
|
newArgs.Append('\"');
|
|
|
|
}
|
|
|
|
|
|
|
|
args = newArgs.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
BfpSpawn* spawn = new BfpSpawn();
|
|
|
|
bool success = false;
|
|
|
|
if ((flags & BfpSpawnFlag_UseShellExecute) != 0)
|
2022-01-03 11:24:45 -05:00
|
|
|
success = spawn->StartWithShellExecute(targetPath, args, workingDir, env, flags, outResult);
|
2019-08-23 11:56:54 -07:00
|
|
|
else
|
2022-01-03 11:24:45 -05:00
|
|
|
success = spawn->StartWithCreateProcess(targetPath, args, workingDir, env, flags, outResult);
|
2019-08-23 11:56:54 -07:00
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
delete spawn;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
spawn->mArgsParamFilePath = tempFileName;
|
|
|
|
return spawn;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSpawn_Release(BfpSpawn* spawn)
|
|
|
|
{
|
|
|
|
if ((!spawn->mArgsParamFilePath.IsEmpty()) && (spawn->mIsDone))
|
|
|
|
BfpFile_Delete(spawn->mArgsParamFilePath.c_str(), NULL);
|
|
|
|
|
|
|
|
delete spawn;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static BOOL CALLBACK TerminateAppEnum(HWND hwnd, LPARAM lParam)
|
|
|
|
// {
|
|
|
|
// DWORD dwID;
|
|
|
|
// GetWindowThreadProcessId(hwnd, &dwID);
|
|
|
|
//
|
|
|
|
// if (dwID == (DWORD)lParam)
|
|
|
|
// {
|
|
|
|
// PostMessage(hwnd, WM_CLOSE, 0, 0);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// return TRUE;
|
|
|
|
// }
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSpawn_Kill(BfpSpawn* spawn, int exitCode, BfpKillFlags killFlags, BfpSpawnResult* outResult)
|
|
|
|
{
|
|
|
|
//::EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM)spawn->mProcessId);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if ((killFlags & BfpKillFlag_KillChildren) != 0)
|
|
|
|
{
|
|
|
|
ImportNTDll();
|
|
|
|
|
|
|
|
HashSet<int> killSet;
|
|
|
|
killSet.Add(spawn->mProcessId);
|
|
|
|
HashSet<int> killedSet;
|
|
|
|
killedSet.Add(spawn->mProcessId);
|
|
|
|
|
|
|
|
// New child processes can launch between the NtQuerySystemInformation call and the actual process termination,
|
|
|
|
// so we need to run multiple loops until we stop finding child processes
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
uint allocSize = 8192;
|
|
|
|
uint8* data;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
data = new uint8[allocSize];
|
|
|
|
|
|
|
|
ULONG wantSize = 0;
|
|
|
|
NTSTATUS status = gNtQuerySystemInformation(SystemProcessInformation, data, allocSize, &wantSize);
|
|
|
|
|
|
|
|
if (status != STATUS_INFO_LENGTH_MISMATCH)
|
|
|
|
{
|
|
|
|
bool foundNew;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
foundNew = false;
|
|
|
|
auto processInfo = (SYSTEM_PROCESS_INFORMATION*)data;
|
|
|
|
|
|
|
|
auto curProcessInfo = processInfo;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (curProcessInfo == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (killSet.Contains((int)(intptr)curProcessInfo->InheritedFromProcessId))
|
|
|
|
{
|
|
|
|
if (killSet.Add((int)(intptr)curProcessInfo->ProcessId))
|
|
|
|
foundNew = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (curProcessInfo->NextEntryOffset == 0)
|
|
|
|
break;
|
|
|
|
curProcessInfo = (SYSTEM_PROCESS_INFORMATION*)((intptr)curProcessInfo + curProcessInfo->NextEntryOffset);
|
|
|
|
}
|
|
|
|
} while (foundNew);
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
allocSize = wantSize + 4096;
|
|
|
|
delete data;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (killedSet.size() == killSet.size())
|
|
|
|
break;
|
|
|
|
|
|
|
|
for (auto pid : killSet)
|
|
|
|
{
|
|
|
|
if (!killedSet.Add(pid))
|
2022-01-03 11:24:45 -05:00
|
|
|
continue;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
HANDLE procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
|
|
|
|
if (procHandle != NULL)
|
|
|
|
{
|
|
|
|
::TerminateProcess(procHandle, exitCode);
|
|
|
|
// DWORD threadId = 0;
|
|
|
|
// ::CreateRemoteThread(procHandle, NULL, 0, (LPTHREAD_START_ROUTINE)ExitProcess, (void*)(intptr)exitCode, 0, &threadId);
|
|
|
|
// if (pid != spawn->mProcessId)
|
|
|
|
// ::CloseHandle(procHandle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (!::TerminateProcess(spawn->mHProcess, (UINT)exitCode))
|
|
|
|
{
|
|
|
|
int lastError = ::GetLastError();
|
|
|
|
OUTRESULT(BfpSpawnResult_UnknownError);
|
|
|
|
return;
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
// BOOL hadConsole = ::FreeConsole();
|
|
|
|
// ::AttachConsole(spawn->mProcessId);
|
|
|
|
// ::SetConsoleCtrlHandler(NULL, true);
|
|
|
|
// ::GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
|
|
|
|
// //::Sleep(2000);
|
|
|
|
// ::FreeConsole();
|
|
|
|
// ::SetConsoleCtrlHandler(NULL, false);
|
|
|
|
|
|
|
|
// if (!::TerminateProcess(spawn->mHProcess, (UINT)exitCode))
|
|
|
|
// {
|
|
|
|
// int lastError = ::GetLastError();
|
|
|
|
// OUTRESULT(BfpSpawnResult_UnknownError);
|
|
|
|
// return;
|
|
|
|
// }
|
2019-08-23 11:56:54 -07:00
|
|
|
OUTRESULT(BfpSpawnResult_Ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT bool BFP_CALLTYPE BfpSpawn_WaitFor(BfpSpawn* spawn, int waitMS, int* outExitCode, BfpSpawnResult* outResult)
|
|
|
|
{
|
|
|
|
if (::WaitForSingleObject(spawn->mHProcess, waitMS) != WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpSpawnResult_UnknownError);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
spawn->mIsDone = true;
|
|
|
|
::GetExitCodeProcess(spawn->mHProcess, (DWORD*)outExitCode);
|
|
|
|
OUTRESULT(BfpSpawnResult_Ok);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpSpawn_GetStdHandles(BfpSpawn* spawn, BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
|
|
|
|
{
|
|
|
|
if (outStdIn != NULL)
|
|
|
|
{
|
|
|
|
*outStdIn = new BfpFile(spawn->mStandardInputWritePipeHandle);
|
2022-01-03 11:24:45 -05:00
|
|
|
spawn->mStandardInputWritePipeHandle = 0;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (outStdOut != NULL)
|
|
|
|
{
|
|
|
|
*outStdOut = new BfpFile(spawn->mStandardOutputReadPipeHandle);
|
2022-01-03 11:24:45 -05:00
|
|
|
spawn->mStandardOutputReadPipeHandle = 0;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (outStdErr != NULL)
|
|
|
|
{
|
|
|
|
*outStdErr = new BfpFile(spawn->mStandardErrorReadPipeHandle);
|
2022-01-03 11:24:45 -05:00
|
|
|
spawn->mStandardErrorReadPipeHandle = 0;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// BfpThread
|
|
|
|
|
|
|
|
BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_Create(BfpThreadStartProc startProc, void* threadParam, intptr stackSize, BfpThreadCreateFlags flags, BfpThreadId* outThreadId)
|
|
|
|
{
|
|
|
|
DWORD creationFlags = 0;
|
|
|
|
if ((flags & BfpThreadCreateFlag_Suspended) != 0)
|
|
|
|
creationFlags |= CREATE_SUSPENDED;
|
|
|
|
if (((flags & BfpThreadCreateFlag_StackSizeReserve) != 0) && (stackSize != 0))
|
|
|
|
creationFlags |= STACK_SIZE_PARAM_IS_A_RESERVATION;
|
|
|
|
|
|
|
|
DWORD threadId;
|
|
|
|
// We have an explicit LPTHREAD_START_ROUTINE cast because BfpThreadStartProc returns 'void'. This doesn't break the calling convention, it just means
|
|
|
|
// the return register value is garbage
|
|
|
|
HANDLE handle = ::CreateThread(NULL, (SIZE_T)stackSize, (LPTHREAD_START_ROUTINE)startProc, threadParam, creationFlags, &threadId);
|
|
|
|
if (outThreadId != NULL)
|
|
|
|
*outThreadId = threadId;
|
|
|
|
|
|
|
|
return (BfpThread*)handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThread_Release(BfpThread* thread)
|
|
|
|
{
|
|
|
|
::CloseHandle((HANDLE)thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SetThreadName(DWORD threadId, const char* name)
|
|
|
|
{
|
|
|
|
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
|
|
|
|
|
|
|
typedef struct tagTHREADNAME_INFO
|
|
|
|
{
|
|
|
|
DWORD dwType; // Must be 0x1000.
|
|
|
|
LPCSTR szName; // Pointer to name (in user addr space).
|
|
|
|
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
|
|
|
DWORD dwFlags; // Reserved for future use, must be zero.
|
|
|
|
} THREADNAME_INFO;
|
|
|
|
|
|
|
|
THREADNAME_INFO info;
|
|
|
|
info.dwType = 0x1000;
|
|
|
|
info.szName = name;
|
|
|
|
info.dwThreadID = threadId;
|
|
|
|
info.dwFlags = 0;
|
|
|
|
|
|
|
|
__try
|
|
|
|
{
|
|
|
|
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
|
|
|
|
}
|
|
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThread_SetName(BfpThread* thread, const char* name, BfpThreadResult* outResult)
|
|
|
|
{
|
|
|
|
ImportKernel();
|
|
|
|
|
|
|
|
HANDLE hThread = (HANDLE)thread;
|
|
|
|
if (hThread == NULL)
|
|
|
|
hThread = ::GetCurrentThread();
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
if (gSetThreadDescription != NULL)
|
|
|
|
{
|
|
|
|
gSetThreadDescription(hThread, UTF8Decode(name).c_str());
|
|
|
|
|
|
|
|
OUTRESULT(BfpThreadResult_Ok);
|
|
|
|
return;
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
SetThreadName(::GetThreadId(hThread), name);
|
|
|
|
|
|
|
|
OUTRESULT(BfpThreadResult_UnknownError);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThread_GetName(BfpThread* thread, char* outName, int* inOutNameSize, BfpThreadResult* outResult)
|
|
|
|
{
|
|
|
|
ImportKernel();
|
|
|
|
|
|
|
|
PWSTR wStr = NULL;
|
|
|
|
if (gGetThreadDescription != NULL)
|
|
|
|
{
|
|
|
|
gGetThreadDescription((HANDLE)thread, &wStr);
|
|
|
|
if (wStr == NULL)
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpThreadResult_UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
String str = UTF8Encode(wStr);
|
|
|
|
TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
|
|
|
|
|
|
|
|
LocalFree(wStr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
OUTRESULT(BfpThreadResult_UnknownError);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_GetCurrent()
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
return (BfpThread*)::OpenThread(THREAD_ALL_ACCESS, TRUE, ::GetCurrentThreadId());
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpThreadId BFP_CALLTYPE BfpThread_GetCurrentId()
|
|
|
|
{
|
|
|
|
return (BfpThreadId)::GetCurrentThreadId();
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT bool BFP_CALLTYPE BfpThread_WaitFor(BfpThread* thread, int waitMS)
|
|
|
|
{
|
|
|
|
return ::WaitForSingleObject((HANDLE)thread, waitMS) == WAIT_OBJECT_0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpThreadPriority BFP_CALLTYPE BfpThread_GetPriority(BfpThread* thread, BfpThreadResult* outResult)
|
|
|
|
{
|
|
|
|
return (BfpThreadPriority)::GetThreadPriority((HANDLE)thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThread_SetPriority(BfpThread* thread, BfpThreadPriority threadPriority, BfpThreadResult* outResult)
|
|
|
|
{
|
|
|
|
// Coincidentally, our priority values map to (THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST)
|
2022-01-03 11:24:45 -05:00
|
|
|
if (::SetThreadPriority((HANDLE)thread, (int)threadPriority))
|
|
|
|
OUTRESULT(BfpThreadResult_Ok);
|
2019-08-23 11:56:54 -07:00
|
|
|
else
|
|
|
|
OUTRESULT(BfpThreadResult_UnknownError);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThread_Suspend(BfpThread* thread, BfpThreadResult* outResult)
|
|
|
|
{
|
|
|
|
DWORD suspendCount = ::SuspendThread((HANDLE)thread);
|
|
|
|
if (suspendCount == (DWORD)-1)
|
|
|
|
{
|
|
|
|
int error = GetLastError();
|
|
|
|
BF_DBG_FATAL("Failed BfpThread_Suspend");
|
2022-01-03 11:24:45 -05:00
|
|
|
OUTRESULT(BfpThreadResult_UnknownError);
|
2019-08-23 11:56:54 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
OUTRESULT(BfpThreadResult_Ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThread_Resume(BfpThread* thread, BfpThreadResult* outResult)
|
|
|
|
{
|
|
|
|
DWORD suspendCount = ::ResumeThread((HANDLE)thread);
|
|
|
|
if (suspendCount == (DWORD)-1)
|
|
|
|
{
|
|
|
|
int error = GetLastError();
|
|
|
|
BF_DBG_FATAL("Failed BfpThread_Resume");
|
|
|
|
OUTRESULT(BfpThreadResult_UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
OUTRESULT(BfpThreadResult_Ok);
|
|
|
|
}
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
|
|
|
|
// Windows 7 SP1 is the first version of Windows to support the AVX API.
|
|
|
|
|
|
|
|
// The value for CONTEXT_XSTATE has changed between Windows 7 and
|
|
|
|
// Windows 7 SP1 and greater.
|
|
|
|
// While the value will be correct for future SDK headers, we need to set
|
|
|
|
// this value manually when building with a Windows 7 SDK for running on
|
|
|
|
// Windows 7 SPI OS bits.
|
|
|
|
|
|
|
|
#undef CONTEXT_XSTATE
|
|
|
|
|
|
|
|
#if defined(_M_X64)
|
|
|
|
#define CONTEXT_XSTATE (0x00100040)
|
|
|
|
#else
|
|
|
|
#define CONTEXT_XSTATE (0x00010040)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Since the AVX API is not declared in the Windows 7 SDK headers and
|
|
|
|
// since we don't have the proper libs to work with, we will declare
|
|
|
|
// the API as function pointers and get them with GetProcAddress calls
|
|
|
|
// from kernel32.dll. We also need to set some #defines.
|
|
|
|
|
|
|
|
#define XSTATE_AVX (XSTATE_GSSE)
|
|
|
|
#define XSTATE_MASK_AVX (XSTATE_MASK_GSSE)
|
|
|
|
|
|
|
|
typedef DWORD64(WINAPI* PGETENABLEDXSTATEFEATURES)();
|
|
|
|
static PGETENABLEDXSTATEFEATURES pfnGetEnabledXStateFeatures = NULL;
|
|
|
|
|
|
|
|
typedef BOOL(WINAPI* PINITIALIZECONTEXT)(PVOID Buffer, DWORD ContextFlags, PCONTEXT* Context, PDWORD ContextLength);
|
|
|
|
static PINITIALIZECONTEXT pfnInitializeContext = NULL;
|
|
|
|
|
|
|
|
typedef BOOL(WINAPI* PGETXSTATEFEATURESMASK)(PCONTEXT Context, PDWORD64 FeatureMask);
|
|
|
|
static PGETXSTATEFEATURESMASK pfnGetXStateFeaturesMask = NULL;
|
|
|
|
|
|
|
|
typedef PVOID(WINAPI* LOCATEXSTATEFEATURE)(PCONTEXT Context, DWORD FeatureId, PDWORD Length);
|
|
|
|
static LOCATEXSTATEFEATURE pfnLocateXStateFeature = NULL;
|
|
|
|
|
|
|
|
typedef BOOL(WINAPI* SETXSTATEFEATURESMASK)(PCONTEXT Context, DWORD64 FeatureMask);
|
|
|
|
static SETXSTATEFEATURESMASK pfnSetXStateFeaturesMask = NULL;
|
|
|
|
|
|
|
|
static uint8 ContextBuffer[4096];
|
|
|
|
static CONTEXT* CaptureRegistersEx(HANDLE hThread, intptr*& curPtr)
|
|
|
|
{
|
|
|
|
PCONTEXT Context;
|
|
|
|
DWORD ContextSize;
|
|
|
|
DWORD64 FeatureMask;
|
|
|
|
DWORD FeatureLength;
|
|
|
|
BOOL Success;
|
|
|
|
PM128A Xmm;
|
|
|
|
PM128A Ymm;
|
|
|
|
|
|
|
|
if (pfnGetEnabledXStateFeatures == (PGETENABLEDXSTATEFEATURES)-1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (pfnGetEnabledXStateFeatures == NULL)
|
|
|
|
{
|
|
|
|
HMODULE hm = GetModuleHandleA("kernel32.dll");
|
|
|
|
if (hm == NULL)
|
|
|
|
{
|
|
|
|
pfnGetEnabledXStateFeatures = (PGETENABLEDXSTATEFEATURES)-1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pfnGetEnabledXStateFeatures = (PGETENABLEDXSTATEFEATURES)GetProcAddress(hm, "GetEnabledXStateFeatures");
|
|
|
|
pfnInitializeContext = (PINITIALIZECONTEXT)GetProcAddress(hm, "InitializeContext");
|
|
|
|
pfnGetXStateFeaturesMask = (PGETXSTATEFEATURESMASK)GetProcAddress(hm, "GetXStateFeaturesMask");
|
|
|
|
pfnLocateXStateFeature = (LOCATEXSTATEFEATURE)GetProcAddress(hm, "LocateXStateFeature");
|
|
|
|
pfnSetXStateFeaturesMask = (SETXSTATEFEATURESMASK)GetProcAddress(hm, "SetXStateFeaturesMask");
|
|
|
|
|
|
|
|
if (pfnGetEnabledXStateFeatures == NULL
|
|
|
|
|| pfnInitializeContext == NULL
|
|
|
|
|| pfnGetXStateFeaturesMask == NULL
|
|
|
|
|| pfnLocateXStateFeature == NULL
|
|
|
|
|| pfnSetXStateFeaturesMask == NULL)
|
|
|
|
{
|
|
|
|
pfnGetEnabledXStateFeatures = (PGETENABLEDXSTATEFEATURES)-1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FeatureMask = pfnGetEnabledXStateFeatures();
|
|
|
|
if ((FeatureMask & XSTATE_MASK_AVX) == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ContextSize = 0;
|
|
|
|
Success = pfnInitializeContext(NULL,
|
|
|
|
CONTEXT_ALL | CONTEXT_XSTATE | CONTEXT_EXCEPTION_REQUEST,
|
|
|
|
NULL,
|
|
|
|
&ContextSize);
|
|
|
|
|
|
|
|
if (ContextSize > sizeof(ContextBuffer))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
Success = pfnInitializeContext(ContextBuffer,
|
|
|
|
CONTEXT_ALL | CONTEXT_XSTATE | CONTEXT_EXCEPTION_REQUEST,
|
|
|
|
&Context,
|
|
|
|
&ContextSize);
|
|
|
|
|
|
|
|
if (Success == FALSE)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
Success = pfnSetXStateFeaturesMask(Context, XSTATE_MASK_AVX);
|
|
|
|
if (Success == FALSE)
|
|
|
|
return Context;
|
|
|
|
|
|
|
|
Success = GetThreadContext(hThread, Context);
|
|
|
|
if (Success == FALSE)
|
|
|
|
return Context;
|
|
|
|
|
|
|
|
Success = pfnGetXStateFeaturesMask(Context, &FeatureMask);
|
|
|
|
if (Success == FALSE)
|
|
|
|
return Context;
|
|
|
|
|
|
|
|
if ((FeatureMask & XSTATE_MASK_AVX) == 0)
|
|
|
|
return Context;
|
|
|
|
|
|
|
|
Xmm = (PM128A)pfnLocateXStateFeature(Context, XSTATE_LEGACY_SSE, &FeatureLength);
|
|
|
|
Ymm = (PM128A)pfnLocateXStateFeature(Context, XSTATE_AVX, NULL);
|
|
|
|
memcpy(curPtr, Ymm, FeatureLength);
|
|
|
|
curPtr += FeatureLength / sizeof(intptr);
|
|
|
|
return Context;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThread_GetIntRegisters(BfpThread* thread, intptr* outStackPtr, intptr* outIntRegs, int* inOutIntRegCount, BfpThreadResult* outResult)
|
|
|
|
{
|
|
|
|
CONTEXT ctx;
|
2022-01-03 11:24:45 -05:00
|
|
|
intptr* curPtr = outIntRegs;
|
|
|
|
CONTEXT* ctxPtr = NULL;
|
|
|
|
|
|
|
|
if (*inOutIntRegCount > 48)
|
|
|
|
ctxPtr = CaptureRegistersEx((HANDLE)thread, curPtr);
|
|
|
|
|
|
|
|
if (ctxPtr == NULL)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
memset(&ctx, 0, sizeof(CONTEXT));
|
|
|
|
ctx.ContextFlags = CONTEXT_ALL;
|
|
|
|
BOOL success = ::GetThreadContext((HANDLE)thread, (CONTEXT*)&ctx);
|
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
int error = GetLastError();
|
|
|
|
OUTRESULT(BfpThreadResult_UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ctxPtr = &ctx;
|
|
|
|
|
|
|
|
DWORD lastError = GetLastError();
|
|
|
|
BF_ASSERT(success);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef BF32
|
2022-01-03 11:24:45 -05:00
|
|
|
* outStackPtr = (intptr)ctxPtr->Esp;
|
|
|
|
if (*inOutIntRegCount < (int)(curPtr - outIntRegs) + 7)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
OUTRESULT(BfpThreadResult_InsufficientBuffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
2022-01-03 11:24:45 -05:00
|
|
|
* outStackPtr = (intptr)ctxPtr->Rsp;
|
|
|
|
if (*inOutIntRegCount < (int)(curPtr - outIntRegs) + 48)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
OUTRESULT(BfpThreadResult_InsufficientBuffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
OUTRESULT(BfpThreadResult_Ok);
|
|
|
|
|
|
|
|
if (outIntRegs == NULL)
|
|
|
|
return;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
#ifdef BF32
|
2022-01-03 11:24:45 -05:00
|
|
|
* (curPtr++) = (intptr)ctxPtr->Eax;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Ebx;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Ecx;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Edx;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Esi;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Edi;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Ebp;
|
2019-08-23 11:56:54 -07:00
|
|
|
#else
|
2022-01-03 11:24:45 -05:00
|
|
|
* (curPtr++) = (intptr)ctxPtr->SegFs; // Testing
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Rax;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Rbx;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Rcx;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Rdx;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Rsi;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Rdi;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->Rbp;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->R8;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->R9;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->R10;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->R11;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->R12;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->R13;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->R14;
|
|
|
|
*(curPtr++) = (intptr)ctxPtr->R15;
|
|
|
|
memcpy(curPtr, &ctxPtr->Xmm0, 16 * 16);
|
|
|
|
curPtr += (16 * 16) / sizeof(intptr);
|
2019-08-23 11:56:54 -07:00
|
|
|
#endif
|
2022-01-03 11:24:45 -05:00
|
|
|
|
|
|
|
* inOutIntRegCount = (int)(curPtr - outIntRegs);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
struct BfpCritSect
|
2020-05-16 10:07:17 -07:00
|
|
|
{
|
|
|
|
CRITICAL_SECTION mCritSect;
|
|
|
|
};
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThread_Sleep(int sleepMS)
|
|
|
|
{
|
|
|
|
::Sleep(sleepMS);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT bool BFP_CALLTYPE BfpThread_Yield()
|
|
|
|
{
|
|
|
|
return ::SwitchToThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
struct BfpThreadInfo
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
intptr mStackBase;
|
|
|
|
intptr mStackLimit;
|
2020-05-16 10:07:17 -07:00
|
|
|
NT_TIB* mTeb;
|
2019-08-23 11:56:54 -07:00
|
|
|
};
|
|
|
|
|
2020-05-16 10:07:17 -07:00
|
|
|
BFP_EXPORT BfpThreadInfo* BFP_CALLTYPE BfpThreadInfo_Create()
|
|
|
|
{
|
|
|
|
BfpThreadInfo* threadInfo = new BfpThreadInfo();
|
|
|
|
threadInfo->mStackBase = 0;
|
|
|
|
threadInfo->mStackLimit = 0;
|
|
|
|
threadInfo->mTeb = (NT_TIB*)NtCurrentTeb();
|
|
|
|
return threadInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThreadInfo_Release(BfpThreadInfo* threadInfo)
|
|
|
|
{
|
|
|
|
delete threadInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
static __declspec(thread) BfpThreadInfo gThreadStackInfo;
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpThreadInfo_GetStackInfo(BfpThreadInfo* threadInfo, intptr* outStackBase, int* outStackLimit, BfpThreadInfoFlags flags, BfpThreadResult* outResult)
|
|
|
|
{
|
|
|
|
if (threadInfo == NULL)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2020-05-16 10:07:17 -07:00
|
|
|
threadInfo = &gThreadStackInfo;
|
|
|
|
if (threadInfo->mTeb == NULL)
|
|
|
|
threadInfo->mTeb = (NT_TIB*)NtCurrentTeb();
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2020-05-16 10:07:17 -07:00
|
|
|
if ((threadInfo->mStackBase == 0) || ((flags & BfpThreadInfoFlags_NoCache) != 0))
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
MEMORY_BASIC_INFORMATION stackInfo = { 0 };
|
|
|
|
// We subtract one page for our request. VirtualQuery rounds UP to the next page.
|
|
|
|
// Unfortunately, the stack grows down. If we're on the first page (last page in the
|
|
|
|
// VirtualAlloc), we'll be moved to the next page, which is off the stack! Note this
|
|
|
|
// doesn't work right for IA64 due to bigger pages.
|
|
|
|
void* currentAddr = (void*)((intptr_t)&stackInfo - 4096);
|
|
|
|
|
|
|
|
// Query for the current stack allocation information.
|
|
|
|
VirtualQuery(currentAddr, &stackInfo, sizeof(MEMORY_BASIC_INFORMATION));
|
|
|
|
|
2020-05-16 10:07:17 -07:00
|
|
|
threadInfo->mStackBase = (uintptr_t)threadInfo->mTeb->StackBase;
|
|
|
|
threadInfo->mStackLimit = (uintptr_t)stackInfo.AllocationBase;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2020-05-16 10:07:17 -07:00
|
|
|
*outStackBase = (intptr)threadInfo->mStackBase;
|
|
|
|
*outStackLimit = (int)(threadInfo->mStackBase - threadInfo->mStackLimit);
|
2019-08-23 11:56:54 -07:00
|
|
|
OUTRESULT(BfpThreadResult_Ok);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
BFP_EXPORT BfpCritSect* BFP_CALLTYPE BfpCritSect_Create()
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
BfpCritSect* critSect = new BfpCritSect();
|
|
|
|
::InitializeCriticalSection(&critSect->mCritSect);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
return critSect;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Release(BfpCritSect* critSect)
|
|
|
|
{
|
|
|
|
::DeleteCriticalSection(&critSect->mCritSect);
|
|
|
|
delete critSect;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Enter(BfpCritSect* critSect)
|
|
|
|
{
|
|
|
|
::EnterCriticalSection(&critSect->mCritSect);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT bool BFP_CALLTYPE BfpCritSect_TryEnter(BfpCritSect* critSect, int waitMS)
|
|
|
|
{
|
|
|
|
if (waitMS == 0)
|
|
|
|
{
|
|
|
|
return ::TryEnterCriticalSection(&critSect->mCritSect);
|
|
|
|
}
|
|
|
|
else if (waitMS == -1)
|
|
|
|
{
|
|
|
|
BfpCritSect_Enter(critSect);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a poor implementation. We should use a mutex if this is required
|
|
|
|
uint32 start = BFTickCount();
|
|
|
|
while ((int)(BFTickCount() - start) < waitMS)
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
if (::TryEnterCriticalSection(&critSect->mCritSect))
|
2019-08-23 11:56:54 -07:00
|
|
|
return true;
|
|
|
|
BfpThread_Yield();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Leave(BfpCritSect* critSect)
|
|
|
|
{
|
|
|
|
::LeaveCriticalSection(&critSect->mCritSect);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DWORD_TO_BFPTLS(val) ((BfpTLS*)(intptr)(val))
|
|
|
|
#define BFPTLS_TO_DWORD(val) ((DWORD)(intptr)(val))
|
|
|
|
|
|
|
|
struct BfpTLS;
|
2022-07-04 10:20:38 -07:00
|
|
|
BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create(BfpTLSProc exitProc)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2022-07-04 10:20:38 -07:00
|
|
|
return DWORD_TO_BFPTLS(::FlsAlloc(exitProc));
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpTLS_Release(BfpTLS* tls)
|
|
|
|
{
|
2022-07-04 10:20:38 -07:00
|
|
|
::FlsFree(BFPTLS_TO_DWORD(tls));
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpTLS_SetValue(BfpTLS* tls, void* value)
|
|
|
|
{
|
2022-07-04 10:20:38 -07:00
|
|
|
::FlsSetValue(BFPTLS_TO_DWORD(tls), value);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void* BFP_CALLTYPE BfpTLS_GetValue(BfpTLS* tls)
|
|
|
|
{
|
2022-07-04 10:20:38 -07:00
|
|
|
return ::FlsGetValue(BFPTLS_TO_DWORD(tls));
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpEvent* BFP_CALLTYPE BfpEvent_Create(BfpEventFlags flags)
|
|
|
|
{
|
|
|
|
BfpEvent* event = new BfpEvent();
|
|
|
|
::InitializeCriticalSection(&event->mCritSect);
|
|
|
|
::InitializeConditionVariable(&event->mCondVariable);
|
|
|
|
event->mSet = (flags & (BfpEventFlag_InitiallySet_Auto | BfpEventFlag_InitiallySet_Manual)) != 0;
|
|
|
|
event->mManualReset = (flags & BfpEventFlag_InitiallySet_Manual) != 0;
|
|
|
|
return event;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpEvent_Release(BfpEvent* event)
|
|
|
|
{
|
|
|
|
::DeleteCriticalSection(&event->mCritSect);
|
|
|
|
delete event;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpEvent_Set(BfpEvent* event, bool requireManualReset)
|
|
|
|
{
|
|
|
|
::EnterCriticalSection(&event->mCritSect);
|
|
|
|
event->mSet = true;
|
|
|
|
if (requireManualReset)
|
|
|
|
event->mManualReset = true;
|
|
|
|
if (event->mManualReset)
|
|
|
|
::WakeAllConditionVariable(&event->mCondVariable);
|
|
|
|
else
|
|
|
|
::WakeConditionVariable(&event->mCondVariable);
|
|
|
|
::LeaveCriticalSection(&event->mCritSect);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpEvent_Reset(BfpEvent* event, BfpEventResult* outResult)
|
|
|
|
{
|
|
|
|
event->mSet = false;
|
|
|
|
event->mManualReset = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT bool BFP_CALLTYPE BfpEvent_WaitFor(BfpEvent* event, int waitMS)
|
|
|
|
{
|
|
|
|
::EnterCriticalSection(&event->mCritSect);
|
|
|
|
while (!event->mSet)
|
|
|
|
{
|
|
|
|
if (!SleepConditionVariableCS(&event->mCondVariable, &event->mCritSect, waitMS))
|
|
|
|
{
|
|
|
|
if (GetLastError() == ERROR_TIMEOUT)
|
|
|
|
{
|
|
|
|
// Timeout
|
|
|
|
LeaveCriticalSection(&event->mCritSect);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!event->mManualReset)
|
|
|
|
event->mSet = false;
|
|
|
|
::LeaveCriticalSection(&event->mCritSect);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct BfpDynLib;
|
|
|
|
BFP_EXPORT BfpDynLib* BFP_CALLTYPE BfpDynLib_Load(const char* fileName)
|
|
|
|
{
|
|
|
|
UTF16String wPath = UTF8Decode(fileName);
|
|
|
|
return (BfpDynLib*)::LoadLibraryW(wPath.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpDynLib_Release(BfpDynLib* lib)
|
|
|
|
{
|
|
|
|
::FreeLibrary((HMODULE)lib);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpDynLib_GetFilePath(BfpDynLib* lib, char* outPath, int* inOutPathSize, BfpLibResult* outResult)
|
|
|
|
{
|
|
|
|
Beefy::String path;
|
|
|
|
|
|
|
|
WCHAR cPath[4096];
|
|
|
|
::GetModuleFileNameW((HMODULE)lib, cPath, 4096);
|
|
|
|
path = UTF8Encode(cPath);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
TryStringOut(path, outPath, inOutPathSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void* BFP_CALLTYPE BfpDynLib_GetProcAddress(BfpDynLib* lib, const char* name)
|
|
|
|
{
|
|
|
|
return ::GetProcAddress((HMODULE)lib, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct BfpFindFileData;
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Create(const char* path, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
UTF16String wPath = UTF8Decode(path);
|
|
|
|
if (!::CreateDirectoryW(wPath.c_str(), NULL))
|
|
|
|
{
|
2019-10-29 04:56:42 -07:00
|
|
|
int lastError = ::GetLastError();
|
|
|
|
switch (lastError)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
case ERROR_ALREADY_EXISTS:
|
|
|
|
OUTRESULT(BfpFileResult_AlreadyExists);
|
|
|
|
break;
|
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
|
|
OUTRESULT(BfpFileResult_NotFound);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
break;
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Rename(const char* oldName, const char* newName, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
UTF16String wOldPath = UTF8Decode(oldName);
|
|
|
|
UTF16String wNewPath = UTF8Decode(newName);
|
|
|
|
if (!::MoveFileW(wOldPath.c_str(), wNewPath.c_str()))
|
|
|
|
{
|
|
|
|
int err = ::GetLastError();
|
|
|
|
switch (err)
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
case ERROR_ALREADY_EXISTS:
|
2019-08-23 11:56:54 -07:00
|
|
|
OUTRESULT(BfpFileResult_AlreadyExists);
|
|
|
|
break;
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
|
|
OUTRESULT(BfpFileResult_NotFound);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Delete(const char* path, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
UTF16String wPath = UTF8Decode(path);
|
|
|
|
if (!::RemoveDirectoryW(wPath.c_str()))
|
|
|
|
{
|
|
|
|
switch (GetLastError())
|
|
|
|
{
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
|
|
OUTRESULT(BfpFileResult_NotFound);
|
|
|
|
break;
|
2019-09-12 09:47:56 -07:00
|
|
|
case ERROR_DIR_NOT_EMPTY:
|
|
|
|
OUTRESULT(BfpFileResult_NotEmpty);
|
|
|
|
break;
|
2019-08-23 11:56:54 -07:00
|
|
|
default:
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT bool BFP_CALLTYPE BfpDirectory_Exists(const char* path)
|
|
|
|
{
|
|
|
|
WIN32_FIND_DATAW findData;
|
|
|
|
|
|
|
|
UTF16String wpath = UTF8Decode(path);
|
|
|
|
if (wpath.length() > 0)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
uint16& endC = wpath[wpath.length() - 1];
|
|
|
|
if ((endC == '\\') || (endC == '/'))
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
wpath.pop_back();
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
HANDLE handleVal = FindFirstFileW(wpath.c_str(), &findData);
|
|
|
|
if (handleVal == INVALID_HANDLE_VALUE)
|
|
|
|
return false;
|
2022-01-03 11:24:45 -05:00
|
|
|
FindClose(handleVal);
|
2019-08-23 11:56:54 -07:00
|
|
|
return (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetSysDirectory(BfpSysDirectoryKind sysDirKind, char* outPath, int* inOutPathLen, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
String path;
|
|
|
|
|
|
|
|
auto _GetKnownFolder = [&](REFKNOWNFOLDERID rfid)
|
2020-01-18 14:42:18 -08:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
PWSTR pStrPtr;
|
2020-01-18 14:42:18 -08:00
|
|
|
int result = SHGetKnownFolderPath(rfid, KF_FLAG_CREATE | KF_FLAG_SIMPLE_IDLIST, NULL, &pStrPtr);
|
2019-08-23 11:56:54 -07:00
|
|
|
if (result != 0)
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
path = UTF8Encode(pStrPtr);
|
|
|
|
CoTaskMemFree(pStrPtr);
|
|
|
|
TryStringOut(path, outPath, inOutPathLen, (BfpResult*)outResult);
|
|
|
|
};
|
|
|
|
|
|
|
|
switch (sysDirKind)
|
|
|
|
{
|
|
|
|
case BfpSysDirectoryKind_Default:
|
|
|
|
_GetKnownFolder(FOLDERID_Desktop);
|
|
|
|
break;
|
|
|
|
case BfpSysDirectoryKind_Home:
|
|
|
|
_GetKnownFolder(FOLDERID_Profile);
|
|
|
|
break;
|
|
|
|
case BfpSysDirectoryKind_System:
|
|
|
|
_GetKnownFolder(FOLDERID_System);
|
|
|
|
break;
|
|
|
|
case BfpSysDirectoryKind_Desktop:
|
|
|
|
_GetKnownFolder(FOLDERID_Desktop);
|
2019-09-12 09:47:56 -07:00
|
|
|
return;
|
|
|
|
case BfpSysDirectoryKind_Desktop_Common:
|
|
|
|
_GetKnownFolder(FOLDERID_PublicDesktop);
|
2019-08-23 11:56:54 -07:00
|
|
|
return;
|
2022-01-03 11:24:45 -05:00
|
|
|
case BfpSysDirectoryKind_AppData_Local:
|
2019-08-23 11:56:54 -07:00
|
|
|
_GetKnownFolder(FOLDERID_LocalAppData);
|
2022-01-03 11:24:45 -05:00
|
|
|
return;
|
2019-08-23 11:56:54 -07:00
|
|
|
case BfpSysDirectoryKind_AppData_LocalLow:
|
|
|
|
_GetKnownFolder(FOLDERID_LocalAppDataLow);
|
|
|
|
return;
|
|
|
|
case BfpSysDirectoryKind_AppData_Roaming:
|
|
|
|
_GetKnownFolder(FOLDERID_RoamingAppData);
|
|
|
|
return;
|
2019-09-10 11:25:53 -07:00
|
|
|
case BfpSysDirectoryKind_Programs:
|
|
|
|
_GetKnownFolder(FOLDERID_Programs);
|
|
|
|
return;
|
|
|
|
case BfpSysDirectoryKind_Programs_Common:
|
|
|
|
_GetKnownFolder(FOLDERID_CommonPrograms);
|
2022-01-03 11:24:45 -05:00
|
|
|
return;
|
2021-05-12 07:24:29 -04:00
|
|
|
case BfpSysDirectoryKind_Documents:
|
|
|
|
_GetKnownFolder(FOLDERID_Documents);
|
|
|
|
return;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TryStringOut(path, outPath, inOutPathLen, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* path, BfpFileCreateKind createKind, BfpFileCreateFlags createFlags, BfpFileAttributes createdFileAttrs, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
UTF16String wPath = UTF8Decode(path);
|
|
|
|
|
|
|
|
//OVERLAPPED
|
|
|
|
|
|
|
|
if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
|
|
|
|
{
|
|
|
|
String pipeName = StrFormat("\\\\%s\\pipe\\%s", ".", path);
|
|
|
|
wPath = UTF8Decode(pipeName);
|
|
|
|
|
|
|
|
if ((createKind == BfpFileCreateKind_CreateIfNotExists) ||
|
|
|
|
(createKind == BfpFileCreateKind_CreateAlways))
|
|
|
|
{
|
|
|
|
bool isOverlapped = false;
|
|
|
|
|
|
|
|
DWORD openMode = 0;
|
|
|
|
DWORD desiredAccess = 0;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_Read) != 0)
|
|
|
|
openMode |= PIPE_ACCESS_INBOUND;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_Write) != 0)
|
|
|
|
openMode |= PIPE_ACCESS_OUTBOUND;
|
|
|
|
|
|
|
|
if (createKind == BfpFileCreateKind_CreateIfNotExists)
|
|
|
|
openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
|
|
|
|
|
|
|
|
int pipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_AllowTimeouts) != 0)
|
|
|
|
{
|
|
|
|
openMode |= FILE_FLAG_OVERLAPPED;
|
|
|
|
isOverlapped = true;
|
|
|
|
}
|
|
|
|
else if ((createFlags & BfpFileCreateFlag_NonBlocking) != 0)
|
2022-01-03 11:24:45 -05:00
|
|
|
pipeMode |= PIPE_NOWAIT;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
HANDLE handle = ::CreateNamedPipeW(wPath.c_str(), openMode, pipeMode, PIPE_UNLIMITED_INSTANCES, 8192, 8192, 0, NULL);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
if (outResult != NULL)
|
|
|
|
{
|
|
|
|
int lastError = GetLastError();
|
|
|
|
switch (lastError)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
*outResult = BfpFileResult_UnknownError;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
|
|
|
|
BfpFile* bfpFile = new BfpFile();
|
|
|
|
bfpFile->mHandle = handle;
|
|
|
|
bfpFile->mIsPipe = true;
|
|
|
|
|
|
|
|
if (isOverlapped)
|
|
|
|
{
|
|
|
|
bfpFile->mAsyncData = new BfpAsyncData();
|
|
|
|
}
|
|
|
|
|
|
|
|
return bfpFile;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
DWORD desiredAccess = 0;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_Append) != 0)
|
|
|
|
desiredAccess |= FILE_APPEND_DATA;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((createFlags & BfpFileCreateFlag_Read) != 0)
|
|
|
|
desiredAccess |= GENERIC_READ;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_Write) != 0)
|
|
|
|
desiredAccess |= GENERIC_WRITE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD shareMode = 0;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_ShareRead) != 0)
|
|
|
|
shareMode |= FILE_SHARE_READ;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_ShareWrite) != 0)
|
|
|
|
shareMode |= FILE_SHARE_WRITE;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_ShareDelete) != 0)
|
2022-01-03 11:24:45 -05:00
|
|
|
shareMode |= FILE_SHARE_DELETE;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
DWORD creationDisposition = 0;
|
|
|
|
if (createKind == BfpFileCreateKind_CreateAlways)
|
|
|
|
{
|
|
|
|
if ((createFlags & BfpFileCreateFlag_Append) != 0)
|
|
|
|
creationDisposition = OPEN_ALWAYS;
|
|
|
|
else
|
|
|
|
creationDisposition = CREATE_ALWAYS;
|
|
|
|
}
|
|
|
|
else if (createKind == BfpFileCreateKind_CreateIfNotExists)
|
2021-08-01 17:30:40 -03:00
|
|
|
{
|
2020-10-06 07:07:45 -07:00
|
|
|
creationDisposition = CREATE_NEW;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
2021-08-01 17:30:40 -03:00
|
|
|
else if (createKind == BfpFileCreateKind_OpenAlways)
|
|
|
|
{
|
|
|
|
creationDisposition = OPEN_ALWAYS;
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
creationDisposition = OPEN_EXISTING;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD attributes = 0;
|
|
|
|
if ((createdFileAttrs & (BfpFileAttribute_Directory | BfpFileAttribute_SymLink | BfpFileAttribute_Device)) != 0)
|
|
|
|
{
|
|
|
|
if (outResult != NULL)
|
|
|
|
*outResult = BfpFileResult_InvalidParameter;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
attributes = FileAttributes_WinToBFP(createdFileAttrs);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if ((createFlags & BfpFileCreateFlag_WriteThrough) != 0)
|
|
|
|
desiredAccess |= FILE_FLAG_WRITE_THROUGH;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_DeleteOnClose) != 0)
|
|
|
|
desiredAccess |= FILE_FLAG_DELETE_ON_CLOSE;
|
|
|
|
if ((createFlags & BfpFileCreateFlag_NoBuffering) != 0)
|
|
|
|
desiredAccess |= FILE_FLAG_NO_BUFFERING;
|
|
|
|
|
|
|
|
HANDLE handle = ::CreateFileW(wPath.c_str(), desiredAccess, shareMode, NULL, creationDisposition, attributes, NULL);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
if (outResult != NULL)
|
|
|
|
{
|
|
|
|
int lastError = GetLastError();
|
2021-01-03 07:04:33 -08:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
switch (lastError)
|
|
|
|
{
|
|
|
|
case ERROR_SHARING_VIOLATION:
|
|
|
|
*outResult = BfpFileResult_ShareError;
|
|
|
|
break;
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
|
|
*outResult = BfpFileResult_NotFound;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*outResult = BfpFileResult_UnknownError;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
|
|
|
|
BfpFile* bfpFile = new BfpFile();
|
|
|
|
bfpFile->mHandle = handle;
|
|
|
|
return bfpFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult)
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
HANDLE h = INVALID_HANDLE_VALUE;
|
2019-08-23 11:56:54 -07:00
|
|
|
switch (kind)
|
|
|
|
{
|
|
|
|
case BfpFileStdKind_StdOut:
|
|
|
|
h = ::GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
break;
|
|
|
|
case BfpFileStdKind_StdError:
|
|
|
|
h = ::GetStdHandle(STD_ERROR_HANDLE);
|
|
|
|
break;
|
|
|
|
case BfpFileStdKind_StdIn:
|
|
|
|
h = ::GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((h == INVALID_HANDLE_VALUE) || (h == 0))
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_NotFound);
|
|
|
|
return NULL;
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
BfpFile* bfpFile = new BfpFile();
|
|
|
|
bfpFile->mHandle = h;
|
|
|
|
bfpFile->mIsStd = true;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
|
|
|
return bfpFile;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2021-07-03 05:29:50 -07:00
|
|
|
BFP_EXPORT intptr BFP_CALLTYPE BfpFile_GetSystemHandle(BfpFile* file)
|
|
|
|
{
|
|
|
|
return (intptr)file->mHandle;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_Release(BfpFile* file)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
if ((file->mHandle != INVALID_HANDLE_VALUE) && (!file->mIsStd))
|
|
|
|
::CloseHandle(file->mHandle);
|
|
|
|
|
|
|
|
delete file;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_Close(BfpFile* file, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
if (file->mHandle != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
::CloseHandle(file->mHandle);
|
|
|
|
file->mHandle = INVALID_HANDLE_VALUE;
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
}
|
|
|
|
else
|
2022-01-03 11:24:45 -05:00
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Write(BfpFile* file, const void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
DWORD bytesWritten = 0;
|
|
|
|
if (::WriteFile(file->mHandle, buffer, (uint32)size, &bytesWritten, NULL))
|
|
|
|
{
|
|
|
|
if (outResult != NULL)
|
|
|
|
{
|
|
|
|
if (bytesWritten != size)
|
|
|
|
*outResult = BfpFileResult_PartialData;
|
|
|
|
else
|
|
|
|
*outResult = BfpFileResult_Ok;
|
|
|
|
}
|
|
|
|
return bytesWritten;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (outResult != NULL)
|
|
|
|
{
|
|
|
|
switch (GetLastError())
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
*outResult = BfpFileResult_UnknownError;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bytesWritten;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct OverlappedReadResult : OVERLAPPED
|
|
|
|
{
|
|
|
|
BfpFile* mFile;
|
|
|
|
intptr mBytesRead;
|
|
|
|
DWORD mErrorCode;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void WINAPI OverlappedReadComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
|
|
|
|
{
|
|
|
|
OverlappedReadResult* readResult = (OverlappedReadResult*)lpOverlapped;
|
|
|
|
readResult->mErrorCode = dwErrorCode;
|
|
|
|
readResult->mBytesRead = dwNumberOfBytesTransfered;
|
|
|
|
readResult->mFile->mAsyncData->SetEvent();
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
if (timeoutMS != -1)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2021-12-31 10:26:32 -05:00
|
|
|
if (file->mAsyncData == NULL)
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_InvalidParameter);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
OverlappedReadResult overlapped;
|
|
|
|
memset(&overlapped, 0, sizeof(OverlappedReadResult));
|
|
|
|
overlapped.mFile = file;
|
|
|
|
|
|
|
|
//TODO: this doesn't set file stream location. It only works for streams like pipes, sockets, etc
|
|
|
|
if (::ReadFileEx(file->mHandle, buffer, (uint32)size, &overlapped, OverlappedReadComplete))
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
if (!file->mAsyncData->WaitAndResetEvent(timeoutMS))
|
|
|
|
{
|
|
|
|
::CancelIoEx(file->mHandle, &overlapped);
|
|
|
|
// There's a chance we completed before we were cancelled -- check on that
|
|
|
|
if (!file->mAsyncData->WaitAndResetEvent(0))
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_Timeout);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (overlapped.mErrorCode == 0)
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
else if (overlapped.mErrorCode == ERROR_OPERATION_ABORTED)
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_Timeout);
|
|
|
|
return 0;
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int lastError = ::GetLastError();
|
|
|
|
if (lastError == ERROR_PIPE_LISTENING)
|
|
|
|
{
|
|
|
|
overlapped.hEvent = file->mAsyncData->mEvent;
|
|
|
|
if (!::ConnectNamedPipe(file->mHandle, &overlapped))
|
|
|
|
{
|
|
|
|
int lastError = ::GetLastError();
|
|
|
|
if (lastError == ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
if (!file->mAsyncData->WaitAndResetEvent(timeoutMS))
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
::CancelIoEx(file->mHandle, &overlapped);
|
2019-08-23 11:56:54 -07:00
|
|
|
// Clear event set by CancelIoEx
|
|
|
|
file->mAsyncData->WaitAndResetEvent(0);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
OUTRESULT(BfpFileResult_Timeout);
|
2022-01-03 11:24:45 -05:00
|
|
|
return 0;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
// Now we have a connection, so retry the read...
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
return overlapped.mBytesRead;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD bytesRead = 0;
|
|
|
|
if (::ReadFile(file->mHandle, buffer, (uint32)size, &bytesRead, NULL))
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (bytesRead != size)
|
2022-01-03 11:24:45 -05:00
|
|
|
OUTRESULT(BfpFileResult_PartialData);
|
2019-08-23 11:56:54 -07:00
|
|
|
else
|
2021-12-31 10:26:32 -05:00
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
2019-08-23 11:56:54 -07:00
|
|
|
return bytesRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lastError = ::GetLastError();
|
|
|
|
switch (lastError)
|
|
|
|
{
|
|
|
|
case ERROR_BROKEN_PIPE: // Just an EOF
|
2021-12-31 10:26:32 -05:00
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
2019-08-23 11:56:54 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
break;
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
return bytesRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_Flush(BfpFile* file)
|
|
|
|
{
|
|
|
|
::FlushFileBuffers(file->mHandle);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT int64 BFP_CALLTYPE BfpFile_GetFileSize(BfpFile* file)
|
|
|
|
{
|
|
|
|
LARGE_INTEGER largeInteger;
|
|
|
|
largeInteger.QuadPart = 0;
|
|
|
|
::GetFileSizeEx(file->mHandle, &largeInteger);
|
|
|
|
return largeInteger.QuadPart;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT int64 BFP_CALLTYPE BfpFile_Seek(BfpFile* file, int64 offset, BfpFileSeekKind seekKind)
|
|
|
|
{
|
|
|
|
DWORD moveMethod;
|
|
|
|
if (seekKind == BfpFileSeekKind_Absolute)
|
|
|
|
moveMethod = FILE_BEGIN;
|
|
|
|
else if (seekKind == BfpFileSeekKind_Relative)
|
|
|
|
moveMethod = FILE_CURRENT;
|
|
|
|
else
|
|
|
|
moveMethod = FILE_END;
|
|
|
|
|
|
|
|
LARGE_INTEGER liOffset;
|
|
|
|
liOffset.QuadPart = offset;
|
|
|
|
LARGE_INTEGER newPos;
|
|
|
|
newPos.QuadPart = 0;
|
|
|
|
::SetFilePointerEx(file->mHandle, liOffset, &newPos, moveMethod);
|
|
|
|
return newPos.QuadPart;
|
|
|
|
}
|
|
|
|
|
2021-08-01 17:38:30 -03:00
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file, BfpFileResult* outResult)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2021-08-01 17:38:30 -03:00
|
|
|
if (!SetEndOfFile(file->mHandle))
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFile_GetTime_LastWrite(const char* path)
|
|
|
|
{
|
|
|
|
UTF16String wPath = UTF8Decode(path);
|
|
|
|
|
|
|
|
BfpTimeStamp lastWriteTime = 0;
|
|
|
|
GetFileInfo(path, &lastWriteTime, NULL);
|
|
|
|
|
|
|
|
return lastWriteTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFile_GetAttributes(const char* path, BfpFileResult* outResult)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
uint32 fileAttributes;
|
|
|
|
GetFileInfo(path, NULL, &fileAttributes);
|
|
|
|
return FileAttributes_WinToBFP(fileAttributes);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_SetAttributes(const char* path, BfpFileAttributes attribs, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
if (!::SetFileAttributesW(UTF8Decode(path).c_str(), FileAttributes_BFPToWin(attribs)))
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_Copy(const char* oldName, const char* newName, BfpFileCopyKind copyKind, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
if (copyKind == BfpFileCopyKind_IfNewer)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
BfpTimeStamp fromTime = 0;
|
|
|
|
GetFileInfo(oldName, &fromTime, NULL);
|
|
|
|
BfpTimeStamp toTime = 0;
|
|
|
|
GetFileInfo(newName, &toTime, NULL);
|
|
|
|
|
|
|
|
if ((toTime != 0) && (toTime >= fromTime))
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UTF16String wOldPath = UTF8Decode(oldName);
|
|
|
|
UTF16String wNewPath = UTF8Decode(newName);
|
|
|
|
if (!::CopyFileW(wOldPath.c_str(), wNewPath.c_str(), copyKind == BfpFileCopyKind_IfNotExists))
|
|
|
|
{
|
|
|
|
switch (::GetLastError())
|
|
|
|
{
|
|
|
|
case ERROR_ALREADY_EXISTS:
|
|
|
|
OUTRESULT(BfpFileResult_AlreadyExists);
|
|
|
|
break;
|
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
|
|
OUTRESULT(BfpFileResult_NotFound);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_Rename(const char* oldName, const char* newName, BfpFileResult* outResult)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
UTF16String wOldPath = UTF8Decode(oldName);
|
|
|
|
UTF16String wNewPath = UTF8Decode(newName);
|
|
|
|
if (!::MoveFileW(wOldPath.c_str(), wNewPath.c_str()))
|
|
|
|
{
|
|
|
|
switch (::GetLastError())
|
|
|
|
{
|
|
|
|
case ERROR_ALREADY_EXISTS:
|
|
|
|
OUTRESULT(BfpFileResult_AlreadyExists);
|
|
|
|
break;
|
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
|
|
OUTRESULT(BfpFileResult_NotFound);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_Delete(const char* path, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
if (!::DeleteFileW(UTF8Decode(path).c_str()))
|
|
|
|
{
|
|
|
|
int lastError = GetLastError();
|
|
|
|
switch (lastError)
|
|
|
|
{
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
|
|
OUTRESULT(BfpFileResult_NotFound);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT bool BFP_CALLTYPE BfpFile_Exists(const char* path)
|
|
|
|
{
|
|
|
|
WIN32_FIND_DATAW findData;
|
|
|
|
HANDLE handleVal = FindFirstFileW(UTF8Decode(path).c_str(), &findData);
|
|
|
|
if (handleVal == INVALID_HANDLE_VALUE)
|
|
|
|
return false;
|
|
|
|
FindClose(handleVal);
|
|
|
|
return (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempPath(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
WCHAR wStr[4096];
|
|
|
|
::GetTempPathW(4096, wStr);
|
|
|
|
|
|
|
|
String str = UTF8Encode(wStr);
|
|
|
|
TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempFileName(char* outName, int* inOutNameSize, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
WCHAR wPath[4096];
|
|
|
|
wPath[0] = 0;
|
|
|
|
::GetTempPathW(4096, wPath);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
WCHAR wFileName[4096];
|
|
|
|
wFileName[0] = 0;
|
|
|
|
GetTempFileNameW(wPath, L"tmp", 0, wFileName);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
String str = UTF8Encode(wFileName);
|
|
|
|
TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetFullPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
WCHAR wPath[4096];
|
|
|
|
wPath[0] = 0;
|
|
|
|
GetFullPathNameW(UTF8Decode(inPath).c_str(), 4096, wPath, NULL);
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
String str = UTF8Encode(wPath);
|
|
|
|
TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPathC, char* outPathC, int* inOutPathSize, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
String inPath = inPathC;
|
|
|
|
String outPath;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-12-26 07:03:35 -08:00
|
|
|
// Check for '/../' backtracking - handle those first
|
|
|
|
{
|
|
|
|
int i = 0;
|
2022-01-03 11:24:45 -05:00
|
|
|
int32 lastComponentStart = -1;
|
2019-12-26 07:03:35 -08:00
|
|
|
String subName;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-12-26 07:03:35 -08:00
|
|
|
while (i < inPath.mLength)
|
|
|
|
{
|
|
|
|
// skip until path separator
|
|
|
|
while ((i < inPath.mLength) && (inPath[i] != DIR_SEP_CHAR) && (inPath[i] != DIR_SEP_CHAR_ALT))
|
|
|
|
++i;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-12-26 07:03:35 -08:00
|
|
|
if (lastComponentStart != -1)
|
|
|
|
{
|
|
|
|
if ((i - lastComponentStart == 2) && (inPath[lastComponentStart] == '.') && (inPath[lastComponentStart + 1] == '.'))
|
|
|
|
{
|
|
|
|
// Backtrack
|
2022-01-03 11:24:45 -05:00
|
|
|
while ((lastComponentStart > 0) &&
|
2019-12-26 07:03:35 -08:00
|
|
|
((inPath[lastComponentStart - 1] == DIR_SEP_CHAR) || (inPath[lastComponentStart - 1] == DIR_SEP_CHAR_ALT)))
|
|
|
|
lastComponentStart--;
|
|
|
|
while ((lastComponentStart > 0) && (inPath[lastComponentStart - 1] != DIR_SEP_CHAR) && (inPath[lastComponentStart - 1] != DIR_SEP_CHAR_ALT))
|
|
|
|
lastComponentStart--;
|
|
|
|
inPath.Remove(lastComponentStart, i - lastComponentStart + 1);
|
|
|
|
i = lastComponentStart;
|
2022-01-03 11:24:45 -05:00
|
|
|
continue;
|
2019-12-26 07:03:35 -08:00
|
|
|
}
|
|
|
|
else if ((i - lastComponentStart == 1) && (inPath[lastComponentStart] == '.'))
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-12-26 07:03:35 -08:00
|
|
|
inPath.Remove(lastComponentStart, i - lastComponentStart + 1);
|
|
|
|
i = lastComponentStart;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2019-12-26 07:03:35 -08:00
|
|
|
++i;
|
|
|
|
// Ignore multiple slashes in a row
|
|
|
|
while ((i < inPath.mLength) && ((inPath[i] == DIR_SEP_CHAR) || (inPath[i] == DIR_SEP_CHAR_ALT)))
|
|
|
|
++i;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2022-01-03 11:24:45 -05:00
|
|
|
lastComponentStart = i;
|
2019-12-26 07:03:35 -08:00
|
|
|
}
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2019-12-26 07:03:35 -08:00
|
|
|
int32 i = 0;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
// for network paths (\\server\share\RestOfPath), getting the display
|
|
|
|
// name mangles it into unusable form (e.g. "\\server\share" turns
|
|
|
|
// into "share on server (server)"). So detect this case and just skip
|
|
|
|
// up to two path components
|
|
|
|
int length = (int)inPath.length();
|
|
|
|
if (length >= 2 && inPath[0] == DIR_SEP_CHAR && inPath[1] == DIR_SEP_CHAR)
|
|
|
|
{
|
|
|
|
int skippedCount = 0;
|
|
|
|
i = 2; // start after '\\'
|
|
|
|
while (i < length && skippedCount < 2)
|
|
|
|
{
|
|
|
|
if (inPath[i] == DIR_SEP_CHAR)
|
|
|
|
++skippedCount;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
outPath.Append(inPath.c_str(), i);
|
|
|
|
}
|
|
|
|
// for drive names, just add it uppercased
|
|
|
|
else if (length >= 2 && inPath[1] == ':')
|
|
|
|
{
|
|
|
|
outPath.Append(toupper(inPath[0]));
|
|
|
|
outPath.Append(':');
|
|
|
|
if ((length >= 3) &&
|
|
|
|
((inPath[2] == DIR_SEP_CHAR) || (inPath[2] == DIR_SEP_CHAR_ALT)))
|
|
|
|
{
|
|
|
|
outPath.Append(DIR_SEP_CHAR);
|
|
|
|
i = 3; // start after drive, colon and separator
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i = 2; // start after drive and colon
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 07:39:53 -07:00
|
|
|
if ((i == 0) && (length >= 1) &&
|
|
|
|
((inPath[0] == DIR_SEP_CHAR) || (inPath[1] == DIR_SEP_CHAR_ALT)))
|
|
|
|
{
|
|
|
|
i++; // start after initial slash
|
|
|
|
outPath.Append(DIR_SEP_CHAR);
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
int32 lastComponentStart = i;
|
|
|
|
bool addSeparator = false;
|
|
|
|
String subName;
|
|
|
|
|
|
|
|
while (i < length)
|
|
|
|
{
|
|
|
|
// skip until path separator
|
|
|
|
while ((i < length) && (inPath[i] != DIR_SEP_CHAR) && (inPath[i] != DIR_SEP_CHAR_ALT))
|
|
|
|
++i;
|
|
|
|
|
|
|
|
if (addSeparator)
|
|
|
|
outPath.Append(DIR_SEP_CHAR);
|
|
|
|
|
|
|
|
SHFILEINFOW info = { 0 };
|
|
|
|
|
|
|
|
subName.Clear();
|
|
|
|
subName = inPath.Substring(0, i);
|
|
|
|
for (int j = 0; j < (int)subName.length(); j++)
|
|
|
|
if (subName[j] == DIR_SEP_CHAR_ALT)
|
|
|
|
subName[j] = DIR_SEP_CHAR;
|
2022-01-03 11:24:45 -05:00
|
|
|
info.szDisplayName[0] = 0;
|
2019-08-23 11:56:54 -07:00
|
|
|
int32 size = (int32)sizeof(SHFILEINFOW);
|
2020-04-28 15:51:22 -07:00
|
|
|
|
|
|
|
WIN32_FIND_DATAW findData;
|
|
|
|
HANDLE handleVal = FindFirstFileW(UTF8Decode(subName).c_str(), &findData);
|
|
|
|
if (handleVal != INVALID_HANDLE_VALUE)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2020-04-28 15:51:22 -07:00
|
|
|
outPath.Append(UTF8Encode(findData.cFileName));
|
|
|
|
FindClose(handleVal);
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// most likely file does not exist.
|
|
|
|
// So just append original path name component.
|
|
|
|
outPath.Append(inPath.Substring(lastComponentStart, i - lastComponentStart));
|
|
|
|
}
|
|
|
|
|
|
|
|
++i;
|
2019-09-23 13:47:21 -07:00
|
|
|
// Ignore multiple slashes in a row
|
|
|
|
while ((i < length) && ((inPath[i] == DIR_SEP_CHAR) || (inPath[i] == DIR_SEP_CHAR_ALT)))
|
|
|
|
++i;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
lastComponentStart = i;
|
2022-01-03 11:24:45 -05:00
|
|
|
addSeparator = true;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
TryStringOut(outPath, outPathC, inOutPathSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
// BfpFindFileData
|
|
|
|
|
|
|
|
struct BfpFindFileData
|
|
|
|
{
|
|
|
|
BfpFindFileFlags mFlags;
|
|
|
|
WIN32_FIND_DATA mFindData;
|
|
|
|
HANDLE mHandle;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define HANDLE_TO_BFPFINDFILEDATA(handle) ((BfpFindFileData*)(handle))
|
|
|
|
#define BFPFINDFILEDATA_TO_HANDLE(findData) ((HANDLE)(findData))
|
|
|
|
|
|
|
|
static bool BfpFindFileData_CheckFilter(BfpFindFileData* findData)
|
|
|
|
{
|
|
|
|
bool isDir = (findData->mFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
|
|
if (isDir)
|
|
|
|
{
|
2022-01-03 11:24:45 -05:00
|
|
|
if ((findData->mFlags & BfpFindFileFlag_Directories) != 0)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
if ((wcscmp(findData->mFindData.cFileName, L".") == 0) || (wcscmp(findData->mFindData.cFileName, L"..") == 0))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((findData->mFlags & BfpFindFileFlag_Files) != 0)
|
|
|
|
return true;
|
2022-01-03 11:24:45 -05:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpFindFileData* BFP_CALLTYPE BfpFindFileData_FindFirstFile(const char* path, BfpFindFileFlags flags, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
UTF16String wPath = UTF8Decode(path);
|
|
|
|
|
|
|
|
BfpFindFileData* findData = new BfpFindFileData();
|
|
|
|
findData->mFlags = flags;
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
FINDEX_SEARCH_OPS searchOps;
|
|
|
|
if ((flags & BfpFindFileFlag_Files) == 0)
|
|
|
|
searchOps = FindExSearchLimitToDirectories;
|
|
|
|
else
|
|
|
|
searchOps = FindExSearchNameMatch;
|
|
|
|
|
|
|
|
findData->mHandle = ::FindFirstFileExW(wPath.c_str(), FindExInfoBasic, &findData->mFindData, searchOps, NULL, 0);
|
|
|
|
if (findData->mHandle == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
if (outResult != NULL)
|
|
|
|
{
|
|
|
|
switch (GetLastError())
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
*outResult = BfpFileResult_UnknownError;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete findData;
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-01-03 11:24:45 -05:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (!BfpFindFileData_CheckFilter(findData))
|
|
|
|
{
|
|
|
|
if (!BfpFindFileData_FindNextFile(findData))
|
|
|
|
{
|
|
|
|
::FindClose(findData->mHandle);
|
|
|
|
if (outResult != NULL)
|
|
|
|
*outResult = BfpFileResult_NoResults;
|
|
|
|
delete findData;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
|
|
|
return findData;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT bool BFP_CALLTYPE BfpFindFileData_FindNextFile(BfpFindFileData* findData)
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (!::FindNextFileW(findData->mHandle, &findData->mFindData))
|
|
|
|
return false;
|
|
|
|
if (BfpFindFileData_CheckFilter(findData))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_GetFileName(BfpFindFileData* findData, char* outName, int* inOutNameSize, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
String name = UTF8Encode(findData->mFindData.cFileName);
|
|
|
|
TryStringOut(name, outName, inOutNameSize, (BfpResult*)outResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_LastWrite(BfpFindFileData* findData)
|
|
|
|
{
|
|
|
|
return *(BfpTimeStamp*)&findData->mFindData.ftLastWriteTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Created(BfpFindFileData* findData)
|
|
|
|
{
|
|
|
|
return *(BfpTimeStamp*)&findData->mFindData.ftCreationTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Access(BfpFindFileData* findData)
|
|
|
|
{
|
|
|
|
return *(BfpTimeStamp*)&findData->mFindData.ftLastAccessTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFindFileData_GetFileAttributes(BfpFindFileData* findData)
|
|
|
|
{
|
|
|
|
return FileAttributes_WinToBFP(findData->mFindData.dwFileAttributes);
|
|
|
|
}
|
|
|
|
|
2020-09-11 16:39:17 -07:00
|
|
|
BFP_EXPORT int64 BFP_CALLTYPE BfpFindFileData_GetFileSize(BfpFindFileData* findData)
|
|
|
|
{
|
|
|
|
return ((int64)findData->mFindData.nFileSizeHigh << 32) | (int64)findData->mFindData.nFileSizeLow;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_Release(BfpFindFileData* findData)
|
|
|
|
{
|
|
|
|
::FindClose(findData->mHandle);
|
|
|
|
delete findData;
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetCurrent(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
|
2022-01-03 11:24:45 -05:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
wchar_t* wCwdStr = _wgetcwd(NULL, 0);
|
|
|
|
String str = UTF8Encode(wCwdStr);
|
|
|
|
free(wCwdStr);
|
2022-01-03 11:24:45 -05:00
|
|
|
TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_SetCurrent(const char* path, BfpFileResult* outResult)
|
|
|
|
{
|
|
|
|
UTF16String wPath = UTF8Decode(path);
|
|
|
|
if (_wchdir(wPath.c_str()) == -1)
|
2022-01-03 11:24:45 -05:00
|
|
|
OUTRESULT(BfpFileResult_UnknownError);
|
|
|
|
else
|
|
|
|
OUTRESULT(BfpFileResult_Ok);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT int BFP_CALLTYPE BfpStack_CaptureBackTrace(int framesToSkip, intptr* outFrames, int wantFrameCount)
|
|
|
|
{
|
|
|
|
return (int)RtlCaptureStackBackTrace(framesToSkip + 1, wantFrameCount, (void**)outFrames, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
BFP_EXPORT void BFP_CALLTYPE BfpOutput_DebugString(const char* str)
|
|
|
|
{
|
|
|
|
OutputDebugStringA(str);
|
|
|
|
}
|