📄 csdn_文档中心_microsoft windows 2000 应用程序兼容性.htm
字号:
{
dwSize = sizeof(dwCSDVersion);
if (RegQueryValueEx(hKey, "CSDVersion",
NULL, NULL, (unsigned char*)&dwCSDVersion,
&dwSize) == ERROR_SUCCESS)
{
fMeetsSPRequirement = (LOWORD(dwCSDVersion) >= dwSPMajor);
}
RegCloseKey(hKey);
}
return fMeetsSPRequirement;
}
return TRUE;
}
}
return FALSE;
}
//
// 此示例适用于 Windows 2000 和更新版本
//
BOOL bIsWindowsVersionOK(DWORD dwMajor, DWORD dwMinor, DWORD dwSPMajor )
{
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = dwMajor;
osvi.dwMinorVersion = dwMinor;
osvi.wServicePackMajor = dwSPMajor;
// 设置条件掩码。
VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL );
VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL );
VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL );
// 执行测试。
return VerifyVersionInfo(&osvi,
VER_MAJORVERSION | VER_MINORVERSION
| VER_SERVICEPACKMAJOR,dwlConditionMask);
}
</CODE></PRE>
<P>首先,需要检查主版本号。如果当前操作系统的主版本号高于所需主版本号,则不需要进行任何检查,直接向下运行即可。如果主版本号相等,则以同样方式检查次版本号。最后再检查服务包号。在我们获得发布的某一版本时,就可以说“没关系,我们不在乎是
Service Pack 3、4 还是
5”。如果主版本号或次版本号有所增长,系统同样可以处理。我们查找了所有要检查版本信息的应用程序,发现它们将分别检查每一组件。它们在运行中会说“噢,主版本号是
5,我只要求 4,不错。次版本号是 .0,很好,但 Service Pack 是 0,我需要的是
3”。很明显,Windows 2000 还没有推出 SP3,所以这种检查版本信息的方法是错误的。</P>
<P>检查 Windows NT 的版本号时需要注意以下细节:检查版本号和服务包信息的方法有三种。第一种是获取
<B>GetVersionEx</B>
的返回值,然后检查“szCSDVersion”字符串。实际的服务包号嵌入在该字符串的某一位置。分析这一串字符实在是比较繁琐,而且您必须始终牢记它是否进行了本地化,这是很难做到的。这不是一种最好的方式。如果系统运行的是
Windows NT
4.0,您只需检查以下注册键值,其中有一个数字是服务包号。提取这一数字,并进行一个“等于”或“大于”比较即可:</P>
<P
class=indent>HCLM\System\CurrentControlSet\Control\Windows\CSDVersion</P>
<P>如果您运行的是 Windows 2000 或更高版本,则仍可以使用
<B>GetVersionEx</B>,但需要将其传递到 <B>OSVERSIONINFOEX</B>
结构,而不是正在使用的<B>OSVERSIONINFO</B> 结构。考虑到起始处操作员成员的大小,Windows 2000
会将其视为一个较大的结构,并且给出一个新的字段(服务包、主版本、次版本),作为进行比较时所用的整数。如果您还是使用
<B>VerifyVersionInfo</B>,您会发现这是最方便的一种方式。</P>
<H3>DLL 版本检查</H3>
<P>在检查 Windows 版本的同时,我们还发现了另一个与版本相关的问题,即用户不检查 DLL 的版本。无论 DLL
是系统目录中的 Microsoft DLL,还是您自己的
DLL,在将其复制到系统前,都必须进行版本检查。检查的目的是防止将旧版的 DLL 复制到新版的上面。</P>
<P>必须确认在用户自己的 DLL
中已添加了版本信息,以便进行检查。进行这一操作非常重要,它能够避免出现各种麻烦。不要试图更改系统目录中的
DLL,甚至不要考虑对系统 DLL 进行升级或降级,或覆盖同一 DLL。系统 DLL 是由 Windows 2000 在
CD 或服务包中提交的、位于系统目录中的 DLL。</P>
<P>如果您打算编制新的 Windows 2000 应用程序,则需要使用 Windows Installer,它能够为您检查
DLL 的版本。只要说明需要将某一特定的 DLL 置于特定的目录中,即可发现 Windows Installer
在为您进行版本检查工作。您不需要再处理这一小块代码。</P>
<H3>DLL Hell</H3>
<P>如果没有正确地检查 DLL 版本,结果毋庸质疑会发生 DLL Hell。我肯定不需要向您解释什么是 DLL
Hell,您一定在这上面花费了不少时间。因为我最初曾参与开发 ctl3d.dll,对这些东西就象熟悉我的邻居一样。</P>
<P>我们花费了几年的时间,努力去突破那些影响 DLL
正常工作的障碍,我们最终认定应用程序开发商无法保持后向兼容性。每个人都在朝这方面努力,这一目标很伟大,但却无法实现。实际上,DLL
不可能保持后向兼容性。结果就是:某一应用程序如果要正常结束,必须取决于某一 DLL 的某个特定版本,而另一应用程序则取决于该
DLL 的另一版本,因为这两个应用程序在这一共享组件(即共享 DLL)方面发生了冲突,导致无法共存于同一系统中。</P>
<P>到目前为止,我们仍然认为对于 Windows 应用程序来说,DLL
共享功能是一个非常良好和重要的组成部分。我们同样认为 DLL 能为您提供的功能还是非常有价值的。问题是如果不能测试 DLL
的版本,而跨应用程序对 DLL 进行全局共享会带来非常多的问题。</P>
<H4>并行 DLL</H4>
<P>在 Windows 2000 中,我们开始实施某些称之为并行 DLL
的内容。我们希望您开发的应用程序也开始向并行版本策略靠拢。在 Windows 2000 中,我们采取了一些预防性措施,以减少
DLL Hell。我们要考虑的第一位的事情是无论安装了何种应用程序,都要保证系统处于受保护状态,保持其完整性。随后我们将讨论
Windows 文件保护。</P>
<P>我们要做的另一件事情是实现组件的并行,同时希望应用程序供应商也这样做。我们针对的是微软的组件;至于您自己的组件,我们也建议您这样做。我们将采用并行版本功能,而对于您自己目前正在进行全局共享的组件,我们也希望您能采用并行版本功能。</P>
<H4>系统稳定性</H4>
<P>Windows NT
小组正在努力进行的另一项工作是确保系统保持长时间的稳定。微软允许通过分发服务包的形式将各种功能吸纳到 Windows NT
中,这是一个问题。安装了 Windows NT
的客户和公司在选取服务包时已非常警惕,因为这些东西绝不仅仅是在更正某些问题。通常情况下它会作出某些更正,然后说“噢,我们在这里添加了这个特性,在那里添加了那个功能”,这些东西使得系统无法达到所预期的稳定性。
</P>
<P>按照常规策略,服务包只能包含对错误的更正,不能有其他内容。如果我们认为操作系统中需要新增某些重要的特性和功能,我们将推出
Windows 2000 的 .x 版本。您会得到类似 Windows NT 5.1、5.2 等此类内容,另外还有针对
Windows NT 的三个不同版本发布的服务包。我们将继续努力保持每一平台功能的完整性,其中甚至涉及到 QFE、错误检查和
hot fix。每次有了新的功能集或新特性,都会发布新版操作系统。</P>
<P>我们在微软所进行的最后一项工作是确保了解随同产品发布了哪些组件,我们强烈建议您遵照执行。我们正在尽最大可能地减少不同产品中发布的组件的数量。如果某一特定组件需要与另一特定组件协同工作,我们会尽量将这两个组件一同发布。对于所有这些能够重新分发的组件,我们将定出发布的结构顺序。</P>
<H3>并行 DLL</H3>
<P>如果需要将组件由全局共享组件或 DLL 更改为新的并行 DLL,需要对 DLL 进行某些改动。这种对 DLL
自身的改动是必须的。您必须得声明:“我所设计的组件将允许同时运行多个版本。”</P>
<P>为了使某一组件成为真正的并行组件,首先需要对 DLL 进行重命名,并且更改可能存在于 OCX 控件、COM
对象中的所有 GUID。这种重命名的工作只需进行一次,就能保证您获得一个并行运行的新 DLL,该 DLL
将不再是全局共享。</P>
<P>DLL
被重命名后,应用程序会将其安装到自主管理的目录中,而不会安装到系统目录。这样,应用程序开发人员就可以说:“我已对带有这一
DLL 的特定版本的产品进行了全面测试,而且我还确认在我再次进行测试之前,这一 DLL
不会进行升级。”这就给了做为开发员的您足够的信心:任何人无法使用共享组件扰乱您的应用程序,导致系统崩溃并将我们带回到 DLL
Hell。</P>
<P>如果您是作为用户使用这些组件,您可以在自己的目录(而不是系统目录)中注册一个相对路径。这样会加载一个落在系统某处的本地版本,而不是全局副本。</P>
<P>我们对 <B>LoadLibrary</B>
功能进行了修改,从而确保:如果应用程序以相对路径注册了一个组件,我们也始终以相对路径完成加载,而不管这一组件是位于系统目录中,还是运行在别的什么地方。由此可以确保您获得用以测试应用程序的那一份副本。</P>
<H4>隔离的应用程序</H4>
<P>我们还修改了 <B>LoadLibrary</B> 代码,以便支持 DLL 重定向。由此管理员可以将 DLL
的加载过程重定向到某一位置,并由本地目录加载 DLL。经过这一处理后,您的 DLL
就可以处于隔离状态。假设某一大单位的某个人要测试他们能否采用您的应用程序,他们安装了另一应用程序,然后测试这两者能否协同工作。他们发现结果是不能,管理员就开始查找这两个应用程序在何处,在哪一组件上发生了冲突。找到这一组件后,管理员从同时使用这两个程序的雇员的角度进行了考虑,提取
DLL(或包含对象的 OCX),并将其置于应用程序所在的目录。然后管理员创建了一个名为 foo.exe 的文件,其后又加上
.local。如果调用 <B>LoadLibrary</B>,<B>LoadLibrary</B> 发现这里有一个
foo.exe.local 文件,它会首先加载应用程序目录中的文件,而不会考虑应用程序用于
<B>LoadLibrary</B>
调用本身的特定路径。这种方式有助于人们区分需要同一组件的不同版本的多个应用程序,使所有这些应用程序运行于同一系统中。</P>
<H3>Windows 文件保护</H3>
<P>为了确保系统的稳定性和平台的可靠性,第一步就是保障系统不会遇到任何 DLL Hell
问题。我们希望无论发生了什么事情,系统仍然能够运行,能够引导,即用户可以对系统的稳定性有充分的信心。</P>
<P>有了 Windows 文件保护 (WFP),如果应用程序试图更改某一系统文件,Windows 2000
会将其恢复原状。对部分功能,应用程序会安装并说:“瞧,我需要这个 DLL
的新版本…”去实现某一功能,或根本这就是一个错误的应用程序,不会正确地执行版本检查功能。Windows 2000
将检查这一点,会发现文件已改动。如果 Windows 2000
发现这是一个系统文件,并声明“我不允许改动这一文件”,它会将文件恢复原状。</P>
<P>如果要升级那些已被系统锁定的文件,只能采用 Windows NT 小组所发放的几种文件替换机制:服务包、QFE 或
hot fix。它们能够实现对系统文件的替换,而其他应用程序却不能。 </P>
<P>举个例子,mfc42.dll 是我们锁定的一个文件。通过语言组自身将不能再升级该 DLL,只有 Windows NT
小组能够更改系统中的这一文件。如果 C 程序设计人员需要升级他们的 DLL(而且假定他们希望在 Windows 2000
上市之后而下一版的 Windows NT 发布之前进行升级),只能采用并行的组件版本功能。</P>
<P>大多数 *.sys、*.dll、*.exe 和 *.ocx 文件以及几个字体文件在保护之列。</P>
<P>如此还存在几个兼容性问题。首先,防病毒程序必须认识并正确处理 Windows
文件保护功能,再在此基础上进行应用程序的备份和恢复;不能简单地对这些文件进行复制、备份和恢复。因为您没有系统所支持的文件替换机制,如果您这样做,将取消
Windows 文件保护功能。</P>
<P>为了防止人们进行这类操作,我们在系统中添加了几个 API。</P>
<H4>WFP API</H4>
<P>第一个 API 是 <B>SFCGetNextProtectedFile</B>。可用这一 API
可以获得所有受保护或能保护的文件的清单。您可以以一个空值开始重复调用这一 API,以获得受保护文件的列表。</P><PRE><CODE>
BOOL WINAPI SfcGetNextProtectedFile
(IN HANDLE RpcHandle,IN PPROTECTED_FILE_DATA ProtFileData );
//
// 此功能将列出受保护文件
//
void ListProtectedFiles(HWND hWnd)
{
HWND hwndList;
PROTECTED_FILE_DATA pfd;
int iCount;
char szFileName[260];
int iLen;
RECT rt;
hwndList = GetWindow(hWnd,GW_CHILD);
if ( hwndList == NULL )
{
GetClientRect(hWnd, &rt);
// 第一次创建“列表”控件
hwndList = CreateWindow("LISTBOX", NULL,
WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_NOINTEGRALHEIGHT |
LBS_USETABSTOPS,
0,20,rt.right,rt.bottom-40,
hWnd,
NULL,
hInst,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -