【Csharp】Thread匿名方法初识

片段代码笔记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/// <summary>
/// 停止创建运行新任务和新线程,等待所有任务和线程执行完成。
/// </summary>
private void StopRuningTaskOrThreadAndExitOrRestart(bool restartFlag)
{
timer1.Enabled = false;//停止触发
if (switchstr == "1" && threadswitchstr == "1")
{
Thread t = new Thread((flag) =>
{
while (threadUseNum > 0)
{
Thread.Sleep(5000);
RefreshMsgBox(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + $"\t=>\t等待子任务执行完成退出:子任务数量:{threadUseNum}");
}
if ((bool)flag)
{
Process.Start(Application.ExecutablePath);
}
Environment.Exit(0);
});
t.IsBackground = true;
t.Start(restartFlag);
}
else if (switchstr == "1")
{
Thread t = new Thread((flag) =>
{
if (taskThread != null)
{
taskThread.Wait();//等待执行完成
}
if ((bool)flag)
{
Process.Start(Application.ExecutablePath);
}
Environment.Exit(0);
});
t.IsBackground = true;
t.Start(restartFlag);
}
}

【C#】SqlBulkCopy实现SQLSRVER批量插入

【C#】SqlBulkCopy实现SQLSRVER批量插入

参考:
SqlBulkCopy 类
http://raylei.cn/index.php/archives/74/
https://www.cxybb.com/article/weixin_30333885/96361368

问题

  • 来自数据源的 Decimal 类型的给定值不能转换为指定目标列的类型 datetime
    实际情况可能很复杂,使用的是泛型进行实体类转DataTable,可能会出现各种类型错误提示。
    自己使用的是调用Copy()方法。
    demo:

    demo.cs
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var TableStruct = dal.GetTableStruct();
    DataTable dataTable = TableStruct.Clone();
    foreach (var item in oD.ToList<StdYpkcday>())
    {
    DataRow dataRow = dataTable.NewRow();
    dataRow["id"] = DBNull.Value;//自增主键。赋值DBNull.Value
    dataRow["code"] = item.Hoscode;
    if (item.Gqsj == null )
    {
    dataRow["time"] = DBNull.Value;
    }
    else
    {
    dataRow["time"] = item.Gqsj;
    }
    dataTable.Rows.Add(dataRow);
    }

    参考:https://stackoverflow.com/questions/18140012/sqlbulkcopy-the-given-value-of-type-string-from-the-data-source-cannot-be-conv

Demo

删除并批量插入.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/// <summary>
/// 删除并批量插入
/// </summary>
/// <param name="_Type"></param>
/// <param name="TableName">批量插入数据的表名</param>
/// <param name="Dt">批量插入的数据</param>
/// <param name="SqlDeleteText">删除的sql语句</param>
public void DeleteAndBulkCopy(string Type,string TableName, DataTable Dt, string SqlDeleteText,bool isIncreaPK)
{
using (SqlConnection conn = new SqlConnection(DBUtility.DbCommon.GetConnectString(Type)))
{
conn.Open();
using (SqlTransaction transaction = conn.BeginTransaction())
{
SqlCommand sqlcom = conn.CreateCommand();
sqlcom.Transaction = transaction;
sqlcom.CommandText = SqlDeleteText;
var BulkCopyOptions = SqlBulkCopyOptions.KeepIdentity;
//自增主键,使用系统生成主键
if (isIncreaPK)
{
BulkCopyOptions = SqlBulkCopyOptions.Default;
}
using (SqlBulkCopy bulk = new SqlBulkCopy(conn, BulkCopyOptions, transaction))
{
if (Dt.Rows.Count > 0)
{
for (int i = 0; i < Dt.Columns.Count; i++)
{
bulk.ColumnMappings.Add(Dt.Columns[i].ColumnName, Dt.Columns[i].ColumnName);
}
bulk.BatchSize = Dt.Rows.Count;
bulk.DestinationTableName = TableName;
}
try
{
sqlcom.ExecuteNonQuery();

bulk.WriteToServer(Dt);
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
}
finally
{
bulk.Close();
conn.Close();
}
}
}
}

}
批量更新.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#region 批量更新
/// <summary>
/// 批量更新
/// </summary>
/// <param name="dataTable"></param>
/// <param name="crateTemplateSql">
/// eg:
/// [xxx] varchar(255) NOT NULL,[xxx] varchar(10) NOT NULL
/// </param>
/// <param name="updateSql">
/// eg:
/// UPDATE bulktest set bulktest.name=Temp.tempname
/// FROM bulktest INNER JOIN #TmpTable Temp
/// ON Temp.temprdn = bulktest.rdn ;
/// DROP TABLE #TmpTable
/// </param>
public void SqlBulkCopyUpdate(DataTable dataTable, string crateTemplateSql, string updateSql)
{
using (SqlConnection conn = new SqlConnection(DBUtility.DbCommon.GetConnectString(DBType)))
{
using (var command = new SqlCommand("", conn))
{
try
{
conn.Open();
//数据库并创建一个临时表来保存数据表的数据
command.CommandText = $" CREATE TABLE #TmpTable ({crateTemplateSql})";
command.ExecuteNonQuery();

//使用SqlBulkCopy 加载数据到临时表中
using (var bulkCopy = new SqlBulkCopy(conn))
{
foreach (DataColumn dcPrepped in dataTable.Columns)
{
bulkCopy.ColumnMappings.Add(dcPrepped.ColumnName, dcPrepped.ColumnName);
}

bulkCopy.BulkCopyTimeout = 660;
bulkCopy.DestinationTableName = "#TmpTable";
bulkCopy.WriteToServer(dataTable);
bulkCopy.Close();
}

// 执行Command命令 使用临时表的数据去更新目标表中的数据 然后删除临时表
command.CommandTimeout = 300;
command.CommandText = updateSql;
int rows = command.ExecuteNonQuery();
}
catch(Exception ex)
{
throw ex;
}
finally
{
conn.Close();
}
}
}
}

