Sunteți pe pagina 1din 14

"Compile error: The code in this project

must be updated for use on 64-bit


systems" error message when you edit a
VBA macro in the 64-bit version of an
Office 2010 program
Print
Email
Article translations
Article ID: 983043 - View products that this article applies to.
Expand all | Collapse all

On This Page
SYMPTOMS
Consider the following scenario:
You write a Microsoft Visual Basic for Applications (VBA) macro code that uses
Declare statements.
Your VBA macro code uses compilation constants. For example, your macro code
uses one the following compilation constants:
o #If VBA7
o #If Win64
You use an #Else block in a conditional block. In the #Else block, you use syntax
for a Declare statement designed to run in Microsoft Visual Basic for Applications
6.0.
You edit the code in a 64-bit version of a Microsoft Office 2010 program.
You try to change the Declare statement in the #Else block.
In this scenario, you receive the following error message:
Microsoft Visual Basic for Applications
Compile error:
The code in this project must be updated for use on 64-bit
systems. Please review and update Declare statements and then
mark them with the PtrSafe attribute.
Note This issue only occurs when you edit the VBA macro. This issue does not occur
when you run the macro.

Back to the top | Give Feedback

RESOLUTION
To resolve this issue, ignore the "Compile error" and run the VBA code in the 64-bit
version of the Office 2010 program.
Back to the top | Give Feedback

MORE INFORMATION
Steps to reproduce the problem
Microsoft provides programming examples for illustration only, without warranty either
expressed or implied, including, but not limited to, the implied warranties of
merchantability and/or fitness for a particular purpose. This article assumes that you are
familiar with the programming language being demonstrated and the tools used to create
and debug procedures. Microsoft support professionals can help explain the functionality
of a particular procedure, but they will not modify these examples to provide added
functionality or construct procedures to meet your specific needs.
If you have limited programming experience, you may want to contact a Microsoft
Certified Partner or Microsoft Advisory Services. For more information, visit these
Microsoft Web sites:
Microsoft Certified Partners - https://partner.microsoft.com/global/30000104
Microsoft Advisory Services - http://support.microsoft.com/gp/advisoryservice
For more information about the support options that are available and about how to
contact Microsoft, visit the following Microsoft Web site:
http://support.microsoft.com/default.aspx?scid=fh;EN-US;CNTACTMS
1. Start the 64-bit version of Microsoft Excel 2010 that is running on a Windows 64bit operating system.
Note By default, a new workbook is opened.
2.
3. Press ALT+F11 to start the Visual Basic for Applications 7.0 IDE window.
4. On the Insert menu, click Module.
5. In the code window that appears, copy and paste the following code:
6. #If VBA7 Then
7.
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As
LongPtr)
8. #Else
9.
Private Declare Sub Sleep Lib "kernel32" (ByVal ms as Long)
10. #End If

11. In each Declare statement, manually change the name of any parameter that is

passed from "ms" to "millisecs."


When you change the second Declare statement, Visual Basic for Applications 7.0 will
report an error that indicates that you have to use PtrSafe. However, the report is incorrect
because the line is in a section that only runs in Visual Basic for Applications 6.0. VBA
6.0 does not use PtrSafe. Therefore, you can safely ignore the error message.
Back to the top | Give Feedback

I've already encountered this problem on people using my in-house tools on new 64 bit machines wi
all I had to do was change lines of code like this:

Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _


(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, By
ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long

To This:

#If VBA7 Then


Private Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "Shell
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String
ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
#Else
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String
ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
#End If

23 down
vote
accepted You will, of course want to make sure that the library you're using is available on both machines, bu
problem.

Note that in the old VB6, PtrSafe isn't even a valid command, so it'll appear in red as though you ha
ever give an error because the compiler will skip the first part of the if block.

Applications using the above code compile and run perfectly on Office 2003, 2007, and 2010 32 and

Actually, the correct way of checking for 32 bit or 64 bit


platform is to use the Win64 constant which is defined in
all versions of VBA (16 bit, 32 bit, and 64 bit versions).
#If Win64 Then
' Win64=true, Win32=true, Win16= false
#ElseIf Win32 Then
' Win32=true, Win16=false
#Else
' Win16=true
#End If

Source: VBA help on compiler constants


answered Jun 25 '12 at 4:29
shareimprove this answer
Dee Bee
211
thats the system arch not
Excel btw HaveAGuess
Oct 14 '13 at 17:25
add a comment
Use PtrSafe and see how that
works on Excel 2010.

up vote 2 down vote

Corrected typo from the book


"Microsoft Excel 2010 Power
Programming with VBA".
#If vba7 and win64 then
declare ptrsafe function
....
#Else
declare function ....
#End If

I've already encountered this problem on people using my in-house tools on new 64 bit
machines with Office 2010.
all I had to do was change lines of code like this:
Private Declare Function ShellExecute Lib "shell32.dll" Alias
"ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As
String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal
nShowCmd As Long) As Long

