Ken Lassesen and Michael Risse
Ken Lassesen is a writer in the Developer Network Technology Group. He does his best coding when listening to Carmina Burana and snacking on lutefisk.
Michael Risse, a product manager for Microsoft Access, contributed to this article.
May 9, 1995
Are you a solution provider or a corporate developer who has created a Microsoft Office-based solution? You may soon face an important challenge to ensure that your application runs successfully under Windows 95.
You may face the issue if your Office 4.x-based (or earlier) solution code (in Visual Basic for Applications, for example) calls the Windows 3.1 API or other 16-bit dynamic-link libraries (DLLs). If your application's users upgrade to Windows 95, and then upgrade their desktop applications to Office for Windows 95 (the 32-bit version of Microsoft's mainline desktop applications, due out this summer), some of your routines will fail, providing a "DLL could not be loaded" error message.
What is going on? Your Visual Basic for Applications, Access Basic, XLM, or WordBasic code is being run in a 32-bit application and is trying to call the Windows 3.1 API or other 16-bit process—unsuccessfully, because a 32-bit application cannot make a direct 16-bit API call. Conversely, a 16-bit application cannot make a direct 32-bit API call.
This may not be obvious. The current, 16-bit version of Microsoft Office and any solutions built on it will run just fine on Windows 95. In addition, the upcoming 32-bit version of Office 95 and any solutions built on it will run without difficulty on Windows 95. However, if you unwittingly mix a 16-bit solution with the 32-bit version of Office, you will find that your application does not work. Before users of your application begin switching to Office 95, you should plan to avoid this situation.
Code written in any of the Office solution-building languages—including WordBasic, Visual Basic for Applications, and XLM in Microsoft Excel; Visual Basic for Applications in Microsoft Project; and Access Basic in Microsoft Access—must be updated when ported to Office 95 if the code calls the Windows 3.1 API or other 16-bit DLLs.
There are two ways to convert your 16-bit Office 4.x solution so that it runs correctly in 32-bit Office 95. Both options require that you do some new work.
The first option is to rewrite and recompile any 16-bit DLL the solution uses to a 32-bit DLL and update the calling code, then update any Windows 3.1 call to its equivalent Win32 call, and after that redistribute the custom solution. This may mean all your users need to upgrade to 32-bit application engines concurrently, or use API wrappers as discussed below.
Most 32-bit Windows APIs have the same names as 16-bit APIs. The documentation in the Developer Network Development Library may show the same arguments with the only apparent difference being that the DLL's name changes from FOOBAR.DLL to FOOBAR32.DLL. This can mask significant differences:
For more information on this, the Visual Basic team is working on a Basic-oriented guide to the Windows 95 API. They hope to release it this summer.
If you don't want to rewrite the 16-bit calls, then the other option is "thunking" (providing an interoperability layer between the 16-bit and 32-bit processes). This has other limitations. Thunking ensures that parameters are pushed correctly on the stack, and that memory addresses are converted from offset (32-bit) to segment::offset (16-bit).
Thunking requires changing both the 16-bit DLL and the 32-bit application solution code. If you can't change the 16-bit DLL for some reason, you must write a new 32-bit wrapper DLL. The 32-bit application (Microsoft Excel, for example) talks to this 32-bit wrapper DLL, which in turn talks to the real 16-bit DLL.
Although it is in some cases trivial to develop thunking functionality, it can involve creating pointers between the two layers, which is quite challenging and may lead to performance problems.
Note that there are different ways to thunk. For more information, check out "Diving into the Requirements for the Windows 95 Logo" by Denise Shephard in the Developer Network Development Library. This article gives an overview of thunking across the Windows platforms and contains pointers to more detailed information on thunking.
Simply converting your declare statements from Windows 3.1 API calls to Windows 95 calls may not be enough. There are other issues besides changing the name of the function's host DLL (such as from USER.DLL to USER32.DLL). For example:
These strategies take care of the problem if you are writing an application for yourself, since you know what your application engine operating system is.
However, if the application is to be distributed across a large organization with many users, you'll have to make it work on all the PCs, which may be running different versions of Windows and different versions of the application engines. Possibilities include everything from running 16-bit applications on a 32-bit operating system to running 32-bit applications on a 16-bit operating system (with the aid of Win32s).
Your code will have to determine what type of application your solution is using as its engine, without making an API call, so that you can make the appropriate call. The solution will be for you to put every API call into a wrapper—a Basic function or subroutine that checks the application and then issues the correct call. For example, in Microsoft Access:
Function Engine32bits%() Engine32Bits=(SysCmd(7) > 2) End Function Declare Function GetProfileString Lib "Kernel" Alias "GetProfileString16"(ByVal lpApplicationName$, ByVal lpKeyName$, ByVal lpDefault$, ByVal lpReturnedString$, ByVal nSize As Integer) As Integer Declare Function GetProfileStringA Lib "Kernel32" Alias "GetProfileString32"(ByVal lpApplicationName$, ByVal lpKeyName$, ByVal lpDefault$, ByVal lpReturnedString$, ByVal nSize As Long) As Long Function GetProfileString(ByVal lpApplicationName$, ByVal lpKeyName$, ByVal lpDefault$, lpReturnedString$, ByVal nSize%) as Integer 'We use 16-bit integer to appear like 16-bit call. If Engine32Bits() Then GetProfileString=GetProfileString32\ (lpApplicationName$, lpKeyName$, lpDefault$, lpReturnedString$, nSize%) Else GetProfileString=GetProfileString16\ (lpApplicationName$, lpKeyName$, lpDefault$, lpReturnedString$, nSize%) End If End Function
Check out the July release of the Development Library for an expanded technical article, "Converting Your 16-bit Office Application to Run Under Windows 95," by the authors of this article. It will describe in greater detail how to deal successfully with the challenges involved in porting your code.
A Microsoft Office product such as Microsoft Excel is called an application. The Microsoft Office family of products includes Microsoft Excel, Microsoft Word for Windows, Microsoft Access, Microsoft Mail, Microsoft Project, and Microsoft PowerPoint.
But developers also use the solutions languages in Microsoft Office products, such as Visual Basic for Applications in Microsoft Excel or Access Basic in Microsoft Access, to add custom functionality to these applications. The resulting solutions are also called applications. Oops, the term application has become fuzzy!
In the latter cases, Microsoft Excel and Microsoft Access are actually functioning as application engines that execute the code—macros, Visual Basic for Applications—that the Office developer has written. More specifically, application engines are the .EXEs that read the application code from another file and execute it.
A Microsoft Excel application (XLS) can be either 16-bit or 32-bit because it is not dependent on the application engine—except when it does API calls.
Traditional compiled applications are targeted for one operating system, which then defines the API calls. Applications executed by application engines, however, are not tied to the operating system; thus, the application may work or fail when making API calls, depending on the application engine used.