#endregion
判断文件类型【一】

判断文件类型【一】

引言

现有一文件,其扩展名未知或标记错误。假设它是一个正常的、非空的文件,且将扩展名更正后可以正常使用,那么,如何判断它是哪种类型的文件?

思路

使用文件签名来判断实际文件类型,又称magic numbersMagic Bytes。关于文件签名介绍,参考List of file signatures

Magic Number:

  • magic number是嵌入在文件开头或附近的数字,指示其文件格式(即文件的类型)。
  • 我们看不到这个数字。
  • 每个文件都有一个数字,代表文件类型的名称,它是十六进制格式。

C#实现参考

C#实现

example.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public class MimeType
{
private static readonly byte[] BMP = { 66, 77 };
private static readonly byte[] DOC = { 208, 207, 17, 224, 161, 177, 26, 225 };
private static readonly byte[] EXE_DLL = { 77, 90 };
private static readonly byte[] GIF = { 71, 73, 70, 56 };
private static readonly byte[] ICO = { 0, 0, 1, 0 };
private static readonly byte[] JPG = { 255, 216, 255 };
private static readonly byte[] MP3 = { 255, 251, 48 };
private static readonly byte[] OGG = { 79, 103, 103, 83, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0 };
private static readonly byte[] PDF = { 37, 80, 68, 70, 45, 49, 46 };
private static readonly byte[] PNG = { 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82 };
private static readonly byte[] RAR = { 82, 97, 114, 33, 26, 7, 0 };
private static readonly byte[] SWF = { 70, 87, 83 };
private static readonly byte[] TIFF = { 73, 73, 42, 0 };
private static readonly byte[] TORRENT = { 100, 56, 58, 97, 110, 110, 111, 117, 110, 99, 101 };
private static readonly byte[] TTF = { 0, 1, 0, 0, 0 };
private static readonly byte[] WAV_AVI = { 82, 73, 70, 70 };
private static readonly byte[] WMV_WMA = { 48, 38, 178, 117, 142, 102, 207, 17, 166, 217, 0, 170, 0, 98, 206, 108 };
private static readonly byte[] ZIP_DOCX = { 80, 75, 3, 4 };

public static string GetMimeType(byte[] file, string fileName)
{

string mime = "application/octet-stream"; //DEFAULT UNKNOWN MIME TYPE

//Ensure that the filename isn't empty or null
if (string.IsNullOrWhiteSpace(fileName))
{
return mime;
}

//Get the file extension
string extension = Path.GetExtension(fileName) == null
? string.Empty
: Path.GetExtension(fileName).ToUpper();

//Get the MIME Type
if (file.Take(2).SequenceEqual(BMP))
{
mime = "image/bmp";
}
else if (file.Take(8).SequenceEqual(DOC))
{
mime = "application/msword";
}
else if (file.Take(2).SequenceEqual(EXE_DLL))
{
mime = "application/x-msdownload"; //both use same mime type
}
else if (file.Take(4).SequenceEqual(GIF))
{
mime = "image/gif";
}
else if (file.Take(4).SequenceEqual(ICO))
{
mime = "image/x-icon";
}
else if (file.Take(3).SequenceEqual(JPG))
{
mime = "image/jpeg";
}
else if (file.Take(3).SequenceEqual(MP3))
{
mime = "audio/mpeg";
}
else if (file.Take(14).SequenceEqual(OGG))
{
if (extension == ".OGX")
{
mime = "application/ogg";
}
else if (extension == ".OGA")
{
mime = "audio/ogg";
}
else
{
mime = "video/ogg";
}
}
else if (file.Take(7).SequenceEqual(PDF))
{
mime = "application/pdf";
}
else if (file.Take(16).SequenceEqual(PNG))
{
mime = "image/png";
}
else if (file.Take(7).SequenceEqual(RAR))
{
mime = "application/x-rar-compressed";
}
else if (file.Take(3).SequenceEqual(SWF))
{
mime = "application/x-shockwave-flash";
}
else if (file.Take(4).SequenceEqual(TIFF))
{
mime = "image/tiff";
}
else if (file.Take(11).SequenceEqual(TORRENT))
{
mime = "application/x-bittorrent";
}
else if (file.Take(5).SequenceEqual(TTF))
{
mime = "application/x-font-ttf";
}
else if (file.Take(4).SequenceEqual(WAV_AVI))
{
mime = extension == ".AVI" ? "video/x-msvideo" : "audio/x-wav";
}
else if (file.Take(16).SequenceEqual(WMV_WMA))
{
mime = extension == ".WMA" ? "audio/x-ms-wma" : "video/x-ms-wmv";
}
else if (file.Take(4).SequenceEqual(ZIP_DOCX))
{
mime = extension == ".DOCX" ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/x-zip-compressed";
}

return mime;
}


}

