Windows NT使用技巧、编程原理及程序示例 [欢迎交流]

[本文最后更新:2000-09]

  1. 利用Net User命令和Cacls命令做用户管理
  2. 利用At命令做日程管理
  3. 利用用户登录脚本实现开机提示
  4. 利用Net Session命令实现登录用户统计
  5. Windows NT局域网管理API函数库(NETAPI)简介
  6. 怎样添加、删除、配置用户?
  7. 怎样修改用户口令?
  8. 怎样编程实现用户的注销和关机?
  9. 怎样获取所有登录用户列表?
  10. 怎样向用户或计算机发送消息?
  11. 怎样实现远程关闭计算机?
  12. UNICODE字符串和普通ASCII字符串怎样转换?
  13. 如何获取系统错误信息?
  14. 什么是Service(服务)程序?
  15. 如何使自己的应用程序成为Service?
  16. 如何编制Service程序?

 

利用Net命令和Cacls命令定制用户管理 ( 返回主题选择 )[欢迎交流]

NT的“域用户管理器”是用来管理域、成员服务器和工作站安全性的工具。使用“域用户管理器”可以:选定要管理的域或计算机;创建和管理用户帐号; 创建和管理组;管理安全性规则。通常网络管理员是用“域用户管理器”来完成。但是,“域用户管理器”每次只能配置一个用户,虽然NT利用组的概念实施用户 管理,事实上对组内用户的配置还是单独进行的。
Net Group和Net User 命令行实用程序是NT下的两个管理域用户的重要程序。它可以完成NT域用户管理器所能完成的所有功能,其区别仅在于不具备友好的界面。Net Group命令行实用程序用于在Windows NT Server 域中添加、显示或更改全局组。该命令仅在 Windows NT Server 域中可用。Net User命令行实用程序用于添加或更改用户帐号或显示用户帐号信息。灵活应用这两个程序,可以完成许多“域用户管理器”不能完成的功能,例如:建立批量用户,详细设置每个用户目录的权限等等。
通常在建立用户组的时候,需要建立一个和组名相匹配的用户目录,并设置此目录的安全性使得该目录只能被管理员和该组成员访问。现在建立一个名为“实验组A”的用户组,并为该组设立一个管理员,可以执行下面的批命令:
Net Group 实验组A /Add /Comment:”建立用户组实验” //建立组
Net User Admin_LabA /Add /Comment:”实验组A管理员” //建立组管理员
Net Group 实验组A Admin_LabA /Add //将组管理员加入组
再为此组建立一个私有目录,执行下面的命令:
Md \\ServerName\STUDENT\实验组A
Cacls \\ServerName\STUDENT\实验组A /r everyone /e /t
Cacls \\ServerName\STUDENT\实验组A /g 实验组A:r /e /t
Cacls \\ServerName\STUDENT\实验组A /g Administrator:f /e /t
现在建立该组下的一般成员,运行下面的命令:
Net User User001 /Add /Comment:”实验组A一般用户”
Net Group 实验组A User001 /Add
为该用户建立自己的私有目录(此目录将只能由管理员、该组管理员和User001访问)
Md \\ServerName\STUDENT\实验组A\User001
Cacls \\ServerName\STUDENT\实验组A /r 实验组A:r /e /t
Cacls \\ServerName\STUDENT\实验组A\User001 /g User001:f /e /t

利用At命令做日程管理 ( 返回主题选择 )[欢迎交流]

