stdVBA
stdVBA copied to clipboard
Mac OS X support
Mac OS X support
Currently the majority of the classes in this repository will work on Windows OS only. In reality office for Mac also exists, and thus e.g. Regular expressions will have to be implemented differently to use AppleScript or JSX instead of COM.
Some libraries are also basically meaningless, e.g. the COM library is utterly meaningless on Mac OS X. It'd be advisable to have a Signal API for Mac OS X though.
In this post we should discuss:
- Which of the classes work and which classes need porting.
- General design choices for code porting.
- This issue should link to other issues where a specific class shall be ported, and specific design choices can be identified.
This post should not discuss:
- Specifics of porting/creating a specific class - These should be in an own issue.
Classes that need porting
Below is a list of classes which are ready to port to Mac OS X. I'll try to identify more as and when they arise:
| Class | Brief description of issues | Status |
|---|---|---|
| stdCallback | Usage of Win32 DLLs | 0% |
| stdArray | Usage of Win32 DLLs | 0% |
| stdLambda | Usage of Win32 DLLs | 0% |
| stdEnumerator | Usage of Win32 DLLs | 0% |
| stdRegex | Usage of Win32 DLLs | 0% |
| stdWindow | Usage of Win32 DLLs | 0% |
| stdAcc | Usage of Win32 DLLs | 0% |
Today I made a pretty large leap towards mac compatibility. One of the major problems we've been facing with Mac compatibility is a large number of the classes uses features which aren't provided by VBA but are provided by APIs.
Well today I figured "well the declare function must do something...". I found 1 example by a japanese author where they appeared to be using VB6 to execute MacOSX functions (not sure how). I noted that some of the dictionaries included DictionaryCreate() something which would be useful for this library...
After a bit more googling, on a seperate occasion I came across the following declaration:
Declare Function GetTickCount Lib "/Applications/Microsoft Excel.app/Contents/Frameworks/MicrosoftOffice.framework/MicrosoftOffice" () As Long
Sub main
Debug.Print GetTickCount()
End Sub
Huh... So frameworks can be used in Declare calls? I've known already for quite a while about the various NodeJS Objective-C bridges out there so I looked into how they do their calls to Objective-C. Turns out they use libobjc of which the full path is /usr/lib/libobjc.A.dylib!!
So I quickly put together this proof of concept and guess what? It works!!
Private Declare Function pObjc_getClass Lib "/usr/lib/libobjc.A.dylib" Alias "objc_getClass" (ByVal s As String) As Long
Private Declare Function pClass_getName Lib "/usr/lib/libobjc.A.dylib" Alias "class_getName" (ByVal lptr As Long) As String
Sub test()
Dim iClassPtr As Long: iClassPtr = objc.getClass("NSObject")
Debug.Print objc.class_getName(iClassPtr) 'NSObject
iClassPtr = objc.getClass("NSRegularExpression")
Debug.Print objc.class_getName(iClassPtr) 'NSRegularExpression
End Sub
Public Function getClass(ByVal s As String) As Long
getClass = pObjc_getClass(s)
End Function
Public Function class_getName(ByVal iClassPointer As Long) As String
Dim sClassName As String
sClassName = pClass_getName(iClassPointer) 'note strings on mac don't seem to be null terminated, so this returns the mother of all strings... Thus we need to trim this class name afterwards.
class_getName = Mid(sClassName, 1, InStr(1, sClassName, vbNullChar))
End Function
Notes
- Transformation of this PoC to e.g. regex and dictionaries is likely to be a big challenge.
- Strings returned by ObjC appear to be null terminated, but Mac VBA doesn't understand this, thus you get an endless blob which likely refers directly to a position in the blob of memory. Hence we have to search for the
vbNullCharto retrieve the specific string.- Methods used by Node-ObjC bridge can be found in the FFI calls here
- To find my notes on frameworks see here
Some shell running code, useful for stdProcess.
#If Mac Then
#If VBA7 Then
' 64 bit Office:mac
Private Declare PtrSafe Function popen Lib "/usr/lib/libc.dylib" (ByVal command As String, ByVal mode As String) As LongPtr
Private Declare PtrSafe Function pclose Lib "/usr/lib/libc.dylib" (ByVal file As LongPtr) As Long
Private Declare PtrSafe Function fread Lib "/usr/lib/libc.dylib" (ByVal outStr As String, ByVal size As LongPtr, ByVal items As LongPtr, ByVal stream As LongPtr) As Long
Private Declare PtrSafe Function feof Lib "/usr/lib/libc.dylib" (ByVal file As LongPtr) As LongPtr
Private file As LongPtr
#Else
' 32 bit Office:mac
Private Declare Function popen Lib "libc.dylib" (ByVal command As String, ByVal mode As String) As Long
Private Declare Function pclose Lib "libc.dylib" (ByVal file As Long) As Long
Private Declare Function fread Lib "libc.dylib" (ByVal outStr As String, ByVal size As Long, ByVal items As Long, ByVal stream As Long) As Long
Private Declare Function feof Lib "libc.dylib" (ByVal file As Long) As Long
Private file As Long
#End If
Public Function execShell(command As String, Optional ByRef exitCode As Long) As String
file = popen(command, "r")
If file = 0 Then
Exit Function
End If
While feof(file) = 0
Dim chunk As String
Dim read As Long
chunk = SPACE(50)
read = fread(chunk, 1, Len(chunk) - 1, file)
If read > 0 Then
chunk = Left$(chunk, read)
execShell = execShell & chunk
End If
Wend
exitCode = pclose(file) ' 0 = success
End Function
#End If
and
Private Declare Function system Lib "libc.dylib" (ByVal command As String) As Long
Sub RunSafari()
Dim result As Long
result = system("open -a Safari http://www.google.com")
Debug.Print Str(result)
End Sub
Src: https://stackoverflow.com/a/64014735/6302131