进制转换

摘录部份常用文件HEX签名,来源GCK’S FILE SIGNATURES TABLE

十六进制签名 ASCII 签名
文件扩展名 文件描述
50 4B 03 04 PK..
ZIP PKZIP archive file (Ref. 1 Ref. 2)
Trailer: filename 50 4B 17 characters 00 00 00
Trailer: (filename PK 17 characters …)
Note: PK are the initals of Phil Katz, co-creator of the ZIP file format and author of PKZIP.
ZIP Apple Mac OS X Dashboard Widget, Aston Shell theme, Oolite eXpansion Pack,
Opera Widget, Pivot Style Template, Rockbox Theme package, Simple Machines
Forums theme, SubEthaEdit Mode, Trillian zipped skin, Virtual Skipper skin
APK Android package
JAR Java archive; compressed file package for classes and data
KMZ Google Earth saved working session file
KWD KWord document
ODT, ODP, OTT OpenDocument text document, presentation, and text document template, respectively.
OXPS Microsoft Open XML paper specification file
SXC, SXD, SXI, SXW OpenOffice spreadsheet (Calc), drawing (Draw), presentation (Impress),and word processing (Writer) files, respectively.
SXC StarOffice spreadsheet
WMZ Windows Media compressed skin file
XPI Mozilla Browser Archive
XPS XML paper specification file
XPT eXact Packager Models

HEX(十六进制)转换byte数组

javascript在线计算:

资料:
Mime-Detective

文件签名表格:
GCK’S FILE SIGNATURES TABLE

参考资料:
Magic number
List of file signatures

拓展阅读:
byte为什么要与上0xff?

C#穿透session隔离———Windows服务启动UI交互程序

C#穿透session隔离———Windows服务启动UI交互程序

写在前面

一开始是因为服务器经常会出现断电、系统崩溃的情况,导致一些正常运行的winform程序在系统故障重启后,每次都需要手动登录触发事件让程序自启。然后想利用windows服务在开启时就会自启来实现开机启动Winform程序。
但是因为从Vista 开始引入了 Session 0 隔离机制,导致windows服务无法直接进行界面交互操作。

