前言

装了和谐版的 cadence SPB17.4, 用的挺好。
有个小瑕疵,服务中的"Cadence License Manager", 不管怎么设置,都有可能在开机后启动不起来(在我的本本上表现,就是有时开机后服务是运行状态,有时是停止状态)。

前几天特意按照大佬们的建议去设置服务的启动项,做了笔记SPB17.4 Cadence License Manager 启动失败的问题,不好使啊。
也不是完全不好使,是有时好使,有时不好使。大部分情况下都不好使,桑心啊。

原因有可能是Cadence License Manager 依赖网络服务或其他基础服务,可能服务开机就启动,那时网络状况不佳,或者就没有网络连接呢,导致服务启动不起来。

一般,开机后,运行cpture或editor失败,才会想起可能Cadence License Manager服务没起来。

自己又想不起来,每次开机后,去手工启动服务。
万一养成这个习惯,那不就是强迫症了。

今天找到一个保证Cadence License Manager 能100%自动启动成功的解决方法。

思路

  • 写一个守护程序(我用vs2022 + vc++ + console弄的),一直转圈检测 cadence服务的状态,每1分钟就检测一次,如果服务不是运行状态,就尝试用程序自动启动一次服务。

  • 建立一个计划任务,登录后30秒,将守护程序运行起来。
    用计划任务而不是将守护程序直接丢到win10开机启动文件夹中的原因是:守护程序因为要检测服务运行状态,启动服务,这些都需要管理员权限。

经过实验,放到开机启动文件夹中,不好使,具体原因未知,因为没写那么细致(e.g. 记录一下文件日志,记录程序退出原因,还是根本就启动不起来,因为没有看见UAC权限弹框),就是给自己用的一个小程序,自己能接受就是全部。

将守护程序放到计划任务中时,就可以指定程序运行的权限和启动时机,可以保证将守护程序自动运行起来.

实现

守护程序实验

环境 :vs2022 + vc++ + console + 服务操作API
260行的小工程。

// @file prj_demons_by_service_name.cpp
// @brief 查询指定的服务是否已经启动,如果没启动,则启动服务。如果服务已经启动,就退出
// 将这个程序(或快捷方式)放入win10的启动文件夹中, 用来确保会启动cadence的服务
// 因为我发现cadence的这个服务,无论怎么设置,开机后都不保证能确定启动起来. 估计是啥依赖环境没满足引起没启动或启动失败。

// 找win10的启动文件夹位置
// 开始菜单输入run, 选中run程序
// 输入 shell:startup 回车,转到win10启动文件夹中
// 我本机的win10启动文件夹为 C:\Users\my_name\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

// @note
// dev env = vs2022 vc++ console

#include "pch.h"

#include <windows.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <tchar.h>

#include <strsafe.h>

#include <locale.h>
#include <conio.h>

#define DST_SERVICE_NAME TEXT("Cadence License Manager")

void show_err_text(LPTSTR lpszFunction, DWORD dwErrSn)
{
    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf = NULL;
    LPVOID lpDisplayBuf = NULL;

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dwErrSn,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf,
        0, NULL);

    // Display the error message and exit the process

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
    StringCchPrintf((LPTSTR)lpDisplayBuf,
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%ls failed with error %d: %ls"),
        lpszFunction, dwErrSn, (LPTSTR)lpMsgBuf);

    printf("%ls\n", (LPCTSTR)lpDisplayBuf);

    LocalFree(lpMsgBuf);
    lpMsgBuf = NULL;

    LocalFree(lpDisplayBuf);
    lpDisplayBuf = NULL;
}

bool is_service_running(const TCHAR* psz_service_name)
{
    bool b_rc = false;

    SC_HANDLE schSCManager = NULL;
    SC_HANDLE schService = NULL;
    SERVICE_STATUS_PROCESS ssStatus;
    // DWORD dwOldCheckPoint;
    // DWORD dwStartTickCount;
    // DWORD dwWaitTime;
    DWORD dwBytesNeeded;

    DWORD dwErrSn = 0;

    do {


        // Get a handle to the SCM database. 

        // 需要管理员权限才行, 否则返回错误码5(拒绝访问)
        schSCManager = OpenSCManager(
            NULL,                    // local computer
            NULL,                    // servicesActive database 
            SC_MANAGER_ALL_ACCESS);  // full access rights 

        if (NULL == schSCManager)
        {
            dwErrSn = GetLastError();
            printf("OpenSCManager failed (%d)\n", dwErrSn);
            show_err_text((LPTSTR)TEXT("error"), dwErrSn);
            break;
        }

        // Get a handle to the service.

        schService = OpenService(
            schSCManager,         // SCM database 
            psz_service_name,            // name of service 
            SERVICE_ALL_ACCESS);  // full access 

        if (schService == NULL)
        {
            printf("OpenService failed (%d)\n", GetLastError());
            break;
        }

        // Check the status in case the service is not stopped. 

        if (!QueryServiceStatusEx(
            schService,                     // handle to service 
            SC_STATUS_PROCESS_INFO,         // information level
            (LPBYTE)&ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded))              // size needed if buffer is too small
        {
            printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
            break;
        }

        b_rc = (SERVICE_RUNNING == ssStatus.dwCurrentState);
    } while (false);

    if (NULL != schService)
    {
        CloseServiceHandle(schService);
        schService = NULL;
    }

    if (NULL != schSCManager)
    {
        CloseServiceHandle(schSCManager);
        schSCManager = NULL;
    }

    return b_rc;
}

