Windows系统软件自启动方法

Windows系统软件自启动方法

很多场景都需要软件在开机时自动启动,本文整理了一些不同系统下软件自启动的方式并进行了对比。

(一) 利用“启动”文件夹

  1. 点击左下角开始菜单,找到启动文件夹,如图:
    在这里插入图片描述
  2. 右键启动文件夹,点击打开,如图:
    在这里插入图片描述
  3. 将软件的快捷方式复制到该文件夹中,重启电脑即可。

适用于:win7,win10(自启动的开关,不需要手动拖拽快捷方式)
优点:不需要修改代码,操作简单方便,没有技术基础的人也可以做到让软件自启动。
缺点:必须要进行手动把快捷方式移到文件夹中才能实现软件自启动,否则软件永远不会自启动。

(二) 使用任务计划

在默认情况下,“任务计划”程序随Windows一起启动并在后台运行。如果把某个程序添加到计划任务文件夹,并将计划任务设置为“系统启动时”或“登录时”,这样也可以实现程序自启动。
“任务计划”也是一个特殊的系统文件夹,单击“开始→程序→附件→系统工具→任务计划”即可打开该文件夹,从而方便进行查看和管理。界面如下图:

在这里插入图片描述

点击创建任务,可以创建一个新任务,可以设置该任务运行的时的账户。选择触发器可以新建触发器,其中可以设置合适开始任务等条件。点击操作可以新建操作,在设置界中选择程序或脚本。点确定即可。
在这里插入图片描述

在这里插入图片描述

适用于:win7,win10
优点:不需要修改代码,建立新任务时可以自定义,按照自己的需求来定制任务。
缺点:相对来讲创建过程复杂。

(三) 修改注册表

注册表是启动程序藏身之处最多的地方,注册表的[HKEY_LOCAL_MACHINE]和[HKEY_CURRENT_USER]键的区别:前者对所有用户有效,后者只对当前用户有效。注册表可以进行程序自启动主要有Run键:
1.Run键
Run键位置是[HKEY_CURRENT_
USER\Software\Microsoft\Windows\CurrentVersion\Run]和[HKEY_
LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run],其下的所有程序在每次启动登录时都会按顺序自动执行。
2.RunOnce键
RunOnce位于[HKEY_CURRENT_USER\Software\Microsoft\Windows\
CurrentVersion\RunOnce]和[HKEY_LOCAL_MACHINE\Software\Microsoft\
Windows\CurrentVersion\RunOnce]键,与Run不同的是,RunOnce下的程序仅会被自动执行一次。
我们重点说明Run键,往Run键下设置指定的数据通常设计调用的接口有:

  • 创建一个指定的注册表键。如果已经存在,则打开。

LONG RegCreateKeyExA (HKEY hKey, LPCTSTR lpSubKey,DWORD Reserved,LPTSTR lpClass, DWORD dwOptions,REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult,LPDWORD lpdwDisposition)
其中hKey是需要打开的主键名称,lpSubKey是需要打开的子健名称,Reserved保留,必须赋值为0,lpClass 为入参,指向一个字符串,该字符串定义了该键的类型。可以为空。该参数可以在操作本地和远程注册表时使用,dwOptions入参,指定密钥的特殊选项,该参数可以是以下值
REG_OPTION_BACKUP_RESTORE,0x00000004L
REG_OPTION_NON_VOLATILE,0x00000000L ,
REG_OPTION_VOLATILE,0x00000001L ,
一般使用REG_OPTION_NON_VOLATILE ,samDesired是定义访问权限,lpSecurityAttributes入参,定义返回的句柄是否可以被子进程继承,为NULL时不能继承。phkResult是返回的句柄。lpdwDisposition是出参,可能是以下值REG_CREATED_NEW_KEY,0x00000001L 该键是新创建的键;REG_OPENED_EXISTING_KEY,0x00000002L 该键是已经存在的键。

  • 在注册表项下设置指定值的数据和类型

LONG RegSetValueExA(HKEY hKey,LPCTSTR lpValueName,DWORD Reserved,DWORD dwType,CONST BYTE *lpData,DWORD cbData);
其中hKey是一个已打开项的句柄,lpValueName:指向一个字符串的指针,该字符串包含了欲设置值的名称。若拥有该值名称的值并不存在于指定的注册表项中,则此函数将其加入到该项。Reserved:保留值,必须强制为0,dwType: 指定将被存储的数据类型,lpData: 指向一个缓冲区,该缓冲区包含了欲为指定值名称存储的数据。cbData:指定由lpData参数所指向的数据的大小,单位是字节。

  • 删除注册表中键值

LONG RegDeleteValueA(HKEY hKey, LPCSTR lpValueName);
其中hKey是一个已打开项的句柄,lpValueName:指向一个字符串的指针,该字符串包含了欲删除值的名称。

具体代码如下:
创建一个界面,按钮的槽函数中写入或删除注册表键值。

构造函数中创建键值:

{
	std::string lpRun = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
	DWORD dwDisposition = 0;
	long lRet = RegCreateKeyExA(HKEY_LOCAL_MACHINE, lpRun.c_str(), 0, NULL, \
        REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &m_hKey, &dwDisposition);

	if (lRet != ERROR_SUCCESS)  //失败
    {
        qDebug() << "RegCreateKeyExA failed!";
    }
}