注意:
使用CreateProcessAsUser与界面交互需要Session Id >0 ,用户会话的必须存在,如果存在服务器重启、注销后,重新开机导致系统只有Session 0存在,此时服务调用后的程序是不会显示界面的。
所以到头来还是没能实现我的想法。。。淦

Session 0 隔离原理

参考:

穿透Session 0 隔离(一)
摘录
用户界面特权隔离

在早期的Windows操作系统中,在同一用户下运行的所有进程有着相同的安全等级,拥有相同的权限。例如,一个进程可以自由地发送一个Windows消息到另外一个进程的窗口。从Windows Vista开始,当然也包括Windows 7、Windows 10,对于某些Windows消息,这一方式再也行不通了。进程(或者其他的对象)开始拥有一个新的属性——特权等级(Privilege Level)。一个特权等级较低的进程不再可以向一个特权等级较高的进程发送消息,虽然他们在相同的用户权限下运行。这就是所谓的用户界面特权隔离(User Interface Privilege Isolation ,UIPI)。

UIPI的引入,最大的目的是防止恶意代码发送消息给那些拥有较高权限的窗口以对其进行攻击,从而获取较高的权限等等,在计算机系统中,这却是一种维护系统安全的合适方式。

windows服务启动UI程序、

对于简单的交互,服务可以通过WTSSendMessage 函数,在用户Session 上显示消息窗口。对于一些复杂的UI 交互,必须调用CreateProcessAsUser 或其他方法(WCF、.NET远程处理等)进行跨Session 通信,在桌面用户上创建一个应用程序界面。

解决思路是:window service创建一个和与当前登陆用户可以交互的进程,这个进程运行在admin权限下,能够调起应用程序的UI

具体的做法是:widow service复制winlogon.exe进程句柄,然后通过调用api函数CreateProcessAsUser()以winlogon.exe权限创建新进程,新创建的进程有winlogon.exe的权限(winlogon.exe运行在system权限下),负责调用程序。

作者原文:

First, we are going to create a Windows Service that runs under the System account. This service will be responsible for spawning an interactive process within the currently active User’s Session. This newly created process will display a UI and run with full admin rights. When the first User logs on to the computer, this service will be started and will be running in Session0; however the process that this service spawns will be running on the desktop of the currently logged on User. We will refer to this service as the LoaderService.

Next, the winlogon.exe process is responsible for managing User login and logout procedures. We know that every User who logs on to the computer will have a unique Session ID and a corresponding winlogon.exe process associated with their Session. Now, we mentioned above, the LoaderService runs under the System account. We also confirmed that each winlogon.exe process on the computer runs under the System account. Because the System account is the owner of both the LoaderService and the winlogon.exe processes, our LoaderService can copy the access token (and Session ID) of the winlogon.exe process and then call the Win32 API function CreateProcessAsUser to launch a process into the currently active Session of the logged on User. Since the Session ID located within the access token of the copied winlogon.exe process is greater than 0, we can launch an interactive process using that token.

参考:
交互式服务
C#开发Windows服务详细流程
C#穿透session隔离———Windows服务启动UI交互程序
Subverting Vista UAC in Both 32 and 64 bit Architectures

问题

windows服务启动winform程序不显示UI问题解决
原因:
xp系统的用户和window service运行在一个session下,在xp以后,windows系统改变了用户会话管理的策略,window service独立运行在session0下,依次给后续的登录用户分配sessionX(X =1,2,3…),session0没有权限运行UI。所以在window xp以后的系统下,window service调用有UI的application时只能看到程序进程但不能运行程序的UI。

参考:
C# windows服务启动winform程序不显示UI问题解决
How can a Windows service execute a GUI application?
穿透Session 0 隔离(二)

C#代码确保应用程序只有一个实例
C#设置程序开机自启动[需登录]

C#设置程序开机自启动[需登录]

写在前面

本来是想在服务器上实现服务器崩溃或者更新后,可以自动启动winform程序,但是以下方法还是需要有用户登录进去。

方法一:将软件的快捷方式创建到计算机的自动启动目录下(不需要管理员权限)

  1. 进入目录:C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
  2. 将应用程序快捷方式剪切(或者复制)并粘贴到目录中
  3. 应用程序快捷方式加入到了启动项,下次重启电脑之后这个应用程序就会自动开机运行了

如果需要针对个人账户进行配置:Win+R输入命令shell:startup ,会直接弹出启动项对应的目录,然后像前面方法一样把应用程序快捷方式复制到启动目录

