首页 巧妙解决兼容性问题

巧妙解决兼容性问题

举报
开通vip

巧妙解决兼容性问题巧妙解决兼容性问题 回 想当年微软高调发布 Windows Vista,突出的兼容性问题成为其在推广时的最大阻力,这道不可逾越的 障碍让“出师未捷身先死”的 Windows Vista,从而成为继 Windows Me 之后微软最失败的操作系统。有鉴 于此,微软在进行 Windows 7 的开发的时候,将应用程序兼容性放在了前所未有的高度,提前两年就开始 为 Windows 7 进行各种兼容性测试, 同时在 Windows 7 上提供了 Windows XP 虚拟模式, 最大程度地保证应 用程序可以平滑地过渡到 ...

巧妙解决兼容性问题
巧妙解决兼容性问题 回 想当年微软高调发布 Windows Vista,突出的兼容性问题成为其在推广时的最大阻力,这道不可逾越的 障碍让“出师未捷身先死”的 Windows Vista,从而成为继 Windows Me 之后微软最失败的操作系统。有鉴 于此,微软在进行 Windows 7 的开发的时候,将应用程序兼容性放在了前所未有的高度,提前两年就开始 为 Windows 7 进行各种兼容性测试, 同时在 Windows 7 上提供了 Windows XP 虚拟模式, 最大程度地保证应 用程序可以平滑地过渡到 Windows 7,从这些我们都可以看出微软的良苦用心。?? >>详细内容 兼容性问题不断 如何进行操作系统版 本检查, 责任编辑:胡铭娅作者:IT168 陈良乔 【内容导航】 ? ? ? 2009-09-27 第 1 页:操作系统版本检查带来的兼容性问题 第 2 页:更好的版本检查 第 3 页:检查操作系统的特性 第 4 页:托管代码检查操作系统特性 文本 Tag: Visual Studio 微软 windows 7 【IT168 专稿】 “帅哥,你有没有年薪 100 万?” “Of cause,当然,sure!” “不好意思,我只跟年薪 100 万以下的人聊天。” 以上只是一个笑话,这位妹妹嫌富爱穷很是让人肃然起敬。在软件世界,也 有很多应用程序有着嫌富爱穷, 喜旧厌新的“美德”。他们只能在旧的操作系统 上运行,面对新的操作系统,它们死活不愿意正常运行,不是默默无语地退出就 是给个“不支持操作系统”的提示。妹妹们嫌富爱穷可以,应用程序们喜旧厌新 就不好了。 操作系统版本检查带来的兼容性问题 无论是普通用户还是应用程序开发人员,在他们将应用程序运行到新的操 作系统时, 他们遇到的最普遍的应用程序兼容性问题是,应用程序检查操作系统 版本失败 所带来的应用程序对新操作系统的不支持。操作系统版本检查本来是 为了确保应用程序所需要的某些操作系统特性确实存在, 有了这些操作系统特性, 应用程序才可 以正常运行。当操作系统版本检查被滥用的时候,很多兼容性问 题由此而产生。 用户可能会遇到应用程序在加载的时候,悄无声息地退出而没有 任何的信息提示。或 者是,用户可能会看到一个类似“此应用程序必须在 Windows XP 或者是其后的操作系统上运行”的错误提示对话框。但是,事实上 这台计算机的操作系统是 Windows 7,当然是在 Windows XP 之后了。错误或者 说是低劣的版本检查,会给用户带来极大的不便。 通常,应用程序因为操作系统版本检查而失败有两个原因: ? 在版本检查的代码中有 Bug。 在主版本号增加, 次版本号减小的情况下, 例如,将版本号从 5.1 变化到 6.0,或者是某个期望的补丁(service pack)没有 安装,甚至你在运行一个更新的操作系统(例如,从 Windows XP SP3 升级到 Windows Vista SP1),这些情况都会导致版本检查会失败。 ? 应用程序开发人员特意设计的版本检查行为, 阻止应用程序在未经过足够 测试的新操作系统上运行。 但是,我们建议你不要阻止应用程序在更新的操作系 统上运行。 除非最终用户许可协议禁止应用程序在更新的操作系统上使用,否 则应用程序不应在操作系统版本号增加的情况下无法运行。 如果应用程序无法运 行,则必须向用户 发送消息,并向日志写入一条消息,然后正常退出。 当一个应用程序在“不兼容”的 Windows 版本上运行的时候, 它可能会显示 一个错误 消息,也可能悄无声息地退出或者是无法正常工作。通常,如果我们 解决了版本检查的问题,它就可以正常工作。当遇到这种问题,最终用户或者是 IT 管理员可以 简单地使用 Windows 7 的 XP 兼容模式,或者是微软所提供的应 用程序兼容性工具集(Application Compatibility Toolkit (ACT)),对应用程 序进行操作系统版本“欺骗”,让应用程序认为他还是运行在较旧的 Windows 版本上,从而解决版本检查的问题,让应用程序在 Windows 7 上正常运行。这 里需要注意的是, 兼容模式仅仅适用于使用非托管代码进 行的操作系统版本检查, 对于托管代码中利用 Environment.OSVersion 或者是通过 P/Invoke 使用 GetVersionEx 进行的操作系统版本检查并不适用。 图 1 Windows 7 的兼容模式 更好的版本检查 识别当前操作系统版本并不是一个确定某 些操作系统特性是否存在的最好 方法。但是,如果你的应用程序无法检查某项特性是否存在, 唯一的办法就是通 过版本检查来确定应用程序的兼容性。当你确定要进行版本检查的时候, 可以考 虑下面的实例代码。 对于非托管代码的应用程序,你必须确保你的版本检查能够应 对更新版本 的 Windows 操作系统。请一定记住:不能阻止版本变化!在下面这段代码中,我 们 使用了 GetVersionEx 来获取操作系统的版本。如果主版本号大于 5(包括 Windows Vista,Windows Server 2008 R2 和 Windows 7),版本检查将通过,如 果等于 5,那么次 版本号应该大于或者等于 1(包括 Windows XP,Windows Server 2003)。 #include #include void main() { OSVERSIONINFO osvi; BOOL bIsWindowsXPorLater; // 初始化 OSVERSIONINFO 结构体 ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); // 获得当前操作系统版本 GetVersionEx(&osvi); // 通过版本号,判断是否是 XP 及其以后的操作系统 bIsWindowsXPorLater = ( (osvi.dwMajorVersion > 5) || ( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion >= 1) )); if(bIsWindowsXPorLater) printf("当前系统满足要 求.\n"); else printf("当前系统不满足要求.\n"); } 在非托管代码中, 我们还可以使用 VerifyVersionInfo 来检查操作系统版本, 以实现对操作系统版本的最小要求(Windows XP SP2)。例如: #include // 检查操作系统最小版本需求 // 如果当前的操作系 统版本在 Windows XP SP2 或者以后(例如,Windows Vista, Windows 7), // 这个函数就 返回 TRUE BOOL TestMinimumOSRequirement() { OSVERSIONINFOEX osvi; // 初始化 OSVERSIONINFOEX 结构体. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); osvi.dwMajorVersion = 5; osvi.dwMinorVersion = 1; // Windows XP osvi.wServicePackMajor = 2; // Service Pack 2 osvi.wServicePackMinor = 0; // 初始化比较条件 // ULONGLONG VER_SET_CONDITION(ULONGLONG dwlConditionMask, // DWORD dwTypeBitMask, BYTE dwConditionMask) ULONGLONG comparisoninformation = 0; BYTE conditionMask = VER_GREATER_EQUAL; // 需要大于或者等于,这里 可以根据需要灵活设置 VER_SET_CONDITION(comparisoninformation, VER_MAJORVERSION, conditionMask); VER_SET_CONDITION(comparisoninformation, VER_MINORVERSION, conditionMask); VER_SET_CONDITION(comparisoninformation, VER_SERVICEPACKMAJOR, conditionMask); VER_SET_CONDITION(comparisoninformation, VER_SERVICEPACKMINOR, conditionMask); // 进行版本比较 return VerifyVersionInfo( &osvi, VER_MAJORVER SION | VER_MINORVERSION | VER_SERVICEP ACKMAJOR | VER_SERVICEPACKMINOR, comparisonin formation); } 相对于非托管代码,托管代码的版本检查就简单的多了。我们可以使用 Version 对象的==, !=, <=, <, >, >=操作符来跟 Environment.OSVersion.Version 对象进 行比较, 以执行正确的版本检查。 例如: static void Main() { // 这段代码将检查当前 系统是否是 Windows XP 或之 后的操作系统 if (Environment.OSVersion.Version < new Version(5, 1)) { MessageBox.Show("本应用程序需要运行在 Windows XP 或以后的操作系 统.", "不兼容的操作系统", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // 符合版本要求,继续运 行 Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault (false); Application.Run(new MainForm()); } 检查操作系统的特性 有一些应用程序需要某些特殊的操作系统特性支持才能正常运行, 所以对 操作系统版本有特殊的要求。 但是如上所述,检查操作系统的版本并不是一个确 保 操作系 统拥有某项特性的最好方法。这是因为操作系统可能有新的特性添加 进来,相比于 使用 GetVersionEx 来检查操作系统平台或者是版本号,更有效的 方法 是直接检查某项特 性本身是否存在。例如,微软打算将 Windows 7 的两个 新特性 Direct2D/DirectWrite 和 Ribbon API 引入到 Windows Vista 中,这样, 虽然 Windows Vista 的主版本号没有变化, 但是却拥有了新的特性。当我们的应 用程序在判断操作系统是否具有 Ribbon 特性而决定是 否使用更加绚丽的用户界 面时, 果仅仅根据版本号来做出判断, 如 就错过了操作系统所 提供的新特性了。 如果可以, 在这些特性不可用的情况下, 你的应用程序也应该能够继续 运行, 虽然减少了功能或者是降低了性能。 非托管代码检查操作系统特性 对于 Win32 应 用程序,我们可以使用下面这些技术来检查操作系统特性: ? 如果一个库没有加载到你的应 用程序,可以使用 LoadLibrary()加载这个 库。如果你对一个已加载的库(例如, kernel32.dll)中的新函数感兴趣,可以调 用 GetModuleHandle()获得这个模块的句柄。 ? 使用 GetProcAddress()获得函数的指针。如果 GetProcAddress()返回 NULL,则表示这个函 数不存在,我们需要提供一些备选 方案 气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载 。将这个指针转化 为合适类型的函数指针就可以使 用操作系统的特性了。对于一些特殊的函数,虽 然他们确实存在,但是可能会返回一个“尚 未实现”的错误。 针对这种情况, 需要特殊处理。 在 Windows 2000 中,有一个函数 SetWaitableTimer 可以设置比较精确的计 时器,下面的代码就演示了如何检查操作系统是 否具有这项特性: // 定义函数指针类型 typedef BOOL (WINAPI *SetWaitableTimerExProc)( __in HANDLE hTimer, __in const LARGE_INTEGER *lpDueTime, __in LONG lPeriod, __in PTIMERAPCROUTINE pfnCompletionRoutine, __in LPVOID lpArgToCompletionRoutine, __in PREASON_CONTEXT WakeContext, __in ULONG TolerableDelay ); LARGE_INTEGER liDueTime; liDueTime.QuadPart = 0; nt period = 1000; unsigned int tolerance = 1000; HANDLE hTimer = // Get timer handle REASON_CONTEXT reasonContext = {0}; reasonContext.Version = 0; reasonContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; reasonContext.Reason.SimpleReasonString = L"MyTimer"; // 获得已经加载模块的句柄 HMODULE hKernel32Module = GetModuleHandle(_T("kernel32.dll")); if (hKernel32Module == NULL) return FALSE; // 获得函数地址 SetWaitableTimerExProc pFnSetWaitableTimerEx = (SetWaitableTimerExProc) ::GetProcAddress(hKernel32Module, "SetWaitableTimerEx"); // 检查函数是否存在 // 也就是判断某些特性是否存在 if (pFnSetWaitableTimerEx == NULL) return FALSE; // 调用函数 if (!pFnSetWaitableTimerEx(hTimer, &liDueTime, period, NULL, NULL, &reasonContext, tolerance) { // 处理错误 } 这样, 我们就可以直接检查操作系统是否具有某项特性,不 会因为版本号而 错过一些非常有用的操作系统特性了。 托管代码检查操作系统特性 对于托 管代码, 我们可以通过 P/Invoke 包装 Win 32 API 从而对其实现调用。 例如: /// /// 通过 P/Invoke 包装 Win32 API 函数 /// internal class Win32 { [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr CreateWaitableTimerEx(IntPtr securityAttrs, string timerName, TimerFlags timerFlags, SyncObjAccessFlags desiredAccess); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CancelWaitableTimer(IntPtr hTimer); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr handle); [DllImport("kernel32.dll", SetLastError = true)] public static extern uint WaitForSingleObject(IntPtr handle, uint timeout); /// /// 激活计时器. 这个函数可 以工作在 Windows 2000 及其以 后的操作系统 /// /// /// /// /// /// /// /// [DllImport("kernel32.dll", SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetWaitableTimer(IntPtr hTimer, ref long dueTime, int period, IntPtr pCompletionRoutime, IntPtr completionRoutineContext, [MarshalAs(UnmanagedType.Bool)] bool resume); /// /// /// /// /// /// /// 通过制定允许的延迟激活计时器 这是 Windows 7 新引入的函数 /// /// /// /// /// [DllImport("kernel32.dll", SetLastError= true)] public static extern bool SetWaitableTimerEx(IntPtr hTimer, ref long dueTime, int period, IntPtr pCompletionRoutime, IntPtr completionRoutineContext, ref ReasonContext wakeContext, uint tolerableDelay); } 现在,我们就可以使用这些 Win 32 API 函数了, 如果操作系统不存在某项 特性,则会抛出异常,通过处理 EntryPointNotFoundException 和 DllNotFoundException 异常,判断特性是否存在,并在不存在的情况下提高备 用方案。 // 使用 Win 32 API 函数 public void Start(long dueTime, int period) { ReasonContext rc = new ReasonContext(); rc.Version = 0; rc.Flags = 1; rc.SimpleReasonString = "MyTimer"; try { // 首先尝试使用 Windows 7 所引入的增强 版本 SetWaitableTimerEx if (!Win32.SetWaitableTimerEx(_hTimer, ref dueTime, period, IntPtr.Zero, IntPtr.Zero, ref rc, 5000)) throw new Win32Exception(Marshal.GetLastWin32Error(), "SetWaitableTimerEx 设置计时 器失败."); IsCoalescingtimer = true; } catch (EntryPointNotFoundException) { // 捕获异常,也就是当前操作系统不是 Windows 7,没有 SetWaitableTimerEx 这一特 性 IsCoalescingtimer = false; // 提供备用方案,调用以前就有的版本 SetWaitableTimer if (!Win32.SetWaitableTimer(_hTimer, ref dueTime, period, IntPtr.Zero, IntPtr.Zero, false)) throw new Win32Exception(Marshal.GetLastWin32Error(), "SetWaitableTimer 设 置计时器 失败."); } _waiterThread = new Thread(WaiterThreadProc); _waiterThread.Name = "Waiter thread for WaitableTimer"; _waiterThread.Start(); } VS2010 与 Win7 共舞: 与数据重定向 UAC 责任编辑:胡铭娅作者:IT168 陈良乔 【内容导航】 ? ? ? 2009-09-21 第 1 页:都是 UAC Virtualization 惹的祸 第 2 页:如何获取正确的文件路径 第 3 页:托管代码 第 4 页:禁用 UAC Virtualization 文本 Tag: Visual Studio 微软 .NET windows 7 【IT168 专稿】回想当年微软高调发 布 Windows Vista 的时候,突出的兼容 性问题成为其在推广时遇到的最大阻力, Windows Vista“出师未捷身先死”, 让 从而成为继 Windows Me 之后微软最失败的操作系统。 有鉴 于此,微软在进行 Windows 7 的开发的时候,将应用程序兼容性放在了前所未有的高度,提 前两年 就开始为 Windows 7 进行各种兼容性测试,同时在 Windows 7 上提供了 Windows XP 虚拟模式,最大程度地保证应用程序可以平滑地过渡到 Windows 7,从这些我 们都可以看出微软的良苦用心。 但是,操作系统的改变,必然会带来一些应用程序兼容性的问题,保持应用 程序的良好兼容新,不 仅仅是微软自家的事情,作为应用程序的开发者,我们 也有不可推卸的责任。你的应用程序是否能够与 Windows 7 良好兼容?这是摆在 每个程序员面前的一个问题。下面我们就以一系列文章,来介绍一下 Windows 7 有了什么新的变化?这些变化会带来那些应用程序兼容性问题?如何让应用程序 与 Windows 7 保持兼容? 很多程序员在设计和实现应 用程序的时候,为了图方便和省事,都有向应 用程序所在的目录“Program Files”,Windows 目录或者操作系统根目录(尤其 是 C:\)写入数据文件的习惯。另外,这些人也习惯于用注册表 HKLM/Software 下的 键值来保存一些数据,比如应用程序的配置参数等等。应用程序一直都工 作的很好,直到万恶的 UAC 的出现。在 Windows 7 中,这些人会发现他们所要创 建的文件没有相应的位置被创建,注册表键值没有被修改。他们问“到底怎么回 事?我的应用程序运行正常并没有报错,但是所创建 的文件怎么就不见了呢?在 其他操作系统上都工作的好好的啊?” 都是 UAC Virtualization 惹的祸 从 Windows Vista 开始,当然也包括 Windows 7,因为 UAC 机制的引入,操 作系统的标准普通用户被限制访问一些核心文件,文件夹和注册表键值。当我们 开发的应用程序试图向这些地方写入数据的时候, 会被重新定向到其他操作系统 认为比较安全的位置。 大多数时候,对于普通用户和应用程序开发人员来说这都 是透明的,并没有给我们带来什么不便。但是在有些时候,事情并非如此。数据 重新定向可能会导致一些奇怪的现象: ——在应用程序中,你写入数据到“Program Files”目录,虽然应用程序 执行正确,没有错误值返回,但是在这个目录下你却找不到你刚刚写入的文件。 ——你的应用程序修改了注册表键值, 但是你在注册表相应的位置却看不到 更新。 ——当你关闭或者启用 UAC 后,你的应用程序找不到某些文件了,原来存在 的文件凭空消失了。 在 Windows Vista 之前,我们通常都是以管理员身份来运行应用程序的。 这样,应用程序就可以自由地对操作系统相关的目录或者注册表键值进行读写。 当我们以普通用户 身份运行这些应用程序时,就会出现这样或者那样无法访问 的错误。Windows Vista 为了减少这种错误,改善对于普通用户的应用程序的兼 容性,同时又不失去其安全性,就利用 UAC Virtualization(UAC 虚拟化访问) 这种机制, 将应用程序的写操作(包括文件和注册表操作)重新定向到了一个预先 在用户的配置文件中定义的目录。 Virtualization 分为文件 Virtualization UAC 和注册表 Virtualization。例如,当一个普通用户运行一个应用程序尝试写 入 数据到 C:\Program Files\Contoso\Settings.ini 时,这个写入操作将被重新 定向到 C:\Users\Username\AppData\Local \VirtualStore\Program Files\Contoso\settings.ini。这就是为什么我们在写入的时候没有任何错误, 但是在相应的目录下找不到我们创建的文件。 同样的, 对注册表 HKLM\Software 的写入操作也会被重新定向到 HKCU\Software\Classes\VirtualStore。下图 1 展示了整 个 UAC Virtualization 的流程。 图1 UAC Virtualization 的流程 这里需要注意的是,UAC Virtualization 仅作用于 32 位的应用程序对系 统文件/目录、注册表的读写。64 位程序、非交互式程序、模拟程序(Processes that impersonate)、内核调用者、Manifest 中含有 requestedExecutionLevel 属性的可执行文件不包含在 Virtualization 的作用范围内。 如何获取正确的文件路径 UAC Virtualization (UAC 虚拟化访问) 只是为了帮助现有的应用程序与 Windows Vista 或者 Windows 7 保持兼容, 减少应用程序错误而设计的。 Windows 为 7 而全新设计的应用程序,不应该再向一些敏感的系统目录或者注册表位置写入 数据。 同时也不应该借助虚拟化技术为一些不正确的应用程序 行为提供补救方案, 这 无异于饮鸩止渴。当更多的应用程序移植到 Windows 7 之后,微软可能在未 来版本的 Windows 取消对 UAC 虚拟存储的支持。例如,64 位应用程序是禁用虚 拟存储的。 在为 Windows 7 新开发应用程序时,我们应该始终开发与标准用户权限相适 应的应用程序, 而不要指望总是在管理员权限下运行你所设计的应用程序。 同时, 更多地在普通用户权限下测试你的应用程序, 而不是在管理员权限下测试你的应 用程序。 当我们那些在 Windows 7 之前设计的应用程序遇到 UAC Virtualization 问 题的时候,我们需要从新设计我们的代码,将文件写入到合适的位置。在改善既 有代码,使之可以与 Windows 7 兼容的时候,我们应该确保以下几点: ——在运行的时候, 应用程序只会将数据保存到每个用户预先定义的位置或 者是%alluserprofile% 中定义的普通用户拥有访问权限的位置。 ——确定你要写入数据的“已知文件夹”(Knownfolders)。通常,所有用户 共用的公共数据文件应该写入到一个全局的公共的位置, 这样所有用户都可以访 问到。而其它数据则应该写入每个用户自己的文件夹。 1 公共数据文件包括日志文件,配置文件(通常是 INI 或者 XML 文件),应用 程序状态文件,比如保存的游戏进程等等。 2 而属于每个用户的文档, 则应该保持在文档目录下,或者是用户自己指定 的目录。 ——当你确定合适的文件保存位置后,不要在代码中明文写出(Hard-code) 你选择的路径。 为了更好地保持兼容性,我们应该采用下面这些 API 来获得操作 系统“已知文件夹(Knownfolders)”的正确路径。 1 C/C++非托管代码: 使用 SHGetKnownFolderPath 函数,通过指定“已知文 件夹”的 KNOWNFOLDERID 作为参数来获得正确的文件夹路径。 ? ? FOLDERID_ProgramData –所有用户都可以访问的应用程序数据适合 放置在这个目录下。 FOLDERID_LocalAppData – 每个用户单独访问的应用程序数据适合 放置在这个目录下。 FOLDERID_RoamingAppData – 每个用户单独访问的应用程序数据 适合放置在这个目录下。 与上面一个目录不同的是,放置在这个目录下 的文件会随着用户迁移, 当一个用户在同一个域中的其他计算机登录的时 候,这些文件会被复制到当前登录的机器上, 就像用户随身携带的公文 包一样。 下面这段代码演示了在非托管代码中如何调用 SHGetKnownFolderPath 函数 获得正确的文件保存路径: #include "shlobj.h" #include "shlwapi.h" //? #define AppFolderName _T("YourApp") #define DataFileName _T("SomeFile.txt") // 构造一个数据文件路径 // dataFilePath 指向一个长度为 MAX_PATH,类型为 TCHAR 的字符串数值 // hwndDlg 是消息对话框的父窗口句柄 // 当有错误发生的时候用于显示错误提示 // includeFileName 用于表示是否在路径后面扩展文件名 BOOL MakeDataFilePath(TCHAR *dataFilePath, HWND hwndDlg, BOOL includeFileName) { // 初始化工作 memset(dataFilePath, 0, MAX_PATH * sizeof(TCHAR)); PWSTR pszPath = NULL; // SHGetKnownFolderPath 函数可以返回一个已知文件见的路径, // 例如我的文档(My Documents),桌面(Desktop), // 应用程序文件夹(Program Files)等等。 // 对于数据文件来说,FOLDERID_ProgramFiles 并不是一个合适的位 置 // 使用 FOLDERID_ProgramFiles 保存所有用户共享的数据文件 // 使用 FOLDERID_LocalAppData 保存属于每个用户自己的文件 (non-roaming). // 使用 FOLDERID_RoamingAppData 保存属于每个用户自己的文件 (roaming). // 对于“随身文件”(Roaming files), // 当一个用户在一个域中的其他计算机登陆的时候, // 这些文件会被复制到当前登录的机器上,就像用户随身携带的公文 包一样 // 获取文件夹路径 if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &pszPath))) // 错误的做法: if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFiles, // 0, NULL, &pszPath))) { // 提示错误 MessageBox(hwndDlg, _T("SHGetKnownFolderPath 无法获取 文件路径"), _T("Error"), MB_OK | MB_ICONERROR); return FALSE; } // 复制路径到目标 变量 _tcscpy_s(dataFilePath, MAX_PATH, pszPath); ::CoTaskMemFree(pszPath); //错误 的做法: _tcscpy_s(dataFilePath, MAX_PATH, _T("C:\\")); // 在路径后面扩展应用程序 所在文件夹 if (!::PathAppend(dataFilePath, AppFolderName)) { // 提示错误 MessageBox(hwndDlg, _T("PathAppend 无法扩展路径"), _T("Error"), MB_OK | MB_ICONERROR); return FALSE; } // 是否添加文件名 if (includeFileName) { // 在路径 后扩展文件名 if (!::PathAppend(dataFilePath, DataFileName)) { // 提示错误 MessageBox(hwndDlg, _T("PathAppend 无法扩展文 件名"), _T("Error"), MB_OK | MB_ICONERROR); return FALSE; } } return TRUE; } 2 托管代码: 使用 System.Environment.GetFolderPath 函数,通过指定我们想 要获取的“已知文件夹”为参 数,从而获取相应的文件夹的正确路径。 ? ? Environment.SpecialFolder.CommonApplicationData – 所有用户 都可以访问的应用 程序数据适合放置在这个目录下。 Environment.SpecialFolder.LocalApplicationData – 每个用户 单独访问的应用程序数据适合放置在这个目录下。 Environment.SpecialFolder.ApplicationData – 每个用户单独访 问的应用程序数据适合 放置在这个目录下。这是“随身文件夹”。 下面这段代码展示了如何在托管代码中获取正确 的文件路径: internal class FileIO { private const string AppFolderName = "YourApp"; private const string DataFileName = "SomeFile.txt"; private static string _dataFilePath; /// /// 构建路径 /// static FileIO() { // Environment.GetFolderPath 返回一个“已知文 件夹”的路径 // Path.Combine 可 以合并两个路径成一个合法的路 径 // ? _dataFilePath = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.Program Files), AppFolderName); //错误的做法: //_dataFilePath = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.CommonApplicat ionD ata), AppFolderName); // 扩展文件名 _dataFilePath = Path.Combine(_dataFilePath, DataFileName); } public static void Save(string text) { // 检查要保存的字符串是否为空 if (String.IsNullOrEmpty(text)) { MessageBox.Show("字符串为空,无法保 持.", "空字符串", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } try { // 获取文件保存的路径 string dirPath = Path.GetDirectoryName(_dataFilePath); // 检查文件夹是否存在 if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dir Path); // 创建文件夹 } catch (Exception ex) { MessageBox.Show(ex.Message, "文件夹创 建失败", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } try { // 保存字符串到文件 StreamWriter sw = new StreamWriter(_dataFilePath); try { sw.Write(text); } finally { // 关闭文件 sw.Close(); } } catch (Exception ex) { MessageBox.Show(ex.Message, " 文件写入 失败", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // ? } } 如果上 面的方法都不适合你,你还可以使用环境变量获取相应 的文件夹路径: ? %ALLUSERSPROFILE% – 所有用户都可以访问的应用程序数据适合放 置在这个目录 下。 %LOCALAPPDATA% – 每个用户单独访问的应用程序数据适合放置在 这个目录下。 - (Windows Vista 或者 Windows 7) ? ? %APPDATA% – 每个用户单独访问的应用程序数据适合放置在这个目 录下。这是“随身 文件夹”。- (Windows Vista 或者 Windows 7) 禁用 UAC Virtualization 凡事都没有绝对。 如果因为一些特殊的要求(众所周知,客户的要求 千奇百 怪, 无奇不有), 我们一定要向 “Program Files”目录写入数据, 这时该怎么办呢?面对这种极其特殊的情况,我们可以在 应用程序的 Manifest 禁用 UAC Virtualization,取消其对数据写操作的重定向。在 项目 属性中,我们设置启用 UAC(Enable User Account Control),并且 在 UAC Execution Level 中设置请求管理员权限。这样,应用程序在启动 的时候, 就会向用户请求管理员权限, 当 应用程序获得管理员执行权限后, 当然可以向任意目录写入数 据, Virtualization 也就 不会起作用了。 UAC ? 图2 通过 Manifest 禁用 UAC Virtualization 对于 64 位应用程序,本身是不具备 UAC Virtualization 机制的,所 以根本不存在禁用的问题。当我们在 64 位应用程序中尝 试向“Program Files”等敏感目录写入数据时,就会遇到一个“拒绝访问”的错误: // 测 试文件夹是否存在 BOOL IsDirectoryExists(TCHAR *dirName) { WIN32_FILE_ATTRIBUTE_DATA dataDirAttrData; if (!::GetFileAttributesEx(dirName, GetFileExInfoStandard, &dataDirAttrData)) { DWORD lastError = ::GetLastError(); if (lastError == ERROR_PATH_NOT_FOUND || lastError == ERROR_FILE_NOT_FOUND || lastError == ERROR_NOT_READY) return FALSE; } return (dataDirAttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; } // ? // 获取文件夹路径 //if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, // 0, NULL, &pszPath))) // 错 误的做法: if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, NULL, &pszPath))) { // 提示错误 MessageBox(hwndDlg, _T("SHGetKnownFolderPath 无 法获取 文件路径"), _T("Error"), MB_OK | MB_ICONERROR); return FALSE; } //? // 检查文件夹 是否存在 if (::IsDirectoryExists(dataFilePath)) { // 如果文件夹不存在,则创建文件 夹 if (!::CreateDirectory(dataFilePath, NULL)) { DWORD dwErrorCode = ::GetLastError(); LPCWSTR lpBuffer; // 获取错误信息 FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSE RTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrorCode, // 错误代码 LANG_NEUTRAL, (LPTSTR)&lpBuffer, 0 , NULL ); // 显示错误对话框 MessageBox(hwndDlg, lpBuffer, _T("创建文 件夹错误"), MB_OK | MB_ICONERROR); LocalFree((HLOCAL)lpBuffer); return FALSE; } } 当这段代码执行到创建文件夹的时候, 会遇到一个“拒绝访问”错误: ? 图3 创建文件夹的“拒绝访问”错误 为了避免这个错误,同样的,我们可以通过在项 目属性中设置,使 得 Manifest 中嵌入 UAC 相关的信息,在应用程序启动的时候请求管理 员 权限,就像我 们在运行其他大多数需要管理器权限的应用程序一样。当 应用程序获得管 理员权限后, 这个错误就不存在了。 但是这里必须要指出, 这种做法是不太安全的,能够 避免尽量避免。 VS 与 Win7 共舞:UAC 惹祸 如何进行安 装程序检测, 责任编辑:胡铭娅作者:IT168 陈良乔 【内容导航】 ? ? 2009-09-22 第 1 页:UAC 又惹祸了 第 2 页:Manifest 指明程序运行权限 第 3 页:嵌入式 Manifest 文本 Tag: Visual Studio 微软 windows 7 【IT168 专稿】 “快说,你是不是安装程序?”操作系统问。 “我不是啊,长官。我虽然长得像,但是我真的不是安装程序啊!”,一个 应用程序扮出一副可怜相,胆胆怯怯的回答道。 “不是?那为什么你的程序名中含有 Install?”,操作系统以怀疑的眼光盯 着他, “所有程序名中含有 Install 的应用程序都是安装程序,都必须在执行的 时候都向用户请求管理员权限!” “是是是,长官!”应用程序心中暗喜,操作系统主动给我机会让我请求管 理员权限, 我求之不得呢, 用户早就厌烦 UAC 了, 肯定直接点击“YES”了事啊, 有了管理员权限,我就可以为所欲为啦,偷偷修改个首页先,哈哈哈哈~~~ UAC 又惹祸了 随着 Windows Vista 引入 UAC(User Access Control)机制,默认情况下, 应用程序都运行在普通用户权限下。 虽然微软出于良好的愿望而在 Vista 中引入 UAC 机制, 但是在 Vista 操作系统中,只要系统稍作改变,它就会频繁弹出对 话框来寻求用户的许可,因此它成为了 Vista 中最受痛恨的一个功能。 虽然如 此,Windows 7 还是继承了这一机制并根据用户的反馈做了相应的改进。为了降 低计算机系统的风险,UAC 机制将执行应用程序的用户权限降低了,这就为那些 在 UAC 机制出 现之前所设计的应用程序的执行带来了兼容性的麻烦。这些旧有 应用程序通常都假设以管理员权限运行,在 Windows 7 上,因为 UAC 的存在,这 一假设不成立了, 最终导致应用程序无法正常运行。一些应用程序确实是需要管 理员权限才可以正常运行的,尤其是安装程序,他们需 要向一些需要特殊权限 的区域, 比如“Program Files”或者是注册表的 HKEY_LOCAL_MACHINE 写入内容, 这种情况它们会遇到访问拒绝的错误,或者是数据被 UAC Virtualization 重定 向到其他位置而无法正确执行。 为了解决这个问题,“聪明”的雷德蒙程序员们想出了一个办法:安装程序 检测。从 Windows Vista 开始,当然也包括 Windows 7,操作系统将采用一些启 发式算法来判断应用程序是不是一个安装程序, 也就是在执行的时候这个程序是 否需要请求管理员权限,如果操作系统判断应用程序是一 个安装程序,就会让 它在执行的时候向用户请求获取管理员权限以便让应用程序正确的执行。 操作系统是如何检测的? 所有在 Windows Vista 之前开发的没有 manifest(包括外部的和内部的)的 32 位应用程序都会进行这种启发式的安装程序检测。操作系统会假设这些应用 程序是旧有 的,他们都需要进行安装程序检测以确定这些应用程序是否管理员 权限才能正常运行。 面对这样的应用程序,操作系统的启发式安装检测通常会通 过以下这些途径来 判断一个 32 位应用程序是不是安装程序: ? 文件名包含关键字:”install”, “setup”和”update”等等。 在版本资源的以下字段内包含关键字:厂商(Vendor)、公司名 (CompanyName)、产品名(ProductName)、文件说明(File Description)、初始文 件名(Original Filename)、 内部文件名(Internal Name)、 导出名(Export Name)。 ? 在可执行文件的 manifest 内包含关键字。 ? 在链接到可执行文件的特定 StringTable 中包含关键字。 ? 在链接到可执行文件的资源文件数据包含关键属性。 ? 可执行文件包含特定的字节序列。 如果找到了,操作系统会认为它需要管理员权限才可以正常运行。一个 UAC 保护盾的图标会覆盖在应用程序图标上, 这就表示应用程序在启动的时候会请求 管理员权限以便它可以正确执行。 图 1 安装程序检测 开好车的就一定是好人吗? 虽然这种启发式安装程序检测可以让一个旧有的安装程序能够直接在 Windows Vista 或者 Windows 7 上正常运行。但是,它本身也带来很多问题。一 方面, 一些不需要管理员权限就可以正常运行的应用程序,就因为文件名中含有 了“setup”等相关的文字, 比如,StockUpdater.exe,就被强行在运作的时候 向用户询问请求管理员权限。 这不仅给用户带来不便,同时也无形中降低了系统 的安全性。 甚至用户会因为担心系统受到损害而取消应用程序的运行。 另一方面, 一些恶意软件也会凭借这种机制的漏洞,将自己伪装成安装程序,从而轻松地, 正大光明地获取管理员权限而给系统安全带来隐患。 与此相反的是,很多自定义的安装程序,并不使用 Microsoft Windows Installer(MSI)技术,并且没有按照启发式安装程序检测的规则命名,这样,操 作系统就不会认为它是一个安装程序。 由于启发式安装程序检测不承认它是安装 程序, 所以用户在运行这些应用程序的时候, 它并不会自动请求管理员权限运行, 这就可能导致应用程序在向一些敏感位置写入数据时遇到拒绝访问错误, 安装过 程半途而废。 就像电影中的台词,“开好车的就一定是好人吗?”同样的,“姓 Setup 的 就一定是安装程序吗?” 想要你就说嘛 既然微软的启发式安装程序检测这么弱智,那么我们只好自己麻烦一下, 简单地为应用程序添加一个 Manifest 文件,告诉操作系统我们到底是不是安装 程 序,是否需要请求管理员权限。如果应用程序的源代码无法获得,我们只需 要在应用程序的同一目录下为应用程序添加一个外部 Manifest 文件。这个 Manifest 文件的名字应该是应用程序文件名加上“.manifest”后缀。例如, StockUpdater.exe 的 Manifest 文件的文件 名应该是 StockUpdater.exe.manifest。 这个 Manifest 文件应该指明此应用程序在运行的 时候是否需要申请管理员权限,或者是 否在普通用户权限下就可以正常运行。 一个典型的外部 Manifest 文件如下所示: 其中,requestedExecutionLevel 属性就表示我们的应用程序正确执行所需 要的权限。它有这样几个可选值: ? asInvoker – 它表示应用程序需要跟创建者相同的权限运行。也就是跟 Windows Explorer 相同的权限运行,通常就是普通用户权限。这个应用程序不 是安装程序并且不会被启发式安装程序检测错误地标记。 ? requireAdministrator – 它表示这个应用程序需要管理员权限才能正常 运行。(它可能是一个安装程序。) ? highestAvailable – 它表示这个应用程序应该以尽可能高的权限运行。 如果当前用户是一个管理员用户,那么它就等同于 requireAdministrator 。如 果当前用户是普通用户,那么它会在运行的时候请求管理员权限。 当我们为应用程序添加外部 Manifest 文件后,操作系统会根据 Manifest 文件中的定义为应用程序制定相应的 UAC 规则。例如,我们可以通过外部 Manifest 文件,为一个自定义的安装程序在执行时请求管理员权限。 图2 嵌入式 Manifest 自定义安装程序 如果我们可以获得应用程序的源代码,我们可以在 Visual Studio 2010 中 为应用程序添加嵌入式的 Manifest 来完成相同的工作。在项目属性中,我们设 置“Linker->Manifest File->UAC Execution Level”,就可以指定应用程序执 行所需要的权限。 图 3 设置 UAC 执行权限等级 同时, 我们可以将 Manifest 文件作为资源嵌入到应用程序中,这样我们就 只需要一个单独应用程序就可以完成所有工作了,而无需额外的一个 Manifest 文件。 同样的, 在项目属性“Manifest Tool->Input and Output->Embed Manifest” 中设置就可以将 Manifest 文件嵌入到应用程序中。图 4 嵌入式 Manifest 在默认情况下,Visual Studio 2010 所创建的新项目,已经含有了嵌入式 的 Manifest。 有了 Manifest 文件,应用程序就像有了身份证一样,操作系统不会再冤枉 好人了。 VS 与 Win7 共舞:性能计数器进行性能 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 责任编辑:胡铭娅作者:IT168 陈良乔 【内容导航】 ? ? ? 2009-09-07 第 1 页:Performance Counters(性能计数器) 第 2 页:使用系统提供的性能计数器 第 3 页:自定义性能计数器 第 4 页:新建一个性能计数器并对文件复制过程进行监控 ? ? 第 5 页:对性能计数器更新及对复制的文件数监视 第 6 页:利用自定义的性能计数器对复制过程监视 展开全部 文本 Tag: Visual Studio 性能优化 windows 7 【IT168 专稿】作为程序员,谁都希望自己的软件性能优异,运行如飞。但 是当我们在看到自己开发的软件像蜗牛一样慢吞吞地运行, 半天没有反应的时候, 我们常常会有这样一些疑问: “我的系统都在忙些什么?CPU 在干啥?” “为什么我的软件性能表现这么低下?” “哪里才是软件的性能瓶颈?什么代码导致了软件的性能低下?” “软件运行到了什么状态?” 面对这些问题,程序员们都在想,要是有个软件仪表仪表,就像汽车的仪 表盘一样, 能够实时向我们报告系统和软件的运行状态就好了!现在, Windows 在 7 中,程序员们的这个梦想成为了现实。通过 Windows 7 所提供的 Performance Counters,Event Tracing for Windows (ETW) ,Windows Management Instrumentation 以及 Windows Performance Toolkit,我们可以实时地获得系 统和在其上运行的各种软件的性能状态信息,圆满地回答上面这些问题。利用这 些丰富的状态信息,我们可以对应用程序进 行诊断调试,性能分析,找到性能 瓶颈,从而对其进行性能调优,给蜗牛软件插上飞的翅膀。 图1 我不是蜗牛,我是飞牛 关于性能分析的这十八般武器各有所长,这里我们先介绍性能计数器。 Performance Counters(性能计数器) 当我们在开发一些对性能期望较高的软件的时候, 简单高效的性能计数器对 发现软件中的性能瓶颈是很有价值的。 虽然我们可以自己实现简单的性能计数器, 但是, 使用 Windows 操作系统本身所提供的 Performance Counters(性能计数器), 我们可以获得更多得天独厚的优势。 性能监视, Windows NT 引入的一种系统功能。 Windows NT 以后, 是 从 Windows 操作系统总是集成了各种性能监视工具, 它提供有关操作系统当前运行状况的信 息,针对各种性能对象提供了数百个性能计数器。性能对 象,就是被性能计数 器监视的对象,我们通常比较关心的监视对象主要有 Processor、Process、 Memory、 TCP/UDP/IP /ICMP、 Physical Disk 等。 性能计数器通常提供操作系统、 应用程序、服务、驱动程 序等的性能相关信息,以此来分析系统瓶颈和对系统 及应用程序性能进行诊断和调优。 除了针对操作系统本身, 通过性能计数器机制, 我们也可以在应用程序或者是 操作系统组件中向性能监视器(Performance Monitor)报告一些与性能有关的统计信息,以此来查看软件的性能信息,对其进 行诊断和调优。 在 Windows 7 中,微软对性能计数器做了进一步的改进和优化。例如,采 用了新的 2.0 版本的核心模块 API、采用 XML 定义、更加强大的性能、更高的可 扩展性和鲁棒 性、增加了多个系统计时器等等。同时,为了方便系统管理员进 行管理,还增加了 PowerShell 对计时器日志文件的处理功能等等,使得性能计 数器的功能 大大增强。 使用系统提供的性能计数器 很多程序员都精于使用 Windows 系统的性能监视器来查看 CPU 和内存的 使用情况或硬盘信息。 性能监视器就像一个听诊器,能够实时地反映当前系统的 状态信息,以帮助我们判断系统是否运作正常。 图 2 性能监视器 通过性能监视器,我们可以直观地获得系统所提供的各种性能监视器的数 据,了解系统的运行状态。但是,如果我们想在应用程序中以编程的方式获得这 些性能计 数器的信息,该怎么处理呢?实际上,在 Visual Studio 2010 中,微 软为我们提供了很多性能计时器供我们在应用程序中使用。在 Visual Studio 2010 的 Server Explorer(服务器浏览器)中,我们可以看到很多系统提供的性能 计数器。 我们可以在应用程序中简单地以控件的形式使用这些性能计数器,获取 系统的各种消息。 图 3 Visual Studio 提供的性能计数器 在这里, 我们演示一下如何在应用程序中使用这些系统提供的性能计数器。 首先, 我们新建一个基于 Visual C#的 Windows 窗体应用程 序。然后,展开 Server Explorer(服务器浏览器) 中的“Memory”(内存) 节点,然后展开“Available Mbytes”(可用 M 字节)计数器, 将该计数器拖到我们的应用程序中的新 Windows 窗体上。接着,我们在窗体上放置一个计时器并在其属性中启用这个计时器,这 个计时器主要用于定时向性能计数器查询当前可用的内存, 然后将这个值更新到 界面 上。最后,我们用一个 Label 标签来显示性能计数器中查询所得的当前可 用内存数。整个窗体设计如下: 图 4 在应用程序中使用性能计数器 完成 Windows 窗体的设计后,我们就可以在计时器的响应函数中,向我们使 用的性能计数器查询当前可用内存数, 并将这个数值更新到窗体的 Label 文本中: private void timer1_Tick(object sender, EventArgs e) { // 查询性能计数器并更新 Label 文本 label1.Text = performanceCounter1.NextValue().ToString(); } 这样, 当我们编译运行这个应用程序时,就可以通过性能计数器实时地得到 当前系统可用内存数了: 图 5 性能计数器实例 当 然, 这里只是一个非常简单的示例, 在实际的应用程序中, 我们不可能这么简单的使用性能计数器。 但是这个实例却向我们演示了如何向我 们自己的应用程序中添加 系统定义的性能计数器,以监控和查询系统性能对象 的信息。 在实际应用中, 我们可以根据某个系统性能对象的状态而采取某种行动, 比如当系统的可用内存比较少 的时候, 我们可以减少程序的并发线程数;当笔记 本的电池电量较低的时候, 我们甚至可以采取自我保护,提示用户保存当前的工 作成果等等。合理地使用这些系统信息,可以使得应用程序的性能更加优化,交 互更加人性化。 自定义性能计数器 在很多情况下, 除了直接使用系统提供的各种性能计数器获取系统信息之外, 我们还希望可以添加自定义的性能计数器, 以监视我们的应用程序中对性能影响 比较大的某些因素。在 Visual Studio 2010 中,除了使用 Server Explorer(服 务器浏 览器)提供的各种性能计数器之外,实际上,我们还可以通过使用 System.Diagnostics 名字空间中的 PerformanceCounterCategory, CounterCreationDataCollection 和 PerformanceCounter 这些类,完全以代码 的形式为我们自己的应用程序添加自定义的计数器类别和性能计数器。当 PerformanceCounter 和 Windows 性能监视器结合使用时,就像一个医生(你自 己)拿着听诊器(Windows 性能监视器)来诊断你的应用程序 (PerformanceCounter)一 样,随时将你的应用程序的内部情况在性能监视器中 反应出来,应用程序中哪里问题,哪里是性能瓶颈,一目了然。 在这里, 我们以一个复制文件的应用程序为例,使用性能计数器来监视当前 所复制的文件数目以及完成总进度的百分比。 首先, 我们新创建一个基于 Visual C#的 Windows Form 应用程序,并设计窗体如下: 图 6 复制文件应用程序 这里,我们并不关注文件选择、文件复制这些操作是如何完成的,我们把关注的 重点放在如何新建一个性能计数器并对文件复制过程进行监控。 为了便于使 用, 我们新创建一个类 FileCopyPerformanceCounters 来封装相关的自定义性能计 数器。在 Visual Studio 2010 中,我们将这个类实现如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; // 为了使用计数器所需要引入的名字空间 using System.Diagnostics; namespace FileCopier { // 文件复制性能计数器 public static class FileCopyPerformanceCounters { // 计数器成员 // 用于记录已经复制的文件数和当前进度百分比 private static PerformanceCounter totalFilesCounter; private static PerformanceCounter percentDoneCounter; public static void Initialize() { // 创建新的计数器数据并添加到计数器数据集合中 CounterCreationDataCollection counters = new CounterCreationDataCollection(); CounterCreationData counter = new CounterCreationData( "Total Files Copied", "Total number of files copied by the application.", PerformanceCounterTyp e.NumberOfItems32); counters.Add(counter); counter = new CounterCreationData( "% Files Copied", "Percent of files copied in the current operation.", PerformanceCounterTyp e.NumberOfItems32); counters.Add(counter); // 创建新的自定义性能计数器类别 // 这个类别收集之前定义的 计数器数据集合 if (PerformanceCounterCategory.Exists("FileCopier")) PerformanceCounterCategory.Delete("Fi leCopier"); PerformanceCounterCategory.Create( "FileCopier", "Instrumentation of the FileCopier application.", PerformanceCounterCat egoryType.SingleInstance, counters); // 创建新的性能计 数器 // 相应的数据保存在之前定义的计数器数据集合中 totalFilesCounter = new PerformanceCounter( "FileCopier", "Total Files Copied", false); percentDoneCounter = new PerformanceCounter( "File Copier", "% Files Copied", false); } // 更新已经 复制的文件数 public static void UpdateTotalFiles(int totalFiles) { // 更新计数器 的值 totalFilesCounter.RawValue = totalFiles; } // 更新复制进度百分比 public static void UpdatePercentDone(int percentDone) { // 更新计数器的值 percentDoneCounter.RawValue = percentDone; } } } 在这段代码中,我们在类中包装了两 个性能计数器 totalFilesCounter 和 percentDoneCounter, 分别用于记录已经复制的文件 数和复制进度的百分比。在 类的初始化函数中,我们完成了这两个自定义性能计数器的创建 工作。 首先我们创建了两个 CounterCreationData 对象,用于收集记录计数器的值,然 后 将这两个 CounterCreationData 对 象添加到计数器数据集合 CounterCreationDataCollection 中。利用这个数据集合,我们就可以创建新的 计数器类别 了,这表示这个 自定义的计数器类别会收集来自这个数据集合的计 数器数据。最后,我们 再创建自定义的性能计数器 PerformanceCounter,并通 过构造函数的参 数(string categoryName, string counterName),指定这个计 数器将数据保存到什么位置。 完成计数器的创建后, 我们新添加了两个函数用于更新计数器的值,便于我 们的应用 程序调用。至此,我们自定义的文件复制计数器就实现完成了。下面我 们来看看如何在我们 的应用程序中使用这个自定义的性能计数器。 首先, 我们需要在构造函数中完成文件复制 性能计数器类的初始化工作,这 一步通过调用 FileCopyPerformanceCounters 类的 Initialize 函数完成: public MainForm() { this.InitializeComponent(); // 初始化计 数器类 FileCopyPerformanceCounters.Initialize(); } 完成性能计数器的初始化后,我们 就可以在文件复制过程中使 用计数器对文件复制过程进行监视和统计了。在“复制”按钮的 响应函数中,我 们用性能计数器记录文件复制数和完成的百分比: private void BtnCopy_Click(object sender, EventArgs args) { // 获得源目录和目标目录 string source = this.txtSourceDirectory.Text; string dest = this.txtDestinationDirectory.Text; // 检查目录合法性 if (!Directory.Exists(source) || !Directory.Exists(dest)) { MessageBox.Show("源目录或者目标目录不 存在.", "Error"); return; } // 设置进度条 this.btnCopy.Enabled = false; this.progressBar.Value = 0; this.progressBar.Style = ProgressBarStyle.Continuous; // 创建后台复制线程 this.worker = new BackgroundWorker(); // 执行复制 this.worker.DoWork += (o, e) => { string[] files = Directory.GetFiles(source); for (int i = 0; i < files.Length; ++i) { Thread.Sleep(1000); File.Copy(files[i], Path.Combine(dest, Path.GetFileName(files[i]))); this.worker.ReportProgress((i nt)((100.0f * i) / files.Length)); // 用计数器记录已经复制的文件 数 FileCopyPerformanceCounters.U pdateTotalFiles(i); } }; this.worker.WorkerReportsProgress = true; this.worker.ProgressChanged += (o, e) => { this.BeginInvoke((MethodInvoker)deleg ate { progressBar.Value = e.ProgressPercentage; // 用计数器记录文件复制完成的 百分比 FileCopyPerformanceCounters.U pdatePercentDone(e.ProgressPercentage); }); }; this.worker.RunWorkerCompleted += (o, e) => { this.BeginInvoke((MethodInvoker)deleg ate { btnCopy.Enabled = true; progressBar.Style = ProgressBarStyle.Marquee; }); }; this.worker.RunWorkerAsync(); } 在这段代码中,我们通过在文件复制 Lambda 表达 式中调用 FileCopyPerformanceCounters 类的 UpdateTotalFiles 函数,实时地对性能计 数器进行了更新, 对复制的文件数进行了监视。 同样地, 在进度条更新的 Lambda 表达式 中,我们对 当前复制进度的百分比计数器进行了更新。 现在,编译整个项目,并以管理员 身份运行这个应用程序,我们就可以利用自定 义的性能计数器对整个复制过程进行监视了。 当我们选择合适的源目录和目标 目录并进行文件复制的时候, 自定义的性能计数器会记录 当前复制完成的文件数 和复制过程的百分比。然后,利用性能监视器,添加我们自定义的性 能计数器分 类, 我们就可以对整个复制过程进行实时的监视了。 图7 添加自定义的性能计数器 在文件复制过程, 我们可以通过性能监视器动态实时地看到整个复制过程了, 如果在 复制过程中有什么异常, 可以非常直观地在性能监视器中反映出来。这对 于我们对应用程 序进行性能调优有很大的帮助: 图 8 对文件复制过程进行监视 在上面这幅性能监视器的截图中, 我们可以清楚地看到 在文件复制过程中有 一段进度上升的速度不一致, 也就是说复制某个文件用了很长的时间, 影响了应 用程序的性能。 这些信息就指引我们对文件复制过程进行进一步的调试,找到这 个文件复制性能下降的原因。 通过性能计数器,我们可以找到应用程序性能瓶颈的所在,但 是要对问题 进行精确定位,我们还需要更多的信息,这个时候,我们就需要 Event Tracing for Windows (ETW)来帮忙了。至于 ETW 是如何与性能计数器通力合作智斗性能 瓶颈这个纸 老虎的,请听下回分解。
本文档为【巧妙解决兼容性问题】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_003124
暂无简介~
格式:doc
大小:78KB
软件:Word
页数:35
分类:企业经营
上传时间:2017-12-21
浏览量:35