作为一个教学实验室,需要由极少数的管理人员维护批量的计算机。鉴于教学实验室的工作特性,经常需要管理人员定期地对实验室中计算机进行硬盘清理、文件复 制的工作。重复性的劳动不仅降低了工作效率,更重要的是,经常由此引入操作失误。将一些不要求交互输入的系统维护程序安排到计算机的调度命令中,不仅可以 让计算机协助完成工作,计算机执行调度命令的可靠性也决不会有任何错误和纰漏。故而,将计算机的日程管理条理化,是改变教学实验室传统人工管理、将教学实 验室管理纳入现代化的必然。以下将以具体实例说明公用实验室管理措施在NT调度服务下的实现。
机房定时提示关机并定时强制关机的实现。为了实现自动关机提示并关机,首先要编制NT下的注销程序和关闭系统程序。可以调用WindowsAPI中的 ExitWindows()和ExitWindowsEx()函数,需要立即强制注销,只需要在程序中调用ExitWindows(0,0);需要立即强 制关机,只需要在程序中调用ExitWindowsEx(EWX_POWEROFF| EWX_FORCE)即可(如果需要配置为重新启动等其他要求,请查阅WindowsAPI中此二函数的具体参数)。假设已经做好注销程序 ScheduleLogoff.exe和关机程序ScheduleShutdown.exe并将它们拷贝到c:\winnt。运行如下命令:
at \\MyStation 21:55 /every: M,T,W,Th,F,S,Su c:\winnt\ScheduleLogoff.exe
at \\MyStation 22:00 /every: M,T,W,Th,F,S,Su c:\winnt\ScheduleShutdown.exe
则在每天的晚9:55自动强制名为MyStation的计算机上的用户注销并在5分钟后自动关闭该计算机。建议在强制用户注销之前几分钟,给用户发送关机提示以便用户有足够的时间保存文件,可以用net send命令行程序来完成这一功能。具体使用如下:
at \\MyStation 21:55 /every: M,T,W,Th,F,S,Su “cmd /c net send 请立即保存程序,5分钟后系统将自动关闭!”
这样,就可以完全的由计算机来控制整个实验室所有计算机的自动关机时间,而机房管理人员则可以将主要精力用于实验辅导而非系统管理。
教学实验室还经常需要进行硬盘整理(如Scandisk.exe)和硬盘清理(如删除用户空间)。同样也可以将所需执行的功能编制为应用程序并加入系统的 调度命令。如安排每周一早晨进行磁盘整理;每天关机前进行用户磁盘空间的清理。合理地调度命令安排,将使机房的管理日程化,同时提高有盘工作站的安全系 数。在多媒体实验室的实际应用中,将用户管理和日程安排相结合,实现了NT有盘工作站的安全防拷贝。

利用用户登录脚本实现开机提示 ( 返回主题选择 )[欢迎交流]

用户的登录脚本(登录脚本存放的路径是:“%systemroot%\system32\REPL\IMPORT\SCRIPTS”)是在用户登录 时候运行的,在此程序中,可以根据需要加入有关的代码,实现特殊的功能。如果将有关实验室的介绍和试验内容放置在网站上,则可以在登录脚本中访问这个网站 从而实现开机提示或联机帮助系统。多媒体通信实验室目前设置为在登录脚本中打开Internet Explorer并连接实验室主页,从而实现开机提示和联机帮助。如果您使用VC来编写这个登录脚本,则简单地调用ShellExecute()函数来打 开网站地址即可。例如:

ShellExecute(NULL,"open","http://192.168.1.20",NULL,NULL,SW_MAXIMIZE);

 

利用Net Session命令实现登录用户统计 ( 返回主题选择 )[欢迎交流]

Net Session是NT下的一个命令行程序。运行它可以列出或断开本地计算机和与之连接的客户端的会话。其基本的使用格式为:

net session [\\computername] [/delete]。

