首页 > 系统相关 >c/c++ 创建windows 服务程序

c/c++ 创建windows 服务程序

时间:2024-06-13 14:57:56浏览次数:27  
标签:服务程序 return SERVICE windows timenow hSCM c++ file NULL

1 项目介绍

本次的项目是设计windows服务程序监听系统时间,对误差的时间进行修改,解决不连网下的本地时间的误差问题。

2 程序设计

当程序直接运行时为创建该程序为windows服务程序,创建的windows服务程序设置为开机自启且运行带参数"-k runservice"以进行区别为创建服务还是运行程序。


#define  _WINSOCK_DEPRECATED_NO_WARNINGS 
#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <windows.h>
#include <winsvc.h>
#include <conio.h>
#include <winuser.h>
#include <tchar.h>

#pragma comment(lib,"Advapi32.lib")
#pragma comment(lib ,"User32.lib")
using namespace std;

TCHAR szServiceName[] = L"timeCompensation";    //服务名

SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle;
// 用于通知服务停止的事件句柄  
HANDLE hStopEvent = NULL;

/*
    @brief :递归创建文件夹
    @param    path : 创建文件夹的最后深路径
    @return     成功返回true ,失败返回false
*/
bool CreateDirectoryRecursive(const std::wstring& path);


/*
    @brief  服务控制主函数,这里实现对服务的控制,当在服务管理器上停止或其它操作时,将会运行此处代码
*/
void WINAPI ServiceCtrlHandler(DWORD controlCode);


/*
    @brief  服务主函数,这在里进行控制对服务控制的注册
*/
void WINAPI ServiceMain(DWORD argc, LPTSTR* argv);

/*
    @brief 程序日志,将内容写入系统日志
*/
void LogEvent(LPCTSTR pFormat, ...);

/*
    @brief  停止windows service 程序并删除程序
    @return 成功则返回true ,失败或者服务不存在则返回false
*/
BOOL Uninstall();

/*
    @brief 判断windows服务程序是否已经安装
    @return 如果已经安装,返回true  ,否则返回false
*/
BOOL IsInstalled();

/*
    @brief    安装windows service 程序
    @return 如果成功安装返回true ,否则返回false
*/
BOOL Install();

/*
    @brief  启动windows service服务程序
    @return 成功启动返回true ,否则返回false
*/
BOOL StartScService();


/*
    @brief 要实现的程序。更新时间,记录文件存在C:/ProgramData/timeUpdate/下的timeCmpensation.txt中,每24小时增加10.064S
*/
void updateTime();




bool CreateDirectoryRecursive(const std::wstring& path) {
    if (path.empty()) {
        return false;
    }

    // 检查路径是否已经存在  
    if (GetFileAttributesW(path.c_str()) != INVALID_FILE_ATTRIBUTES) {
        // 如果路径存在且是目录,返回成功  
        if (FILE_ATTRIBUTE_DIRECTORY & GetFileAttributesW(path.c_str())) {
            return true;
        }
        // 如果路径存在但不是目录,返回失败  
        return false;
    }

    // 拆分路径,找到最后一个\之前的部分(即父目录)  
    size_t found = path.find_last_of(L"\\/");
    if (found == std::wstring::npos) {
        // 如果没有找到\或/,则路径只包含一个目录名  
        return CreateDirectoryW(path.c_str(), NULL);
    }

    // 递归创建父目录  
    std::wstring parentPath = path.substr(0, found);
    if (!CreateDirectoryRecursive(parentPath)) {
        return false;
    }

    // 创建当前目录  
    return CreateDirectoryW(path.c_str(), NULL);
}