To This:
#If VBA7 Then
Private Declare PtrSafe Function ShellExecute Lib "shell32.dll"
Alias "ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile
As String, ByVal lpParameters As String, ByVal lpDirectory As String,
ByVal nShowCmd As Long) As Long
#Else
Private Declare Function ShellExecute Lib "shell32.dll" Alias
"ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile
As String, ByVal lpParameters As String, ByVal lpDirectory As String,
ByVal nShowCmd As Long) As Long
#End If

You will, of course want to make sure that the library you're using is available on both
machines, but so far nothing I've used has been a problem.
Note that in the old VB6, PtrSafe isn't even a valid command, so it'll appear in red as
though you have a compile error, but it won't actually ever give an error because the
compiler will skip the first part of the if block.

Applications using the above code compile and run perfectly on Office 2003, 2007, and
2010 32 and 64 bit.

How to make your VBA macros x64 compatible


Overview
SolidWorks 2013 includes VBA 7the latest version of Microsofts Visual Basic for Applications.
This is great news for several reasons, the most significant being: 1) VBA user forms no longer
pop-up behind the SolidWorks application window, and 2) it proves to us that both Microsoft and
SolidWorks Corporation are serious about maintaining the VBA language, which is the language
of most SolidWorks API code.
VBA7 is 100% compatible with VBA6 (the version included in SolidWorks 2012 and earlier). For
this reason, all SolidWorks API code that you write in pre-2013 should work in 2013, and vice
versa. Nothing about the SolidWorks API itself has changed that requires modification.
That being said, you may have discovered that some of your macros no longer work in
SolidWorks 2013. When you try to run the macro, you may get the following error.

Why is this? Were we not told that VBA7 is 100% compatible with VBA6? The issue isnt with
VBA. The reason your macros are failing is because they are not set up to reference 64 bit DLL
files, or the DLL files you are trying to reference are not 64 bit compatible. This includes ActiveX
controls, which have the extension .ocx but are actually still DLLs underneath. So if you are using

any of the Microsoft Common Controls in your user form (e.g., slider, calendar, web browser) then
you are using ActiveX controls that will fail in VBA7.
First Im going to give you the quick-and-dirty steps on how to (hopefully) fix your dilemma. Later,
if youre so inclined, you can keep reading and learn the technical why behind the dilemma.

Fixing the Problem


Fixing the problem comprises three steps: 1) obtaining a 64 bit version of the DLL that is causing
problems, 2) re-declaring its functions using new VBA7 keywords, 3) including conditional code
that allows for backward compatibility on VBA6.

1. Get a 64 bit version of the DLL


If you wrote the DLL yourself, then re-compile the DLL to work on Any CPU. This will allow the
DLL to run on 32 or 64 bit systems.
If someone else wrote the DLL, you will need to search online or contact them to determine if a
64 bit version available. An example is the SolidWorks Document Manager (SwDocumentMgr.dll).
With the advent of 64 bit architecture, SolidWorks Corporation recompiled this DLL to work on 64
bit computers. If, for some reason, you cannot obtain a 64 bit version of this DLL then your only
options are 1) find another 64 bit compatible DLL containing a function that does the same thing,
2) write your own DLL with the desired function and compile it for 64 bit, 3) if the DLL works on
Microsofts COM framework (as opposed to .NET) then you can actually create a custom 32 bit
server DLL to act as an interface between it and VBA7. For more information on the last
solution, consult the excellent SolidWorks World 2013 presentation on VBA7 by Frank Lindeman,
available here.
If the DLL is a Microsoft common library (Win32), then you can rest assured that the DLL has
been recompiled for use on 64 bit. An easy way to determine whether the DLL is a Win32 DLL is
to google it. If there is an article describing it at MSDN then it is probably a Win32 API function.
Four of the most common Win32 DLLs are kernel23.dll, gdi32.dll, user32.dll, and shell32.dll.
As mentioned in the Overview, ActiveX controls are technically just wrappers for an underlying
DLL. The most popular ActiveX controls are Microsoft Common Controls (mscomctl.ocx).

