How to create a Windows Service with CMake that uses Juce

Is this even possible? It must be. My problem is that cmake expects me to use add_executable when I create a windows service, but then I can’t link with juce libraries. If I use juce_add_console_app, then it can’t find the entry point for the service. I’ve seen questions related to services in the forum but they don’t seem to address this issue.
Is there an easy way to resolve this entry point issue?

You’ll need to customize your apps main() to do this.

A Windows Service requires a main function that calls StartServiceCtrlDispatcher with a service entry function. JUCE applications typically use juce::JUCEApplicationBase::main, which conflicts with this requirement.

juce_add_console_app sets up the project to use JUCE’s main function, which prevents you from defining the service entry point.

Example:

CMakeLists.txt:

project(MyJuceService)

add_executable(MyJuceService)

target_sources(MyJuceService PRIVATE
    Source/Main.cpp
    Source/ServiceMain.cpp
)

# Manually link JUCE modules
find_package(JUCE CONFIG REQUIRED)
target_link_libraries(MyJuceService PRIVATE
    juce::juce_core
    juce::juce_events
    juce::juce_data_structures
)

# Ensure service uses the correct subsystem
set_target_properties(MyJuceService PROPERTIES LINK_FLAGS "/SUBSYSTEM:CONSOLE")

# Define service name for use in code
target_compile_definitions(MyJuceService PRIVATE SERVICE_NAME="MyJuceService")

In your projects’ main code:


#include <windows.h>
#include <juce_core/juce_core.h>

SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle;

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
void WINAPI ServiceCtrlHandler(DWORD ctrlCode);
void StartService();

int main()
{
    SERVICE_TABLE_ENTRY serviceTable[] = {
        { const_cast<LPSTR>("MyJuceService"), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
        { NULL, NULL }
    };

    if (!StartServiceCtrlDispatcher(serviceTable))
    {
        juce::Logger::writeToLog("Failed to start service");
    }

    return 0;
}

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
    serviceStatusHandle = RegisterServiceCtrlHandler("MyJuceService", ServiceCtrlHandler);

    if (serviceStatusHandle == nullptr)
        return;

    serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    serviceStatus.dwCurrentState = SERVICE_START_PENDING;
    serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    serviceStatus.dwWin32ExitCode = 0;
    serviceStatus.dwServiceSpecificExitCode = 0;
    serviceStatus.dwCheckPoint = 0;
    serviceStatus.dwWaitHint = 0;

    SetServiceStatus(serviceStatusHandle, &serviceStatus);

    StartService();
}

void StartService()
{
    serviceStatus.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(serviceStatusHandle, &serviceStatus);

    // JUCE-based code can run here
    while (serviceStatus.dwCurrentState == SERVICE_RUNNING)
    {
        juce::Thread::sleep(1000);
    }
}

void WINAPI ServiceCtrlHandler(DWORD ctrlCode)
{
    if (ctrlCode == SERVICE_CONTROL_STOP)
    {
        serviceStatus.dwCurrentState = SERVICE_STOPPED;
        SetServiceStatus(serviceStatusHandle, &serviceStatus);
    }
}

Thanks for the quick response, I have tried what you suggested (actually I think I have tried this exact same pattern before), but I am still getting this error message:

[build] MSVCRTD.lib(exe_main.obj) : error LNK2019: unresolved external symbol main referenced in function “int __cdecl invoke_main(void)” (?invoke_main@@YAHXZ)
[build] Hint on symbols that are defined and could potentially match:
[build] “public: static int __cdecl juce::JUCEApplicationBase::main(void)” (?main@JUCEApplicationBase@juce@@SAHXZ)

Weird. Do you have your main() defined for sure?

100%. I also tried
int main(int argc, char *argv)
but it made no difference.
I think the problem may be because this service is a second target in my CMakeLists.txt, the first target it a full juce based application that otherwise builds just fine. One thing I can’t get my head around is how I can build another target, using add_subdirectory(), but still reuse the JUCE build that was referenced in the parent directory? And because of this, I keep both targets in the same CMakeLists.txt, and maybe some juce related rules I have set for target1, are conflicting with target2.
Now I am trying again using a separate CMakeLists.txt, in a sub-folder, but now I am hitting the same wall again, I can’t reach the JUCE source directory the same way as I did in the parent project, it keeps giving me compiler errors for juce source files.

Some more details: In the root cmake txt file I have referenced juce like this:

CPMAddPackage(NAME Juce
        GITHUB_REPOSITORY juce-framework/JUCE
        DOWNLOAD_ONLY TRUE
        GIT_TAG 8.0.6)

add_subdirectory(${Juce_SOURCE_DIR})

And then at the end of the txt file I used add_subdirectory to add my second txt file.
And I am assuming that ${Juce_SOURCE_DIR} was inherited to the second txt file, but for some reason I can’t use it anymore the same way as I did in the root txt.