/*
void updateTime() {

    CreateDirectoryRecursive(L"C:/ProgramData/timeUpdate"); //递归创建文件夹

    //unsigned long size = 255;
    //WCHAR UserName_b[255];
    //char UserName_e[255];
    //GetUserName(UserName_b, &size);//GetUserName()函数同上
    //WideCharToMultiByte(CP_ACP, 0, UserName_b, -1, &UserName_e[0], 255, nullptr, nullptr);   //将wchar_t * 转换成char *

    char filePath[256];
    snprintf(filePath, 255, "C:/ProgramData/timeUpdate/timeCompensation.txt");

    time_t timep;
    time(&timep); //获取从1970至今过了多少秒,存入time_t类型的timep

    //获取精确到毫秒的时间
    SYSTEMTIME timenow;
    GetLocalTime(&timenow);
    //printf("%I64d秒 , %I64d毫秒\n", timenow.wSecond, timenow.wMilliseconds);


    //下一步llt,是转化成(毫秒级)的Unix时间戳,将tm的秒时间戳*1000加上timenow的毫秒时间戳得到毫秒时间戳
    time_t llt = timep * 1000 + timenow.wMilliseconds;
    FILE* file;
    if (0 != fopen_s(&file, filePath, "r+")) {
        //打不开说明服务刚创建,程序写下时间戳就退出
        if (0 != fopen_s(&file, filePath, "w")) {
            printf("打开文件失败\n");
            return;
        }
        fprintf(file, "%I64d", llt);
        fclose(file);
        return;
    }
    //获取文件内的时间戳
    char buf[64] = {};
    fread_s(buf, sizeof(buf), 1, sizeof(buf), file);
    fclose(file);

    time_t file_times = atoll(buf);

    if (llt - file_times >= 1000 * 60 * 60 * 24) {
        //每超过24小时就执行时间补偿,增加时间补偿 ,24小时/补偿10.064S ,但是获取本机电脑到设置完时间,有1-2毫秒的程序运行时间差,所以多加1毫秒
        time_t add_count = (llt - file_times) / (1000 * 60 * 60 * 24); //计算有多少个24小时
        time_t kllt = (llt - file_times) % (1000 * 60 * 60 * 24); //保存没有被算进去的毫秒数
        time_t llt_new = llt + add_count * (long long)(10064 + 1);   //更新现在的毫秒数
        time_t timestamp = llt_new / 1000;  //更新当前秒数
        time_t Milliseconds = llt_new % 1000;   //计算当前剩余的毫秒数


        tm t;
        _localtime64_s(&t, &timestamp);    //将更新后时间戳转化成标准时间,年份需要加1900,月份加1(0-11)
        timenow.wYear = t.tm_year + 1900;
        timenow.wMonth = t.tm_mon + 1;
        timenow.wDay = t.tm_mday;
        timenow.wHour = t.tm_hour;
        timenow.wMinute = t.tm_min;
        timenow.wSecond = t.tm_sec;
        timenow.wDayOfWeek = t.tm_wday;
        timenow.wMilliseconds = Milliseconds;
        SetLocalTime(&timenow);

        if (0 != fopen_s(&file, filePath, "w")) {
            printf("打开文件失败\n");
            return;
        }
        //更新保留的时间,在每24小时更新后,剩余的不足24小时加入下次计算
        time_t wllt = llt_new - kllt;
        fprintf(file, "%lld", wllt);
        fclose(file);

    }
    

}
*/