其命令参数为:
\\computername 标识要列出或断开会话的计算机。
/delete 结束与 \\computername 计算机会话并关闭本次会话期间计算机的所有打开文件。如果省略 \\computername 参数,将取消与本地计算机的所有会话。
如果键入不带参数的 net session 则可以显示所有与本地计算机的会话的信息。
要显示某个用户的会话,请在命令中添加 \\computername 参数。单个用户的显示还包括用户所连接到的共享资源列表。当客户机用户与服务成功连接时,将记录该会话。当两个系统在同一个网络中,并且服务器接受用户名 和密码时,就出现成功会话。客户机用户必须先与服务器建立会话,才能使用服务器资源,而且客户机用户在建立与服务器的会话之前必须连接到网络资源。客户端 和服务器之间只能有一个会话,但可以有许多资源入口点和与资源的连接。要设置在自动断开某个会话之前暂停的时间,请使用 net config server 命令的 /autodisconnect 选项设置 autodisconnect 特征。自动断开连接对用户是透明的,因为当用户再次使用资源时,Windows NT 自动重新连接会话。要结束与服务器的会话,使用 /delete 选项与 \\computername。
net session 命令也可以写为 net sessions或net sess。使用 net session 命令显示访问服务器的计算机名和用户名、它们是否打开文件及每个用户会话的空闲时间。显示结果类似于下面的内容:
计算机 用户名 客户类型 打开空闲时间
\\BASSETT CHRISDR NT 1 00:00:13
\\SHARONCA Administrator DOS LM 2.1 0 01:05:13
根据这个显示结果,我们可以得到实时的再线用户信息。利用重定向功能将显示的结果送入一个临时文本(net session > tmp_sessions.txt),分析该文本格式并由NT计算机名用户名长度要求可知:计算机名、用户名和客户类型均固定占用占用22个字符的长度。故可以从此文本中分离在线计算机和在线用户的信息,从而实现在线用户的统计和监控。

 

Windows NT局域网管理API函数库(NETAPI) ( 返回主题选择 )[欢迎交流]

在Windows NT 的系统目录下,存放有关于局域网管理的动态链接库NetAPI32.DLL。含有一组网络管理的函数定义。这些函数被分成以下类型:

英文名称 中文解释
Alert functions 网络警告函数
ApiBuffer functions 内存管理函数
Distributed File System (Dfs) functions 分布文件系统管理函数
File functions 文件管理函数
Group functions 组管理函数
Local Group functions 本地组管理函数
Message functions 消息管理函数
Remote Utility functions 远程信息服务函数
Replicator functions 复制服务管理函数
Schedule functions 日程管理函数
Server functions 服务器管理函数
Service functions 服务管理函数
Session functions 会话管理函数
Share functions 共享资源管理函数
Statistics functions 网络统计函数
User functions 用户管理函数
Workstation and workstation user functions 工作站及工作站用户管理函数

所有表中列出的函数,囊括了Windows NT所有的网络管理功能,合理地使用这些函数,可以编制功能强大的Windows NT网络管理软件。例如现在比较流行的一些英文版Windows NT工具如:RemoteShutdown(远程关机)、svcmgr(远程服务管理)、Devicelock(外设权限设置)等软件全部都是在这些网络管理函数基础上实现的。

 

什么是Service(服务)程序? ( 返回主题选择 )[欢迎交流]

Service程序和普通的应用程序有一个根本的区别:Service程序可以在无用户登录和用户已经注销的情况下运行,而应用程序在没有用户注销 的时候是会被终止的。您可以通过控制面板的“服务”项来启动、暂停和停止一个服务的运行,当然您也可以通过网络来访问其他装有Windows NT的计算机(当然可能需要安装特定的程序例如:svcmgr)。

在Windows NT 下,各种Service都存在Service Control Manager Database中,因此我们可以通过对Service Control Manager Database进行操作来实现对Service的编 程。从而用自己的程序来启动、停止、和暂停一个服务的运行 。(Delphi下的例子:Delphi-Service.pas)

如何使自己的应用程序成为Service? ( 返回主题选择 )[欢迎交流]

[方法一] 利用工具软件
(1)在nt的toolkit里有个应用软件(srvinstw.exe)可以把普通程序设成service
(2)Rehan Nadeem的AppWizard to Create MFC Based Windows NT Service Projects

[方法二] 直接修改注册表(推荐)
(1)打开键:“HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services”
(2)新建一个项名为“<你的服务的名字>”
(3)在它下面建立以下键
“DisplayName”,字符串类型,值为“<你希望在控制面板服务项打开的时候能够看到的服务显示的名称>”;
“Description”,字符串类型,值为“<关于服务的简要说明>”;
“ImagePath”,字符串类型,值为“<你的可执行文件的全路径>”;
“ObjectName”,字符串类型,值为“<LocalSystem>”;
“ErrorControl”,DWORD类型,值为“0”
“Start”,DWORD类型,值为“0x00000003”
“Type”,DWORD类型,值为“0x00000110”

