Windows 版 (精华区)
发信人: bali (阿奔), 信区: Windows
标 题: Microsoft Windows 2000 应用程序兼容性(2)
发信站: 紫 丁 香 (Fri May 5 23:33:28 2000), 转信
系统稳定性
Windows NT 小组正在努力进行的另一项工作是确保系统保持长时间的稳定。微软
允许通过分发服务包的形式将各种功能吸纳到 Windows NT 中,这是一个问题。
安装了 Windows NT 的客户和公司在选取服务包时已非常警惕,因为这些东西绝
不仅仅是在更正某些问题。通常情况下它会作出某些更正,然后说“噢,我们在
这里添加了这个特性,在那里添加了那个功能”,这些东西使得系统无法达到所
预期的稳定性。
按照常规策略,服务包只能包含对错误的更正,不能有其他内容。如果我们认为
操作系统中需要新增某些重要的特性和功能,我们将推出 Windows 2000 的 .x
版本。您会得到类似 Windows NT 5.1、5.2 等此类内容,另外还有针对 Window
s NT 的三个不同版本发布的服务包。我们将继续努力保持每一平台功能的完整性
,其中甚至涉及到 QFE、错误检查和 hot fix。每次有了新的功能集或新特性,
都会发布新版操作系统。
我们在微软所进行的最后一项工作是确保了解随同产品发布了哪些组件,我们强
烈建议您遵照执行。我们正在尽最大可能地减少不同产品中发布的组件的数量。
如果某一特定组件需要与另一特定组件协同工作,我们会尽量将这两个组件一同
发布。对于所有这些能够重新分发的组件,我们将定出发布的结构顺序。
并行 DLL
如果需要将组件由全局共享组件或 DLL 更改为新的并行 DLL,需要对 DLL 进行
某些改动。这种对 DLL 自身的改动是必须的。您必须得声明:“我所设计的组件
将允许同时运行多个版本。”
为了使某一组件成为真正的并行组件,首先需要对 DLL 进行重命名,并且更改可
能存在于 OCX 控件、COM 对象中的所有 GUID。这种重命名的工作只需进行一次
,就能保证您获得一个并行运行的新 DLL,该 DLL 将不再是全局共享。
DLL 被重命名后,应用程序会将其安装到自主管理的目录中,而不会安装到系统
目录。这样,应用程序开发人员就可以说:“我已对带有这一 DLL 的特定版本的
产品进行了全面测试,而且我还确认在我再次进行测试之前,这一 DLL 不会进行
升级。”这就给了做为开发员的您足够的信心:任何人无法使用共享组件扰乱您
的应用程序,导致系统崩溃并将我们带回到 DLL Hell。
如果您是作为用户使用这些组件,您可以在自己的目录(而不是系统目录)中注
册一个相对路径。这样会加载一个落在系统某处的本地版本,而不是全局副本。
我们对 LoadLibrary 功能进行了修改,从而确保:如果应用程序以相对路径注册
了一个组件,我们也始终以相对路径完成加载,而不管这一组件是位于系统目录
中,还是运行在别的什么地方。由此可以确保您获得用以测试应用程序的那一份
副本。
隔离的应用程序
我们还修改了 LoadLibrary 代码,以便支持 DLL 重定向。由此管理员可以将 D
LL 的加载过程重定向到某一位置,并由本地目录加载 DLL。经过这一处理后,您
的 DLL 就可以处于隔离状态。假设某一大单位的某个人要测试他们能否采用您的
应用程序,他们安装了另一应用程序,然后测试这两者能否协同工作。他们发现
结果是不能,管理员就开始查找这两个应用程序在何处,在哪一组件上发生了冲
突。找到这一组件后,管理员从同时使用这两个程序的雇员的角度进行了考虑,
提取 DLL(或包含对象的 OCX),并将其置于应用程序所在的目录。然后管理员
创建了一个名为 foo.exe 的文件,其后又加上 .local。如果调用 LoadLibrary
,LoadLibrary 发现这里有一个 foo.exe.local 文件,它会首先加载应用程序目
录中的文件,而不会考虑应用程序用于 LoadLibrary 调用本身的特定路径。这种
方式有助于人们区分需要同一组件的不同版本的多个应用程序,使所有这些应用
程序运行于同一系统中。
Windows 文件保护
为了确保系统的稳定性和平台的可靠性,第一步就是保障系统不会遇到任何 DLL
Hell 问题。我们希望无论发生了什么事情,系统仍然能够运行,能够引导,即
用户可以对系统的稳定性有充分的信心。
有了 Windows 文件保护 (WFP),如果应用程序试图更改某一系统文件,Windows
2000 会将其恢复原状。对部分功能,应用程序会安装并说:“瞧,我需要这个
DLL 的新版本…”去实现某一功能,或根本这就是一个错误的应用程序,不会正
确地执行版本检查功能。Windows 2000 将检查这一点,会发现文件已改动。如果
Windows 2000 发现这是一个系统文件,并声明“我不允许改动这一文件”,它
会将文件恢复原状。
如果要升级那些已被系统锁定的文件,只能采用 Windows NT 小组所发放的几种
文件替换机制:服务包、QFE 或 hot fix。它们能够实现对系统文件的替换,而
其他应用程序却不能。
举个例子,mfc42.dll 是我们锁定的一个文件。通过语言组自身将不能再升级该
DLL,只有 Windows NT 小组能够更改系统中的这一文件。如果 C 程序设计人员
需要升级他们的 DLL(而且假定他们希望在 Windows 2000 上市之后而下一版的
Windows NT 发布之前进行升级),只能采用并行的组件版本功能。
大多数 *.sys、*.dll、*.exe 和 *.ocx 文件以及几个字体文件在保护之列。
如此还存在几个兼容性问题。首先,防病毒程序必须认识并正确处理 Windows 文
件保护功能,再在此基础上进行应用程序的备份和恢复;不能简单地对这些文件
进行复制、备份和恢复。因为您没有系统所支持的文件替换机制,如果您这样做
,将取消 Windows 文件保护功能。
为了防止人们进行这类操作,我们在系统中添加了几个 API。
WFP API
第一个 API 是 SFCGetNextProtectedFile。可用这一 API 可以获得所有受保护
或能保护的文件的清单。您可以以一个空值开始重复调用这一 API,以获得受保
护文件的列表。
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_NOI
NTEGRALHEIGHT |
LBS_USETABSTOPS,
0,20,rt.right,rt.bottom-40,
hWnd,
NULL,
hInst,
NULL);
}
else
SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
ZeroMemory(&pfd,sizeof(PROTECTED_FILE_DATA));
iCount = 0;
while ( g_pfnSfcGetNextProtectedFile(NULL, &pfd) != 0 )
{
// 为此“ANSI 应用程序”将 WCHAR 转换到 ANSI
iLen = WideCharToMultiByte(CP_ACP,NULL,pfd.FileName, wcslen(pfd.
FileName),
szFileName,260,NULL,NULL);
szFileName[iLen] = '\0';
SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szFileName);
iCount++;
}
}
另一个更为直接的 API 是 SfcIsFileProtected。该 API 能更为便捷地为绝大多
数应用程序直接调用,并回答以下问题:“看看这一文件,它是否受到保护?”
但是请记住,它需要指向这一文件的完整路径。您不能只是简单地指定 NTS.sys
,而是需要给出到达 NTS.sys 所处位置的路径。如果您将这一文件名传递给 AP
I,它会说:“是的,这个文件已受到保护”,或“这不是一个受保护的文件”。
在您进行任何备份或恢复操作时都需要使用该 API。如果您希望进行任何安装设
置,或可能会更新某一系统文件,您都可以调用这一 API。如果您希望将某一目
标文件置于系统目录中,在进行这一操作之前,您需要调用这一 API,以避免取
消 Windows 文件保护功能。下一版的 Windows Installer(与 Windows 2000 一
同发布)将在复制文件之前进行检查,因此不会意外地启动 WFP。
BOOL WINAPI SfcIsFileProtected (IN HANDLE RpcHandle,IN LPCWSTR ProtFil
eName);
//
// 此函数使用“文件”打开对话框,以便从用户获取文件名并检查其是否受保护
。
void CheckFileForProtection(HWND hWnd)
{
OPENFILENAME OpenFileName;
CHAR szFile[MAX_PATH] = "\0";
CHAR szSystem32[MAX_PATH];
strcpy( szFile, "");
ZeroMemory(&OpenFileName, sizeof(OPENFILENAME));
// 填充 OPENFILENAME 结构以支持模板和挂接。
OpenFileName.lStructSize = sizeof(OPENFILENAME);
OpenFileName.hwndOwner = hWnd;
OpenFileName.hInstance = hInst;
OpenFileName.lpstrFile = szFile;
OpenFileName.nMaxFile = sizeof(szFile);
OpenFileName.lpstrTitle = "Select a File";
OpenFileName.Flags = OFN_FILEMUSTEXIST;
if (g_pfnSHGetFolderPath != NULL )
g_pfnSHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, NULL, szSystem32)
;
else
szSystem32[0] = '\0';
OpenFileName.lpstrInitialDir = szSystem32;
// 调用公共对话函数。
if (GetOpenFileName(&OpenFileName))
{
// 检查文件
WCHAR wzFileName[260];
int iLen;
iLen = MultiByteToWideChar(CP_ACP,NULL,szFile, strlen(szFile), w
zFileName, 260);
wzFileName[iLen] = '\0';
if (g_pfnSfcIsFileProtected(NULL, wzFileName) == TRUE )
{
MessageBox(hWnd,"Is Protected", szFile, MB_OK);
}
else
MessageBox(hWnd,"Is NOT Protected", szFile, MB_OK);
}
组件检查
我们发现导致无法在 Windows 2000 上安装应用程序的另一个原因是组件检查功
能。显然,我们操作系统的每一版本都是由多个不同的组件组成。这些组件包括
TAPI、MAPI、Microsoft DirectX(R) 等等。我们发现应用程序会对什么组件处
于什么位置,以及是否存在某一组件作出自己的假设。应用程序会因为甲组件存
在而假定乙组件也存在,因为某一组件的版本 2 存在而认定另一组件的版本 3
也必然存在。如果您需要用到某一组件,则一定要检查系统中是否存在这一组件
,以及它是否位于正确的级别。
除此之外,开发人员还会假设 Windows NT 中没有 DirectX。有时候某一语句为
真,而且应用程序在编制时也认为它可能为真。然后在对其进行检查时,因假设
Windows 2000 没有 DirectX,它就会声明:“噢,我无法运行。”但是 Windo
ws 2000 带有 DirectX,这一假设是不正确的。
我们遇到的另一问题是硬编码问题,应用程序如果假定组件所处的位置是错误的
,则根本无法对路径进行硬编码。
另一个例子是,Windows 2000 现在包含 TAPI(最新版本为 TAPI 3.0)和 Dire
ctX(最新版本为 7),而不是默认情况下的 MAPI。过去总是认为如果操作系统
是 Windows NT,其中应该包含 MAPI。但现在的情况不同了。如果您使用某一组
件,则一定要检查这一组件是否存在,而不能根据平台或组件等作出任何假设。
在错误的位置安装文件
我们在安装方面所发现的另一问题是人们放置文件的位置出错。如果您对别人置
于某处的某些文件进行升级,那没关系;只要将它们置于用户所希望的位置即可
。但有些文件在第一次安装时,需要将其置于“Program Files”目录下。
注意 不一定是 C:\Program Files。有时候是这一目录,有时候则不是。比如
在我的机器上就不是。我一般都将 C 分区留给 Windows 98,而将 Windows NT
分区安装到其他分区。
我们希望您尽可能不要将文件置于 Windows 目录,或 System32 等任一子目录。
虽然这样做本身没有什么坏处,过去我们也常常希望您如此,因此我们无法完全
禁止这一做法,但我们现在希望系统中的所有文件更加有组织一些。我们已经发
现,用户对 System32 目录下成百上千的文件深感头痛,对这些文件一无所知,
因此也无法删除他们。您每每听到人们这样说:“每年,您都要清理系统,并从
头开始重装 Windows”。卸载和清理程序已成为软件市场上最流行的应用程序。
我们希望系统保持有序状态,并使操作过程更加简化。
可能的话,我们甚至希望您将某些共享组件也写到别处;Microsoft Visual Bas
ic(R) 在这方面就是一个比较好的例子。许多应用程序都会用到这一组件。旧版
Visual Basic 总是安装在系统目录中,而现在我们将它置于 Program Files 的
一个指定文件夹内。
如果要查找文件应该放置的位置,可以调用一个名为 SHGetFolderPath 。SHGet
FolderPath 是 Windows 2000 的一个组成部分。它在第二版的 Windows 98 中也
已经存在。如果您发现系统中没有这一 API,您可以对S HFolder.dll 进行分发
。该 DLL 将解开这一名为 SHGetFolderPath 的新 API 的外壳。这一 API 了解
每一特殊文件夹在系统中所处的位置。您可以在 SDK 中找到该 API。它是一个非
常长的清单,其中列出了您能够考虑选用的每一个文件夹。
需要注意,旧版的平台只支持以下四个 CSLID。如果您要查找某一特定目录,发
现该目录不存在,SHGetFolderPath 能够为您创建该目录,条件是您已指定了这
一操作,但在 Windows 95 等后向平台中,这一过程只能对以下四个 CSIDL 有效
:
CSIDL_PERSONAL
CSIDL_APPLICATIONDATA
CSIDL_MYPICTURES
CSIDL_LOCAL_APPLICATIONDATA
以下代码示例显示了 SHGetFolderPath 的应用方式;它是一个非常简单易用的
API。有一点需要注意:如果希望代码在所有平台上都能运行(包括 Windows 20
00 等自带 SHGetFolderPath 的平台和 Windows 95 等不带 SHGetFolderPath 的
平台),应用程序必须与 SHFolder.dll 中的实施过程进行动态链接。
// SHGetFolderPath can work everywhere SHFOLDER is install
ed.
HMODULE hModSHFolder = LoadLibrary("shfolder.dll");
if ( hModSHFolder != NULL )
{
(*(FARPROC*)&g_pfnSHGetFolderPath =
GetProcAddress(hModSHFolder,"SHGetFolderPathA"
));
}
else
g_pfnSHGetFolderPath = NULL;
}
if (g_pfnSHGetFolderPath != NULL )
g_pfnSHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, NULL, szSystem32)
;
else
szSystem32[0] = '\0';
OpenFileName.lpstrInitialDir = szSystem32;
--
在时间面前,没有永恒
------一个热爱自由的人
※ 来源:.紫 丁 香 bbs.hit.edu.cn.[FROM: 202.108.67.114]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:213.133毫秒