荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Jobs (温少), 信区: WinNT
标  题: Microsoft Windows 2000 Application Compatibility
发信站: BBS 荔园晨风站 (Sun Nov 28 09:44:10 1999), 站内信件

Microsoft Windows 2000 Application Compatibility
Kyle Marsh
Microsoft Corporation

November 1999

Summary: Discusses some of the issues that make applications incompatible on
Microsoft? Windows? 2000. (23 printed pages) Covers:

Introduction
Setup and Installation Issues
Windows 2000 Compatibility Issues
Application Stability Issues
Windows Platform Differences

Introduction
Over the past few months, I've been given the task of finding out what the
application compatibility issues are for the Windows 2000 operating system.
What I'm actually going to talk about here are the issues that make
applications incompatible on Windows 2000. Nobody really cares about what makes
an application compatible.

I've been working with the Windows 2000 test team, who have been testing
hundreds of applications over the past several months. We've been documenting
what makes applications work and not work on Windows 2000. The issues that we'
ve found fall into four categories:

Applications that don't install on Windows 2000. This is our biggest issue by
far. There's nothing terribly different about the way applications install on
Windows 2000; the problem is that applications are just not allowing themselves
to install on the new version of the operating system.


Changes we have made in the operating system that will affect how applications
run. Whenever the Microsoft Windows NT? development team was faced with a
choice between making the system more reliable or more robust as a platform and
keeping applications compatible, they always chose to make it more reliable.
One of the main goals of the development of Windows 2000 was to make the system
more reliable as a platform. Unfortunately, some of the changes that had to be
made to get us there have caused applications to be incompatible on Windows
2000.


Changes we've made to the operating system that should not cause applications
to be incompatible, but do break some applications.


Applications that have been targeted too closely at the Windows 9x platform.
Because we've been targeting Windows 2000 to upgrade a lot of users from
Windows 9x, we have been testing Windows 9x applications, moving them onto
Windows 2000. We've been finding that some applications are targeted too
closely to Windows 9x.
Setup and Installation Issues
The first category we'll cover are setup and installation issues; far and away
the most common problem is when we can't get applications to install on Windows
2000. Actually, the most common reason for applications not installing is that
Windows 2000 is version 5.0 of Windows NT.

The test team tests applications in a number of ways. They will test an
application by installing the application on a Windows 2000-based system. The
team will also test the application by installing the application on Windows NT
4.0 or Windows 95, and then upgrading the system to Windows 2000.

When we take a clean machine, put Windows 2000 on it, and install the
application, we're finding much lower compatibility numbers than we do from the
upgrade scenarios, either from Windows NT 4.0 or from Windows 98.

Version Checking
The number one reason applications are not installing on Windows 2000 is that
they're not dealing with the version number correctly. We're finding that a lot
of applications do something like what the following sample code is doing. They'
ll go ahead and call GetVersionEX, and then they'll write up an "if" statement
saying "If it's version 3, then I probably shouldn't install because I don't
work without the new shell, and if it's version 4, then I can install and set
things up." The problem occurs when it's version 5; there's no other part of
this "if" statement. So, we're finding a lot of things that are just falling
off the floor because they're not letting themselves install because the
version number is 5.0.

if (osvi.dwMajorVersion == 3)
   {
   // do this
   }
else if (osvi.dwMajorVersion == 4)
   {
   // do that
   }

The test team has gone ahead and lied to a lot of these applications. In
earlier builds there was a way for us to tweak the return value from
GetVersionEx. We would tweak that return, lie to the application, say it was
version 4.0, and then it would go ahead and install and work just fine. There
are a few applications that are purposely preventing installation on Windows
2000. It is understandable for virus scanners or other low-level utilities to
be tied to a particular operating system version. However, these applications
will display a message saying so. The applications we found would either just
not install, or not run correctly, without a message to the user at all.

How would you do the version-number checking correctly? On Windows 2000 we're
adding a new API: VerifyVersionInfo, which will go ahead and check the major
version, the minor version, and the service pack in a hierarchy. When the new
version of the operating system comes around, your application will still
install and run on it. There are actually many more options and ways to use
VerifyVersionInfo, but to just check "Is this operating system up to what I
need to do?" you could call using these three flags, and check the major
version, the minor version, and the service pack. You would say something like
"My application needs Windows NT version 4.0, with SP 2," and then ask
VerifyVersionInfo "Is this OS that I'm running on up to that standard?" It
would return either a true or a false.

VerifyVersionInfo(&osvi,
      VER_MAJORVERSION |
      VER_MINORVERSION |
      VER_SERVICEPACKMAJOR,
      dwlConditionMask);

If you do check versioning this way, you will meet the Windows 2000 application
specification, which basically says, "You must install on a new version of the
operating system, whenever it's available."

The problem with using VerifyVersionInfo is that it's now only available on the
Windows 2000 platform. In order to check the versioning on older platforms such
as Windows 95, you're going to have to use GetVersionEx. If you look at the
following example code, you'll see that it's doing the same thing that
VerifyVersionInfo would do. We're checking the major version, the minor version,
 and the service pack via a hierarchy.

