一键安装IIS的点点滴滴——For所有Microsoft的操作系统(下)

接着上一篇的讲,下面我们将讨论Windows7、Windows Server 2008 以及Windows Vista的IIS安装:

Windows7的IIS的安装文件其实在装系统的时候就已经拷贝在电脑里了的,这也就是说我们不需要再那么费劲搞出i386文件了,这对我们来说是很方便的(Windows Server 2008 和Windows Vista也是这样)。我们只需要执行安装的命令就可以了。具体安装有两种方式:

1.一种比较简单,是通过

start /w pkgmgr /iu:IIS-WebServerRole;IIS-WebServer;IIS-CommonHttpFeatures;IIS-StaticContent;IIS-DefaultDocument;IIS-DirectoryBrowsing;IIS-HttpErrors;IIS-HttpRedirect;IIS-ApplicationDevelopment;IIS-ASPNET;IIS-NetFxExtensibility;IIS-ASP;IIS-ISAPIExtensions;IIS-ISAPIFilter;IIS-ServerSideIncludes;IIS-HealthAndDiagnostics;IIS-HttpLogging;IIS-LoggingLibraries;IIS-RequestMonitor;IIS-HttpTracing;IIS-CustomLogging;IIS-ODBCLogging;IIS-Security;IIS-BasicAuthentication;IIS-WindowsAuthentication;IIS-DigestAuthentication;IIS-ClientCertificateMappingAuthentication;IIS-IISCertificateMappingAuthentication;IIS-URLAuthorization;IIS-RequestFiltering;IIS-IPSecurity;IIS-Performance;IIS-WebServerManagementTools;IIS-ManagementConsole;IIS-ManagementScriptingTools;IIS-ManagementService;IIS-IIS6ManagementCompatibility;IIS-Metabase;IIS-WMICompatibility;IIS-LegacyScripts;IIS-LegacySnapIn;WAS-WindowsActivationService;WAS-ProcessModel;WAS-NetFxEnvironment;WAS-ConfigurationAPI

的命令来执行。关键是前面的命令,pkgmgr就是Windows 7的IIS安装命令。/iu:是安装配置,如果是要卸载IIS,则直接把/iu:该为/uu:即可!后面的一大串名字都是表明需要安装哪些文件,打开Windows 7的Windows功能面板,你会看见Internet信息服务的选项,这些选项都是Checkbox,如果是要安装IIS,直接在需要安装的选项前面打勾就行了,这行命令后面的一大串文件名的意思也就是表示要在哪些选项前面打上勾而已。

2.第二种方式比较复杂,但是我最开始没有发现第一种方法,就用的这一种,整个过人让人纠结啊~~~,先找到的资料是:http://learn.iis.net/page.aspx/133/using-unattend-setup-to-install-iis-7/,他用的方法是执行命令:

start /w pkgmgr /n:unattend.xml

这里的unattended.xml文件内容是:

<?xml version="1.0" ?>
<unattend xmlns="urn:schemas-microsoft-com:unattend"  
    xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<servicing>
   <!-- Install a selectable update in a package that is in the Windows Foundation namespace -->
   <package action="configure">
      <assemblyIdentity
         name="Microsoft-Windows-Foundation-Package"
         version="6.0.5308.6"
         language="neutral"
         processorArchitecture="x86"
         publicKeyToken="31bf3856ad364e35"
         versionScope="nonSxS"
      />
    <selection name="IIS-WebServerRole" state="true"/>
    <selection name="WAS-WindowsActivationService" state="true"/>
    <selection name="WAS-ProcessModel" state="true"/>
    <selection name="WAS-NetFxEnvironment" state="true"/>
    <selection name="WAS-ConfigurationAPI" state="true"/>
  </package>
</servicing>
</unattend>
这里标记上红色字体的就是最让我纠结的地方:

(1)先说x86。它表示你正在运行中的操作系统的位数,一般来说有两种类型:amd64和x86。大家应该都知道,amd64表示64位操作系统,x86表示32位操作系统。大家注意一下,这里所说的位数不是指处理器硬件,而是说我们安装的操作系位数。举个简单的例子,如果我们买的电脑是32位处理器,那么只能装32位操作系统(但现在市场上应该大部分都是64位处理器了)。如果电脑是64位处理器,则可以安装32位操作系统或者64位操作系统。在Win7下或者Win2008下可以通过运行cmd,执行systeminfo命令来查看自己的系统信息:

大家可以看到,这里系统类型说的就是我安装的操作系统是64位的,硬件处理器同时也是64位的。

那么根据客户的操作系统位数不同,我们的xml文件自然也要相应改变。

问题6:如何通过程序判断客户机的操作系统位数呢?