void updateTime() {

    CreateDirectoryRecursive(L"C:/ProgramData/timeUpdate"); //递归创建文件夹

    //unsigned long size = 255;
    //WCHAR UserName_b[255];
    //char UserName_e[255];
    //GetUserName(UserName_b, &size);//GetUserName()函数同上
    //WideCharToMultiByte(CP_ACP, 0, UserName_b, -1, &UserName_e[0], 255, nullptr, nullptr);   //将wchar_t * 转换成char *

    char filePath[256];
    snprintf(filePath, 255, "C:/ProgramData/timeUpdate/timeCompensation.txt");

    /*
    1秒 = 1000毫秒
    1毫秒 = 1000 微秒
    1微秒 = 1000 纳秒
    */
    // SYSTEMTIME结构表示的是本地时间,而FILETIME表示的是UTC时间(协调世界时)
    FILETIME ft;
    SYSTEMTIME st;
    GetSystemTimeAsFileTime(&ft); // 获取当前时间,此值表示自 1601 年 1 月 1 日开始以来的 100 纳秒单位数
    FileTimeToLocalFileTime(&ft, &ft);  //GetSystemTimeAsFileTime获取的是UTC的时间 ,而我们这里的是UTC+8 所以要转为本地时间再使用

    // 放入高32位和低32位得到64位,将FILETIME转换为64位整数
    ULARGE_INTEGER ui;
    ui.LowPart = ft.dwLowDateTime;
    ui.HighPart = ft.dwHighDateTime;


    time_t llt = ui.QuadPart;
    FILE* file;
    if (0 != fopen_s(&file, filePath, "r+")) {
        //打不开说明服务刚创建,程序写下时间戳就退出
        if (0 != fopen_s(&file, filePath, "w")) {
            printf("打开文件失败\n");
            return;
        }
        fprintf(file, "%lld", llt);
        fclose(file);
        return;
    }
    //获取文件内的时间戳
    char buf[64] = {};
    fread_s(buf, sizeof(buf), 1, sizeof(buf), file);
    fclose(file);

    time_t file_times = atoll(buf);

    if (llt - file_times >= 864000000000) { 
        //每超过24小时就执行时间补偿,增加时间补偿 ,24小时/补偿10.064S ,但是获取本机电脑到设置完时间
        time_t add_count = (llt - file_times) / 864000000000; //计算超出多少个24小时
        time_t kllt = llt - file_times % 864000000000;
        ui.QuadPart = ui.QuadPart + add_count * 10064 * 10000;   //计算时间增加了多少

        ft.dwLowDateTime = ui.LowPart ;
        ft.dwHighDateTime = ui.HighPart ;
        FileTimeToSystemTime(&ft, &st); //调整时间后将FILETIME转换为SYSTEMTIME

        SetLocalTime(&st);


        if (0 != fopen_s(&file, filePath, "w")) {
            printf("打开文件失败\n");
            return;
        }
        //更新保留的时间,在每24小时更新后,剩余的不足24小时加入下次计算
        time_t wllt = ui.QuadPart - kllt;   
        fprintf(file, "%lld", wllt);
        fclose(file);

    }
}


BOOL IsInstalled()
{
    BOOL bResult = FALSE;

    //打开服务控制管理器
    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

    if (hSCM != NULL)
    {
        //打开服务
        SC_HANDLE hService = ::OpenService(hSCM, szServiceName, SERVICE_QUERY_CONFIG);
        if (hService != NULL)
        {
            bResult = TRUE;
            ::CloseServiceHandle(hService);
        }
        ::CloseServiceHandle(hSCM);
    }
    return bResult;
}


BOOL Install()
{
    if (IsInstalled())
        return TRUE;

    //打开服务控制管理器
    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCM == NULL)
    {
        MessageBox(NULL, L"Couldn't open service manager", szServiceName, MB_OK);
        return FALSE;
    }

    // Get the executable file path
    TCHAR szFilePath[MAX_PATH];
    ::GetModuleFileName(NULL, szFilePath, MAX_PATH);    //
    _tcscat(szFilePath , L" -k runservice");    //加参数,标明服务入口

    //创建服务
    SC_HANDLE hService = ::CreateService(
        hSCM, szServiceName, szServiceName,
        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
        SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
        szFilePath, NULL, NULL, L"", NULL, NULL);

    if (hService == NULL)
    {
        ::CloseServiceHandle(hSCM);
        MessageBox(NULL, L"Couldn't create service", szServiceName, MB_OK);
        return FALSE;
    }

    ::CloseServiceHandle(hService);
    ::CloseServiceHandle(hSCM);
    return TRUE;
}

