Moving to 64 bits: DLLs and OCXs

Posted by Matt Gilarde on 05-Apr-2013 13:30

This is the first in a series of posts about the practical issues that arise when moving an application to the 64-bit GUI client. I will discuss issues raised in the 64-bit Windows GUI Client Release Notes in more depth. The first topic is DLLs and OCX controls.

64-bit Windows can run 32-bit applications. However, a 32-bit process cannot load a 64-bit DLL and a 64-bit process cannot load a 32-bit DLL. It is possible for a 16-bit application to call a 32-bit DLL on 32-bit versions of Windows because 16-bit and 32-bit applications both run in a 32-bit address space so they can address the same amount of memory. There's no such luck on 64-bit Windows - 32-bit code can only address 4GB of memory, while 64-bit code can address 8TB (terabytes). 32-bit code can't interoperate directly with 64-bit code (or vice versa) because they can't see the same amount of memory. They also have different pointer sizes, which I'll talk more about below.

If your ABL application relies on 32-bit DLLs you have some work to do. If you have the source code for the DLLs you may be able to port them to 64-bits. If they are third-party DLLs without 64-bit equivalents (which is very common) or the source code is long gone, you'll need to find a way to replace the functionality they provided.

The same issue applies to OCX controls. OCX controls are little more than DLLs with a different extension. You can't use a 32-bit OCX control in a 64-bit application. If your application relies on OCX controls you might find yourself stuck, as there are very few 64-bit OCX controls. This problem is affecting us as we work on the 64-bit GUI client - it turns out that we don't own most of the OCX controls that we have been shipping with the 32-bit version of OpenEdge (don't worry, they are properly licensed). Progress sold the controls to another company many years ago and then licensed them back so we could continue shipping them (for $495, so we certainly got our money's worth out of that deal over the last 15 years or so). We didn't just ship the OCX controls - we used them in our tools, so we are faced with the same problem anyone who used them will face. If your application makes heavy use of OCX controls they may be your biggest barrier to moving it to the 64-bit GUI client.

Getting back to DLLs, if you do find replacements for the ones you're using you may still have some work to do. If you're passing pointer values to an external procedure, those pointers become 64 bits wide in the 64-bit client. It's common to pass a pointer using a parameter of type LONG. This won't work for a 64-bit pointer. You need to use a 64-bit type, INT64. One bit of good news is that MEMPTR parameters handle this for you. They automatically adjust to the correct pointer width.

A common use of external procedures is to call the Windows Win32 API. Many Win32 API calls will work without modification because Microsoft didn't change the names of the system DLLs (kernel32.dll, user32.dll, etc.) on 64-bit Windows. They also did us all a favor by guaranteeing that handle types like HWND and HINSTANCE will not use the high 32 bits of the handle value, even though the handles themselves have expanded to 64 bits. You can continue to safely pass handle types as LONG parameters to and from external procedures. However, it is common to build structures in MEMPTRs to pass to the Win32 API and the sizes of the structure elements are likely to be larger on 64-bit Windows. You'll have to review each structure you're using and you'll probably have to adjust the sizes and offsets of some structure members.

Another difference between external procedures on 32-bit Windows and 64-bit Windows is the calling convention. On 32-bit Windows you had to specify the correct calling convention (STDCALL, CDECL, or PASCAL) in your external procedure declarations to avoid corrupting the stack. On 64-bit Windows there is only one calling convention, called FASTCALL. 64-bit OpenEdge ignores any calling convention you specify in an external procedure declaration, so that's one thing you don't have to worry about.

If you have any questions or want more detail about DLLs and OCXs, or anything to do with the 64-bit GUI client, feel free to ask. My next post will be about using the GUI for .NET with the 64-bit client, where, thankfully, the news is much better.

All Replies

Posted by Jeff Ledbetter on 05-Apr-2013 13:50

This is good informtion.I recently ported one our libraries to 64-bit as it needs to run under an 64-bit OE AppServer."If you're passing pointer values to an external procedure, those pointers become 64 bits wide in the 64-bit client. It's common to pass a pointer using a parameter of type LONG. This won't work for a 64-bit pointer."Can you expand on this? I did not change my parameter types from LONG to INT64 and all testing passed. Do I have a possible ticking time-bomb on my hands?Thanks.Jeff

Posted by Matt Gilarde on 05-Apr-2013 14:11

Passing a pointer in a LONG parameter will work as long as the value of the pointer doesn't exceed 2147483647 (0x000000007FFFFFFF). That is, if the pointer's value can be held in a signed 32-bit value it will work. If you pass a pointer value that is too large for a LONG you will see error "Value 2147483648 does not fit in long DLL datatype. (13712)". Since you can't know in advance whether a pointer in your application will ever exceed that value it's best to assume that they will. One of my future posts will be about how to test a 64-bit application to flush out these sorts of issues.

Posted by gus on 08-Apr-2013 08:48

Good stuff, Matt.

Note that some of what Matt says about calling 64-bit dll's applies equally well to calling 64-bit .so's on UNIX and Linux. In a 32-bit executable addresses are 32 bits wide and you cannot call 64-bit shared libraries. In a 64-bit executable addresses are 64-bits wide and you cannot call 32-bit shared libraries.

This thread is closed