回答:我第一个想法是查看注册表,在HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\WINDOWS NT\CURRENTVERSION的BuildLabEx中我找到了想要的:

但是泉哥说了,最好不要通过查看注册表中的值内容来实现,于是只能换个方法。

他提到了一点是,64位操作系统下必定会有HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node文件,如果是32位系统则不会有,于是我们便测试了一下,发现这个判断有个严重问题,他根本就不是判断所在操作系统的位数,而是运行的程序的位数,例如,我们如果把Visual Studio上方的x64改为x86,那么运行时的程序就会找不到注册表中刚才那个Wow6432Node文件,相反如果改为x64运行,则找得到。所以这和具体操作系统位数并无直接关系,此方法不可行。在这里我也提醒一下各位读者,有很多网上的判别位数的方法其实都是判断的运行程序的位数,而不是真正操作系统的位数,大家可以通过下图的方法,更换运行时程序的位数来测试!

于是我又开始探索其他思路,在Visual Studio 2010中的环境变量有个方法让我惊喜:System.Environment.Is64BitOperatingSystem,他能够成功的判断操作系统位数,但是失望的是Framework4.0才有这个方法,我们软件目前用的是Framework3.5做平台,只能放弃这个方法了。哎!

在反复尝试中终于又发现一个好方法:Kernel32.dll中的IsWow64Process,具体参考MSDN文档http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx

同时看到了一篇很好的文章http://cfx.codeplex.com/SourceControl/changeset/view/39074#,他是先判断IntPtr.Size 是否是8,很明显,只有64位操作系统才会使int的大小为8位,32位操作系统只能是4位。所以如果Size是8则一定是64位操作系统,但这只是充分非必要条件,如果Size不等于8,我们需要调用Kernel32.dll的IsWow64Process方法来判断:

 

代码
/// <summary>
        
/// The function determines whether the current operating system is a 
        
/// 64-bit operating system.
        
/// </summary>
        
/// <returns>
        
/// The function returns true if the operating system is 64-bit; 
        
/// otherwise, it returns false.
        