BOOL StartScService()
{
    if (!IsInstalled()) 
        return FALSE;

    //打开服务控制管理器
    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCM == NULL)
    {
        MessageBox(NULL, L"Couldn't open service manager", szServiceName, MB_OK);
        return FALSE;
    }

    SC_HANDLE hService = ::OpenService(hSCM, szServiceName, SERVICE_START);

    if (hService == NULL)
    {
        ::CloseServiceHandle(hSCM);
        MessageBox(NULL, L"Couldn't open service", szServiceName, MB_OK);
        return FALSE;
    }
    BOOL bDelete = ::StartService(hService , NULL ,NULL);
    if (!bDelete) {

        //【1056】-服务的实例已在运行中。
        char buf[255];
        snprintf(buf , 255,"StartService serviceHandle=%p -> fail(%ld)", hService, GetLastError());
        wchar_t wideStr[255] ;

        // 使用MultiByteToWideChar进行转换  
        // 这里使用CP_ACP,它代表ANSI代码页。根据你的具体情况,你可能需要使用不同的代码页  
        MultiByteToWideChar(CP_ACP, 0, buf, strlen(buf), wideStr, 255);

        MessageBox(NULL, wideStr, szServiceName, MB_OK);

    }
    else {
        wchar_t wideStr[255];
        char buf[255];
        snprintf(buf, 255, "StartService serviceHandle=%p -> succ", hService);
        // 使用MultiByteToWideChar进行转换  
        // 这里使用CP_ACP,它代表ANSI代码页。根据你的具体情况,你可能需要使用不同的代码页  
        MultiByteToWideChar(CP_ACP, 0, buf, strlen(buf), wideStr, 255);

        MessageBox(NULL, wideStr, szServiceName, MB_OK);
    }

    ::CloseServiceHandle(hService);
    ::CloseServiceHandle(hSCM);
    return TRUE;
}


void LogEvent(LPCTSTR pFormat, ...)
{
    TCHAR    chMsg[256];
    HANDLE  hEventSource;
    LPTSTR  lpszStrings[1];
    va_list pArg;

    va_start(pArg, pFormat);
    vswprintf_s(chMsg, pFormat, pArg);
    va_end(pArg);

    lpszStrings[0] = chMsg;

    hEventSource = RegisterEventSource(NULL, szServiceName);
    if (hEventSource != NULL)
    {
        ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*)&lpszStrings[0], NULL);
        DeregisterEventSource(hEventSource);
    }
}


BOOL Uninstall()
{
    if (!IsInstalled())
        return TRUE;

    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

    if (hSCM == NULL)
    {
        MessageBox(NULL, L"Couldn't open service manager", szServiceName, MB_OK);
        return FALSE;
    }

    SC_HANDLE hService = ::OpenService(hSCM, szServiceName, SERVICE_STOP | DELETE);

    if (hService == NULL)
    {
        ::CloseServiceHandle(hSCM);
        MessageBox(NULL, L"Couldn't open service", szServiceName, MB_OK);
        return FALSE;
    }
    SERVICE_STATUS status;
    ::ControlService(hService, SERVICE_CONTROL_STOP, &status);

    //删除服务
    BOOL bDelete = ::DeleteService(hService);
    ::CloseServiceHandle(hService);
    ::CloseServiceHandle(hSCM);

    if (bDelete)
        return TRUE;

    LogEvent(szServiceName,L"Service could not be deleted");
    return FALSE;
}



void WINAPI ServiceCtrlHandler(DWORD controlCode)
{
    switch (controlCode)
    {
    case SERVICE_CONTROL_STOP:
        // 设置服务状态为SERVICE_STOPPED_PENDING  
        serviceStatus.dwCurrentState = SERVICE_STOPPED;
        serviceStatus.dwWin32ExitCode = 0;
        SetServiceStatus(serviceStatusHandle, &serviceStatus);

        // 通知服务主循环停止  
        SetEvent(hStopEvent);
        return;
    default:
        break;
    }

}

void WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
    // 注册服务控制处理函数
    serviceStatusHandle = RegisterServiceCtrlHandler(szServiceName, ServiceCtrlHandler);

    // 设置服务状态
    serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    serviceStatus.dwCurrentState = SERVICE_RUNNING;
    serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    serviceStatus.dwWin32ExitCode = 0;
    serviceStatus.dwServiceSpecificExitCode = 0;
    serviceStatus.dwCheckPoint = 0;
    serviceStatus.dwWaitHint = 0;
    SetServiceStatus(serviceStatusHandle, &serviceStatus);

    // 创建一个自动重置的事件,用于通知停止  
    hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hStopEvent == NULL)
    {
        // 处理错误  
        return;
    }

    // 执行服务的主要逻辑
    if (serviceStatus.dwCurrentState == SERVICE_RUNNING)
    {
        while (1) {
            //以下用于循环函数
            
            updateTime();
            // 检查停止事件是否被设置  
            if (WaitForSingleObject(hStopEvent, 0) == WAIT_OBJECT_0)
            {
                // 停止事件被触发,退出循环  
                break;
            }
            //休眠一小时
            Sleep(1000*60*60);

        }
    
    }
    // 停止服务时的清理逻辑
    // 设置服务状态为停止
    serviceStatus.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus(serviceStatusHandle, &serviceStatus);
}