BOOL bIsWindowsVersionOK(DWORD dwMajor, DWORD dwMinor, DWORD dwSPMajor )
   {
   OSVERSIONINFO osvi;
   // Initialize the OSVERSIONINFO structure.
   //
   ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
   GetVersionEx((OSVERSIONINFO*)&osvi);
   // First the major version
   if ( osvi.dwMajorVersion > dwMajor )
      return TRUE;
   else if ( osvi.dwMajorVersion == dwMajor )
      {
      // Then the minor
      if (osvi.dwMinorVersion > dwMinor )
         return TRUE;
      else if (osvi.dwMinorVersion == dwMinor )
         {
         // OK, better check the Service Pack
         if ( dwSPMajor && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT )
            {
            HKEY   hKey;
            DWORD   dwCSDVersion;
             DWORD   dwSize;
            BOOL   fMeetsSPRequirement = FALSE;

            if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                         "System\\CurrentControlSet\\Control\\Windows", 0,
                         KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
               {
               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;

   }

//
//This sample is for Windows2000 and later versions
//
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;
   // Set up the condition mask.
   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
);
   // Perform the test.
   return VerifyVersionInfo(&osvi,
                             VER_MAJORVERSION | VER_MINORVERSION |
VER_SERVICEPACKMAJOR,
                          dwlConditionMask);
     }

First, you'll need to check the major version. If the major version of the
running operating system is higher than the major version we need, we don't
need to check anymore; we can just go ahead and run. If the major versions are
equal, we check the minor versions in the same way. Finally, we check the
service pack. Anytime we get a point release, this will allow you to say "Yes,
this is good, we don't need to worry about whether this is Service Pack 3, 4,
or 5." If the major release or the minor release has gone up, it's all right.
We're finding all kinds of applications that will check the versioning, and
they'll check each component separately. They'll go ahead and say "Yes, it's
major version 5, I only needed 4, and that's good. It's minor version .0, and
that's good, but it's not Service Pack 0, and I need Service Pack 3." Obviously
there is no Service Pack 3 for Windows 2000 yet, so that's not the right way to
check the versioning.

One detail on checking the version number on Windows NT: There are three
different ways to check for version number, for the service pack information.
First, you can get the return value out of GetVersionEx, and check the string
"szCSDVersion." Somewhere embedded in that string is the actual service pack
number. It's a real pain to parse through, and you can't always remember if it'
s localized or not; it's really not the best way to do it. If you're running on
Windows NT 4.0, you should just check for the following registry key, and in
the registry key is just the service pack as a number, so you can take it and
do an "equal" or "is greater than" comparison:

HCLM\System\CurrentControlSet\Control\Windows\CSDVersion

If you're running on Windows 2000 or later, you can still use GetVersionEx, but
pass it the OSVERSIONINFOEX structure rather than the OSVERSIONINFO structure
you've been using. Windows 2000 will see that it's the bigger structure because
of the size of the operator member at the beginning, and will give you a new
field梥ervice pack, major version, and minor version梐s integers with which you
can compare. Again, if you use VerifyVersionInfo, you'll find that it's the
easiest way.

DLL Version Checking
Along with checking the versions of Windows, another version problem we're
seeing is that people are not checking the versions of DLLs. You always have to
check the version of a DLL before you copy it onto a system, whether it's a
Microsoft DLL in the system directory, or one of your own DLLs. You always need
to check the version so you're not copying an older version of a DLL on top of
a newer one.

You must make sure you've actually added versioning information to your DLLs so
you can check them. It is very important that you do that to avoid all kinds of
nasty problems. If you are trying to change a DLL in the system directory, you
have to stop now. Don't even think about upgrading or downgrading a system DLL,
or copying over the same one. A system DLL is a DLL that is sitting in the
system directory that has been delivered by Windows 2000 on the CD or in a
service pack.

If you're going to be making new Windows 2000 applications, you're going to be
using Windows Installer, which always checks the version of a DLL for you. Just
by saying that you want to put a specific DLL in a particular directory, you'll
find that the Windows Installer will always do version checking for you. That's
a bit of code that you won't have to deal with anymore.

DLL Hell
The consequence of not checking the DLL version number correctly, of course,
has been DLL Hell. I'm sure I don't need to tell you about DLL Hell. I'm sure
you've spent a lot of time there. Because I was one of the people originally
working on ctl3d.dll, it's really kind of my neighborhood.

After years of banging our heads against the walls trying to make DLLs work
right, we've determined that the application providers simply cannot maintain
backward compatibility. Everybody has tried very hard to do that, and it's a
great goal, but it doesn't work. In practice, it is impossible to have backward
compatibility for your DLLs. The result: an application invariably ends up
relying on a particular version of a DLL to work, and some other application
depends on another version of that DLL, and the two applications can't live on
the same system together because they are colliding over this shared component,
this shared DLL.

Now, we still think the ability to share DLLs is a good and important part of
an application for Windows. We also think that all of the benefits you can get
from a DLL are still valuable. The problem is that globally sharing a DLL
across applications without being able to test the version has led to way too
many problems.

Side-by-side DLLs
With Windows 2000 we're starting to implement something we're calling
side-by-side DLLs. What we want your applications to do is to start moving to a
side-by-side versioning strategy. On Windows 2000 we're taking a few proactive
steps to help reduce DLL Hell. The first thing is, we're going to make sure the
system stays protected, remains intact, no matter what applications get
installed. We'll discuss Windows File Protection shortly.

Another thing we're going to do, and we want application vendors to do as well,
is to enable your components to be side-by-side. We're doing this for Microsoft
components; we're also suggesting that you do it for your own components. We're
going to adopt side-by-side versioning, and we want you to adopt side-by-side
versioning for all of your components that are currently globally shared.

System stability
Another thing that the Windows NT team is committed to is insuring that the
system stays stable over time. One of our problems at Microsoft is that we've
allowed all kinds of features to creep into the Windows NT platform via service
pack distributions, and it has made consumers and corporations that install
Windows NT leery of picking up a service pack, because it's more than just a
few fixes. It's often a few fixes, and "Oh, we've added this feature here, and
we've added this functionality there," that's made the system less stable than
it really should have been.

As a policy, service packs will only contain bug fixes and nothing more. If we
have new features and functions that we see as important changes to the
operating system, we're going to ship that as a point release to Windows 2000.
You would have something like Windows NT 5.1, 5.2, and so on, with service
packs released for each of the three different versions of Windows NT out
there. We'll continue to make sure we keep each platform fully functional, as
far as QFEs, bug checking, and hot fixes are concerned. Every time there's a
new set of functionalities or new features, it will mean a new release of the
operating system.

The final thing we're doing at Microsoft, and we strongly encourage you to do
as well, is making sure we know which components are shipped with which
products. We are working to minimize the amount of components that get shipped
around in different products. If a particular component works with another
particular component, we'll always work to ship those together. We'll structure
how we're shipping out all of these redistributable components.

Side-by-Side DLLs
When you need to change your component from being a globally shared component
or DLL to a new side-by-side DLL, you'll need to make some changes to your DLL.
You're going to have to make some changes to the DLL itself. You're going to
have to say, "I'll design my component in such a way that it will allow
multiple versions to run at the same time."

To move a component toward becoming a true side-by-side component, you'll first
need to rename the DLL itself, and also change any GUIDs that the OCX control,
the COM object, might have. You only need to do this renaming once, and then
you'll effectively have a new DLL that you can be sure runs side by side,
rather than being globally shared.

Once a DLL is renamed applications can install it into their own managed
directories instead of installing it into the system directory. This means an
application developer can say "I've fully tested my product with a particular
version of this DLL, and I'm going to make sure this DLL doesn't get upgraded
on me until I have tested it again." This allows you as a developer to make
sure no one else is going to mess with your application with a shared component
that causes a crash and brings us back into DLL Hell.

When you use one of these components as a consumer, you register a relative
path in your own local directory, instead of in the system directory. It will
load a local copy instead of a global copy that's off in the system somewhere.

We've modified the LoadLibrary functionality to ensure that if an application
has registered a component with a relative path, we always load it with a
relative path, no matter whether one exists in the system directory or running
somewhere else. This will ensure that you're getting the copy that you tested
your application with.

Isolated applications
We have also modified the LoadLibrary code to support DLL redirection. This
allows an administrator to redirect a load of a DLL in one location to load the
DLL from the local directory. Your DLL can now become isolated after the fact.
Suppose someone in a large corporate site is testing to see whether they can
use your application. They've installed another application and they want to
see if the two can work together. They find that the two cannot, and the
administrator then needs to go through the work of discovering where, with
which component, the two applications are colliding. Having found that
component, and understanding that employees need to use both applications, the
administrator takes the DLL (or the OCX containing the object), and puts it in
the directory with the application. The administrator then creates a file with
a foo.exe name, and follows that with a .local. When LoadLibrary is called, and
LoadLibrary finds that there's a foo.exe.local file, it will load the
components from the applications directory first, regardless of the particular
path that the application has used to the LoadLibrary call itself. This will
help people to roll out multiple applications that need different versions of a
component, allowing all of them to run on the same system.

Windows File Protection
The first step in making the system more robust, making the platform more
reliable, is to make sure the system itself doesn't fall into any DLL Hell
issues. We want to make sure that no matter what happens, the system still runs,
 still boots梩hat users can rely on the stability of the system.

With Windows File Protection (WFP), if an application tries to change one of
the system files, Windows 2000 is going to change it right back. Your
application may install and say, "Oh look, I need a new version of this DLL,"
for some functionality, or it may be a really bad application and doesn't do
the version checking correctly. Windows 2000 will then look for this and see
that the file has been changed. Once Windows 2000 sees that it is a system file
and says, "I can't allow that file to be changed," it will change it back
again.

The only way to upgrade a file that has been locked down by the system is
through a few supported file replacement mechanisms sent out by the Windows NT
team: service packs, QFEs, or hot fixes. They'll have a way to do a system file
replacement, but no other application will.

For example, one of the files that we've locked down is the mfc42.dll. That DLL
is no longer upgradeable by the languages group itself, only the Windows NT
group will be allowed to change that DLL on your system. The only way for the C
people to update their DLL (and I assume they do want to update it between the
time when Windows 2000 ships and the next version of Windows NT ships) will be
by adopting side-by-side component versioning.

Most *.sys, *.dll, *.exe and *.ocx files and a few font files are under
protection.

There are a couple of compatibility issues. First, antivirus applications must
be aware and work with Windows File Protection, as do backup and restore
applications; you can't just copy, back up, and restore these files. Because
you are not a system supported file replacement mechanism, if you do that you'
ll be kicking off Windows File Protection.

To help people stop doing this, we've added a couple of APIs to the system.

WFP APIs
The first API is SFCGetNextProtectedFile. You can use this API to give you a
list of all files that are protected or could be protected. You'd start with a
null and call it repeatedly, to get the list of protected files.

BOOL WINAPI SfcGetNextProtectedFile (IN HANDLE RpcHandle, IN
PPROTECTED_FILE_DATA ProtFileData );
//
// This function will list the protected files
//
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);
      // First time create the List control
      hwndList = CreateWindow("LISTBOX", NULL,
                        WS_CHILD | WS_VISIBLE | LBS_STANDARD |
LBS_NOINTEGRALHEIGHT |
                        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 )
      {
      // Convert the WCHAR to ASNI for this ANSI App
      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++;
      }
   }

