前言
装了和谐版的 cadence 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%成功的。