Unfortunately, Microsoft has decided not to update the underlying DLLs to support 64 bit, so you
will definitely need a workaround. If you cant find an alternative DLL and dont mind writing your
own, keep in mind that since these controls are really just wrappers for underlying Win32 API
calls, it is certainly possible to write a DLL that accesses those Win32 API calls directly.

2. Modify the function declarations and associated variables


Once you have a 64 bit version of the DLL, all you need to do is modify the function declaration to
include the PtrSafe keyword after Declare and change all Long variables used in the function to
LongPtr. For example, examine the difference in declaration with this Win32 API function called
SHGetPathFromIDList:
32 bit
Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias
"SHGetPathFromIDListA" (ByVal pidl As Long, ByVal pszPath As String) As Long

64 bit
Private Declare PtrSafe Function SHGetPathFromIDList Lib "shell32.dll" Alias
"SHGetPathFromIDListA" (ByVal pidl As LongPtr, ByVal pszPath As String) As
LongPtr

A few additional comments:


1. In your macro, you may have many more Long variables associated with the function that are
declared on separate lines. If you do not convert these to LongPtr, then you may either get a
Type mismatch compile error, or SolidWorks will crash entirely. Watch the video at the beginning
of the article for an example of this. Conversely, you might also notice that it is not necessary to
change all Long variables to LongPtr. This is because the only variables that need converting are
those that hold pointers. If you dont know whether a variable corresponds to pointer (and
therefore should be converted to LongPtr), simply verify by using this Microsoft-published text file
of PtrSafe Win32 API calls. Alternatively, you can indiscriminately convert all Long variables to
LongPtr, since it doesnt hurt anything.

2. The inclusion of the PtrSafe keyword simply means you think the DLL is safe for 64 bit use.
This doesnt mean it actually is safe! If you arent certain if a DLL is 64 bit, you can determine this
using tools like Process Explorer or Dependency Walker.
3. Even if your DLL is compiled for 64 bit and you are using PtrSafe and LongPtr properly, your
macro will still fail if your DLL has any dependent DLLs that are not compiled for 64 bit.

3. Make your code compatible with VBA6 and VBA7


The PtrSafe and LongPtr keywords that we used just a moment ago are unrecognized by VBA6.
Does this mean we have to create a version of our macros for use on 64 bit and another version
for 32 bit? Thankfully, no. This is where Conditional Compilation Constants come in. Using these
constants, we can actually tell the VBA pre-processor to exclude certain lines of code during
compilation. Specifically, well use the VBA7 constant to test for the version of VBA were using.
If it is VBA7, well use our declaration involving PtrSafe and LongPtr. Otherwise, well use the
VBA6 declaration. The syntax looks like this:
#If VBA7 Then
Private Declare PtrSafe Function SHGetPathFromIDList Lib "shell32.dll" Alias
"SHGetPathFromIDListA" (ByVal pidl As LongPtr, ByVal pszPath As String) As
LongPtr
#Else
Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias
"SHGetPathFromIDListA" (ByVal pidl As Long, ByVal pszPath As String) As Long
#EndIf

Not too bad, is it? Note that you can use conditional compilation constants anywhere in your
code, including LongPtr declarations within other functions and sub-procedures.

Technical Background
I hope by this point you know what you need to do to correct any 64 bit compatibility issues youre
having with your macros. For some of you, however, this isnt enough. You want to know the
why behind all of this. This next section should answer of that for you.

To answer the why question, we need to begin by understanding why 64 bit computers exist in
the first place. As anyone whos taken a basic course in digital logic knows, the smallest unit of
information in a computer is a bit, which represents a 1 or a 0. The smallest amount of
information that your computer can write to and read from, however, is not a bit but a byte, which
is composed of 8 bits. Next, what you need to understand is that when data is stored in RAM
(memory), each byte has an address. Just like a postal address, your computer needs to know
where data is being stored in memory in order to read it or write to it. On a 32 bit system, these
addresses are composed of 4 bytes. Since each byte is composed of 8 bits, that means we have
32 bits total to work with. In binary, that means our total number of possible bytes we can address
with a 32 bit memory address is 2^32, or 4,294,967,296. As you know, thats equal to 4 GiB.
Now, as we all know, 4GB just isnt good enough! We want more memory lots more! But with
only 32 bits, we cant address all of that memory. The solution? Start making addresses that are
64 bits in length. This will allow for a whopping 16 Exabytes of possible memory. (Note: No doubt
it will only be a couple of decades before I look back and laugh at myself for finding 16 Exabytes
so astounding!)
Continuing on, we need to know that addresses are also called pointers. Since these pointers
are composed 4 bytes, we need store them in a data type of this size. If you open up the
Microsoft VBA Help in the VB Editor and locate the Data Type Summary article, youll see that
Long and Object data types listed as containing 4 bytes. Not surprisingly, these are the data types
used for pointers. In the case of the SolidWorks API, the Object data type is used to store the
pointer for our SolidWorks object interfaces like ISldWorks, IModelDoc2, etc. In the case of DLL
functions, the pointers are often times declared as Long.
VBA6 is the version of VBA used in the late 90s and the following decade. It can only handle 32
bit addresses. Now that computers are using 64 bit addresses, a new version of VBA was needed
to handle addresses of this length. Thus VBA7 was created. Since VBA7 uses 64 bit memory
addresses, any DLLs that it references must be 64 bit compatible. This is where the problem
comes in for so many users: the DLLs referenced in their macros are still compiled for 32 bit
computers. For many DLLs, this issue is solved by recompiling the DLL for 64 bit.

