Help with Semaphores

Joseph RW 85 Reputation points
2025-08-28T15:29:13.9966667+00:00

Hi Everyone, I am experimenting with Semaphores as I am reading the Windows System 10 programming book, I have this code, when it's run at first it's supposed to spin up the Semaphore Server and that works fine , but when I run another instance of it it's supposed to run the Semaphore Client, but on each attempt it just starts the server and the Client code never executes, I have confirmed with Process explorer that the Semaphore is indeed created. But no matter what I do the Client never runs.

#include <Windows.h>
#include <wininet.h>
#include <stdio.h>
#include <wchar.h>
#include <iostream>
#pragma comment(lib, "wininet.lib") // Link with wininet.lib
#define MAX_DOWNLOAD_COUNT 3
// Define a structure named 'DownloadObj'
struct DownloadObj {
    WCHAR url[500];
    WCHAR save_to[250];
};
BOOL DownloadHelper(WCHAR* url, WCHAR* save_to) {
    BOOL success = FALSE;
    DWORD bytesRead = 0;
    BYTE buffer[4096];
    DWORD bytesWritten = 0;
    HINTERNET hInternet = NULL;
    HINTERNET hUrl = NULL;
    HANDLE fileHandle = INVALID_HANDLE_VALUE;
    // Initialize WinINet
    hInternet = ::InternetOpenW(L"WinINet Downloader/1.0",
        INTERNET_OPEN_TYPE_PRECONFIG,
        NULL, NULL, 0);
    if (!hInternet) {
        printf("InternetOpenW failed: %lu\n", GetLastError());
        goto cleanup;
    }
    // Open URL
    hUrl = ::InternetOpenUrlW(hInternet, url, NULL, 0,
        INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 0);
    if (!hUrl) {
        printf("InternetOpenUrlW failed: %lu\n", GetLastError());
        goto cleanup;
    }
    // Create output file
    fileHandle = ::CreateFileW(
        save_to,
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    if (fileHandle == INVALID_HANDLE_VALUE) {
        printf("CreateFileW failed: %lu\n", GetLastError());
        goto cleanup;
    }
    printf("Downloading\n");
    // Download loop
    do {
        if (!::InternetReadFile(hUrl, buffer, sizeof(buffer), &bytesRead)) {
            printf("InternetReadFile failed: %lu\n", GetLastError());
            goto cleanup;
        }
        if (bytesRead == 0) {
            success = TRUE;
            break;
        }
        if (!::WriteFile(fileHandle, buffer, bytesRead, &bytesWritten, NULL) || bytesWritten != bytesRead) {
            printf("WriteFile failed: %lu\n", GetLastError());
            goto cleanup;
        }
    } while (TRUE);
cleanup:
    if (fileHandle != INVALID_HANDLE_VALUE) {
        ::CloseHandle(fileHandle);
    }
    if (hUrl) {
        ::InternetCloseHandle(hUrl);
    }
    if (hInternet) {
        ::InternetCloseHandle(hInternet);
    }
    return success;
}
DWORD WINAPI DownloadFile(LPVOID lpParam) {
    DownloadObj* params = static_cast<DownloadObj*>(lpParam);
    WCHAR* url = params->url;
    WCHAR* save_to = params->save_to;
    HANDLE hSem = ::OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, TRUE, L"MyDownloadSemaphore");
    if (!hSem) {
        printf("Client Failed\n");
        return 42;
    }
    // WAIT for a free slot (a permit from the semaphore).
    // This call will block if MAX_CONCURRENT_DOWNLOADS are already running.
    DWORD dwWaitResult = WaitForSingleObject(hSem, INFINITE);
    switch (dwWaitResult) {
    case WAIT_OBJECT_0:
        wprintf(L"Downloading: %s\n", url);
        wprintf(L"To: %s\n", save_to);
        // Perform the actual download (this is a blocking call)
        if (!DownloadHelper(url, save_to)) {
            printf("Download Failed: %u\n", ::GetLastError());
        }
        // CRITICAL: Release the semaphore permit back to the pool.
        // This allows one of the waiting threads to start its download.
        if (!ReleaseSemaphore(hSem, 1, NULL)) {
            std::cout << "Fatal Error: Failed to release semaphore!" << std::endl;
        }
        break;
        // Handle other cases (like WAIT_TIMEOUT or WAIT_ABANDONED) if needed.
    }
    ::CloseHandle(hSem);
    // Do not delete params; it's not heap-allocated
    return 42;
}
int main() {
    SECURITY_ATTRIBUTES sa = { sizeof(sa) };
    sa.nLength = sizeof(sa);
    sa.bInheritHandle = TRUE;
    HANDLE hSemaphore = ::CreateSemaphoreW(&sa, MAX_DOWNLOAD_COUNT, MAX_DOWNLOAD_COUNT, L"MyDownloadSemaphore");
    if (hSemaphore) {
        printf("Semaphore Server Created  ...!!!\n");

        getchar();
        ::CloseHandle(hSemaphore);
    } else {
        if (::GetLastError() == ERROR_ALREADY_EXISTS) {
			
			printf("Semaphore Client Created  ...!!!\n");

            struct DownloadObj downloads[5] = {
                {L"http://127.0.0.1/a-guide-to-kernel-exploitation.pdf", L"1_.pdf"},
                {L"http://127.0.0.1/694705872-Beginners-Guide-to-Exploitation-on-ARM-Vol-1.pdf", L"2_.pdf"},
                {L"http://127.0.0.1/ai_m.pdf", L"3_.pdf"},
                {L"http://127.0.0.1/ai__eye.pdf", L"4_.pdf"},
                {L"http://127.0.0.1/ai_agents.pdf", L"5_.pdf"}
            };
            std::cout << "\nHello World!\n";
            HANDLE hThreads[5];
            for (int i = 0; i < 5; i++) {
                hThreads[i] = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DownloadFile, &downloads[i], 0, NULL);
                printf("Thread: %d\n", i);
                if (!hThreads[i]) {
                    printf("Thread: %d Failed\n", i);
                }
            }
            // Wait for all five threads to finish
            ::WaitForMultipleObjects(
                5, // number of objects in the array
                hThreads, // array of object handles
                TRUE, // bWaitAll: TRUE to wait for all objects to be signaled
                INFINITE // dwMilliseconds: INFINITE to wait indefinitely
            );
            for (int i = 0; i < 5; i++) {
                ::CloseHandle(hThreads[i]);
            }
        } else {
            printf("CreateSemaphoreW failed with unexpected error: %lu\n", ::GetLastError());
        }
    }
    return 0;
}
Developer technologies | C++
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. RLWA32 50,496 Reputation points
    2025-08-28T15:52:07.2+00:00
        if (hSemaphore) {
            printf("Semaphore Server Created  ...!!!\n");
    
            getchar();
            ::CloseHandle(hSemaphore);
        } else {
            if (::GetLastError() == ERROR_ALREADY_EXISTS) {
    
    

    For the CreateSemaphore - "If the function succeeds, the return value is a handle to the semaphore object. If the named semaphore object existed before the function call, the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS."

    So on the second call the function succeeds, but you only test for ERROR_ALREADY_EXISTS if it fails.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.