Skip to content

Conversation

@Stateford
Copy link
Owner

…tion for testing

@Stateford Stateford self-assigned this Jan 19, 2026
Copilot AI review requested due to automatic review settings January 19, 2026 02:22
@Stateford Stateford merged commit 314aa84 into master Jan 19, 2026
5 of 9 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors thread handling and improves memory management in the Display-Lock Windows application. It centralizes mutex management, adds proper error handling for memory operations, enhances thread safety, and includes configuration files for testing and AI assistance.

Changes:

  • Refactored thread closing functions to pass thread handles by pointer for proper cleanup
  • Improved memory management with proper realloc error handling and safer string operations (wcscpy -> wcscpy_s)
  • Centralized mutex lifecycle management in APPLICATION_ARGS structure
  • Added volatile qualifiers for thread-shared boolean flags
  • Removed unsafe _fcloseall() calls and improved file operation error handling
  • Added configuration files (CLAUDE.md, .claude/settings.local.json) and test resources

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/test_resources/test1.DLOCK New test resource file for settings read tests
src/ui.c Updated closeThread call to pass thread handle by pointer
src/main.c Centralized mutex management, improved dialog creation error handling, added accelerator table cleanup, refactored closeThread calls
src/components/win.c Added volatile qualifier to isRunning pointer for thread safety
src/components/update.c Fixed realloc memory leak by using temporary pointer
src/components/settings.c Replaced unsafe wcscpy with wcscpy_s, added fread error handling, removed _fcloseall()
src/components/menu.c Updated closeThread to accept pointer to thread handle and volatile status pointer
src/components/applications.c Enhanced memory safety with malloc/realloc checks, replaced wcscpy with wcscpy_s, removed _fcloseall(), improved variable naming, centralized mutex usage
include/menu.h Updated closeThread function signature
include/common.h Added magic number constants, updated closeThread signature, added volatile qualifiers, added mutex to APPLICATION_ARGS
include/applications.h Updated closeApplicationThread function signature
CLAUDE.md New documentation file for Claude Code AI assistant
.claude/settings.local.json New configuration file for Claude Code permissions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +178 to +184
{
HWND hMainDlg = CreateDialog(NULL, MAKEINTRESOURCE(IDD_MAIN_VIEW), hWnd, MainWindow);
if (hMainDlg == NULL)
{
// Dialog creation failed
return -1;
}
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling returns -1 from a window procedure during WM_CREATE message. According to Windows API documentation, returning -1 from WM_CREATE will cause CreateWindow to fail, but the proper way to signal an error in WM_CREATE is to return -1, not from within a block. However, this return happens inside a scoped block and might not properly propagate. Consider using DestroyWindow and then returning -1, or restructuring the error handling.

Copilot uses AI. Check for mistakes.
{
HANDLE mutex = CreateMutex(NULL, FALSE, APPLICATION_MUTEX_NAME);
WaitForSingleObject(mutex, INFINITE);
WaitForSingleObject(args->mutex, INFINITE);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for WaitForSingleObject. While INFINITE timeout is used, the function can still fail and return WAIT_FAILED. Consider checking the return value and handling errors appropriately to avoid undefined behavior if the wait operation fails.

Copilot uses AI. Check for mistakes.
Comment on lines +632 to +639
DWORD waitResult = WaitForSingleObject(args.mutex, INFINITE);

HANDLE mutex = CreateMutex(NULL, FALSE, APPLICATION_MUTEX_NAME);
DWORD result = WaitForSingleObject(mutex, INFINITE);

if ((result == 0) || (result == WAIT_ABANDONED))
if ((waitResult == WAIT_OBJECT_0) || (waitResult == WAIT_ABANDONED))
{
APPLICATION_SETTINGS *settings = &applicationList.applications[current_selected];
DialogBoxParam(hInst, MAKEINTRESOURCE(IDC_APP_SETTINGS), hDlg, appSettingsProc, (LPARAM)settings);
APPLICATION_SETTINGS *appSettings = &applicationList.applications[current_selected];
DialogBoxParam(hInst, MAKEINTRESOURCE(IDC_APP_SETTINGS), hDlg, appSettingsProc, (LPARAM)appSettings);
}
ReleaseMutex(mutex);
CloseHandle(mutex);
ReleaseMutex(args.mutex);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential undefined behavior if WaitForSingleObject fails. When the wait result is neither WAIT_OBJECT_0 nor WAIT_ABANDONED (e.g., WAIT_FAILED), ReleaseMutex is still called without having acquired the mutex, which will fail and could leave the application in an inconsistent state. Add an else clause or early return to skip ReleaseMutex when the wait operation doesn't succeed.

Copilot uses AI. Check for mistakes.
Comment on lines +603 to +610
DWORD waitResult = WaitForSingleObject(args.mutex, INFINITE);

HANDLE mutex = CreateMutex(NULL, FALSE, APPLICATION_MUTEX_NAME);
DWORD result = WaitForSingleObject(mutex, INFINITE);

if ((result == 0) || (result == WAIT_ABANDONED))
if ((waitResult == WAIT_OBJECT_0) || (waitResult == WAIT_ABANDONED))
{
SendMessage(controls.listView, LB_DELETESTRING, current_selected, 0);
removeApplication(&applicationList, current_selected);
}
ReleaseMutex(mutex);
CloseHandle(mutex);
ReleaseMutex(args.mutex);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential undefined behavior if WaitForSingleObject fails. When the wait result is neither WAIT_OBJECT_0 nor WAIT_ABANDONED (e.g., WAIT_FAILED), ReleaseMutex is still called without having acquired the mutex, which will fail and could leave the application in an inconsistent state. Add an else clause or early return to skip ReleaseMutex when the wait operation doesn't succeed.

Copilot uses AI. Check for mistakes.
Comment on lines +583 to +594
DWORD waitResult = WaitForSingleObject(args.mutex, INFINITE);

HANDLE mutex = CreateMutex(NULL, FALSE, APPLICATION_MUTEX_NAME);
DWORD result = WaitForSingleObject(mutex, INFINITE);

if ((result == 0) || (result == WAIT_ABANDONED))
if ((waitResult == WAIT_OBJECT_0) || (waitResult == WAIT_ABANDONED))
{

APPLICATION_SETTINGS application;
ZeroMemory(&application, sizeof(application));
createApplicationSettings(ofn.lpstrFile, &application);

if (addApplication(&applicationList, application))
SendMessage(controls.listView, LB_ADDSTRING, 0, (LPARAM)application.application_name);
}
ReleaseMutex(mutex);
CloseHandle(mutex);
ReleaseMutex(args.mutex);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential undefined behavior if WaitForSingleObject fails. When the wait result is neither WAIT_OBJECT_0 nor WAIT_ABANDONED (e.g., WAIT_FAILED), ReleaseMutex is still called without having acquired the mutex, which will fail and could leave the application in an inconsistent state. Add an else clause or early return to skip ReleaseMutex when the wait operation doesn't succeed.

Copilot uses AI. Check for mistakes.
{
// TODO: check better error checking
*thread = (HANDLE)_beginthreadex(NULL, 0, callback, args, 0, NULL);
*thread = (HANDLE)(uintptr_t)_beginthreadex(NULL, 0, callback, args, 0, NULL);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast through uintptr_t is good for suppressing warnings when converting between HANDLE and unsigned int, but the underlying issue is that _beginthreadex returns unsigned int, not HANDLE. Consider using a temporary variable to store the thread ID separately if needed, or document why this cast pattern is necessary.

Copilot uses AI. Check for mistakes.
// Magic number constants
#define MAX_WINDOW_COUNT 35
#define MAX_TITLE_LENGTH 500
#define MAX_CLASS_NAME 500
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant MAX_CLASS_NAME is defined but never used in the codebase. Consider removing it to keep the code clean, or document where it's intended to be used if it's for future functionality.

Suggested change
#define MAX_CLASS_NAME 500

Copilot uses AI. Check for mistakes.
Comment on lines +533 to +537
args.mutex = CreateMutex(NULL, FALSE, APPLICATION_MUTEX_NAME);
if (args.mutex == NULL)
{
// Mutex creation failed
return (INT_PTR)FALSE;
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static variable args is not properly initialized before WM_INITDIALOG. If WM_INITDIALOG is called multiple times (which can happen in Windows dialog lifecycle), the mutex will be recreated without properly cleaning up the previous handle, leading to a resource leak. Consider checking if args.mutex is already initialized before creating a new mutex, or ensure proper cleanup.

Suggested change
args.mutex = CreateMutex(NULL, FALSE, APPLICATION_MUTEX_NAME);
if (args.mutex == NULL)
{
// Mutex creation failed
return (INT_PTR)FALSE;
if (args.mutex == NULL)
{
args.mutex = CreateMutex(NULL, FALSE, APPLICATION_MUTEX_NAME);
if (args.mutex == NULL)
{
// Mutex creation failed
return (INT_PTR)FALSE;
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants