Windows系统软件自启动方法
很多场景都需要软件在开机时自动启动,本文整理了一些不同系统下软件自启动的方式并进行了对比。
(一) 利用“启动”文件夹
- 点击左下角开始菜单,找到启动文件夹,如图:

- 右键启动文件夹,点击打开,如图:

- 将软件的快捷方式复制到该文件夹中,重启电脑即可。
适用于: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());
}
}
图中不乱码的为转换成宽字符后再写入注册表的结果,乱码为没有转换字符串调用接口的结果。