René Nyffenegger
René Nyffenegger

Reputation: 40499

What is the purpose of the ptrSafe attribute of a declare statement

In VBA, I need to declare a function in a DLL with the ptrSafe attribute if I am using it in a 64-bit Office environment.

As far as I can see, the ptrSafe does not technically change anything so that I am not sure what the purpose of the ptrSafe attribute actually is.

Upvotes: 2

Views: 2978

Answers (2)

Sancarn
Sancarn

Reputation: 2824

Realistically, as far as I am concerned, the keyword serves no function other than ensuring that old code will not compile in VBA7.

Existing Declare statements won't compile in 64-bit VBA until they've been marked as safe for 64-bit by using the PtrSafe attribute

Office Client Development

This having the impact that Microsoft Office could provide a standard error to users which try to run 32-bit code in VBA7 and force VBA developers to re-assess their code. If code was simply ran, it might cause a crash, which in some cases could happen on Workbook_Open or similarly "randomly".

Personally, I feel that introduction of the keyword is awful design, and contributes to the schism between VBA and VB6. The use of keyword PtrSafe provides no guarantees in reality, other than that the user has added the keyword. It does not guarantee they have checked for handles and converted those to LongPtr. And as a result of this bad design, we now need to use complex macros to compile code for both 64 and 32 bit office...

#if Win64 then
    Private Const NULL_PTR as LongLong = 0^
#else
    Private Const NULL_PTR as Long = 0&
#end if

#if VBA7 then
    Private Declare PtrSafe Function OpenClipboard Lib "user32" (Optional ByVal hWnd As LongPtr = NULL_PTR) As Long
    '...
#else
    Private Declare Function OpenClipboard Lib "user32" (Optional ByVal hWnd As Long = NULL_PTR) As Long
    '...
#end if

No, instead, Microsoft would have been better:

  1. Releasing a patch for 32-bit VBA and VB6 which adds the LongPtr type.
  2. Kept old declare syntax as it was

If this were done, the following would suffice for both 32 and 64 bit systems:

Private Declare Function OpenClipboard Lib "user32" (Optional ByVal hWnd As LongPtr = 0) As Long

And hell even if they didn't release a patch for 32-bit VBA/VB6 we could still use macros and an Enum:

#If VBA7 = 0 then
  Enum LongPtr
    [_]
  End Enum
#End If
Private Declare Function OpenClipboard Lib "user32" (Optional ByVal hWnd As LongPtr = 0) As Long
'...

But alas, because of the introduction of this keyword we now have to define all our statements twice if we want to remain compatible:

#If VBA7 then
  Private Declare PtrSafe Function OpenClipboard Lib "user32" (Optional ByVal hWnd As LongPtr = 0) As Long
  '...
#Else 
  Enum LongPtr
    [_]
  End Enum
  Private Declare Function OpenClipboard Lib "user32" (Optional ByVal hWnd As LongPtr = 0) As Long
  '...
#End If

If I were Microsoft I'd make PtrSafe keyword optional. Doing so would do a world of good.

Upvotes: 1

TourEiffel
TourEiffel

Reputation: 4424

PtrSafe serves to tell VBA that API declarations are safe: Ptr (pointer) Safe (safe, safe)

This attribute indicates that we target the 64-bit version.

For Example

Take the ShellExecute API function, which opens any file in its default application without knowing it. For example, an .xlsx document will open in Excel, a .pdf will open in Acrobat Reader, unless you have installed another PDF reader (such as Sumatra PDF).

The API declaration of this function has changed to 64-bit version. To make 32 and 64-bit versions coexist in the same database, write this:

' --- DECLARATION API WINDOWS
#If VBA7 Then
  Private Declare PtrSafe Function ShellExecute _
    Lib "shell32.dll" _
    Alias "ShellExecuteA" ( _
      ByVal hwnd As LongPtr, ByVal lpOperation As String, _
      ByVal lpFile As String, ByVal lpParameters As String, _
      ByVal lpDirectory As String, ByVal nShowCmd As Long) _
    As LongPtr
#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

Original content can be found there

Upvotes: 0

Related Questions