int main(int argc, char* argv[]) {
    /* 
    SYSTEMTIME timenow_s ,timenow_e;
    GetLocalTime(&timenow_s);
    updateTime();
    GetLocalTime(&timenow_e);
    printf(" %d年, %d月,%d日,%d时,%d分,%d秒 ,%d毫秒\n",
        timenow_s.wYear, timenow_s.wMonth, timenow_s.wDay, timenow_s.wHour, timenow_s.wMinute, timenow_s.wSecond, timenow_s.wMilliseconds);
    printf(" %d年, %d月,%d日,%d时,%d分,%d秒 ,%d毫秒\n",
        timenow_e.wYear, timenow_e.wMonth, timenow_e.wDay, timenow_e.wHour, timenow_e.wMinute, timenow_e.wSecond, timenow_e.wMilliseconds);
    _getch();
    return 0;
    */

    //GetModuleFileName(NULL, filePath_b, static_cast<DWORD>(256)); //获取当前程序的绝对路径
    //WideCharToMultiByte(CP_ACP, 0, filePath_b, -1, &filePath_e[0], 256, nullptr, nullptr);   //将wchar_t * 转换成char *
    

    
    if (argc == 3) {
        if ( 0 == strcmp(argv[1], "-k")) {
            if (0 == strcmp(argv[2], "runservice")) {
                SERVICE_TABLE_ENTRY serviceTable[] =
                {
                    { szServiceName, ServiceMain },
                    { NULL, NULL }
                };
                // 启动服务控制分派器
                StartServiceCtrlDispatcher(serviceTable);
            }
        }
    }
    else {
        if (IsInstalled()) {
            Uninstall();
        }
        if (!Install()) {
            return 0;
        }
        StartScService();
    }
    return 0;
}

3 遇到的问题

3.1 存储路径错误导致操作文件异常

当初为了想其他程序一样规范,使保存时间戳的文件存储在 "C:\User\用户名" 下,使用 GetUserName 函数获取了用户名,但是它只能返回创建当前进程的用户名,并非真正的当前登录用户名。如果当前进程是服务进程,或者是由服务进程所创建,则GetUserName获得的用户名会是"SYSTEM"。当时找了半天资料都是权限和路径不对什么的,都解决不了,无法写入文件(获取到的不是用户名而是SYSTEM的话没有那个文件夹)无法自动创建。最终选择将保存的时间戳更改保存到 C:\ProgramData 目录下。

3.2 时间的换算

在修改时间时需要的是毫秒级别的,所以使用的是 SYSTEMTIME结构体 ,但是 GetLocalTime(Out LPSYSTEMTIME lpSystemTime);函数获取到的是日期,所以当时使用了time(time_t Time)来获取秒级的时间戳来进行转换,但是这样会显得麻烦,且两次获取的话还是可能会产生误差。所以后面使用了FILETIME结构体,使用GetSystemTimeAsFileTime(Out LPFILETIME lpSystemTimeAsFileTime)函数来获取100纳秒级的时间戳来进行计算。但是它获取到的为1601 年 1 月 1 日开始以来的 100 纳秒单位数,获取的是UTC的时间 。而我们这里的是需要的是本地时间,设置时间是使用的SetLocalTime(In CONST SYSTEMTIME lpSystemTime)函数也是使用本地时间,如果直接使用FileTimeToSystemTime(In CONST FILETIME* lpFileTime, Out LPSYSTEMTIME lpSystemTime)来将修改后的时间戳来转换的话会导致少8小时,所以获取时间戳后需要使用FileTimeToLocalFileTime(In CONST FILETIME* lpFileTime, Out LPFILETIME lpLocalFileTime)函数将时间转换为本地时间再使用。

4.参考文献

本文参考CSDN博主「Jackchenyj」的原创文章《用C/C++创建windows服务程序》 。原文链接:https://blog.csdn.net/chenyujing1234/article/details/8023816

标签:服务程序,return,SERVICE,windows,timenow,hSCM,c++,file,NULL
From: https://www.cnblogs.com/mzcl/p/18244086

