/* akvirtualcamera, virtual camera for Mac and Windows. * Copyright (C) 2020 Gonzalo Exequiel Pedone * * akvirtualcamera is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * akvirtualcamera is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with akvirtualcamera. If not, see . * * Web-Site: http://webcamoid.github.io/ */ #include #include #include #include #include #include #include #include #include #include #include #include "service.h" #include "PlatformUtils/src/messageserver.h" #include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/timer.h" #include "VCamUtils/src/logger/logger.h" #define AkServiceLogMethod() \ AkLoggerLog("Service::", __FUNCTION__, "()") #define AkServicePrivateLogMethod() \ AkLoggerLog("ServicePrivate::", __FUNCTION__, "()") namespace AkVCam { struct AssistantDevice { std::string broadcaster; std::vector listeners; bool horizontalMirror; bool verticalMirror; Scaling scaling; AspectRatio aspectRatio; bool swapRgb; AssistantDevice(): horizontalMirror(false), verticalMirror(false), scaling(ScalingFast), aspectRatio(AspectRatioIgnore), swapRgb(false) { } }; typedef std::map AssistantPeers; typedef std::map DeviceConfigs; class ServicePrivate { public: SERVICE_STATUS m_status; SERVICE_STATUS_HANDLE m_statusHandler; MessageServer m_messageServer; AssistantPeers m_servers; AssistantPeers m_clients; DeviceConfigs m_deviceConfigs; Timer m_timer; std::mutex m_peerMutex; ServicePrivate(); static void stateChanged(void *userData, MessageServer::State state); static void checkPeers(void *userData); void sendStatus(DWORD currentState, DWORD exitCode, DWORD wait); inline static uint64_t id(); void removePortByName(const std::string &portName); void releaseDevicesFromPeer(const std::string &portName); void requestPort(Message *message); void addPort(Message *message); void removePort(Message *message); void setBroadCasting(Message *message); void setMirroring(Message *message); void setScaling(Message *message); void setAspectRatio(Message *message); void setSwapRgb(Message *message); void frameReady(Message *message); void listeners(Message *message); void listener(Message *message); void broadcasting(Message *message); void mirroring(Message *message); void scaling(Message *message); void aspectRatio(Message *message); void swapRgb(Message *message); void listenerAdd(Message *message); void listenerRemove(Message *message); }; GLOBAL_STATIC(ServicePrivate, servicePrivate) } DWORD WINAPI controlHandler(DWORD control, DWORD eventType, LPVOID eventData, LPVOID context); BOOL WINAPI controlDebugHandler(DWORD control); AkVCam::Service::Service() { } AkVCam::Service::~Service() { } BOOL AkVCam::Service::install() { AkServiceLogMethod(); WCHAR fileName[MAX_PATH]; if (!GetModuleFileName(nullptr, fileName, MAX_PATH)) { AkLoggerLog("Can't read module file name"); return false; } auto scManager = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); if (!scManager) { AkLoggerLog("Can't open SCManager"); return false; } auto service = CreateService(scManager, TEXT(DSHOW_PLUGIN_ASSISTANT_NAME), TEXT(DSHOW_PLUGIN_ASSISTANT_DESCRIPTION), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, fileName, nullptr, nullptr, nullptr, nullptr, nullptr); if (!service) { AkLoggerLog("Can't create service"); CloseServiceHandle(scManager); return false; } // Add detailed description to the service. SERVICE_DESCRIPTION serviceDescription; WCHAR description[] = TEXT(DSHOW_PLUGIN_DESCRIPTION_EXT); serviceDescription.lpDescription = description; auto result = ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &serviceDescription); // Configure the service so it will restart if fail. WCHAR rebootMsg[] = L"Service failed restarting..."; std::vector actions { {SC_ACTION_RESTART, 5000} }; SERVICE_FAILURE_ACTIONS failureActions { INFINITE, rebootMsg, nullptr, DWORD(actions.size()), actions.data() }; result = ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS, &failureActions); // Run the service StartService(service, 0, nullptr); CloseServiceHandle(service); CloseServiceHandle(scManager); return result; } void AkVCam::Service::uninstall() { AkServiceLogMethod(); auto scManager = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); if (!scManager) { AkLoggerLog("Can't open SCManager"); return; } auto sevice = OpenService(scManager, TEXT(DSHOW_PLUGIN_ASSISTANT_NAME), SERVICE_ALL_ACCESS); if (sevice) { if (ControlService(sevice, SERVICE_CONTROL_STOP, &servicePrivate()->m_status)) { AkLoggerLog("Stopping service"); do { Sleep(1000); QueryServiceStatus(sevice, &servicePrivate()->m_status); } while(servicePrivate()->m_status.dwCurrentState != SERVICE_STOPPED); } if (!DeleteService(sevice)) { AkLoggerLog("Delete service failed"); } CloseServiceHandle(sevice); } else { AkLoggerLog("Can't open service"); } CloseServiceHandle(scManager); } void AkVCam::Service::debug() { AkServiceLogMethod(); SetConsoleCtrlHandler(controlDebugHandler, TRUE); servicePrivate()->m_messageServer.start(true); } void AkVCam::Service::showHelp(int argc, char **argv) { AkServiceLogMethod(); UNUSED(argc) auto programName = strrchr(argv[0], '\\'); if (!programName) programName = strrchr(argv[0], '/'); if (!programName) programName = argv[0]; else programName++; std::cout << "Usage: " << programName << " [options]" << std::endl; std::cout << std::endl; std::cout << "Webcamoid virtual camera server." << std::endl; std::cout << std::endl; std::cout << "Options:" << std::endl; std::cout << std::endl; std::cout << "\t-i, --install\tInstall the service." << std::endl; std::cout << "\t-u, --uninstall\tUnistall the service." << std::endl; std::cout << "\t-d, --debug\tDebug the service." << std::endl; std::cout << "\t-h, --help\tShow this help." << std::endl; } AkVCam::ServicePrivate::ServicePrivate() { AkServicePrivateLogMethod(); this->m_status = { SERVICE_WIN32_OWN_PROCESS, SERVICE_STOPPED, 0, NO_ERROR, NO_ERROR, 0, 0 }; this->m_statusHandler = nullptr; this->m_messageServer.setPipeName(L"\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME_L); this->m_messageServer.setHandlers({ {AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(ServicePrivate::frameReady) }, {AKVCAM_ASSISTANT_MSG_REQUEST_PORT , AKVCAM_BIND_FUNC(ServicePrivate::requestPort) }, {AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(ServicePrivate::addPort) }, {AKVCAM_ASSISTANT_MSG_REMOVE_PORT , AKVCAM_BIND_FUNC(ServicePrivate::removePort) }, {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , AKVCAM_BIND_FUNC(ServicePrivate::listenerAdd) }, {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE, AKVCAM_BIND_FUNC(ServicePrivate::listenerRemove) }, {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS , AKVCAM_BIND_FUNC(ServicePrivate::listeners) }, {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER , AKVCAM_BIND_FUNC(ServicePrivate::listener) }, {AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , AKVCAM_BIND_FUNC(ServicePrivate::broadcasting) }, {AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING, AKVCAM_BIND_FUNC(ServicePrivate::setBroadCasting)}, {AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING , AKVCAM_BIND_FUNC(ServicePrivate::mirroring) }, {AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING , AKVCAM_BIND_FUNC(ServicePrivate::setMirroring) }, {AKVCAM_ASSISTANT_MSG_DEVICE_SCALING , AKVCAM_BIND_FUNC(ServicePrivate::scaling) }, {AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING , AKVCAM_BIND_FUNC(ServicePrivate::setScaling) }, {AKVCAM_ASSISTANT_MSG_DEVICE_ASPECTRATIO , AKVCAM_BIND_FUNC(ServicePrivate::aspectRatio) }, {AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO , AKVCAM_BIND_FUNC(ServicePrivate::setAspectRatio) }, {AKVCAM_ASSISTANT_MSG_DEVICE_SWAPRGB , AKVCAM_BIND_FUNC(ServicePrivate::swapRgb) }, {AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB , AKVCAM_BIND_FUNC(ServicePrivate::setSwapRgb) }, }); this->m_timer.setInterval(60000); this->m_timer.connectTimeout(this, &ServicePrivate::checkPeers); } void AkVCam::ServicePrivate::stateChanged(void *userData, MessageServer::State state) { UNUSED(userData) switch (state) { case MessageServer::StateAboutToStart: AkVCam::servicePrivate()->sendStatus(SERVICE_START_PENDING, NO_ERROR, 3000); break; case MessageServer::StateStarted: AkVCam::servicePrivate()->sendStatus(SERVICE_RUNNING, NO_ERROR, 0); break; case MessageServer::StateAboutToStop: AkVCam::servicePrivate()->sendStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); break; case MessageServer::StateStopped: AkVCam::servicePrivate()->sendStatus(SERVICE_STOPPED, NO_ERROR, 0); break; } } void AkVCam::ServicePrivate::checkPeers(void *userData) { auto self = reinterpret_cast(userData); std::vector removePorts; self->m_peerMutex.lock(); std::vector allPeers { &self->m_clients, &self->m_servers }; for (auto peers: allPeers) for (auto &peer: *peers) { Message message; message.messageId = AKVCAM_ASSISTANT_MSG_ISALIVE; message.dataSize = sizeof(MsgIsAlive); MessageServer::sendMessage(peer.second, &message); auto requestData = messageData(&message); if (!requestData->alive) removePorts.push_back(peer.first); } self->m_peerMutex.unlock(); for (auto &port: removePorts) { AkLoggerLog(port, " died, removing..."); self->removePortByName(port); } } void AkVCam::ServicePrivate::sendStatus(DWORD currentState, DWORD exitCode, DWORD wait) { AkServicePrivateLogMethod(); this->m_status.dwControlsAccepted = currentState == SERVICE_START_PENDING? 0: SERVICE_ACCEPT_STOP; this->m_status.dwCurrentState = currentState; this->m_status.dwWin32ExitCode = exitCode; this->m_status.dwWaitHint = wait; if (currentState == SERVICE_RUNNING || currentState == SERVICE_STOPPED) this->m_status.dwCheckPoint = 0; else this->m_status.dwCheckPoint++; SetServiceStatus(this->m_statusHandler, &this->m_status); } uint64_t AkVCam::ServicePrivate::id() { static uint64_t id = 0; return id++; } void AkVCam::ServicePrivate::removePortByName(const std::string &portName) { AkServicePrivateLogMethod(); AkLoggerLog("Port: ", portName); this->m_peerMutex.lock(); std::vector allPeers { &this->m_clients, &this->m_servers }; bool breakLoop = false; for (auto peers: allPeers) { for (auto &peer: *peers) if (peer.first == portName) { peers->erase(portName); breakLoop = true; break; } if (breakLoop) break; } bool peersEmpty = this->m_servers.empty() && this->m_clients.empty(); this->m_peerMutex.unlock(); if (peersEmpty) this->m_timer.stop(); this->releaseDevicesFromPeer(portName); } void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName) { for (auto &config: this->m_deviceConfigs) if (config.second.broadcaster == portName) { config.second.broadcaster.clear(); Message message; message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING; message.dataSize = sizeof(MsgBroadcasting); auto data = messageData(&message); memcpy(data->device, config.first.c_str(), (std::min)(config.first.size(), MAX_STRING)); this->m_peerMutex.lock(); for (auto &client: this->m_clients) MessageServer::sendMessage(client.second, &message); this->m_peerMutex.unlock(); } else { auto it = std::find(config.second.listeners.begin(), config.second.listeners.end(), portName); if (it != config.second.listeners.end()) config.second.listeners.erase(it); } } void AkVCam::ServicePrivate::requestPort(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string portName = data->client? AKVCAM_ASSISTANT_CLIENT_NAME: AKVCAM_ASSISTANT_SERVER_NAME; portName += std::to_string(this->id()); AkLoggerLog("Returning Port: ", portName); memcpy(data->port, portName.c_str(), (std::min)(portName.size(), MAX_STRING)); } void AkVCam::ServicePrivate::addPort(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string portName(data->port); std::string pipeName(data->pipeName); bool ok = true; this->m_peerMutex.lock(); AssistantPeers *peers; if (portName.find(AKVCAM_ASSISTANT_CLIENT_NAME) != std::string::npos) peers = &this->m_clients; else peers = &this->m_servers; for (auto &peer: *peers) if (peer.first == portName) { ok = false; break; } if (ok) { AkLoggerLog("Adding Peer: ", portName); (*peers)[portName] = pipeName; } size_t nPeers = this->m_servers.size() + this->m_clients.size(); this->m_peerMutex.unlock(); if (ok && nPeers == 1) this->m_timer.start(); data->status = ok; } void AkVCam::ServicePrivate::removePort(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); this->removePortByName(data->port); } void AkVCam::ServicePrivate::setBroadCasting(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); std::string broadcaster(data->broadcaster); data->status = false; if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; if (this->m_deviceConfigs[deviceId].broadcaster == broadcaster) return; AkLoggerLog("Device: ", deviceId); AkLoggerLog("Broadcaster: ", broadcaster); this->m_deviceConfigs[deviceId].broadcaster = broadcaster; data->status = true; this->m_peerMutex.lock(); for (auto &client: this->m_clients) { Message msg(message); MessageServer::sendMessage(client.second, &msg); } this->m_peerMutex.unlock(); } void AkVCam::ServicePrivate::setMirroring(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); data->status = false; if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; if (this->m_deviceConfigs[deviceId].horizontalMirror == data->hmirror && this->m_deviceConfigs[deviceId].verticalMirror == data->vmirror) return; this->m_deviceConfigs[deviceId].horizontalMirror = data->hmirror; this->m_deviceConfigs[deviceId].verticalMirror = data->vmirror; data->status = true; this->m_peerMutex.lock(); for (auto &client: this->m_clients) { Message msg(message); MessageServer::sendMessage(client.second, &msg); } this->m_peerMutex.unlock(); } void AkVCam::ServicePrivate::setScaling(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); data->status = false; if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; if (this->m_deviceConfigs[deviceId].scaling == data->scaling) return; this->m_deviceConfigs[deviceId].scaling = data->scaling; data->status = true; this->m_peerMutex.lock(); for (auto &client: this->m_clients) { Message msg(message); MessageServer::sendMessage(client.second, &msg); } this->m_peerMutex.unlock(); } void AkVCam::ServicePrivate::setAspectRatio(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); data->status = false; if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; if (this->m_deviceConfigs[deviceId].aspectRatio == data->aspect) return; this->m_deviceConfigs[deviceId].aspectRatio = data->aspect; data->status = true; this->m_peerMutex.lock(); for (auto &client: this->m_clients) { Message msg(message); MessageServer::sendMessage(client.second, &msg); } this->m_peerMutex.unlock(); } void AkVCam::ServicePrivate::setSwapRgb(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); data->status = false; if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; if (this->m_deviceConfigs[deviceId].swapRgb == data->swap) return; this->m_deviceConfigs[deviceId].swapRgb = data->swap; data->status = true; this->m_peerMutex.lock(); for (auto &client: this->m_clients) { Message msg(message); MessageServer::sendMessage(client.second, &msg); } this->m_peerMutex.unlock(); } void AkVCam::ServicePrivate::frameReady(AkVCam::Message *message) { AkServicePrivateLogMethod(); this->m_peerMutex.lock(); for (auto &client: this->m_clients) MessageServer::sendMessage(client.second, message); this->m_peerMutex.unlock(); } void AkVCam::ServicePrivate::listeners(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; data->nlistener = this->m_deviceConfigs[deviceId].listeners.size(); if (data->nlistener > 0) { memcpy(data->listener, this->m_deviceConfigs[deviceId].listeners[0].c_str(), std::min(this->m_deviceConfigs[deviceId].listeners[0].size(), MAX_STRING)); } data->status = true; } void AkVCam::ServicePrivate::listener(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; auto nlistener = this->m_deviceConfigs[deviceId].listeners.size(); if (data->nlistener >= nlistener) { data->status = false; return; } memcpy(data->listener, this->m_deviceConfigs[deviceId].listeners[data->nlistener].c_str(), std::min(this->m_deviceConfigs[deviceId].listeners[data->nlistener].size(), MAX_STRING)); data->status = true; } void AkVCam::ServicePrivate::broadcasting(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; memcpy(data->broadcaster, this->m_deviceConfigs[deviceId].broadcaster.c_str(), std::min(this->m_deviceConfigs[deviceId].broadcaster.size(), MAX_STRING)); data->status = true; } void AkVCam::ServicePrivate::mirroring(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; data->hmirror = this->m_deviceConfigs[deviceId].horizontalMirror; data->vmirror = this->m_deviceConfigs[deviceId].verticalMirror; data->status = true; } void AkVCam::ServicePrivate::scaling(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; data->scaling = this->m_deviceConfigs[deviceId].scaling; data->status = true; } void AkVCam::ServicePrivate::aspectRatio(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; data->aspect = this->m_deviceConfigs[deviceId].aspectRatio; data->status = true; } void AkVCam::ServicePrivate::swapRgb(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; data->swap = this->m_deviceConfigs[deviceId].swapRgb; data->status = true; } void AkVCam::ServicePrivate::listenerAdd(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; auto &listeners = this->m_deviceConfigs[deviceId].listeners; std::string listener(data->listener); auto it = std::find(listeners.begin(), listeners.end(), listener); if (it == listeners.end()) { listeners.push_back(listener); data->nlistener = listeners.size(); data->status = true; this->m_peerMutex.lock(); for (auto &client: this->m_clients) { Message msg(message); MessageServer::sendMessage(client.second, &msg); } this->m_peerMutex.unlock(); } else { data->nlistener = listeners.size(); data->status = false; } } void AkVCam::ServicePrivate::listenerRemove(AkVCam::Message *message) { AkServicePrivateLogMethod(); auto data = messageData(message); std::string deviceId(data->device); if (this->m_deviceConfigs.count(deviceId) < 1) this->m_deviceConfigs[deviceId] = {}; auto &listeners = this->m_deviceConfigs[deviceId].listeners; std::string listener(data->listener); auto it = std::find(listeners.begin(), listeners.end(), listener); if (it != listeners.end()) { listeners.erase(it); data->nlistener = listeners.size(); data->status = true; this->m_peerMutex.lock(); for (auto &client: this->m_clients) { Message msg(message); MessageServer::sendMessage(client.second, &msg); } this->m_peerMutex.unlock(); } else { data->nlistener = listeners.size(); data->status = false; } } DWORD WINAPI controlHandler(DWORD control, DWORD eventType, LPVOID eventData, LPVOID context) { UNUSED(eventType) UNUSED(eventData) UNUSED(context) AkLoggerLog("controlHandler()"); DWORD result = ERROR_CALL_NOT_IMPLEMENTED; switch (control) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: AkVCam::servicePrivate()->sendStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); AkVCam::servicePrivate()->m_messageServer.stop(); result = NO_ERROR; break; case SERVICE_CONTROL_INTERROGATE: result = NO_ERROR; break; default: break; } auto state = AkVCam::servicePrivate()->m_status.dwCurrentState; AkVCam::servicePrivate()->sendStatus(state, NO_ERROR, 0); return result; } BOOL WINAPI controlDebugHandler(DWORD control) { AkLoggerLog("controlDebugHandler()"); if (control == CTRL_BREAK_EVENT || control == CTRL_C_EVENT) { AkVCam::servicePrivate()->m_messageServer.stop(); return TRUE; } return FALSE; } void WINAPI serviceMain(DWORD dwArgc, LPTSTR *lpszArgv) { UNUSED(dwArgc) UNUSED(lpszArgv) AkLoggerLog("serviceMain()"); AkLoggerLog("Setting service control handler"); AkVCam::servicePrivate()->m_statusHandler = RegisterServiceCtrlHandlerEx(TEXT(DSHOW_PLUGIN_ASSISTANT_NAME), controlHandler, nullptr); if (!AkVCam::servicePrivate()->m_statusHandler) return; AkVCam::servicePrivate()->sendStatus(SERVICE_START_PENDING, NO_ERROR, 3000); AkLoggerLog("Setting up service"); AkVCam::servicePrivate()->m_messageServer .connectStateChanged(AkVCam::servicePrivate(), &AkVCam::ServicePrivate::stateChanged); AkVCam::servicePrivate()->m_messageServer.start(true); }