mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-10-01 15:38:29 +00:00
Fixed crash if initialization of EGL failed but was tried again later.
The internal function SDL_EGL_LoadLibrary() did not delete and remove a mostly uninitialized data structure if loading the library first failed. A later try to use EGL then skipped initialization and assumed it was previously successful because the data structure now already existed. This led to at least one crash in the internal function SDL_EGL_ChooseConfig() because a NULL pointer was dereferenced to make a call to eglBindAPI().
This commit is contained in:
284
visualtest/src/windows/windows_process.c
Normal file
284
visualtest/src/windows/windows_process.c
Normal file
@@ -0,0 +1,284 @@
|
||||
/* See COPYING.txt for the full license governing this code. */
|
||||
/**
|
||||
* \file windows_process.c
|
||||
*
|
||||
* Source file for the process API on windows.
|
||||
*/
|
||||
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_test.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "SDL_visualtest_process.h"
|
||||
|
||||
#if defined(__WIN32__)
|
||||
|
||||
void
|
||||
LogLastError(char* str)
|
||||
{
|
||||
LPVOID buffer;
|
||||
DWORD dw = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buffer,
|
||||
0, NULL);
|
||||
SDLTest_LogError("%s: %s", str, (char*)buffer);
|
||||
LocalFree(buffer);
|
||||
}
|
||||
|
||||
int
|
||||
SDL_LaunchProcess(char* file, char* args, SDL_ProcessInfo* pinfo)
|
||||
{
|
||||
BOOL success;
|
||||
char* working_directory;
|
||||
char* command_line;
|
||||
int path_length, args_length;
|
||||
STARTUPINFO sui = {0};
|
||||
sui.cb = sizeof(sui);
|
||||
|
||||
if(!file)
|
||||
{
|
||||
SDLTest_LogError("Path to executable to launched cannot be NULL.");
|
||||
return 0;
|
||||
}
|
||||
if(!pinfo)
|
||||
{
|
||||
SDLTest_LogError("pinfo cannot be NULL.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the working directory of the process being launched, so that
|
||||
the process can load any resources it has in it's working directory */
|
||||
path_length = SDL_strlen(file);
|
||||
if(path_length == 0)
|
||||
{
|
||||
SDLTest_LogError("Length of the file parameter is zero.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
working_directory = (char*)SDL_malloc(path_length + 1);
|
||||
if(!working_directory)
|
||||
{
|
||||
SDLTest_LogError("Could not allocate working_directory - malloc() failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_memcpy(working_directory, file, path_length + 1);
|
||||
PathRemoveFileSpec(working_directory);
|
||||
if(SDL_strlen(working_directory) == 0)
|
||||
{
|
||||
SDL_free(working_directory);
|
||||
working_directory = NULL;
|
||||
}
|
||||
|
||||
/* join the file path and the args string together */
|
||||
if(!args)
|
||||
args = "";
|
||||
args_length = SDL_strlen(args);
|
||||
command_line = (char*)SDL_malloc(path_length + args_length + 2);
|
||||
if(!command_line)
|
||||
{
|
||||
SDLTest_LogError("Could not allocate command_line - malloc() failed.");
|
||||
return 0;
|
||||
}
|
||||
SDL_memcpy(command_line, file, path_length);
|
||||
command_line[path_length] = ' ';
|
||||
SDL_memcpy(command_line + path_length + 1, args, args_length + 1);
|
||||
|
||||
/* create the process */
|
||||
success = CreateProcess(NULL, command_line, NULL, NULL, FALSE,
|
||||
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
|
||||
NULL, working_directory, &sui, &pinfo->pi);
|
||||
if(working_directory)
|
||||
{
|
||||
SDL_free(working_directory);
|
||||
working_directory = NULL;
|
||||
}
|
||||
SDL_free(command_line);
|
||||
if(!success)
|
||||
{
|
||||
LogLastError("CreateProcess() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
SDL_GetProcessExitStatus(SDL_ProcessInfo* pinfo, SDL_ProcessExitStatus* ps)
|
||||
{
|
||||
DWORD exit_status;
|
||||
BOOL success;
|
||||
|
||||
if(!pinfo)
|
||||
{
|
||||
SDLTest_LogError("pinfo cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
if(!ps)
|
||||
{
|
||||
SDLTest_LogError("ps cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the exit code */
|
||||
success = GetExitCodeProcess(pinfo->pi.hProcess, &exit_status);
|
||||
if(!success)
|
||||
{
|
||||
LogLastError("GetExitCodeProcess() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(exit_status == STILL_ACTIVE)
|
||||
ps->exit_status = -1;
|
||||
else
|
||||
ps->exit_status = exit_status;
|
||||
ps->exit_success = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SDL_IsProcessRunning(SDL_ProcessInfo* pinfo)
|
||||
{
|
||||
DWORD exit_status;
|
||||
BOOL success;
|
||||
|
||||
if(!pinfo)
|
||||
{
|
||||
SDLTest_LogError("pinfo cannot be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
success = GetExitCodeProcess(pinfo->pi.hProcess, &exit_status);
|
||||
if(!success)
|
||||
{
|
||||
LogLastError("GetExitCodeProcess() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(exit_status == STILL_ACTIVE)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK
|
||||
CloseWindowCallback(HWND hwnd, LPARAM lparam)
|
||||
{
|
||||
DWORD pid;
|
||||
SDL_ProcessInfo* pinfo;
|
||||
|
||||
pinfo = (SDL_ProcessInfo*)lparam;
|
||||
|
||||
GetWindowThreadProcessId(hwnd, &pid);
|
||||
if(pid == pinfo->pi.dwProcessId)
|
||||
{
|
||||
DWORD result;
|
||||
if(!SendMessageTimeout(hwnd, WM_CLOSE, 0, 0, SMTO_BLOCK,
|
||||
1000, &result))
|
||||
{
|
||||
if(GetLastError() != ERROR_TIMEOUT)
|
||||
{
|
||||
LogLastError("SendMessageTimeout() failed");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
SDL_QuitProcess(SDL_ProcessInfo* pinfo, SDL_ProcessExitStatus* ps)
|
||||
{
|
||||
DWORD wait_result;
|
||||
if(!pinfo)
|
||||
{
|
||||
SDLTest_LogError("pinfo argument cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
if(!ps)
|
||||
{
|
||||
SDLTest_LogError("ps argument cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* enumerate through all the windows, trying to close each one */
|
||||
if(!EnumWindows(CloseWindowCallback, (LPARAM)pinfo))
|
||||
{
|
||||
SDLTest_LogError("EnumWindows() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wait until the process terminates */
|
||||
wait_result = WaitForSingleObject(pinfo->pi.hProcess, 1000);
|
||||
if(wait_result == WAIT_FAILED)
|
||||
{
|
||||
LogLastError("WaitForSingleObject() failed");
|
||||
return 0;
|
||||
}
|
||||
if(wait_result != WAIT_OBJECT_0)
|
||||
{
|
||||
SDLTest_LogError("Process did not quit.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the exit code */
|
||||
if(!SDL_GetProcessExitStatus(pinfo, ps))
|
||||
{
|
||||
SDLTest_LogError("SDL_GetProcessExitStatus() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
SDL_KillProcess(SDL_ProcessInfo* pinfo, SDL_ProcessExitStatus* ps)
|
||||
{
|
||||
BOOL success;
|
||||
DWORD exit_status, wait_result;
|
||||
|
||||
if(!pinfo)
|
||||
{
|
||||
SDLTest_LogError("pinfo argument cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
if(!ps)
|
||||
{
|
||||
SDLTest_LogError("ps argument cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* initiate termination of the process */
|
||||
success = TerminateProcess(pinfo->pi.hProcess, 0);
|
||||
if(!success)
|
||||
{
|
||||
LogLastError("TerminateProcess() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wait until the process terminates */
|
||||
wait_result = WaitForSingleObject(pinfo->pi.hProcess, INFINITE);
|
||||
if(wait_result == WAIT_FAILED)
|
||||
{
|
||||
LogLastError("WaitForSingleObject() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the exit code */
|
||||
success = GetExitCodeProcess(pinfo->pi.hProcess, &exit_status);
|
||||
if(!success)
|
||||
{
|
||||
LogLastError("GetExitCodeProcess() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ps->exit_status = exit_status;
|
||||
ps->exit_success = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
349
visualtest/src/windows/windows_screenshot.c
Normal file
349
visualtest/src/windows/windows_screenshot.c
Normal file
@@ -0,0 +1,349 @@
|
||||
/* See COPYING.txt for the full license governing this code. */
|
||||
/**
|
||||
* \file windows_screenshot.c
|
||||
*
|
||||
* Source file for the screenshot API on windows.
|
||||
*/
|
||||
|
||||
#include "SDL_visualtest_process.h"
|
||||
#include <SDL.h>
|
||||
#include <SDL_test.h>
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#if defined(__WIN32__)
|
||||
#include <Windows.h>
|
||||
|
||||
void LogLastError(char* str);
|
||||
|
||||
static int img_num;
|
||||
static SDL_ProcessInfo screenshot_pinfo;
|
||||
|
||||
/* Saves a bitmap to a file using hdc as a device context */
|
||||
static int
|
||||
SaveBitmapToFile(HDC hdc, HBITMAP hbitmap, char* filename)
|
||||
{
|
||||
BITMAP bitmap;
|
||||
BITMAPFILEHEADER bfh;
|
||||
BITMAPINFOHEADER bih;
|
||||
DWORD bmpsize, bytes_written;
|
||||
HANDLE hdib, hfile;
|
||||
char* bmpdata;
|
||||
int return_code = 1;
|
||||
|
||||
if(!hdc)
|
||||
{
|
||||
SDLTest_LogError("hdc argument is NULL");
|
||||
return 0;
|
||||
}
|
||||
if(!hbitmap)
|
||||
{
|
||||
SDLTest_LogError("hbitmap argument is NULL");
|
||||
return 0;
|
||||
}
|
||||
if(!filename)
|
||||
{
|
||||
SDLTest_LogError("filename argument is NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!GetObject(hbitmap, sizeof(BITMAP), (void*)&bitmap))
|
||||
{
|
||||
SDLTest_LogError("GetObject() failed");
|
||||
return_code = 0;
|
||||
goto savebitmaptofile_cleanup_generic;
|
||||
}
|
||||
|
||||
bih.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bih.biWidth = bitmap.bmWidth;
|
||||
bih.biHeight = bitmap.bmHeight;
|
||||
bih.biPlanes = 1;
|
||||
bih.biBitCount = 32;
|
||||
bih.biCompression = BI_RGB;
|
||||
bih.biSizeImage = 0;
|
||||
bih.biXPelsPerMeter = 0;
|
||||
bih.biYPelsPerMeter = 0;
|
||||
bih.biClrUsed = 0;
|
||||
bih.biClrImportant = 0;
|
||||
|
||||
bmpsize = ((bitmap.bmWidth * bih.biBitCount + 31) / 32) * 4 * bitmap.bmHeight;
|
||||
|
||||
hdib = GlobalAlloc(GHND, bmpsize);
|
||||
if(!hdib)
|
||||
{
|
||||
LogLastError("GlobalAlloc() failed");
|
||||
return_code = 0;
|
||||
goto savebitmaptofile_cleanup_generic;
|
||||
}
|
||||
bmpdata = (char*)GlobalLock(hdib);
|
||||
if(!bmpdata)
|
||||
{
|
||||
LogLastError("GlobalLock() failed");
|
||||
return_code = 0;
|
||||
goto savebitmaptofile_cleanup_hdib;
|
||||
}
|
||||
|
||||
if(!GetDIBits(hdc, hbitmap, 0, (UINT)bitmap.bmHeight, bmpdata,
|
||||
(LPBITMAPINFO)&bih, DIB_RGB_COLORS))
|
||||
{
|
||||
SDLTest_LogError("GetDIBits() failed");
|
||||
return_code = 0;
|
||||
goto savebitmaptofile_cleanup_unlockhdib;
|
||||
}
|
||||
|
||||
hfile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if(hfile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
LogLastError("CreateFile()");
|
||||
return_code = 0;
|
||||
goto savebitmaptofile_cleanup_unlockhdib;
|
||||
}
|
||||
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
|
||||
bfh.bfSize = bmpsize + bfh.bfOffBits;
|
||||
bfh.bfType = 0x4D42;
|
||||
|
||||
bytes_written = 0;
|
||||
if(!WriteFile(hfile, (void*)&bfh, sizeof(BITMAPFILEHEADER), &bytes_written, NULL) ||
|
||||
!WriteFile(hfile, (void*)&bih, sizeof(BITMAPINFOHEADER), &bytes_written, NULL) ||
|
||||
!WriteFile(hfile, (void*)bmpdata, bmpsize, &bytes_written, NULL))
|
||||
{
|
||||
LogLastError("WriteFile() failed");
|
||||
return_code = 0;
|
||||
goto savebitmaptofile_cleanup_hfile;
|
||||
}
|
||||
|
||||
savebitmaptofile_cleanup_hfile:
|
||||
CloseHandle(hfile);
|
||||
|
||||
/* make the screenshot file writable on cygwin, since it could be overwritten later */
|
||||
#if defined(__CYGWIN__)
|
||||
if(chmod(filename, 0777) == -1)
|
||||
{
|
||||
SDLTest_LogError("chmod() failed");
|
||||
return_code = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
savebitmaptofile_cleanup_unlockhdib:
|
||||
GlobalUnlock(hdib);
|
||||
|
||||
savebitmaptofile_cleanup_hdib:
|
||||
GlobalFree(hdib);
|
||||
|
||||
savebitmaptofile_cleanup_generic:
|
||||
return return_code;
|
||||
}
|
||||
|
||||
/* Takes the screenshot of a window and saves it to a file. If only_client_area
|
||||
is true, then only the client area of the window is considered */
|
||||
static int
|
||||
ScreenshotWindow(HWND hwnd, char* filename, SDL_bool only_client_area)
|
||||
{
|
||||
int width, height;
|
||||
RECT dimensions;
|
||||
HDC windowdc, capturedc;
|
||||
HBITMAP capturebitmap;
|
||||
HGDIOBJ select_success;
|
||||
BOOL blt_success;
|
||||
int return_code = 1;
|
||||
|
||||
if(!filename)
|
||||
{
|
||||
SDLTest_LogError("filename argument cannot be NULL");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_generic;
|
||||
}
|
||||
if(!hwnd)
|
||||
{
|
||||
SDLTest_LogError("hwnd argument cannot be NULL");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_generic;
|
||||
}
|
||||
|
||||
if(!GetWindowRect(hwnd, &dimensions))
|
||||
{
|
||||
LogLastError("GetWindowRect() failed");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_generic;
|
||||
}
|
||||
|
||||
if(only_client_area)
|
||||
{
|
||||
RECT crect;
|
||||
if(!GetClientRect(hwnd, &crect))
|
||||
{
|
||||
SDLTest_LogError("GetClientRect() failed");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_generic;
|
||||
}
|
||||
|
||||
width = crect.right;
|
||||
height = crect.bottom;
|
||||
windowdc = GetDC(hwnd);
|
||||
if(!windowdc)
|
||||
{
|
||||
SDLTest_LogError("GetDC() failed");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_generic;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
width = dimensions.right - dimensions.left;
|
||||
height = dimensions.bottom - dimensions.top;
|
||||
windowdc = GetWindowDC(hwnd);
|
||||
if(!windowdc)
|
||||
{
|
||||
SDLTest_LogError("GetWindowDC() failed");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_generic;
|
||||
}
|
||||
}
|
||||
|
||||
capturedc = CreateCompatibleDC(windowdc);
|
||||
if(!capturedc)
|
||||
{
|
||||
SDLTest_LogError("CreateCompatibleDC() failed");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_windowdc;
|
||||
}
|
||||
capturebitmap = CreateCompatibleBitmap(windowdc, width, height);
|
||||
if(!capturebitmap)
|
||||
{
|
||||
SDLTest_LogError("CreateCompatibleBitmap() failed");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_capturedc;
|
||||
}
|
||||
select_success = SelectObject(capturedc, capturebitmap);
|
||||
if(!select_success || select_success == HGDI_ERROR)
|
||||
{
|
||||
SDLTest_LogError("SelectObject() failed");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_capturebitmap;
|
||||
}
|
||||
blt_success = BitBlt(capturedc, 0, 0, width, height, windowdc,
|
||||
0, 0, SRCCOPY|CAPTUREBLT);
|
||||
if(!blt_success)
|
||||
{
|
||||
LogLastError("BitBlt() failed");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_capturebitmap;
|
||||
}
|
||||
|
||||
/* save bitmap as file */
|
||||
if(!SaveBitmapToFile(windowdc, capturebitmap, filename))
|
||||
{
|
||||
SDLTest_LogError("SaveBitmapToFile() failed");
|
||||
return_code = 0;
|
||||
goto screenshotwindow_cleanup_capturebitmap;
|
||||
}
|
||||
|
||||
/* free resources */
|
||||
|
||||
screenshotwindow_cleanup_capturebitmap:
|
||||
if(!DeleteObject(capturebitmap))
|
||||
{
|
||||
SDLTest_LogError("DeleteObjectFailed");
|
||||
return_code = 0;
|
||||
}
|
||||
|
||||
screenshotwindow_cleanup_capturedc:
|
||||
if(!DeleteDC(capturedc))
|
||||
{
|
||||
SDLTest_LogError("DeleteDC() failed");
|
||||
return_code = 0;
|
||||
}
|
||||
|
||||
screenshotwindow_cleanup_windowdc:
|
||||
if(!ReleaseDC(hwnd, windowdc))
|
||||
{
|
||||
SDLTest_LogError("ReleaseDC() failed");
|
||||
return_code = 0;;
|
||||
}
|
||||
|
||||
screenshotwindow_cleanup_generic:
|
||||
return return_code;
|
||||
}
|
||||
|
||||
/* Takes the screenshot of the entire desktop and saves it to a file */
|
||||
int SDLVisualTest_ScreenshotDesktop(char* filename)
|
||||
{
|
||||
HWND hwnd;
|
||||
hwnd = GetDesktopWindow();
|
||||
return ScreenshotWindow(hwnd, filename, SDL_FALSE);
|
||||
}
|
||||
|
||||
/* take screenshot of a window and save it to a file */
|
||||
static BOOL CALLBACK
|
||||
ScreenshotHwnd(HWND hwnd, LPARAM lparam)
|
||||
{
|
||||
int len;
|
||||
DWORD pid;
|
||||
char* prefix;
|
||||
char* filename;
|
||||
|
||||
GetWindowThreadProcessId(hwnd, &pid);
|
||||
if(pid != screenshot_pinfo.pi.dwProcessId)
|
||||
return TRUE;
|
||||
|
||||
if(!IsWindowVisible(hwnd))
|
||||
return TRUE;
|
||||
|
||||
prefix = (char*)lparam;
|
||||
len = SDL_strlen(prefix) + 100;
|
||||
filename = (char*)SDL_malloc(len * sizeof(char));
|
||||
if(!filename)
|
||||
{
|
||||
SDLTest_LogError("malloc() failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* restore the window and bring it to the top */
|
||||
ShowWindowAsync(hwnd, SW_RESTORE);
|
||||
/* restore is not instantaneous */
|
||||
SDL_Delay(500);
|
||||
|
||||
/* take a screenshot of the client area */
|
||||
if(img_num == 1)
|
||||
SDL_snprintf(filename, len, "%s.bmp", prefix);
|
||||
else
|
||||
SDL_snprintf(filename, len, "%s_%d.bmp", prefix, img_num);
|
||||
img_num++;
|
||||
ScreenshotWindow(hwnd, filename, SDL_TRUE);
|
||||
|
||||
SDL_free(filename);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* each window of the process will have a screenshot taken. The file name will be
|
||||
prefix-i.png for the i'th window. */
|
||||
int
|
||||
SDLVisualTest_ScreenshotProcess(SDL_ProcessInfo* pinfo, char* prefix)
|
||||
{
|
||||
if(!pinfo)
|
||||
{
|
||||
SDLTest_LogError("pinfo argument cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
if(!prefix)
|
||||
{
|
||||
SDLTest_LogError("prefix argument cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
img_num = 1;
|
||||
screenshot_pinfo = *pinfo;
|
||||
if(!EnumWindows(ScreenshotHwnd, (LPARAM)prefix))
|
||||
{
|
||||
SDLTest_LogError("EnumWindows() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user