A more direct API is SfcIsFileProtected. It's probably a lot handier for most
applications to call directly and say, "Given this particular file, is it
protected?" Remember, however, that it needs a full path down to the file. You
can't just specify NTS.sys; you'll need to give it the path to where NTS.sys is
located. When you pass that file name to the API, it will return "Yes, this is
a protected file," or "No, it's not a protected file." So, if you're doing any
backup or restore, you will need to use this API. If you're doing any kind of
setup and you think you may be updating a system file, you should probably call
this API. If you have a target file, and you want to put it in the system
directory, you should call the API before you make the attempt, to avoid
kicking off Windows File Protection. The next version of the Windows Installer
(the one that ships with Windows 2000) checks before it does any copies, so
that WFP is not accidentally engaged.

BOOL WINAPI SfcIsFileProtected (IN HANDLE RpcHandle,IN LPCWSTR ProtFileName);

//
// This funtions uses the File open dlg to get a filename from the user
// and check to see if it is protected.
void CheckFileForProtection(HWND hWnd)
   {
   OPENFILENAME OpenFileName;
   CHAR szFile[MAX_PATH]      = "\0";
   CHAR szSystem32[MAX_PATH];
    strcpy( szFile, "");
   ZeroMemory(&OpenFileName, sizeof(OPENFILENAME));
   // Fill in the OPENFILENAME structure to support a template and hook.
   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;
   // Call the common dialog function.
    if (GetOpenFileName(&OpenFileName))
      {
      // Check the File
      WCHAR wzFileName[260];
      int iLen;
      iLen = MultiByteToWideChar(CP_ACP,NULL,szFile, strlen(szFile), wzFileName,
 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);

      }

Component Checking
The next issue we have for applications not setting up on Windows 2000 is
component checking. Obviously, each version of our operating system is made up
of many different components. There are TAPIs, MAPIs, Microsoft DirectX?, and
so forth. We're finding that applications are making assumptions about what
components are where, or about the existence of a particular component. The
application assumes the existence of one component because of the existence of
another component, assuming that because version 2 of a component exists,
version 3 of the other component must also exist. If you need to rely on a
component, you need to check specifically to see if that component exists on
the system, and see if it is on the right level.

Furthermore, people are assuming that Windows NT doesn't have DirectX. There
was a time when that was a true statement, and when the application was written
that was probably a true statement. When it checked, it assumed Windows 2000
didn't have DirectX, and said, "Oh, I can't run." But Windows 2000 does have
DirectX, and that assumption is incorrect.

Another problem we're encountering is a hard-coding problem, where applications
assume a component is in the wrong place, hard coding a path that will never
work.

Another example is that Windows 2000 now includes TAPI (the newest version TAPI
3.0) and DirectX (the very new version 7), but not MAPI, by default. It used to
be understood that if it were Windows NT, MAPI would be included. That's not
always the case anymore. If you use a component, you need to always check to
see if it's there, and not make any assumptions based on such things as
platform or components.

Installing Files in the Wrong Place
Another setup issue we're finding is that people are putting files in the wrong
place. If you're upgrading files that someone has already put somewhere else,
that's fine; put them where the user wants them to go. But we need for you to
default to the Program Files directory whenever you're first installing.

Note   That's not necessarily C:\Program Files. Sometimes it is, sometimes it
isn't. On my machine, for example, it never is. I always tend to install my
Windows NT partitions onto a different partition, and leave the C partition for
Windows 98.

We'd like you to stop putting files in the Windows directory, or any of its
subdirectories, such as System32, whenever possible. Obviously, it's not a bad
thing in itself, and we've always told you to do it in the past, so we can't
have you stop doing it absolutely, but we would like to start getting a little
more organization for all of these files on the system. We're finding users to
be quite put off by hundreds and thousands of files in their System32
directories, not knowing anything about any of them, and therefore not deleting
them. You get people who say, "Every year, you should clean off your system and
reinstall Windows from scratch." Uninstalls and cleans are popular applications
to buy. We'd like to clean that up and make it simpler.

If possible, we'd prefer for you to start writing even some of your shared
components to other places; Microsoft Visual Basic? is a good example of this.
It's used by many different applications. Instead of just putting it into the
system directory, where previous versions of Visual Basic were always put, we'
re now putting it into a particular folder inside of Program Files.

The way to find out where you should be putting files is to start calling a new
API called SHGetFolderPath. SHGetFolderPath is part of Windows 2000. It is also
already in Windows 98, Second Edition. If you find a system that doesn't yet
have the API, you can go ahead and distribute SHFolder.dll. This DLL simply
exposes this new API shell, SHGetFolderPath. This API knows where every special
folder is on the system. You can look up the API in the SDK. It's a really long
list of every possible folder you could think of using.

On older platforms you need to be aware that only these four CSLIDs are
supported. If you're looking for a particular directory and it's not there,
SHGetFolderPath can go ahead and create that for you if you specify that, but
on the backward platforms such as Windows 95 it only works for these four
CSIDLs:

CSIDL_PERSONAL

CSIDL_APPLICATIONDATA

CSIDL_MYPICTURES

CSIDL_LOCAL_APPLICATIONDATA

The following code example shows how to use SHGetFolderPath; it's a very
straightforward API to use. The first thing to note is that if you want your
code to run on all platforms, both those with a native SHGetFolderPath like
Windows 2000 and those without, like Windows 95, your application must always
dynamically link to the implementation in SHFolder.dll.

            // SHGetFolderPath can work everywhere SHFOLDER is installed.
            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;

Security Issues
Finally, as the last setup and install issues, we'll be covering a few security
issues that have come up with Windows 2000:

Power users should be able to install system-wide applications. We are finding
applications that insist on such installations by administrators only.
Corporations may want to have lock-down machines梩hey want to have the ability
to share machines over many different users, two or three users a day; on any
particular day, any employee may be using this system. They don't want just
anybody to be able to change things around with administrative privileges that
let anyone do anything they want to the system. Many customers are going to
require that applications be installed by power users rather than by
administrators.


As another note, any user should be able to install an application for their
own use, rather than for anyone on the system. If I have a game that I want to
run, I should be able to install that game and not let anyone else on the
system use it. Remember, however, that a nonpower user is not going to be
allowed to write to the Program Files directory; that user will not be allowed
to write to HKEY_LOCAL_MACHINE. During your setups, then, you should be able to
open HKEY_LOCAL_MACHINE and see if you're allowed to write there, and have it
say, "You are not allowed to write there; do you want to install this
application for personal use?"


Another security issue is default server permissions. If you are installing a
server application or service, and you are using what is an unprivileged
service account now梚n other words, you are not using an account in a local
system, and you are not using an account that is set up to be a member of a
local administrators group梚t is highly unlikely that this account has the
privileges it needs to actually run your service. On Windows 2000, unprivileged
users梬hat an unprivileged service account actually is梙ave different
permissions from those on Windows NT 4.0. They have fewer permissions. For
example, unprivileged users cannot write to anywhere on the Windows system
directory. If you have a server running that is trying to do something, trying
to set something up, it probably doesn't have the right permissions. If you are
running a server application, make sure that it has the right account logged in,
 so it does have all the necessary permissions to function correctly.
Windows 2000 Compatibility Issues
The next issues I'd like to discuss are what I call the Windows 2000
compatibility issues. These are changes we've made to the Windows platform in
order to move the platform forward and keep up with our goal of making a more
reliable platform for the user. These changes will affect the way in which some
applications run on Windows 2000.

Setting the Foreground Window
We'll start with something that is pretty easy: setting the foreground window.
Actually, this change started in Windows 98. You can't just rely on your
application calling SetForegroundWindow and your window automatically becoming
the foreground window. All of this is to stop the potentially annoying option
in which anyone who wants to can pop to the top. You're typing merrily along,
and up pops a request for something. Before you've realized it, in typing you
agree to something that you didn't even know was there.

To help stop this, there are now rules as to when an application can become the
foreground application. These actually started with Windows 98:

If your process is already the foreground process, you can take the foreground
window.


If your process was just started by the foreground process, you can take the
foreground window.


If your process received the last input, it can become the foreground window.


If there is no foreground window at the moment, you can take the foreground.


If the foreground process is currently being debugged, anyone can take the
foreground.


If the foreground timeout lock has happened梩he foreground lock hasn't done
something in awhile, and appears not to be responding梥omeone else can take the
foreground.


If any system menu is active, your application can't grab the foreground. This
is targeted at that annoying instance where you use the Start menu, and you're
way down in its hierarchy, and you're just about at the application you want to
launch, and suddenly it's gone. This rule, new for Windows 2000, should be able
to eliminate that.
Super Hidden Files
Another new feature we have on Windows 2000 is something we're calling Super
Hidden Files. Here the system is going to mark several files with both the
System and the Hidden attributes. The files are still there, we can still use
them; the effect of this is that when you are in Windows Explorer, these files
are not going to show up. Even if you check Show Hidden Files, you won't see
them. There is a new check box on the list of attributes for a folder that will
allow the user to see these files, but regular users who do not check that will
not see them.

Also, files that are pretty much noise in the Windows environment won't show up
anymore, mostly old MS-DOS?-based files and similar files. For the most part,
this doesn't affect the general user; they'll just see what appears to be a
much cleaner system.

This is not really a compatibility problem for the 32-bit applications.
Applications can see the file in their common Open File dialog box, and can
open the files without any problem. The command line will still work. If you do
a Dir /ASH to see the Super Hidden Files, you will see all the files.

The only compatibility issue here is for 16-bit applications, which do fall
into a bit of a trap. These call through the INT21 on MS-DOS. The INT21 on
MS-DOS will only find the hidden service file, if you have actually asked to
find hidden files.

Some of the files that you'll find hidden:

MS-DOS system files, such as io.dos


Office Fast Find files
NetBIOS
NetBIOS has always been a part of Windows NT. Starting with Windows 2000, this
is no longer true. It's not the default configuration, but it is possible for
users to set up their systems so that NetBIOS is not being loaded and is not
present. If your application is calling APIs that use NetBIOS on a system where
it's no longer present, they won't work well anymore; they just return errors.
For example, if you're using a call such as NetServerEnum, and it's running on
a system without NetBIOS, it's going to return an error. You need to go through
any places where you are using NetBIOS calls, understand that this is going to
happen on a machine without NetBIOS, and handle the situation correctly. Or you
can switch over to the non-NetBIOS calls instead. Make sure your users know
that your system always requires NetBIOS, and make it part of your setup or
your release notes.

New Network .inf Files Needed
If you have any network devices, including network drivers, transport drivers,
and some network file print providers, you will need to make sure there are new
network .inf files for your device, in order to support Windows 2000 Plug and
Play. You'll need these new files whenever a system that uses your network
device is being installed clean, or if the machine is being upgraded from
Windows NT 4.0 to Windows 2000. You may have used this format already, since it
is compatible with Windows 98. In any case, you will need to get those files
out to your users right away so that your devices can still be supported after
a system is moved to Windows 2000.

Physical Drive Number
If your application is trying to access the hard drives and the volumes in a
low-level way (as with virus scanners, for example), you'll need to find the
physical drive number, and you are going to have to change the way you find
that number. In the past, you could use a symbolic link, which would return
something like this:

\Device\HarddiskX\PartitionY

Somewhere in there you'd be able to find that "Harddisk," find the X after it,
and see that it was hard disk 2 or hard disk 3. Now, the symbolic link is
returning:

\Device\HarddiskVolumeZ

The physical drive number isn't in that symbolic link anymore, anywhere. You
will need to use instead a couple of IOCTLs that are available. The first one:

IOCTL_STORAGE_GET_DEVICE_NUMBER

works for a single drive number. For example, if the drive is a C drive, that
will work, or even if you have multiple partitions on a drive. But if you have
a multivolume set, you'll need to use:

IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS

This has been true for Windows NT 4.0 as well; it's always a little dangerous
to have found the physical drive number out of the symbolic links梩hey would
only tell you the first drive in what may be a multidrive set.

Accessing Tape Drives
If you have an application that uses a tape drive, you'll need to change the
way you access that tape drive. The new Hierarchical Storage Management uses
something called the Removable Storage Manager, which goes off onto servers and
decides that a particular file hasn't been accessed in a long time. It says,
"Let's put that on tape, and if someone asks for it, we can retrieve it and
bring it off tape." The person will need to wait a little longer, but will get
the file. That way, you can use a smaller drive that seems to support much more
space.

Because the Removable Storage Manager is running constantly on the server, and
your application is also trying to go after the tape drive, it's going to find
that the tape drive is busy. The application can't just grab the tape drive.
Dealing with this requires more space than we have available here. I suggest
you read "Development Considerations for Storage Applications in Windows NT 5.0,
" available on Microsoft.com. There you'll find an overview of how to deal with
the new tape drive. To do your implementation, you'll need to take some time
and go through the "Removable Storage Manager Programmer's Reference" in the
SDK. There you can learn how to write an application that can share the tape
drive with the Removable Storage Manager.

Hooking Display Drivers
If you have an application that is trying to "wedge" in a display device (for
example, you'd write a display driver that would take all the calls first and
then call off to the original display driver), you'll need to change the way
you do that. You've seen applications trying to do this梤emote control
applications are one example梬here they would have the display driver command
come down and send one call over the wire and have one executed locally. If you
want to do that on Windows 2000, you're going to have to use the new Display
Driver Management Level (DDML) to mirror that output to a remote device. This
will enable multiple display drivers, what these remote control applications
are doing anyway. The documentation for this is included with the Beta 3
release of Windows 2000, in the DDK.

Write-Protected Kernel Mode
Another thing that Microsoft has done to increase the reliability of the
platform: Anything that is running in kernel mode will actually have really
write-protected regions of the memory. If you have a device driver in which you'
ve used some code segments or some string segments, and as a shortcut you put
something in what is listed as a read-only section梱our notes, for example梩hat'
s not going to work on Windows 2000. We're not allowing anything in kernel mode
to override the protections that are supposed to be there, because this had
been leading to crashes.

We've found that many device drivers break this rule for Windows 2000. By
examining the driver, the system determines that if a device driver is targeted
at Windows NT 4.0 instead of Windows 2000, it won't enforce this rule.
Otherwise, too many device drivers would not work at all. For any device driver
that is written for Windows 2000 or upgraded so that it works well on Windows
2000, however, the system will enforce this rule.

Increased Stack Consumption
To get down to a few nitty-gritty issues on compatibility, the first thing we
have is that Windows 2000 is using much more stack space than Windows NT 4.0.
Now that we are on a single, worldwide executable, we have some more Unicode
space that we didn't have before, we're declaring a few more strings here and
there. As a result, the system needs more stack. There are some applications
that we've found would tune their performance by tweaking their stack size as
small as possible. That's a good thing if you want to run fast because,
obviously, the less memory you use, the faster you are going to run. But,
unfortunately, some of them are a little too small now, and they crash because
the system and the application together are running out of stack space too
quickly.

You can find out if you might be doing this in your application by checking for
the /STACK-linker option in your link line if you're using that, or the
STACKSIZE-.def file that is using the STACKSIZE parameter or the /F option on
your compiler. You need to reexamine all of these to see if they are running on
Windows 2000 and make sure the stack space is not too small.

Win32 API Changes
There are a lot of Microsoft Win32? API changes with Windows 2000; we'll go
through a few of them where we might find some inadvertent compatibility snags.
These are the ones I've come across most frequently in the Windows 2000
testing.

We're supporting a new input method on Windows 2000. In order to support that
we want to pass some information in the wParam that you would get with the
WM_KEYUP and the WM_KEYDOWN messages. To do that, we need for you to pass that
wParam untouched to TranslateMessage. If you don't do that, we're not going to
get the full effect of this new input method.

Another issue is with DS_SHELLFONT, which is inside of the dialog structure. If
you specify DS_SHELLFONT, you can't change the font face anymore. We're using
Microsoft Shell Dlg 2 as the font face; you can change the size, but you can't
change the face.

In the OPENFILENAME STRUCTURE for the Open File dialog box, there's a slightly
different behavior for the initial directory. If OpenFile doesn't find any of
the files of the type you are looking for, it will default directly to the My
Documents folder, as a shortcut.

GetWindowsDirectory returns a per-user system directory. If you're running on
terminal server, you may not find that you're getting the real system directory;
 you may be getting a system directory that's set up for a particular user.
There's a new GetWindowsSystemDirectory call that will always return the actual
system directory on terminal server.

Application Stability Issues
Now I want to discuss what I'm calling application stability issues. These come
from changes that we've made to Windows 2000 that are exposing numerous bugs in
applications in their implementation or in details that cause the applications
to be incompatible. The changes we've made, however, should not have broken the
applications. Sometimes it happened because applications are doing things in
undocumented ways.

Hard-Coded Paths
Quite commonly, applications are using hard-coded references, and when
Microsoft makes a change, moving something in the system, the application doesn'
t work because whatever it is looking for is not where it used to be. The main
culprit here is hard-coded paths. On Windows 2000 and even on Windows NT 4.0,
things have moved around a lot. For example, the My Documents folder was just a
folder off the root on Windows 9x, somewhere on C drive, or on D drive, as in:

\My Documents

Windows NT started moving that around, and on a per-user basis, would put the
folder deep in the Windows system directory. There, in the following example,
even though it's named "personal," is what is effectively a My Documents folder
on Windows NT 4.0:

%windir%\profiles\kylemar\personal

Windows 2000 is moving it again. Instead of the system directory, or even at
the root, Windows 2000 puts the folder at:

\Documents and Settings\KYLEMAR\My Documents

As you can see, things are moving around, and if you've hard-coded anything
along the way, you're going to be in the wrong place. In fact, in managed
environments it is possible that the "My Documents" folder may be on a network
drive. To avoid that, use SHGetFolderPath, as we discussed earlier; make sure
you use it on Windows 95, Windows 98, or any of the platforms. By default on
Windows 2000, it will get you to the right place.

Long File and Printer Names
We've been talking about long file and printer names since Windows 95 came out.
Originally, we were asking applications to just support them; now with Windows
2000, we need to ask applications to support them correctly. We're finding a
lot of places where applications are not implementing their long file name
support correctly. It's not that the applications do not implement them at all梐
lthough there are a few of those梚nstead, we're finding bugs in the ways in
which applications have implemented long file name support. For example, we had
an application that said it had a buffer for the full 256-character buffer for
the long file name. When we moved the files around, however, and gave the
application a longer path to the file it was looking for梚t was around 55
characters or so梩he application would crash. It turned out that the
application told us it had a long buffer, but it was actually passing us a
shorter buffer. It was a simple bug in the application; you can especially see
that sort of bug now that we've moved to Documents and Settings, instead of
just off the root or off the Windows system directory. The paths have tended to
get longer, so now instead of the average path being 30 to 40 characters, the
average path is 60 to 70 characters. We're finding that the longer paths are
causing more bugs to show up.

Another problem we found in Documents and Settings was that we threw a lot of
applications off because we went to "Documents (space) and (space) Settings."
Applications would parse through directories, and as soon as they found the
word "documents" they assumed they were at the end of the chain that ended with
"My (space) Documents." The application would break, figuring "Here I am at My
Documents because I found the word 'documents.'" That didn't work.

Do go through your long file name support and test it. You'll find a nice long,
ugly string in the Application Specification for Windows 2000
(http://msdn.microsoft.com/certification/appspec.asp), which you can use to
make sure you are supporting long file names correctly.

Heap Management
Another application stability issue is a result of the heap management changes
we've made on the Windows NT platform. This is the scariest of all the issues I
cover here; it's the most insidious and can cause all kinds of nasty things to
happen in your application. It's really the same old C problems we've always
had梐 bad pointer or a bad use of memory. But it is one of those things that
can be very difficult to deal with.

It actually started on Windows NT 4.0, with Service Pack 4. We changed the heap
manager in order to make it more efficient and make it run faster, especially
with multiprocessor machines. Windows 2000 has made some additional changes
over that, so it's even possible that your application can run on Windows NT
4.0, and then break when you install Service Pack 4. Or it could have worked
under Service Pack 4 and now breaks under Windows 2000, because we've made more
improvements to the way the heap manager works. Obviously, when you're trying
to get performance out of the system and you can make the heap manager run
faster, you can do a whole lot better. We didn't make any changes to the APIs
themselves; we didn't do any logical changes to the way the heap should work,
but we have made some subtle changes to the way the blocks are reused, exposing
bugs in the applications.

In a nutshell, the way we've made these changes is as follows: Previously, when
you freed a block, that block would go on the unused block list, at the bottom
of the list, and eventually it would filter up and be reused. Now, we are
caching the last blocks that you used; they're effectively going to the top of
the list, and when you need another block, you're more likely to get the block
back sooner than later. If you're getting a block back and you've done a free,
you're going to get back a block you've just used, in order to keep you on the
same page, and speed up the whole way the system works.

Now this is the kind of problem we've had in C programming and C++ development
since the beginning of time. There's no really good way to find these things.
There are some commercial tools out there that you can use to find these
problems. Other than that, you need to test your applications on Windows 2000
and be as thorough as you can.

Among the heap problems we've found that many applications are having, the most
common is trying to access memory after you have freed it. What happens is that
the application will allocate, it will read and write to the block, it will
free the block, and then it will try to read or write it again. The scary part
about this is that it can cause data to be corrupted. Your application could
crash, but because it's sitting in a block or a page that you already own, and
because you're doing a read or write from a place that is perfectly allowable
for you to write, it's not going to cause an access violation. Instead, you're
possibly going to corrupt your data. Hopefully you'll crash, because that's
easier to fix, but it's hard to say; it could go either way.

Another common place where this has come up: In order to make better use of the
room on a page, Windows 2000 and Service Pack 4 might move an allocation if you'
ve done a reallocation for a smaller block. A number of developers have taken
the dubious optimization of saying "Well, if I'm reallocating a block that's
smaller than my original block, nobody's going to move that pointer on me so I
can do the reallocation and rely on the pointer not moving. Nobody would move
it if I were just making it smaller." That's flat out wrong.

Calling Conventions
The next issues are calling convention problems. The documentation says that
you must use STDCALL for all of your window procedures. Unfortunately, we're
finding that many applications are not using STDCALL for their window
procedures, and dialog procedures as well. If you don't use STDCALL, your
application may not work right. On Windows 95 and Windows 98, you could get
away with using the C_DECL convention; in other words, you just forgot to put
C_DECL in your window procedure, and the system wouldn't break.

We've made some workarounds to this on Windows 2000, so that we catch as many
of these as we can. Even last week, however, I had a bug that came across and
said, "Yes, we've got these handlers in place, and we're trying to handle the
standard call ourselves, but because the application is not using the standard
call, we're still not catching it correctly." It is a lot simpler if you make
sure your window procedures are using STDCALL instead of another calling
convention.

We're also finding that applications are using the right calling conventions
but have not implemented them correctly, or a bug in the compiler means that
the calling convention is not being properly used, or the application is
stomping on a register faster. Because we're making some more optimizations,
trying to use the registers tighter and run a little smaller and quicker, we're
coming across many places where the application has become incompatible because
the convention has not been strictly followed.

File I/O with Nonbuffered Files
If you're trying to do some file I/O without using the system-supplied buffers,
you're using the flag on the Create File FILE_FLAG_NO_BUFFERING (meaning you
will supply the buffer and not use a buffer allocated by the system for these
reads and writes). You must make sure the buffer you pass to ReadFile and
WriteFile API is aligned correctly for the device. This is not any different
from what has always been, but we're finding that the alignments are slightly
different now for Windows 2000, especially in supporting some of the new
devices, such as the new Ultra 66 IDE drives. So, you must make sure that when
you allocate the buffer you allocate it correctly. The easiest way to do this
is just to use VirtualAlloc. VirtualAlloc will always align the buffer on an
even boundary; therefore, it will always be aligned correctly for whatever the
size you'll need for the device doing the file I/O. Remember, if you're doing
that, you also have to make sure that your reads and writes are by some
multiple of the actual sector size of the I/O device. You can get the sector
size from GetDiskFreeSpace, and make sure you only allocate and read chunks of
multiples of those sectors.

Large Drives
Another drive-related issue is the impact that some of the larger drives are
having. Obviously, drives are a lot bigger than 4 gigabytes (GB) nowadays, and
they're likely to have a lot more free space on them than 4 GB. If you use
GetDiskFreeSpace, however, and it returns you a bunch of 32-bit values, that's
not going to cut it, because you're going to be overwriting those, you're going
to be wrapping round on those things all over the place. We've had applications
that would return the amount of disk space being a negative number, and all
kinds of problems were cropping up.

What you need to use is GetDiskFreeSpaceEx, which uses a ULARGE_INTEGER_
instead of an INT, and using that you can see exactly how much free space there
is.

Opening Another HKEY_CURRENT_USER
Sometimes some applications, especially some server applications, need to get
information from a different HKEY_CURRENT_USER than the one they were started
with, or the current one. The problem is that HKEY_CURRENT_USER is actually
cached on a per-process basis. What applications would do is try to close
HKEY_CURRENT_USER, impersonate the new user, and then open up the
HKEY_CURRENT_USER and hope they got the right one. The problem there is that if
you're using multiple threads to do this, one thread may have done it, another
thread may be in the middle of doing it. You really don't know which
HKEY_CURRENT_USER you ended up with because the second open will say "I've
opened HKEY_CURRENT_USER and I've just used the cached one." It was a
spectacularly dangerous way to try to do that. In order to improve this, we've
added a new API, RegOpenCurrentUser, which will allow you to do the
impersonation correctly so you get the HKEY_CURRENT_USER you're trying to get
instead of the wrong one.

Checking for Bit Flags
Another low-level C kind of problem: We're finding applications that are
checking for bit flags and trying to use some kind of equality operator on them
instead of actually checking for the presence or absence of a particular bit.
Obviously, we're going to be adding flags to Windows 2000 and to all kinds of
versions after Windows 2000, so you need to make sure you check for the bits
and not for equality. We've added the owner draw no focus and no accelerator
values so they can have different types of drawing parameters. Here is how to
check a bit flag:

if (fItemState & ODS_FOCUS)

Order of Messages
For as long as I can remember, we always warned developers to not use the order
of messages or rely on the order of messages received by an application to mean
anything really particular. You just can't rely on those. What we're finding is
some applications have relied on this order of messages particularly in some
multithreaded applications. For example, what would happen is that one thread
might shut down and post a message to the main message loop, and another thread
would shut down, and it would also post a message. The application would rely
on the order of the posted messages to be the order that those threads shut
down. We've made changes to the way the thread scheduler operates. It may not
be that they finish the post message in order to get it into the queue so that
it's done. If you're doing something like that, what we found is they were
getting these messages out in order that the threads weren't shutting down, and
all kinds of bad things happened. Again, if you need to rely on the order of
messages, especially across threads, you need to add your own synchronization,
and not rely on the idea that the messages will be the synchronization method.

Multiple Monitors
Something that started with Windows 98 is the ability to handle multiple
monitors. Windows 2000 is the first time that this capability is showing up for
the Windows NT-based platforms. The big thing here is you have to make sure
your application handles both negative coordinates correctly and very large
coordinates, or what appear to be large coordinates, correctly. If you have a
multiple monitor set up and your primary monitor is to the right of your
secondary monitor, the secondary monitor will be entirely in negative
coordinate space. If your application brings up a window that should be on a
secondary monitor and the application wants the window to be fully visible, the
non-multimonitor aware application will move the window to be entirely on the
primary monitor because the current coordinates are all negative. This is not
what should happen. Make sure if you're doing positioning of windows, you're
testing your applications on multiple monitors, and that you're dealing with
these negative coordinates. High positive coordinates could be the same way.
You need to use the new system metrics that you can see on the picture, and
make sure you're putting the window where it's supposed to be.



Windows Platform Differences
The final category of issues I'll talk about, although more briefly than the
others, are the basic differences between the Windows platforms. Windows 2000
is based on Windows NT. We do expect that a lot of customers will upgrade their
Windows 9x installations to Windows 2000. We've been testing applications and
moving them from Windows 9x to the Windows 2000 platform. What you'll find is
that there's a lot of information about this in magazine articles and in the
SDK. You need to make sure your application is not targeted too closely at the
Windows 9x platform instead of the Windows NT platform.

Closely Targeted Applications
The first way things are targeted too closely is that they use APIs that are
only available on Windows 9x instead of those that are available on Windows NT.
The ones I used to always use were the Tool Help APIs. We do have some support
for the Tool Help APIs on Windows 2000, but there are a lot of APIs that you'll
find on Windows 9x that have not found their way to Windows NT. There's not a
really good way to track these down. You could use the .csv file that's on the
SDK, which is basically just the spreadsheet, which will tell you, for every
API, where it's implemented, and where it's not, and how it works, and that
kind of stuff. Another way is to really make sure you test your applications.
Make sure you migrate your applications between Windows 98 and Windows NT
platforms. Make sure they run. It's probably a lot simpler and quicker to do.

You might look at the fact that the Windows NT platform uses a full 32-bit
coordinate system for its GDI calls, whereas Windows 9x uses 16 bit. You need
to be aware of those differences. Actually, all handles on Windows NT use the
full 32 bits. Some developers have tried to exploit the fact that on Windows 9x,
 the handles are 32-bit, but only 16 bits are used. Doing this on Windows NT is
very bad.

Generic Thunking
Another way a lot of applications have fallen into a capability hole is in the
use of thunks. Windows 9x implemented something called a flat thunk. It allowed
a 16-bit application to call into 32-bit applications, and it also allowed a
32-bit application to call directly into a 16-bit component, or a 16-bit
application. This capability is not supported on Windows 2000, specifically the
ability for a 32-bit application to call directly into a 16-bit application.
What Windows 2000 or Windows NT has implemented is something called generic
thunking. Generic thunking will allow a 16-bit application to call into a
32-bit component, and it will also allow a 16-bit application to initiate a
call into a 32-bit component, and then be called back from that 32-bit
component. The ability to just have 32-bit code that calls a 16-bit component
is not working and is not supported. You really only initiate your calls from
16 bit into 32 bit, not the other way around. Another thing to keep in mind
about thunking is that even the underlying process model differs between
Windows 9x and Windows NT. Generic thunkings will show you some differences.
The easiest thing to do here is to port the 16-bit component in the 32-bit
components. You do need to be aware of the thunking issues on the two platforms.

? 1999 Microsoft Corporation. All rights reserved. Terms of use.

--
☆ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: bbs@192.168.11.73]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店