建议自己写一个MyService.REG文件直接运行即可,内容如下:

REGEDIT4

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<你的服务的名字>]
"DisplayName"="<你希望在控制面板服务项打开的时候能够看到的服务显示的名称>"
"Description"="<关于服务的简要说明>"
"ImagePath"="<你的可执行文件的全路径>"
"ObjectName”,字符串类型,值为“<LocalSystem>”;
"ErrorControl"=dword:00000000
"Start"=dword:00000003
"Type"=dword:00000110

如何编制Service程序? ( 返回主题选择 )[欢迎交流]

[编制Service程序的例子]
(1)用Delphi制作。“New”一个“Service Application”即可
(2)用VC制作。这里提供两个例子:ntservice_sample_project.zip(推荐)、cntservice_src.zip

[Windows98下使一个应用程序成为一个Service](只适用于Windows98)
在自己的应用程序的开始部分加入以下代码:
HINSTANCE hDllInst = LoadLibrary("KERNEL32.DLL");
//Windows2000下的kernel.dll中没有RegisterServiceProcess函数
if(hDllInst)
{
typedef DWORD (WINAPI *MYFUNC)(DWORD,DWORD);
MYFUNC RegisterServiceProcessAlias = NULL;
RegisterServiceProcessAlias = (MYFUNC)GetProcAddress(hDllInst, "RegisterServiceProcess");
if(RegisterServiceProcessAlias)
{
RegisterServiceProcessAlias(GetCurrentProcessId(),1);
}
FreeLibrary(hDllInst);
}

 

用户的注销和关机实现 ( 返回主题选择 )[欢迎交流]

和Windows 9x下不同,如果编程实现关机和用户注销,必须先取得有关的权限。

需要利用LookupPrivilegeValue()函数和AdjustTokenPrivileges()函数来获取关闭系统有关的权限“SE_SHUTDOWN_NAME”;然后再调用关闭系统函数ExitWindows()。上述函数使用示例请参照ShutDown.Cpp文件中格式。
如果您需要从网络关闭系统,请参照怎样实现远程关闭计算机?。
如果在您选择了ExitWindows()函数的参数以后仍然不能关闭计算机的电源,请参照怎样使Windows NT4.0关闭系统的时候关闭电源?

所有登录用户列表获取 ( 返回主题选择 )[欢迎交流]

获取用户列表过程为:获取当前NT局域网上的所有工作站和服务器信息(利用函数NetServerEnum());获取每一台工作站和服务器上的用户信息(利用函数NetWkstaUserEnum())并加入自己的列表。上述函数调用格式请参照KeeperDlg.cpp文件中的OnGetWksList()函数和GetWksLogonUser()函数。

该文件中还用了NETAPI函数组的内存分配函数(NetApiBufferAllocate() 和NetApiBufferFree()),本人在使用过程中发现释放内存时调用NetApiBufferFree()函数经常出错,如果您有什么建议请与本人联系。

UNICODE字符串和普通ASCII字符串的转换 ( 返回主题选择 )[欢迎交流]

在编写Windows NT应用程序的时候,经常会遇到一个问题:函数的参数是“LPWSTR”类型,即UNICODE字符串,而我们平常的字符串是ASCII字符串。所以编写 一个通用的“LPWSTR”类型字符串和“LPSTR”类型字符串的转换程序是非常必要的。可以参考KeeperDlg.cpp文件中的ASCIIToUnicode(LPSTR SourceStr)函数和UnicodeToASCII(LPWSTR SourceStr)函数。

向用户和计算机发送消息 ( 返回主题选择 )[欢迎交流]

利用NETAPI中的NetMessageNameAdd()函数、NetMessageBufferSend()函数和NetMessageNameDel()函数实现,具体调用格式请参照KeeperDlg.cpp文件中的NetSendMsg(LPSTR WksName)函数。

远程关机的实现 ( 返回主题选择 )[欢迎交流]