方法二:修改计算机注册表的方式(需要管理员权限)

开始

Windows自启动原理

在Windows操作系统下,主要有2个文件夹和8个注册表键项控制程序的自启动,通过修改“Run”键值实现自启动程序是比较常见的方法。
具体的位置是:HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Run

代码

在知道注册表中自启动位置所在后,只需要将需要启动的程序路径添加至指定路径中就可以实现开机自启动功能。

Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value;
using (Mutex mutex = new Mutex(false, "Global\\" + appGuid))
{
if (!mutex.WaitOne(0, false))
{
return;
}
//判断是否开启自启动
string strFilePath = Application.ExecutablePath;
string strFileName = System.IO.Path.GetFileName(strFilePath);
try
{
//自启动功能是否开启
var AutoRunFlag = ConfigurationManager.AppSettings["AutoRunFlag"] == "1" ? true : false;
//自启动是否已经存在
var flag = SystemHelper.IsAutoRun(strFilePath, strFileName);
if (AutoRunFlag != flag)
{
#region 获取管理员权限,进行注册表写入操作
//当前用户是管理员的时候,直接启动应用程序并且写入注册表
//如果不是管理员,则使用启动对象启动程序,以确保使用管理员身份运行
//获得当前登录的Windows用户标示
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
//判断当前登录用户是否为管理员
if (principal.IsInRole(WindowsBuiltInRole.Administrator))
{
try
{
//如果是管理员,注册自启动代码
SystemHelper.SetAutoRun(strFilePath, strFileName, AutoRunFlag);
}
catch(Exception ex)
{
MessageBox.Show( ex.Message, "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
//创建启动对象
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.FileName = Application.ExecutablePath;
//设置启动动作,确保以管理员身份运行
startInfo.Verb = "runas";
try
{
System.Diagnostics.Process.Start(startInfo);
}
catch
{
return;
}
//退出
Application.Exit();
}
#endregion
}
//启动程序
Application.Run(new Form1());
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
//return;
}
}


}

SystemHelper.cs 参考自:C# winform程序实现开机自启动,并且识别是开机启动还是双击启动

SystemHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public sealed class SystemHelper
{
private SystemHelper() { }
/// <summary>
/// 设置程序开机启动
/// </summary>
/// <param name="strAppPath">应用程序exe所在文件夹</param>
/// <param name="strAppName">应用程序exe名称</param>
/// <param name="bIsAutoRun">自动运行状态</param>
public static void SetAutoRun(string strAppPath, string strAppName, bool bIsAutoRun)
{
try
{
if (string.IsNullOrWhiteSpace(strAppPath)
|| string.IsNullOrWhiteSpace(strAppName))
{
throw new Exception("应用程序路径或名称为空!");
}
RegistryKey reg = Registry.LocalMachine;
RegistryKey run = reg.CreateSubKey(@"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\");
if (bIsAutoRun)
{
run.SetValue(strAppName, strAppPath);
}
else
{
if (null != run.GetValue(strAppName))
{
run.DeleteValue(strAppName);
}
}
run.Close();
reg.Close();
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
/// <summary>
/// 判断是否开机启动
/// </summary>
/// <param name="strAppPath">应用程序路径</param>
/// <param name="strAppName">应用程序名称</param>
/// <returns></returns>
public static bool IsAutoRun(string strAppPath, string strAppName)
{
try
{
RegistryKey reg = Registry.LocalMachine;
RegistryKey software = reg.OpenSubKey(@"SOFTWARE");
RegistryKey run = reg.OpenSubKey(@"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\");
object key = run.GetValue(strAppName);
software.Close();
run.Close();
if (null == key || !strAppPath.Equals(key.ToString()))
{
return false;
}
return true;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
}

运行环境

Win10
.NET Framework4.5
VS2019

填坑

  1. 注册表地址
    使用代码修改注册表的方式,如果需要查看注册信息,需要确认程序是32-bit还是64-bit。
    32位,查看地址为 :HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVers ion\Run
    64位,查看地址为 :HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

    操作系统 64位程序访问的注册表 32位程序访问的注册表
    64位系统 HKEY_LOCAL_MACHINE\SOFTWARE HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node
    32位系统 - HKEY_LOCAL_MACHINE\SOFTWARE

    参考资料:

  2. 以管理员权限打开程序:
    参考资料:

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×