bool try_to_run_service(const TCHAR* psz_service_name)
{
    bool b_rc = false;

    SC_HANDLE schSCManager = NULL;
    SC_HANDLE schService = NULL;
    SERVICE_STATUS_PROCESS ssStatus;
    // DWORD dwOldCheckPoint;
    // DWORD dwStartTickCount;
    // DWORD dwWaitTime;
    DWORD dwBytesNeeded;

    DWORD dwErrSn = 0;

    do {
        // Get a handle to the SCM database. 

        // 需要管理员权限才行, 否则返回错误码5(拒绝访问)
        schSCManager = OpenSCManager(
            NULL,                    // local computer
            NULL,                    // servicesActive database 
            SC_MANAGER_ALL_ACCESS);  // full access rights 

        if (NULL == schSCManager)
        {
            dwErrSn = GetLastError();
            printf("OpenSCManager failed (%d)\n", dwErrSn);
            show_err_text((LPTSTR)TEXT("error"), dwErrSn);
            break;
        }

        // Get a handle to the service.

        schService = OpenService(
            schSCManager,         // SCM database 
            psz_service_name,            // name of service 
            SERVICE_ALL_ACCESS);  // full access 

        if (schService == NULL)
        {
            printf("OpenService failed (%d)\n", GetLastError());
            break;
        }

        // Check the status in case the service is not stopped. 

        if (!QueryServiceStatusEx(
            schService,                     // handle to service 
            SC_STATUS_PROCESS_INFO,         // information level
            (LPBYTE)&ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded))              // size needed if buffer is too small
        {
            printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
            break;
        }

        b_rc = (SERVICE_RUNNING == ssStatus.dwCurrentState);
        if (b_rc)
        {
            break; // 服务当前状态就是running, 不用再启动服务了
        }

        // 尝试启动服务
        if (!StartService(
            schService,  // handle to service 
            0,           // number of arguments 
            NULL))      // no arguments 
        {
            dwErrSn = GetLastError();
            printf("StartService failed (%d)\n", dwErrSn);
            show_err_text((LPTSTR)TEXT("error"), dwErrSn);
            break;
        }

        printf("Service start pending...\n");
        b_rc = true;
    } while (false);

    if (NULL != schService)
    {
        CloseServiceHandle(schService);
        schService = NULL;
    }

    if (NULL != schSCManager)
    {
        CloseServiceHandle(schSCManager);
        schSCManager = NULL;
    }

    return b_rc;

}

int main()
{
    bool b_rc = false;
    int i_retry_cnt = 0;

    setlocale(LC_ALL, "chs"); // 控制台显示中文

    do {
        Sleep(1000 * 1); // 每转一圈, 都睡1秒, 然后再干活. 放置出现意外情况导致CPU占用率太高

        if (is_service_running(DST_SERVICE_NAME))
        {
            printf("service[%ls] was runing ...\n", DST_SERVICE_NAME);
            // break; // 如果服务已经启动了,退出程序

            // 即使检测到服务已经启动了,也不能退出
            // 发现即使成功启动服务,这个烂服务有可能还会停掉...
        }
        else {
            // 尝试启动服务
            b_rc = try_to_run_service(DST_SERVICE_NAME);
            printf("[%d] try to run service[%ls] %s\n", ++i_retry_cnt, DST_SERVICE_NAME, b_rc ? "success" : "failed");
        }

        // 睡1分钟, 再检测
        // 因为发现即使成功启动服务,这个烂服务有可能还会停掉...
        Sleep(1000 * 60);
    } while (true);
    return EXIT_SUCCESS;
}

编译好后,将exe拷贝出来,放到工程根目录,防止不小心被改掉或被删除。
在这里插入图片描述

建立计划任务启动守护程序

打开控制面板
在这里插入图片描述
打开系统与安全
在这里插入图片描述
打开管理工具
在这里插入图片描述
打开计划任务
在这里插入图片描述
创建计划任务
在这里插入图片描述

任务的设置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设置cadence服务为手动

如果cadence服务设置为自动,如果能每次都可靠启动服务,我也不用写守护程序由计划任务来启动了。
在这里插入图片描述
在这里插入图片描述

开机以后的验证

重启计算机后,已经实验了几次,都可以可靠将cadence服务启动起来。
看守护程序的UI日志,只启动了一次服务,以后每次检测,都是cadence服务是运行状态。

我很怀疑以前cadence服务不能可靠启动的原因是开机时,网络还没连上。
因为cadence服务依赖网络服务。
在这里插入图片描述

开机以后的效果

在这里插入图片描述

总结

自己遇到啥问题后,先看看网上各位大佬有没有解决方案。
如果有,就试试,省时间。
如果没有(或者找到的方法不好使),就得自己想折了。
分析问题,猜测故障原因,整理解决问题的思路,实践验证,重复以上4步,直到完全搞定(凑合能用也行,自己能接受就是全部)😃

补充 - 2022_0408_1035

今天要装一个软件,需要断网才能装。
装完软件,需要重启。
计算机重启后,我特意看了一下,cadence服务的守护程序并没有启动,好久好久都没启动。想起来,我设置计划服务时,勾选了"只有任何网络连接时才启动"这个选项。

等我连接网络连接后,守护程序立刻启动起来了。
这挺好的,确保了没有网络连接不会启动cadence服务。有了网络连接后,立刻就可以启动cadence服务。这样cadence服务的启动就是100%成功的。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