相关文章

  • windows 新增加的功能 揭秘
    1老c++没有被抛弃新c++是老c++的扩展使用新c/c++依然可以使用老代码的系统函数‘API’2html5其实就增加了一个视频播放器,这个播放器看着很‘牛’ 其实它真的很牛兼容性好!3windows10和11在桌面右键有显示功能里面设置图形卡选独立显卡那个芯片那个进程......
  • Windows Server 2012 R2部署网站后IIS,只要访问网址应用程序池就自动停止,浏览器页面提
     解决方案:1.查看windows日志通过文件资源管理器查看:打开文件资源管理器,找到“我的电脑”。右键点击“管理”。在弹出的窗口中,点击“事件查看器”。在事件查看器中,点击“Windows日志”即可查看各种日志信息 2.找到错误信息......
  • 32位和64位的Windows7均不支持UEFI启动方式?试试看!
    前言今天小白突然想起:自己已经接近8年没有安装过32位的Windows系统了,这8年装的上百台电脑都是用的64位Windows。今天 闲来无事  嗯……应该算是有小伙伴提出了个问题:这位小伙伴表示:自己无论安装32位还是64位的Windows7都无法使用UEFI的启动方式。但按照小白之前的装......
  • 微软在Windows上做了个安卓子系统…
    前言曾经小白想着如果Windows在不安装模拟器的情况下,可以安装并运行安卓软件,那这个功能一定很香。在2021年,微软面向开发者推出WSA支持。在第二年的时候,用户就可以在Windows上使用安卓软件。这个功能可把我乐坏了,对于手边没有安卓手机的小白来说,这简直是一个天大的好新闻……......
  • c++定义了类在main函数中使用的一个坑现象的解决,让我理解了栈,堆和内存之间关系。
    首先描述一下我的坑是啥?我的坑就是写了一个对集料颗粒进行角度计算的类,在main函数中使用采用了类定义申明,这样使用导致一个坑,这个类中对于集料的数目进行了宏定义,发现数据如果超过20个,编译就报错,当时没有太在意这个坑,没有思考什么原因。也就将就者用了。后来对接同事说,这个颗粒数......
  • [C++ Primer] 字符串、向量和数组
    [C++Primer]字符串、向量和数组标准库类型string标准库类型string表示可变长的字符序列,使用该类型需包含string头文件。作为标准库的i一部分,string定义在命名空间std中。拷贝初始化:使用等号(=)初始化一个变量直接初始化:不使用等号strings5="hiya"; //拷贝初始化s......
  • [C++ Primer] 变量和基本类型
    [C++Primer]变量和基本类型变量默认初始化如果定义变量时没有指定初值,则变量默认初始化,此时变量被赋予“默认值”。默认值到底是什么由变量类型决定,同时定义变量的位置也会对此有影响。内置类型:其默认值由定义的位置决定。定义于任何函数之外的变量被初始化为0。绝大多数......
  • 【C++面向对象】重载操作符
    C++将运算符重载扩展到自定义的数据类型,它可以让对象操作更美观。例如字符串string用加号(+)拼接、cout用两个左尖括号(<<)输出。运算符重载函数的语法:返回值operator运算符(参数列表);运算符重载函数的返回值类型要与运算符本身的含义一致。非成员函数版本的重载运算符函数:形......
  • 2024年华为OD机试真题-围棋的气-C++-OD统一考试(C卷D卷)
     2024年OD统一考试(D卷)完整题库:华为OD机试2024年最新题库(Python、JAVA、C++合集) 题目描述:围棋棋盘由纵横各19条线垂直相交组成,棋盘上一共19x19=361个交点,对弈双方一方执白棋,一方执黑棋,落子时只能将棋子置于交点上。“气”是围棋中很重要的一个概念,某个棋子有几口气,是指......
  • c++ 实现优先队列
    优先队列底层是堆,下面给出大根堆代码Push是在新节点到根节点的路径上寻找合适的插入位置;Pop是删除根节点之后维护大根堆,顺便为最后一个元素寻找合适的位置;1#include<bits/stdc++.h>23usingnamespacestd;45template<classT>6classp_queue{7pri......