• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
bombless
博客园    首页    新随笔    联系   管理    订阅  订阅

在MinGW或VS 2005 SP1环境下创建一个要求UAC权限的程序。

之前在给U盘挂自制操作系统的那篇教程里,我给写了一个pdev程序用来输出windows下,系统各磁盘的主引导记录。

 

不过那个程序用起来每次都要记得用管理员权限启动程序,这多少增加了不便。(特别是对带UAC功能的win7\vista来说)。

 

如果能让程序启动时自动要求提权,那当然更符合windows程序的一般习惯。

 

我查到了一篇这种做法的教程:

http://www.zu14.cn/2010/05/14/delphi-win32-program-on-windows7-vista-uac-administrator-rights/

 

也就是需要用到以下这个xml文件(保存成uac.manifest文件,不过还可以保存成任意的.manifest文件也行。

程序清单文件
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

 

下面具体讲讲在MinGW或者VS2005 SP1的情况下该怎么做。

 

VS2005下简单,只需要在项目的资源里加这个uac.manifest文件即可。

我在VS2005的“添加资源”一项里没有找到“添加程序清单”,索性我选择了添加xml文件,并保存为uac.manifest,把以上内容复制到文件中,

再编译,就生成了启动时会提示UAC提权的提示框。(这种要求UAC提权的程序不能直接用F5调试运行,在VC里如果要直接看效果可以按Ctrl + F5 直接运行)。

 

在MinGW下,这个问题要稍复杂那么一点点。

 

需要在.rc资源文件里添加一个

#include <winuser.h>
1 RT_MANIFEST uac.manifest

这一段。添加#include <winuser.h>文件包含是因为在winuser.h里有对RT_MANIFEST声明,

#define RT_MANIFEST 24

把这段资源脚本文件保存为uac.rc,放到与前面说的uac.manifest相同的目录下,最后需要用MinGW工具中的windres程序将它处理成.res资源文件。

命令如下:

windres --input-format=rc -O coff -i uac.rc -o uac.res

这样就产生了uac.res资源文件,再接下来用gcc将它和程序源文件一起编译就可以了。于是问题解决!

当然如果嫌这样的.rc文件长了,或者不愿意include一个winuser.h文件,可以写成更加简短的版本:

1 24 uac.manifest

 

效果一样。(在VC中也可以做同样处理,如果你想在资源里添加对程序清单的描述的话。)

 

写这篇文章参考了以下几个网址,如果需要深入这个问题可以作为参考:

http://www.cnblogs.com/dflying/archive/2007/03/21/683190.html  

(Windows Vista for Developers——第四部分:用户帐号控制(User Account Control,UAC))

这篇文章需要详细关注“使用应用程序清单”这一部分。

 

http://msdn.microsoft.com/en-us/library/aa381043%28v=VS.85%29.aspx

http://msdn.microsoft.com/fr-fr/library/bb773175%28en-us,VS.85%29.aspx

 

上面2篇MSDN文章中关于RT_MANIFEST的部分值得注意。

 

总之原理就是,PE文件的资源中有一种类型是RT_MANIFEST,它的内容实际上是一个xml文件,被称为程序清单文件。

换句话说,这个xml格式的.manifest文件文件会被打包进可执行程序文件中。

而程序清单文件中描述了运行程序需要的权限,因此借助这个机制,有UAC管理功能的windows系统会在启动程序时向用户要求提权。

 

下面把我的pdev程序贴一下,作为一个要求UAC提权的程序的有趣的例子。它可以判断系统中有哪些磁盘,并打印出磁盘的主引导记录。

(注意,这类程序即使在命令行打开,windows仍然会打开一个新的命令行窗口运行程序,并且程序路径是一个完整路径,

因此这时候我们的命令行程序不得不总是在将要推出前用系统的pause命令做暂停,否则用户看到的会是一个一闪而过的窗口。

 

 

代码
#include <stdio.h>
#include
<stdlib.h>
#include
<windows.h>
void ShowError(){
void *pError;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_SYSTEM
|
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,GetLastError(),
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
(LPTSTR)
&pError,0,NULL);
MessageBox(
0,(PTSTR)pError,0,0);
LocalFree(pError);
}
void ReadDevice(HANDLE hDeviceHandle,int DriveNumber,FILE *output_file){
BYTE buffer[
512];
DWORD nBytes;
int iCount;
if(hDeviceHandle != INVALID_HANDLE_VALUE){
printf(
"#正在尝试读取物理驱动器%d ...",DriveNumber);
ReadFile(hDeviceHandle,buffer,
sizeof buffer,&nBytes,NULL);
}
else{
if(GetLastError() == ERROR_FILE_NOT_FOUND && DriveNumber > 0){
printf(
"#以上%d项就是系统中现有的全部物理驱动器。",DriveNumber);
system(
"pause");
exit(
0);
}
perror(
"读取设备时发生错误");
puts(
"请检查您是否正以管理员账户运行此程序,");
printf(
"或许物理驱动器%d不存在.\n",DriveNumber);
ShowError();
system(
"pause");
exit(
-1);
}
if(nBytes > 0){
printf(
"\n#读取了%d字节的数据:\n",nBytes);
for(iCount = 0; iCount < sizeof buffer; ++iCount){
if(iCount % 16 == 0){
printf(
"\n %04X\t|",iCount);
}
printf(
" %02X",buffer[iCount]);
}
printf(
"\n ----\t ");
for(iCount = 0; iCount < 16; ++iCount){
printf(
" --");
}
printf(
"\n\n");
if(output_file != NULL){
fwrite(buffer,
sizeof buffer,1,output_file);
printf(
"已将以上%d字节的数据写入文件。\n",sizeof buffer);
}

}
else{
perror(
"失败。");
ShowError();
system(
"pause");
exit(
-1);
}
}
int main(int argc,char *argv[]){
HANDLE hDeviceHandle
= NULL;
int iCount;
FILE
*output_file = NULL;
TCHAR DriveNameBuffer[MAX_PATH]
= TEXT("");
PTSTR DriveNamePrefix
= TEXT("\\\\.\\PHYSICALDRIVE");
if(argc > 1){//带命令行参数,说明用户在命令行里使用本程序
if(argc == 3 && !strcmp("-o",argv[1])){
printf(
"#以512字节为单位连续输出到文件%s...\n",argv[2]);
output_file
= fopen(argv[2],"wb");
if(output_file == NULL){
perror(
"无法打开指定的文件");
system(
"pause");
exit(
-1);
}
}
else{
printf(
"Usage:\n"
"%s -o [filename ...]"
"\n将系统中所有的物理驱动器的主引导记录依次写入文件。\n",
argv[
0]);
printf(
"\n不合法的命令行参数,将退出程序。");
system(
"pause");
exit(
0);
}
}
else{//不带命令行参数,很可能用户是直接双击程序运行的。
output_file = NULL;
}
for(iCount = 0; iCount < 256; ++iCount){
wsprintf(DriveNameBuffer,TEXT(
"%s%d"),DriveNamePrefix,iCount);
hDeviceHandle
= CreateFile(
DriveNameBuffer,
GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,
0,0);
ReadDevice(hDeviceHandle,iCount,output_file);
CloseHandle(hDeviceHandle);
}
return 0;
}

编译之前我们先用之前的uac.manifest文件和uac.rc文件准备好我们的uac.res资源文件:

windres --input-format=rc -O coff -i uac.rc -o uac.res

之后将uac.res文件和程序一起打包:

gcc -o pdev pdev.c uac.res

 

这下大功告成!

posted @ 2010-12-29 20:43  bombless  阅读(3158)  评论(2)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3