1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 19:48:20 +02:00
Beef/BeefySysLib/platform/win/Platform.cpp

3813 lines
97 KiB
C++
Raw Normal View History

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 "../../third_party/putty/wildcard.h"
2019-08-23 11:56:54 -07:00
#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 void Crash_Error(const char* msg);
typedef void(*BfpCrashErrorFunc)(const char* str);
2019-08-23 11:56:54 -07:00
static bool gTimerInitialized = false;
static int gTimerDivisor = 0;
CritSect gBfpCritSect;
BfpCrashErrorFunc gCrashErrorFunc = Crash_Error;
2019-08-23 11:56:54 -07:00
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
2023-01-13 07:00:11 -05:00
// It's possible we can run this test in multiple threads at once, we could wrap a CritSect around it but
2019-08-23 11:56:54 -07:00
// 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
char* failMsg = new char[message.length() + file.length() + 64];
sprintf(failMsg, "%s in %s:%d", message.c_str(), file.c_str(), line);
BfpSystem_FatalError(failMsg, "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;
} */
2023-01-13 07:00:11 -05:00
//QueueRemove(fileWatcher);
2019-08-23 11:56:54 -07:00
//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);
}
static int64 gCPUFreq = -1;
static int64 gStartCPUTick = -1;
static int64 gStartQPF = -1;
static void(*sOldSIGABRTHandler)(int signal) = nullptr;
static void InitCPUFreq()
{
if (gStartCPUTick == -1)
2022-01-03 11:24:45 -05:00
{
gStartCPUTick = __rdtsc();
LARGE_INTEGER largeVal = { 0 };
QueryPerformanceCounter(&largeVal);
gStartQPF = largeVal.QuadPart;
}
}
static void Crash_Error(const char* msg)
{
HMODULE hMod = GetModuleHandleA(NULL);
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)((uint8*)hMod + pDosHdr->e_lfanew);
bool isCLI = pNtHdr->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI;
if (isCLI)
fprintf(stderr, "**** FATAL APPLICATION ERROR ****\n%s\n", msg);
else
::MessageBoxA(NULL, msg, "FATAL ERROR", MB_ICONSTOP);
_set_purecall_handler(nullptr);
_set_invalid_parameter_handler(nullptr);
signal(SIGABRT, sOldSIGABRTHandler);
abort();
}
static void Crash_Error_CrashHandler(const char* msg)
{
CrashCatcher::Get()->Crash(msg);
}
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
{
gCrashErrorFunc = Crash_Error;
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)
{
char msg[1024];
sprintf(msg, "Bfp build version '%d' does not match requested version '%d'", BFP_VERSION, version);
BfpSystem_FatalError(msg, "BFP FATAL ERROR");
2019-08-23 11:56:54 -07:00
}
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.
sOldSIGABRTHandler = signal(SIGABRT, &AbortHandler);
if (sOldSIGABRTHandler == SIG_ERR)
sOldSIGABRTHandler = nullptr;
2022-01-03 11:24:45 -05:00
}
2019-08-23 11:56:54 -07:00
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_InitCrashCatcher(BfpSystemInitFlags flags)
{
CrashCatcher::Get()->Init();
if ((flags & BfpSystemInitFlag_SilentCrash) != 0)
CrashCatcher::Get()->SetCrashReportKind(BfpCrashReportKind_None);
gCrashErrorFunc = Crash_Error_CrashHandler;
}
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)
{
CrashCatcher::Get()->SetCrashReportKind(crashReportKind);
2019-08-23 11:56:54 -07:00
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfoFunc(BfpCrashInfoFunc crashInfoFunc)
{
CrashCatcher::Get()->AddCrashInfoFunc(crashInfoFunc);
2019-08-23 11:56:54 -07:00
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfo(const char* str)
{
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;
}
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_ShutdownCrashCatcher()
{
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)
{
int errorLen = (int)strlen(error);
int titleLen = (int)strlen(title);
char* str = new char[errorLen + 1 + titleLen + 1];
strcpy(str, title);
strcat(str, "\n");
strcat(str, error);
gCrashErrorFunc(str);
}
else
gCrashErrorFunc(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
{
LARGE_INTEGER largeVal = { 0 };
QueryPerformanceFrequency(&largeVal);
int64 qpfFreq = largeVal.QuadPart;
2022-01-03 11:24:45 -05: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,
2023-01-13 07:00:11 -05:00
NULL, // security descriptor
2019-08-23 11:56:54 -07:00
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);
2023-01-13 07:00:11 -05:00
//fileWatcher->mFileHandle = NULL;
2019-08-23 11:56:54 -07:00
//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;
HANDLE mProcess;
2019-08-23 11:56:54 -07:00
String mImageName;
BfpProcess()
{
mProcessId = -1;
mInfo = NULL;
mProcess = 0;
}
2019-08-23 11:56:54 -07:00
};
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)
{
HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, processId);
2024-10-17 16:25:05 -04:00
if (hProc == NULL)
{
OUTRESULT(BfpProcessResult_NotFound);
return NULL;
}
2024-10-17 16:25:05 -04:00
2019-08-23 11:56:54 -07:00
BfpProcess* process = new BfpProcess();
process->mProcessId = processId;
process->mProcess = hProc;
2019-08-23 11:56:54 -07:00
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);
if (process->mProcess != NULL)
::CloseHandle(process->mProcess);
2019-08-23 11:56:54 -07:00
delete process;
}
BFP_EXPORT bool BFP_CALLTYPE BfpProcess_WaitFor(BfpProcess* process, int waitMS, int* outExitCode, BfpProcessResult* outResult)
{
if (process->mProcess == NULL)
{
OUTRESULT(BfpProcessResult_NotFound);
return false;
}
if (::WaitForSingleObject(process->mProcess, waitMS) != WAIT_OBJECT_0)
{
OUTRESULT(BfpProcessResult_NotFound);
return false;
}
DWORD errorCode = 0;
GetExitCodeProcess(process->mProcess, &errorCode);
if (outExitCode != NULL)
*outExitCode = (int)errorCode;
OUTRESULT(BfpProcessResult_Ok);
return true;
}
2019-08-23 11:56:54 -07:00
//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);
2024-10-17 16:25:05 -04:00
if (hProc == NULL)
2019-08-23 11:56:54 -07:00
{
2024-10-17 16:25:05 -04:00
OUTRESULT(BfpProcessResult_NotFound);
2019-08-23 11:56:54 -07:00
return;
}
WCHAR wName[MAX_PATH] = { 0 };
2019-08-23 11:56:54 -07:00
::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;
}
2023-01-13 07:00:11 -05:00
// 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
2019-08-23 11:56:54 -07:00
// 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;
2023-01-13 07:00:11 -05:00
// Duplicate the parent handle to be non-inheritable so that the child process
2019-08-23 11:56:54 -07:00
// doesn't have access. This is done for correctness sake, exact reason is unclear.
2023-01-13 07:00:11 -05:00
// One potential theory is that child process can do something brain dead like
2019-08-23 11:56:54 -07:00
// closing the parent end of the pipe and there by getting into a blocking situation
2023-01-13 07:00:11 -05:00
// as parent will not be draining the pipe at the other end anymore.
2019-08-23 11:56:54 -07:00
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;
2023-01-13 07:00:11 -05:00
// set up the environment block parameter
2019-08-23 11:56:54 -07:00
2024-07-23 07:56:23 +02:00
if ((flags & BfpSpawnFlag_NoActivateWindow) != 0)
{
startupInfo.dwFlags |= STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOWNOACTIVATE;
}
// set up the environment block parameter
2019-08-23 11:56:54 -07:00
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();
2024-07-23 07:56:23 +02:00
creationFlags |= CREATE_UNICODE_ENVIRONMENT;
2021-01-03 07:04:33 -08:00
}
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);
2023-01-13 07:00:11 -05:00
//
2019-08-23 11:56:54 -07:00
// if (dwID == (DWORD)lParam)
// {
// PostMessage(hwnd, WM_CLOSE, 0, 0);
// }
2023-01-13 07:00:11 -05:00
//
2019-08-23 11:56:54 -07:00
// 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);
2023-01-13 07:00:11 -05:00
// ::FreeConsole();
2022-01-03 11:24:45 -05:00
// ::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
}
}
2024-07-19 10:31:33 +02:00
BFP_EXPORT int BFP_CALLTYPE BfpSpawn_GetProcessId(BfpSpawn* spawn)
{
return spawn->mProcessId;
}
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.
2023-01-13 07:00:11 -05:00
// 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
2022-01-03 11:24:45 -05:00
// Windows 7 SPI OS bits.
#undef CONTEXT_XSTATE
#if defined(_M_X64)
#define CONTEXT_XSTATE (0x00100040)
#else
#define CONTEXT_XSTATE (0x00010040)
#endif
2023-01-13 07:00:11 -05:00
// 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
2022-01-03 11:24:45 -05:00
// 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;
2022-01-03 11:24:45 -05:00
static CONTEXT* CaptureRegistersEx(HANDLE hThread, intptr*& curPtr)
{
if (ContextBuffer == NULL)
ContextBuffer = new uint8[4096];
2022-01-03 11:24:45 -05:00
PCONTEXT Context;
DWORD ContextSize;
DWORD64 FeatureMask;
2023-01-13 07:00:11 -05:00
DWORD FeatureLength;
2022-01-03 11:24:45 -05:00
BOOL Success;
PM128A Xmm;
PM128A Ymm;
2023-01-13 07:00:11 -05:00
if (pfnGetEnabledXStateFeatures == (PGETENABLEDXSTATEFEATURES)-1)
2022-01-03 11:24:45 -05:00
return NULL;
2023-01-13 07:00:11 -05:00
2022-01-03 11:24:45 -05:00
if (pfnGetEnabledXStateFeatures == NULL)
{
HMODULE hm = GetModuleHandleA("kernel32.dll");
if (hm == NULL)
{
2023-01-13 07:00:11 -05:00
pfnGetEnabledXStateFeatures = (PGETENABLEDXSTATEFEATURES)-1;
2022-01-03 11:24:45 -05:00
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)
{
2023-01-13 07:00:11 -05:00
pfnGetEnabledXStateFeatures = (PGETENABLEDXSTATEFEATURES)-1;
2022-01-03 11:24:45 -05:00
return NULL;
}
}
FeatureMask = pfnGetEnabledXStateFeatures();
2023-01-13 07:00:11 -05:00
if ((FeatureMask & XSTATE_MASK_AVX) == 0)
return NULL;
2022-01-03 11:24:45 -05:00
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);
2023-01-13 07:00:11 -05:00
if (Success == FALSE)
return NULL;
2022-01-03 11:24:45 -05:00
Success = pfnSetXStateFeaturesMask(Context, XSTATE_MASK_AVX);
2023-01-13 07:00:11 -05:00
if (Success == FALSE)
return Context;
2022-01-03 11:24:45 -05:00
2023-01-13 07:00:11 -05:00
Success = GetThreadContext(hThread, Context);
if (Success == FALSE)
return Context;
2022-01-03 11:24:45 -05:00
Success = pfnGetXStateFeaturesMask(Context, &FeatureMask);
if (Success == FALSE)
2023-01-13 07:00:11 -05:00
return Context;
2022-01-03 11:24:45 -05:00
2023-01-13 07:00:11 -05:00
if ((FeatureMask & XSTATE_MASK_AVX) == 0)
return Context;
2022-01-03 11:24:45 -05:00
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
2023-01-13 07:00:11 -05: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;
2023-01-13 07:00:11 -05: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);
2023-01-13 07:00:11 -05:00
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)
{
2023-01-13 07:00:11 -05:00
// Timeout
2019-08-23 11:56:54 -07:00
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))
{
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)
{
2019-08-23 11:56:54 -07:00
PWSTR pStrPtr;
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
{
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;
}
2024-07-23 07:56:23 +02:00
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetFromHandle(intptr handle, BfpFileResult* outResult)
{
return new BfpFile((HANDLE)handle);
}
2019-08-23 11:56:54 -07:00
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)
{
bool forceNormalRead = false;
if ((file->mIsStd) && (file->mHandle == GetStdHandle(STD_INPUT_HANDLE)))
{
DWORD consoleMode = 0;
GetConsoleMode(file->mHandle, &consoleMode);
if ((consoleMode & ENABLE_LINE_INPUT) != 0)
{
forceNormalRead = true;
}
else
{
INPUT_RECORD record;
DWORD numRead;
while (true)
{
if (timeoutMS != -1)
{
if (!GetNumberOfConsoleInputEvents(file->mHandle, &numRead))
{
forceNormalRead = true;
break;
}
if (numRead == 0)
{
OUTRESULT(BfpFileResult_Timeout);
return 0;
}
}
if (!ReadConsoleInput(file->mHandle, &record, 1, &numRead))
{
forceNormalRead = true;
break;
}
if (numRead > 0)
{
if ((record.Event.KeyEvent.bKeyDown) && (record.Event.KeyEvent.uChar.AsciiChar != 0))
{
memset(buffer, record.Event.KeyEvent.uChar.AsciiChar, 1);
OUTRESULT(BfpFileResult_Ok);
return 1;
}
}
}
}
}
if ((timeoutMS != -1) && (!forceNormalRead))
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)
{
2024-07-23 07:56:23 +02:00
case ERROR_PIPE_LISTENING:
OUTRESULT(BfpFileResult_PipeListening);
break;
2019-08-23 11:56:54 -07:00
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
// Check for '/../' backtracking - handle those first
{
int i = 0;
2022-01-03 11:24:45 -05:00
int32 lastComponentStart = -1;
String subName;
2022-01-03 11:24:45 -05: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
if (lastComponentStart != -1)
{
if ((i - lastComponentStart == 2) && (inPath[lastComponentStart] == '.') && (inPath[lastComponentStart + 1] == '.'))
{
// Backtrack
2022-01-03 11:24:45 -05:00
while ((lastComponentStart > 0) &&
((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;
}
else if ((i - lastComponentStart == 1) && (inPath[lastComponentStart] == '.'))
2022-01-03 11:24:45 -05:00
{
inPath.Remove(lastComponentStart, i - lastComponentStart + 1);
i = lastComponentStart;
continue;
}
}
2019-08-23 11:56:54 -07: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-08-23 11:56:54 -07: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
}
}
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;
// 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;
Beefy::String mWildcard;
2019-08-23 11:56:54 -07:00
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)
{
if ((findData->mFlags & BfpFindFileFlag_Directories) == 0)
return false;
if ((wcscmp(findData->mFindData.cFileName, L".") == 0) || (wcscmp(findData->mFindData.cFileName, L"..") == 0))
return false;
2019-08-23 11:56:54 -07:00
}
else
{
if ((findData->mFlags & BfpFindFileFlag_Files) == 0)
return false;
2022-01-03 11:24:45 -05:00
}
Beefy::String fileName = UTF8Encode(findData->mFindData.cFileName);
Beefy::MakeUpper(fileName);
if (!wc_match(findData->mWildcard.c_str(), fileName.c_str()))
return false;
return true;
2019-08-23 11:56:54 -07:00
}
BFP_EXPORT BfpFindFileData* BFP_CALLTYPE BfpFindFileData_FindFirstFile(const char* path, BfpFindFileFlags flags, BfpFileResult* outResult)
{
Beefy::String findStr = path;
Beefy::String wildcard;
2023-01-13 07:00:11 -05:00
int lastSlashPos = std::max((int)findStr.LastIndexOf('/'), (int)findStr.LastIndexOf('\\'));
if (lastSlashPos != -1)
{
wildcard = findStr.Substring(lastSlashPos + 1);
findStr = findStr.Substring(0, lastSlashPos + 1);
findStr.Append("*");
}
if (wildcard == "*.*")
wildcard = "*";
2019-08-23 11:56:54 -07:00
BfpFindFileData* findData = new BfpFindFileData();
findData->mFlags = flags;
findData->mWildcard = wildcard;
Beefy::MakeUpper(findData->mWildcard);
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;
UTF16String wPath = UTF8Decode(findStr);
2019-08-23 11:56:54 -07:00
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);
}