其中m_hKey作为成员变量HKEY m_hKey,是操作注册表的句柄。

槽函数为:

{
    if (m_hKey == nullptr)
    {
        return;
    }
	if (m_bAutoRun)
    {
        QString qStrAppPath = QApplication::applicationFilePath();
        qStrAppPath.replace("/", "\\");

        DWORD dwRet = qStrAppPath.length() * 2;
        // QtGuiApplication1应用程序名字(不加后缀.exe) 
        long lRet = RegSetValueExA(m_hKey, "QtGuiApplication1", 0, REG_SZ, (PBYTE)qStrAppPath.toStdString().c_str(), dwRet);
        if (lRet != ERROR_SUCCESS)  //失败
        {
            qDebug() << "RegSetValueExA failed!";
        }
    }
    else
    {
        RegDeleteValueA(m_hKey, "QtGuiApplication1");
    }
}

点击按钮后写注册表成功,查看注册表键值如下:
在这里插入图片描述

适用于:win7,win10
优点:一般软件中均会有一个选项是否开启自启动,勾选与取消勾选实际上就是通过这种方式修改注册表实现的。
缺点:需要有一点编码能力。

注意:
1.我们开发的项目为了能够兼容32位系统和64位系统,一般应用程序都是32位的。在操作注册表时就涉及到32位和64位注册表的问题。64位windows系统中的注册表分为32位注册表和64位注册表项。为了防止注册表键冲突,注册表在某些键也分成了两个部分。一部分是专门给64位系统访问的,另一部分是专门给32位系统访问的。当32位程序去访问某些键值的时候,和文件转向类似,系统也会自动地把程序的访问转向到Wow6432Node下面。调用RegCreateKeyEx创建项时要samDesired要赋值权限与上KEY_WOW64_64KEY时,是在正常的路径下,即\Software\Microsoft\Windows\CurrentVersion\Run中;samDesired要赋值权限与上KEY_WOW64_32KEY时,创建项被重定向到了Wow6432Node节点中,\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Run中。
经测试,在win7和win10上无论传入KEY_WOW64_64KEY还是KEY_WOW64_32KEY程序均可自启动成功。
2.上述三个接口均还有另外一个版本,名称的最后一个字母不是A而是W,而不带最后一个字母的接口是如下宏控制的:

#ifdef UNICODE
#define RegCreateKeyEx  RegCreateKeyExW
#else
#define RegCreateKeyEx  RegCreateKeyExA
#endif // !UNICODE

即UIcode下,如果调用RegCreateKeyEx,实际上调用的是RegCreateKeyExW,而调用该接口时必须要注意传入的参数(路径,软件名等),必须要转换成宽字符再传入,否则创建接口RegCreateKeyExW会报错87,参数错误,调用写入注册表接口RegSetValueExW会导致写入的注册表项乱码。所以在传入参数前应先利用MultiByteToWideChar接口把字符串转换为宽字符字符串。为了避免上述问题,建议直接调用末尾是A的接口。
具体代码如下:

string转成wstring的函数

void StringToWstring(std::wstring& szDst, std::string str)
{
    std::string temp = str;
    int len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)temp.c_str(), -1, NULL, 0);
    wchar_t * wszUtf8 = new wchar_t[len + 1];
    memset(wszUtf8, 0, len * 2 + 2);
    MultiByteToWideChar(CP_ACP, 0, (LPCSTR)temp.c_str(), -1, (LPWSTR)wszUtf8, len);
    szDst = wszUtf8;
    std::wstring r = wszUtf8;
    delete[] wszUtf8;
}

构造函数中创建键值

{
std::string lpRun = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
    std::wstring wszDest;
    StringToWstring(wszDest, lpRun);

    DWORD dwDisposition = 0;
    long lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wszDest.c_str(), 0, NULL, \
        REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &m_hKey, &dwDisposition);

    if (lRet != ERROR_SUCCESS)  //失败
    {
        qDebug() << "RegCreateKeyExW failed!";
    }
}

槽函数为:

{
    if (m_hKey == nullptr)
    {
        return;
    }
    if (m_bAutoRun)
    {
        QString qStrAppPath = QApplication::applicationFilePath();
        qStrAppPath.replace("/", "\\");
        std::wstring wszDest;
        StringToWstring(wszDest, qStrAppPath.toStdString());
        DWORD dwRet = qStrAppPath.length() * 2;

        std::wstring wszDestName;
        StringToWstring(wszDestName, "QtGuiApplication1");
 
        long lRet = RegSetValueExW(m_hKey, wszDestName.c_str(), 0, REG_SZ, (PBYTE)wszDest.c_str(), dwRet);
        if (lRet != ERROR_SUCCESS)  //失败
        {
            qDebug() << "RegSetValueExW failed!";
        }
    }
    else
    {
        std::wstring wszDestName;
        StringToWstring(wszDestName, "QtGuiApplication1");
        RegDeleteValueW(m_hKey, wszDestName.c_str());
    }
}

图中不乱码的为转换成宽字符后再写入注册表的结果,乱码为没有转换字符串调用接口的结果。
在这里插入图片描述


版权声明:本文为weixin_43554422原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。