/// </returns>
        public static bool Is64BitOperatingSystem()
        {
            
if (IntPtr.Size == 8)  // 64-bit programs run only on Win64
            {
                
return true;
            }
            
else  // 32-bit programs run on both 32-bit and 64-bit Windows
            {
                
// Detect whether the current process is a 32-bit process 
                
// running on a 64-bit system.
                bool flag;
                
return ((DoesWin32MethodExist("kernel32.dll""IsWow64Process"&&
                    IsWow64Process(GetCurrentProcess(), 
out flag)) && flag);
            }
        }

        
/// <summary>
        
/// The function determins whether a method exists in the export 
        
/// table of a certain module.
        
/// </summary>
        
/// <param name="moduleName">The name of the module</param>
        
/// <param name="methodName">The name of the method</param>
        
/// <returns>
        
/// The function returns true if the method specified by methodName 
        
/// exists in the export table of the module specified by moduleName.
        
/// </returns>
        static bool DoesWin32MethodExist(string moduleName, string methodName)
        {
            IntPtr moduleHandle 
= GetModuleHandle(moduleName);
            
if (moduleHandle == IntPtr.Zero)
            {
                
return false;
            }
            
return (GetProcAddress(moduleHandle, methodName) != IntPtr.Zero);
        }

        [DllImport(
"kernel32.dll")]
        
static extern IntPtr GetCurrentProcess();

        [DllImport(
"kernel32.dll", CharSet = CharSet.Auto)]
        
static extern IntPtr GetModuleHandle(string moduleName);

        [DllImport(
"kernel32", CharSet = CharSet.Auto, SetLastError = true)]
        
static extern IntPtr GetProcAddress(IntPtr hModule,
            [MarshalAs(UnmanagedType.LPStr)]
string procName);

        [DllImport(
"kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [
return: MarshalAs(UnmanagedType.Bool)]
        
static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);

这样就可正确判断操作系统位数了!其实如果找不到这个方法,我就想只能从程序中执行systeminfo命令,然后接收到所有的输出信息,再进行解析,判断是否是x64Base-PC,但现在不用那么麻烦了O(∩_∩)O~~

(2)接下来恼火的就是那个Windows版本号~~~

从注册表的BuildLabEx可以获得,但是既然泉哥说了最好不要碰注册表,那么还是另想办法吧~~~在.Net的环境变量中找是一个方法:

 OperatingSystem os = System.Environment.OSVersion;

但不巧的是,这个os里面只能找到版本号中的3个参数,6.1.7600,最后一个参数他无能为力了!于是我们就尝试直接用这个参数写入xml,安装IIS,然是它不认,就说参数错误!我再试6.1.7600.0,也不行!再试6.1.7600.*,还是错误!看来不获得最后一个版本号参数是通不过的了!在网上找了又找,终于找到一个突破口!

http://www.22333.com/os/28318.html 执行

slmgr.vbs -dlv

命令,可以获取完整的版本号信息:

既然它能得到,那么我肯定也可以获得啦!~~~在本地搜索slmgr.vbs,找到它后打开,我对VBScript不是很熟,泉哥帮我看了看,虽然他也不熟,但是找东西的速度真是没话说,两三下就找到了最终调用的方法!它是通过slmgr.ini来获取信息的,里面调用了一个方法

Set colServices = g_objWMIService.ExecQuery("SELECT " & strQuery & " FROM " & ServiceClass)

原来他是调用WMI的函数来实现,WMI调用方法参考的http://andrewensley.com/2009/10/c-detect-windows-os-version-%E2%80%93-part-2-wmi/

于是我们的程序就可以这样写了:

代码
       internal string OSVersion
        {
            
get
            {
                
if (osVersion == null)
                {
                    var ver 
= new System.Management.ManagementObjectSearcher("select Version from SoftwareLicensingService");
                    var items 
= ver.Get();
                    
foreach (var item in items)
                    {
                        
object version = item.GetPropertyValue("Version");
                        osVersion 
= version.ToString();
                    }
                }
                
return osVersion;
            }
        }

这个OSVersion属性就是我们想要的结果!因此第二种方法也就可以实现了!虽然最终我们的程序用的是第一种方法,但是第二种方法的探索过程中得到的一些结果和经验以后很可能会用到,所以还是拿出来与大家分享^_^

经过我测试,Windows Server 2008(32位)、Windows Server 2008 R2(64位) 和 Windows Vista(32位和64位) 系统的IIS安装和Win7的一样!

最后我们需要做的事情就是自动的判断客户机的操作系统,然后选择合适的方法安装!

问题7:如何判断客户机操作系统类型?

回答:参考了MSDN文档http://msdn.microsoft.com/en-us/library/ms724833(VS.85).aspx

这里详尽的描述了Windows的各个版本,我看见网上有许多只是根据Version number来判断了,看了这幅图你就会发现,其实这种判断并不准确!很多系统都是同一个Version number。

这里我使用Other那一列的方法来判断。这里先声明一下,VER_NT_WORKSTATION和VER_SUITE_WH_SERVER是两个常量,他们的值分别为0x0000001和0x00008000,这个可以在上面给出的网址中找到。Other中还有个OSVERSIONINFOEX参数,这个参数是个结构,通过执行GetVersionEx方法可以为它赋值,我们可以通过判断OSVERSIONINFOEX的各个属性来找到对应的系统名称。所以我们程序的步骤是:先构造一个OSVERSIONINFOEX数组,这个数组的结构是:

这里是C++语法,要将他变为C#语法真是费了一番周折!DWORD-->Int32,WORD-->Int16,BYTE-->Byte,TCHAR-->CHAR[128]。前面的还好说,最后那个Char[]数组比较讨厌了,在C#中结构是值类型,里面如果只是有CHAR[]还可以,但是要是明确的把这个字符数组初始化为Char[128]就不行了,如果是直接在结构里面写Char[] sz=new Char[128]也是不行的,因为结构里不能有实例字段初始值设定项。所以又只有上网找解决办法,果然还是网上牛人多!http://www.cnblogs.com/flier/archive/2004/08/14/33245.html解决代码如下:

代码
[StructLayout(LayoutKind.Sequential, Pack = 1)]
    
public struct _OSVERSIONINFOEX
    {
        
public Int32 dwOSVersionInfoSize;
        
public Int32 dwMajorVersion;
        
public Int32 dwMinorVersion;
        
public Int32 dwBuildNumber;
        
public Int32 dwPlatformId;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst 
= 128)]
        
public Char[] szCSDVersion;
        
public Int16 wServicePackMajor;
        
public Int16 wServicePackMinor;
        
public Int16 wSuiteMask;
        
public Byte wProductType;
        
public Byte wReserved;
    }

这样就可以了!里面有句UnmanagedType,我想很可能是通过这个方法把这个字符数组变为非托管代码。

然后我们就可以直接用了:

代码
 /// <summary>
    
/// 获得OS信息
    