When you call a function from a DLL in a VBA macro, you need to use the Declare statement. For
example, lets say you have a DLL called diskspace.dll that resides in C:\somepath\. Here is
how you would declare it in your module:
Declare Function getdiskinfo Lib "c:\somepath\diskinfo.dll"
(ByVal mydrive As String, ByVal myvolume As String, free As Long) As Long

On 64 bit computers, however, this will not work because our Long variables, which are being
used as pointers, cannot handle 64 bit addresses. Basically, we need a new version of Long that
can hold a 64 bit memory type. In VBA7, Microsoft created this new variable for us. It is called
LongLong, and it only works in 64 bit applications. Converting our Long variables to LongLong
isnt the only modification we have to make, however. In order to indicate that the DLL is safe for
64 bit use, we also need to insert the PtrSafe keyword after the Declare keyword. So all together
we have this:
Declare PtrSafe Function getdiskinfo Lib "c:\somepath\diskinfo.dll"
(ByVal mydrive As String, ByVal myvolume As String, free As LongLong) As
LongLong

Keep in mind that this will ONLY work on 64 bit versions of our application. For applications like
Microsoft Office, however, you can actually have a 32 bit version of Office running on a 64 bit
computer. In this case, LongLong wouldnt work, so Microsoft created a data type that transforms
into Long or LongLong depending on the application. This data type is called LongPtr.
SolidWorks x86 (32 bit) cannot be installed on a 64 bit computer, but you might as well use
LongPtr anyway:
Declare PtrSafe Function getdiskinfo Lib "c:\somepath\diskinfo.dll"
(ByVal mydrive As String, ByVal myvolume As String, free As LongPtr) As LongPtr

To summarize: once your DLL is compiled for 64 bit, all you need to do is change the pointers to
LongPtr and insert the PtrSafe keyword after the Declare keyword. Moreover, you can use what
are called Conditional Compilation Constants to allow for backward compatibility of your code,
which was demonstrated in any earlier section of the post.

Before I finish up, I want to share something interesting regarding the SolidWorks API and 64 bit.
Even though 64 bit computers have been supported by SolidWorks since SolidWorks 2006,
SolidWorks 2013 is the first version to contain VBA7 (since Microsoft did not make VBA7
available to third-party vendors until recently). That means that for SolidWorks 2006-2012 64 bit,
VBA6 was still being used. How did that work? It worked because SolidWorks Corporation
created an out-of-process COM server called swVBAserver.exe to handle the interaction between
64 bit SolidWorks and 32 bit VBA. (You may have noticed swVBAserver.exe in your Task
Manager.) Normally, if VBA and SolidWorks were both 32 bit or 64 bit, VBA can run inside the
SLDWORKS.EXE process. When this isnt the case, however, VBA must run out of process,
which also causes the API to run slightly slower. The out of process scenario is also what caused
the infamous issue of user forms appearing behind the SolidWorks application: it is the
swVBAserver.exe that owned the form, not SLDWORKS.EXE. In SolidWorks 2013, VBA does run
inside the SLDWORKS.EXE process, so the correct owner is identified and no additional code is
necessary. (Note: swVBAserver.exe is still used in SolidWorks 2013 and later, which is why you
will see it in the Task Manager, but not as a proxy server for 32 bit VBA. It is also used to drive
equation updates using IEquationMgr.)
If you want more resources on the technical aspects of the move to 64 bit, Id encourage you to
check out the following:

S-ar putea să vă placă și