The problem arises when you try to use a 32-bit application from the Microsoft Office suite to run code written in its 16-bit counterpart, or vice versa, and the code contains API or DLL calls. When the 32-bit version of Excel is running, it expects custom code to call 32-bit DLLs and the Windows 95 API. Likewise, when the 16-bit version of Excel is running, it expects calls to 16-bit DLLs and the Windows 3.1 API.
The same is true for Access, Project, and Word. Execute an API or DLL call of the wrong bitness from within any of these programs and the result is an unrecoverable run-time error.
The good news is that you don't have to kiss your custom code goodbye. The amount of reworking that's required is often quite minimal. It depends primarily on whether your application is expected to run on both 16-bit and 32-bit versions of the Office suite and whether you have access to source code of custom 16-bit DLLs.
The majority of Office users -- more than 90 percent by Microsoft's estimate -- are exempt from this problem. Who's left? People whose office applications extend the Office suite by relying on explicit API and DLL calls outside the host application and who are now porting that code to the Office 95 suite.
"Most [complex] applications have at least some API calls," notes Tim Tow, a consultant for Lex Software Systems Inc., in Winslow, Wash.
CODE IMPROVEMENTS. Fortunately, problems are easily assuaged in all but the most intractable instances. There are three ways to handle the situation.
The first and easiest way is to edit your code so it makes calls to the Windows 95 API and 32-bit DLLs instead of the Windows 3.1 API and 16-bit DLLs. Start by changing all your 16-bit API calls to 32-bit calls. (Of course, once you make the change, the new 32-bit calls will no longer work in the 16-bit version of the application.)
For example, it's a minor inconvenience to update the 16-bit GetVersion API call:
Declare Function GetVersion Lib "KERNEL" () As Long
to its 32-bit counterpart:
Declare Function GetVersion Lib "KERNEL32" () As Long
Only the location of the API call has changed, from KERNEL to KERNEL32. Most 32-bit Windows APIs follow a similar pattern -- the name of the API location is the same as in the 16-bit version, with the characters "32" appended. Likewise, most 32-bit API calls use the same data type for their return parameters ("Long" in the example) as their 16-bit counterparts.
Despite the apparent simplicity of this solution, there are a couple of things to watch out for. First, be aware that there are some API calls whose parameters (the argument inside the parentheses) change type between the 16-bit and 32-bit versions, and you must make this change when you update the API call.
To find out which API calls require updating, you can consult WIN32API.TXT, a text file included on the July 1995 Microsoft Developer Network Development Library CD.
Another source is the Win32 Programmer's Reference, which also is included on the CD. These sources document 32-bit API calls, including parameter types.
Second, be sensitive to case, because API calls under 32-bit Windows are. To keep your code from choking on an errant uppercase or lowercase character when you upgrade it to a 32-bit call, Microsoft recommends that you use the Alias keyword, as follows:
Declare Function GetVersion Lib "KERNEL32" Alias "GetVersion" ( ) As Long
Now your code will process the GetVersion API call no matter how you capitalize it -- even if you use several different methods of capitalization within the same subroutine.
With your 16-bit API calls updated, you can turn to 16-bit DLL calls. Here, the solution is simple and straightforward: Have the developer who created the DLL recompile it into a 32-bit version.
If you don't have access to the source code for the 16-bit DLL, plan to scramble a bit, because you've just run up against the 16-to-32-bit barrier. The 32-bit Windows uses a flat, linear addressing model that's fundamentally incompatible with 16-bit Windows' segmented stacks. It's up to you to come up with an intermediate process that converts the addresses and surmounts the stack differences.
This is where "thunking" enters the picture. To thunk, you create a 32-bit DLL that mediates between the 16-bit and 32-bit stacks, ensuring that data is correctly pushed onto and taken off of the stacks. Although this may be a simple matter, it can quickly get out of hand if a complex set of memory pointers is required. A thunking DLL requires a special thunking compiler, and Windows 95 thunks differently than Windows NT.
When all is said and done, you'll probably find yourself at the mercy of a thunking expert who routinely handles these issues. It just might be worth your while to look for that original DLL source code one more time.
ON THE FENCE. While your organization is still running both 16-bit and 32-bit versions of Office, further revisions are required because your code must be capable of running in either a 16-bit environment (such as Excel 5) or a 32-bit environment (such as Excel 95). This is fairly simple: Write a checking subroutine that returns the bitness of the application.
Run this subroutine at the very beginning of your custom code and use it to set a global Bitness variable. Then, when it's time to make an API or DLL call, use the value of the global Bitness variable to select either the 16-bit call or the 32-bit call.
For example, in a Visual Basic for Applications module for Excel or Project, you would begin by declaring a Public Bitness variable: Public Bitness As Integer.
You would then declare the necessary calls, both 16-bit and 32-bit versions. Here, we use the GetVersion example introduced in the previous section.
Declare Function GetVersion16 Lib "KERNEL" Alias "GetVersion" () As Long
Declare Function GetVersion32 Lib "KERNEL32" Alias "GetVersion" () As Long
Here's the checking code:
Function BitnessCheck () If InStr(Application.Operating System, "32") then Bitness = 32 End Function
You would include a call to the BitnessCheck function in an application's Auto_Open method.
Finally, in the places where your code calls the GetVersion API, you would use an if-then-else statement to determine which API to call:
Function GetVersion () As Long If Bitness=32 then GetVersion=GetVersion32() Else GetVersion=GetVersion16() End If End Function
When your code encounters the GetVersion function, it checks the Bitness variable, then makes the appropriate API call. (In the case of DLL calls, this solution assumes that you have both a 16-bit and a 32-bit version of the DLL available. If not, you will have to thunk when you call the 16-bit DLL from 32-bit Office, as described in the preceding section.)
The bitness-checking code varies slightly for Access and Word. For Access, use:
Function BitnessCheck () If SysCmd(7)>2 then Bitness = 32 End Function
In Microsoft Word, use:
Function BitnessCheck () If Val (GetSystemInfo$(23)) > 6.3 or Len(GetSystemInfo$(23))=0 then Bitness = 32 End Function
SOURCES OF COMFORT. For more information on the subject of 16-to-32-bit compatibility within Office applications, Microsoft has written a white paper entitled "Porting Your 16-Bit Office-Based Solutions to 32-Bit Office." Additional information can be found in The Office Resource Kit, which is available from Microsoft Press.
All this information, so neatly categorized and readily available, raises the inevitable question: Why didn't Microsoft do this for us? Why not, for example, allow the Office 95 versions of Access, Excel, Project, and Word to automatically convert 16-bit API calls to 32-bit calls? Why not program the capability to check for bitness and swap between APIs and DLLs automatically? The answer, of course, is that it's harder than it looks.
"If you really, really, really, really, really, really want to piss off developers, touch their code," says Michael Risse, lead product manager for Access. "You can never know what they were intending with the API calls and the DLL calls they were calling. People intentionally write code that is counter to everything we suggest or advise."
Kelly Conatser is a member of the editorial review board and author of Windows Without Trying, coming this fall from Que Publishing.
Office 95 decision tree
If your enterprise plans to upgrade permanently and irrevocably to the Office 95 suite...
* Edit your code so it makes calls to the Windows 95 API and 32-bit DLLs instead of the Windows 3.1 API and 16-bit DLLs.
If you don't have access to the source code for the 16-bit DLL...
* Create an intermediate DLL that "thunks" between the 16-bit and 32-bit processes.
If your company wants to run both the 16-bit and 32-bit versions of the Office suite...
* Add code that checks for the "bitness" of the current application and uses the appropriate API and DLL calls.