用ExitWindow()和exitWindowEx()函数只能实现本地计算机的用户注销与系统关闭(注意:此时需要获取有关的操作权限,具体参照怎样编程实现用户的注销和关机?),如果需要通过网络远程关机,则应该调用InitiateSystemShutdown()函数,具体调用格式如下:

InitiateSystemShutdown((LPTSTR)WksName,
"系统1分钟将会自动关闭,请保存文件到服务器!",
60,
TRUE,
FALSE
);

用户添加、删除和配置 ( 返回主题选择 )[欢迎交流]

NETAPI函数库中函数引用的数据类型在VC的相应头文件中有定义。如: USER_INFO_0 、USER_INFO_1 、USER_INFO_2 等是有关用户管理函数所用到的部分结构体。详细的信息可以阅读Windows API函数库介绍。下面给出几个简单的用户管理函数的应用。

1 添加新用户的实现

USER_INFO_1 user_info;
DWORD parm_err = 0;
user_info.usri1_name = L"SampleUser";
user_info.usri1_password = L"123456";
user_info.usri1_priv = USER_PRIV_USER;
user_info.usri1_comment = L"Sample User";
user_info.usri1_flags = UF_SCRIPT;
result = NetUserAdd( NULL,1,(LPBYTE)&user_info, &parm_err );
if(result != NERR_Success) ErrorMsg("建立用户失败!",result);

2 删除用户的实现

LPWSTR username = L"bb";
LPWSTR servername = L"mmc";
result = NetUserDel(servername,username);
if(result != NERR_Success) ErrorMsg("用户删除失败!",result);

3 配置现有用户

USER_INFO_1 user_info;
DWORD parm_err = 0;
user_info.usri1_name = L"SampleUser";
user_info.usri1_password = L"123456";
user_info.usri1_priv = USER_PRIV_USER;
user_info.usri1_comment = L"Sample User";
user_info.usri1_flags = UF_SCRIPT;
result = NetUserSetInfo(lpszPrimaryDC, lpszUser, 1, (LPBYTE)&user_info, &parm_err );
if(result != NERR_Success) ErrorMsg("配置用户失败!",result);

修改用户口令的实现 ( 返回主题选择 )[欢迎交流]

调用函数NetUserChangePassword(),具体格式参照以下内容:

OnChangePassword()
{
LPWSTR wUserName = L"aa";
LPWSTR wComputerName = L"mmc";
LPWSTR wOldPassword = L"aaabbb";
LPWSTR wNewPassword = L"123456";
NET_API_STATUS nas;

nas = NetGetDCName(NULL,
L"mmc_lab",
(LPBYTE *)&wComputerName
);

if(nas != NERR_Success) {
ErrorMsg("主控域查找失败!",nas);
}

nas = NetUserChangePassword(
wComputerName,
wUserName,
wOldPassword,
wNewPassword
);

if(nas == NERR_InvalidComputer){
MessageBox(NULL, "Invalid Computer Error!", MB_OK);
}
if(nas == NERR_UserNotFound){
MessageBox(NULL, "User not found Error!", MB_OK);
}
if(nas == ERROR_ACCESS_DENIED){
MessageBox(NULL, "Access deny Error!", MB_OK);
}
if(nas == NERR_NotPrimary){
MessageBox(NULL, "NOT primary Error!", MB_OK);
}
if(nas == NERR_PasswordTooShort){
MessageBox(NULL, "PasswordTooShort Error!", MB_OK);
}
if(nas != NERR_Success) {
ErrorMsg("密码更改失败!",nas);
// DisplayErrorText( nas );
}
else{
MessageBox("OK!",NULL,MB_OK);
}

}

获取系统错误信息 ( 返回主题选择 )[欢迎交流]

在函数执行完毕的时候,调用ErrorMsg("XX函数执行错误信息",GetLastError());

void ErrorMsg(CString Msg,DWORD dwLastError)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
MessageBox((LPCTSTR )lpMsgBuf, Msg, MB_OK);
LocalFree( lpMsgBuf );
}