/// </summary>
    class CheckOSInfo
    {
        [DllImport(
"kernel32.dll")]
        
public static extern bool GetVersionEx(ref _OSVERSIONINFOEX osVersionInfo);
        [DllImport(
"user32.dll")]
        
public static extern int GetSystemMetrics(int sm_SERVERR2);
        

        
public static string GetOSName()
        {
            _OSVERSIONINFOEX osVersionInfo 
= new _OSVERSIONINFOEX();
            osVersionInfo.dwOSVersionInfoSize 
= 156;

            
if (GetVersionEx(ref osVersionInfo))
            {
                
switch (osVersionInfo.dwMajorVersion)
                {
                    
case 5:
                        
switch (osVersionInfo.dwMinorVersion)
                        {
                            
case 0:
                                
return "Microsoft Windows 2000";
                            
case 1:
                                
return "Microsoft Windows XP";
                            
case 2:
                                
if ((osVersionInfo.wSuiteMask & 0x00008000!= 0)
                                    
return "Microsoft Windows Home Server";
                                
if (osVersionInfo.wProductType == 1 && CheckOSBitness.Is64BitOperatingSystem())
                                    
return "Microsoft Windows XP";
                                
if(GetSystemMetrics(89)==0)
                                    
return "Microsoft Windows Server 2003";
                                
else
                                    
return "Microsoft Windows Server 2003 R2";
                        }
                        
break;
                    
case 6:
                        
switch (osVersionInfo.dwMinorVersion)
                        {
                            
case 0:
                                
if (osVersionInfo.wProductType == 1)
                                    
return "Microsoft Windows Vista";
                                
else
                                    
return "Microsoft Windows Server 2008";
                            
case 1:
                                
if (osVersionInfo.wProductType == 1)
                                    
return "Microsoft Windows 7";
                                
else
                                    
return "Microsoft Windows Server 2008 R2";
                        }
                        
break;
                }
            }
            
return null;
        }
    }

最开始两句是调用一个外部DLL的函数的方法。

这样我们就成功的知道了客户所使用的操作系统类型了!

总结一下,Windows 7以前的版本,即包括Windows XP和Windows Server 2003的各个版本的IIS安装,都需要i386文件和配置选项文本文件,其中64位版本的系统还需要AMD64文件,而Windows 7、Windows Vista、Windows Server 2008的IIS安装则只用执行一句命令行就行了。

最终,我们的一键安装IIS程序有一个Source文件夹(里面装有所有版本的i386、amd64以及其他配置文件)和一个我们的程序(获取操作系统类型以及执行一些cmd命令)。

最后我们要做的只是把这些所有文件选中,打包压缩成一个自解压文件,自解压文件是exe格式,可直接执行解压。在高级的“自解压选项”中可以设置解压路径,这里我选择的是临时文件夹。另外,还需要设置解压后运行的程序,这里填写我们编写的控制台应用程序的文件名。上两张图:


然后点确定,就压缩好了。把这个exe文件放在任何一台装过.Net Framework3.5 及以上的系统上,直接运行,就可以静默安装IIS了,另外我同时也做了个一键卸载IIS的压缩包。

最初我是想把这个压缩包直接发放出来供大家下载的,但是由于种种原因:

1.可能涉及到微软的版权问题,所以i386文件和amd64文件我不能提供给大家,所以只能是大家自己按照我上篇文章的方法获得这些文件。不过我后面提供下载的源代码中的Source文件夹中提供了各个操作系统的bat文件,这些bat文件是我上篇文章中提到过的,用于从光盘中拷贝出所需的i386和amd64文件。读者们只需在执行bat文件时传入2个参数:源文件夹路径和要拷贝到的目标文件夹路径,即可获得该操作系统所需的所有IIS安装文件。

2.如果是提供的下载包含了各个系统的i386文件,最后exe文件差不多有43M左右,文件太大了不方便下载。

3.如果是提供exe文件下载,不免有人会怀疑携带有病毒或木马,不敢下载。

4.等等

鉴于以上原因,不能把最终的IIS一键安装包提供给大家,也请大家理解。

我的源程序中有4个项目:IISClickInstall(一键安装IIS)、IISClickInstall(一键卸载IIS)、IISClickRequiredFiles(生成获取IIS安装文件的bat文件)、IISManager(一些主要方法)。

在提供的下载中,除了源程序文件,我还提供了最后需要生成exe的所有文件,只是Source文件夹中原本应该存放各个系统的i386文件和amd64文件等等,我全部替换为了之前所说的bat文件,大家可以通过bat文件自己获得i386文件和amd64文件放入对应文件夹,然后把这些文件夹按照上面的方法打包成exe自解压文件即可!

到这里,关于IIS的安装内容已经全部结束,其中肯定有许多不足的地方,欢迎大家多多批评指正,共同进步!

这里是下载地址!欢迎大家下载~~~^_^
原文地址:https://www.cnblogs.com/cdts_change/p/1681392.html