How can I make a network connection with Visual Basic from Microsoft Access? - ms-access

We have a Visual Basic application inside of Microsoft Access and we need to make a network connection. With VB6, there was a handy little control called WinSock that made this possible, but I can't find anything similar for the stripped down VB version that exists inside of Microsoft Access. Any ideas?
Since I'm not getting any answers, I'll try to clarify what I need this for.
My application sends out an email, and we're currently using a built-in Outlook object to create a message and send it in the background. The drawback is that it prompts the user to approve an "outside program" to send an email, which is frustrating our users and seems unnecessary. All of the other emailing options I've been able to find online require us to either download or purchase a control, which would be too labor intensive for us to deploy to all of our users.
I was hoping to use a socket control to manually connect to the SMTP server and send a message (since this is trivial in other languages) but I can't find any way to make a TCP connection in VBA.

I just dealt with this very issue in the last month. For various reasons, CDO was not adequate, direct use of MAPI way too complex, and the Outlook prompt you complain about completely unacceptable.
I ended up using Outlook Redemption. It's widely used by Access developers, though I found it to be rather convoluted and not terribly well-documented. But it is doing the job quite well.

The email "security" feature added by Microsoft has frustrated many developers. I don't know of an elegant solution. I've used the freeware app ClickYes Express with success, but of course that's not the answer you seek.

For the specific problem mentioned in the OP, there is a better solution. 'save' mail to Outlook. Do not 'send' it. It gives the user explicit control over what is sent, and when, and does not generate pop-up dialogs. A triple win.
But since you ask....
Option Explicit
Public Const AF_INET = 2 'internetwork: UDP, TCP, etc.
Public Const SOCK_STREAM = 1 'Stream socket
Public Const SOCKET_ERROR = -1
Type sockaddr_in
sin_family As Integer
sin_port As Integer
sin_addr As Long
sin_zero As String * 8
End Type
#If Win32 Then
'for WSAStartup() function.
Public Const WSADESCRIPTION_LEN = 256
Public Const WSASYS_STATUS_LEN = 128
Public Const WSA_DescriptionSize = WSADESCRIPTION_LEN + 1
Public Const WSA_SysStatusSize = WSASYS_STATUS_LEN + 1
Type wsaData
wVersion As Integer
wHighVersion As Integer
szDescription As String * WSA_DescriptionSize
szSystemStatus As String * WSA_SysStatusSize
iMaxSockets As Integer
iMaxUdpDg As Integer
lpVendorInfo As String * 200
End Type
#If Not VBA7 Then
'Use this section for Excel 95
Type Hostent
h_name As Long '32 bit pointer
h_aliases As Long '32 bit pointer
h_addrtype As Integer 'String * 2 (declared as short)
h_length As Integer 'String * 2 (declared as short)
h_addr_list As Long '32 bit pointer
End Type
Public Declare Function closesocket Lib "ws2_32.dll" (ByVal s As Long) As Long
Public Declare Function connect Lib "ws2_32.dll" (ByVal sID As Long, ByRef name As sockaddr_in, ByVal namelen As Long) As Long
Public Declare Function htons Lib "ws2_32.dll" (ByVal hostshort As Integer) As Integer
Public Declare Function inet_addr Lib "ws2_32.dll" (ByVal cp As String) As Long
Public Declare Function recv Lib "ws2_32.dll" (ByVal s As Long, ByRef buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
Public Declare Function recvstr Lib "ws2_32.dll" (ByVal s As Long, ByVal buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
Public Declare Function send Lib "ws2_32.dll" (ByVal s As Long, ByRef buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
Public Declare Function socket Lib "ws2_32.dll" (ByVal af As Long, ByVal s_type As Long, ByVal Protocol As Long) As Long
Public Declare Function WSAStartup Lib "ws2_32.dll" (wVersionRequested As Integer, lpWSAData As wsaData) As Long
Public Declare Function WSACleanup Lib "ws2_32.dll" () As Long
'Public Declare Function setsockopt Lib "ws2_32.dll" (ByVal s As Long, ByVal level As Long, ByVal optname As Long, optval As Any, ByVal optlen As Long) As Long
Public Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Public Declare Function gethostbyname Lib "ws2_32.dll" (ByVal host_name As String) As Long
#Else
'on Win64, ws2_32.dll in system32 has the file description "32-bit DLL" and uses 64bit pointers (morons)
'on Win64 as on Win32, 32-bit numbers are called int.
'on VBA7/64, as on VBA6/32, 32 bit numbers are called long.
'delete following duplicate section for Excel 95
Type Hostent
h_name As LongPtr '32/64 bit pointer
h_aliases As LongPtr '32/64 bit pointer
h_addrtype As Integer 'String * 2 (declared as short)
h_length As Integer 'String * 2 (declared as short)
h_addr_list As LongPtr '32/64 bit pointer
End Type
Public Declare PtrSafe Function closesocket Lib "ws2_32.dll" (ByVal sID As LongPtr) As Long
Public Declare PtrSafe Function connect Lib "ws2_32.dll" (ByVal sID As LongPtr, ByRef name As sockaddr_in, ByVal namelen As Long) As Long
Public Declare PtrSafe Function htons Lib "ws2_32.dll" (ByVal hostshort As Integer) As Integer
Public Declare PtrSafe Function inet_addr Lib "ws2_32.dll" (ByVal cp As String) As Long
Public Declare PtrSafe Function recv Lib "ws2_32.dll" (ByVal sID As LongPtr, ByRef buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
Public Declare PtrSafe Function recvstr Lib "ws2_32.dll" (ByVal sID As LongPtr, ByVal buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
Public Declare PtrSafe Function send Lib "ws2_32.dll" (ByVal sID As LongPtr, ByRef buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
Public Declare PtrSafe Function socket Lib "ws2_32.dll" (ByVal af As Long, ByVal s_type As Long, ByVal Protocol As Long) As Long
Public Declare PtrSafe Function WSAStartup Lib "ws2_32.dll" (wVersionRequested As Integer, lpWSAData As wsaData) As Long
Public Declare PtrSafe Function WSACleanup Lib "ws2_32.dll" () As Long
'Public Declare PtrSafe Function setsockopt Lib "ws2_32.dll" (ByVal sID As Long, ByVal level As LongPtr, ByVal optname As Long, optval As Any, ByVal optlen As Long) As Long
Public Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As LongPtr)
Public Declare PtrSafe Function gethostbyname Lib "ws2_32.dll" (ByVal host_name As String) As LongPtr
#End If
#Else
'OSX
'delete following duplicate section for Excel 95
'No 64bit version of Excel is available yet for the OSX
Type Hostent
h_name As Long '32 bit pointer
h_aliases As Long '32 bit pointer
h_addrtype As Long '32 bit int (declared as int)
h_length As Long '32 bit int (declared as int)
h_addr_list As Long '32 bit pointer
End Type
'ssize_t is a signed type. signed version of size_t,
'used where a size may instead contain a negative error code
'size_t is the unsigned integer type of the result of the sizeof operator
'size_t is an unsigned integer type of at least 16 bit
'or libsystem.dylib ?
Public Declare Function socket Lib "libc.dylib" (ByVal af As Long, ByVal s_type As Long, ByVal Protocol As Long) As Long
Public Declare Function connect Lib "libc.dylib" (ByVal s As Long, ByRef name As sockaddr_in, ByVal namelen As Long) As Long
' or read ?
Public Declare Function recv Lib "libc.dylib" (ByVal s As Long, buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
Public Declare Function send Lib "libc.dylib" (ByVal s As Long, buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long
Public Declare Function htons Lib "libc.dylib" (ByVal Host_Short As Integer) As Integer 'x x x, but seems to work !!!
Public Declare Function inet_addr Lib "libc.dylib" (ByVal cp As String) As Long
Public Declare Function closesocket Lib "libc.dylib" Alias "close" (ByVal s As Long) As Long
Public Declare Function setsockopt Lib "libc.dylib" (ByVal s As Long, ByVal level As Long, ByVal optname As Long, optval As Any, ByVal optlen As Long) As Long
Public Declare Function gethostbyname Lib "libc.dylib" (ByVal host_name As String) As Long
Public Declare Sub CopyMemory Lib "libc.dylib" Alias "memmove" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
#End If
Private Function MyData(I_SocketAddress As sockaddr_in, Register As Integer, dataword As Long, serr As String) As Long
Dim strSend As String
Dim count As Integer
Dim bArray() As Byte
Dim errCode As Integer
Dim socketID As Long
socketID = socket(AF_INET, SOCK_STREAM, 0)
errCode = connect(socketID, I_SocketAddress, Len(I_SocketAddress))
count = send(socketID, ByVal strSend, Len(strSend), 0)
If count <> Len(strSend) Then
errCode = -1
serr = "ERROR: network failure on send, " & Err.LastDllError()
Else
count = RecvB(socketID, bArray, maxLength)
dodata bArray
End If
DoEvents
Call closesocket(socketID)
MyData = errCode
End Function
Private Function RecvB(socketID As Long, bArray() As Byte, ByVal maxLength As Integer) As Integer
Dim c As String * 1
Dim b As Byte
Dim buf() As Byte
Dim Length As Integer
Dim count As Long
Dim i As Integer
Dim dStartTime As Variant
Dim nErr As Long
Const iFlags = 0
ReDim bArray(1 To maxLength)
ReDim buf(1 To maxLength)
dStartTime = Time
While (Length < maxLength) And (4 > DateDiff("s", dStartTime, Time))
DoEvents
count = recv(socketID, buf(1), maxLength, iFlags)
If count = SOCKET_ERROR Then '-1
nErr = Err.LastDllError()
If nErr = 0 Then
RecvB = -1
Else
RecvB = -nErr
End If
'Debug.Print "socket_error in RecvB. lastdllerror:", nErr
Exit Function '
End If '
For i = 1 To count
bArray(Length + i) = buf(i)
Next
Length = Length + count
Wend
RecvB = Length
End Function
This is TCP code, not email code. It's also includes OSX VBA TCP code, which I have not previously published.

Related

Prevent Linked Table to SQL Server Showing Actual Values for Binary

We have a user table in a SQL Server 2014 that I link to in an Access database front end and the password is in binary 64 so that the password cannot be seen if someone were to open the table somehow in SSMS.
But Access knows all this and completely converts it to the actual password. How do I get around this yet still use it to validate data entered into a login form?
You hash the password. Storing passwords as plaintext without hashing is a major bad practice.
Read more about hashing on Wikipedia. The short version it's a one-way operation: if you have the password, you can create the hash, but if you have the hash, there's no way to get the password except trying to hash random passwords and see if they're the same.
However, hashing in VBA is rather complicated. There are more simple answers that use .Net hashing objects, but I use the CNG API, which has numerous advantages such as hardware crypto support, zero dependencies and flexibility in the choice of algorithm:
Public Declare PtrSafe Function BCryptOpenAlgorithmProvider Lib "BCrypt.dll" (ByRef phAlgorithm As LongPtr, ByVal pszAlgId As LongPtr, ByVal pszImplementation As LongPtr, ByVal dwFlags As Long) As Long
Public Declare PtrSafe Function BCryptCloseAlgorithmProvider Lib "BCrypt.dll" (ByVal hAlgorithm As LongPtr, ByVal dwFlags As Long) As Long
Public Declare PtrSafe Function BCryptCreateHash Lib "BCrypt.dll" (ByVal hAlgorithm As LongPtr, ByRef phHash As LongPtr, pbHashObject As Any, ByVal cbHashObject As Long, ByVal pbSecret As LongPtr, ByVal cbSecret As Long, ByVal dwFlags As Long) As Long
Public Declare PtrSafe Function BCryptHashData Lib "BCrypt.dll" (ByVal hHash As LongPtr, pbInput As Any, ByVal cbInput As Long, Optional ByVal dwFlags As Long = 0) As Long
Public Declare PtrSafe Function BCryptFinishHash Lib "BCrypt.dll" (ByVal hHash As LongPtr, pbOutput As Any, ByVal cbOutput As Long, ByVal dwFlags As Long) As Long
Public Declare PtrSafe Function BCryptDestroyHash Lib "BCrypt.dll" (ByVal hHash As LongPtr) As Long
Public Declare PtrSafe Function BCryptGetProperty Lib "BCrypt.dll" (ByVal hObject As LongPtr, ByVal pszProperty As LongPtr, ByRef pbOutput As Any, ByVal cbOutput As Long, ByRef pcbResult As Long, ByVal dfFlags As Long) As Long
Public Function NGHash(pData As LongPtr, lenData As Long, Optional HashingAlgorithm As String = "SHA1") As Byte()
'Erik A, 2019
'Hash data by using the Next Generation Cryptography API
'Loosely based on https://learn.microsoft.com/en-us/windows/desktop/SecCNG/creating-a-hash-with-cng
'Allowed algorithms: https://learn.microsoft.com/en-us/windows/desktop/SecCNG/cng-algorithm-identifiers. Note: only hash algorithms, check OS support
'Error messages not implemented
On Error GoTo VBErrHandler
Dim errorMessage As String
Dim hAlg As LongPtr
Dim algId As String
'Open crypto provider
algId = HashingAlgorithm & vbNullChar
If BCryptOpenAlgorithmProvider(hAlg, StrPtr(algId), 0, 0) Then GoTo ErrHandler
'Determine hash object size, allocate memory
Dim bHashObject() As Byte
Dim cmd As String
cmd = "ObjectLength" & vbNullString
Dim Length As Long
If BCryptGetProperty(hAlg, StrPtr(cmd), Length, LenB(Length), 0, 0) <> 0 Then GoTo ErrHandler
ReDim bHashObject(0 To Length - 1)
'Determine digest size, allocate memory
Dim hashLength As Long
cmd = "HashDigestLength" & vbNullChar
If BCryptGetProperty(hAlg, StrPtr(cmd), hashLength, LenB(hashLength), 0, 0) <> 0 Then GoTo ErrHandler
Dim bHash() As Byte
ReDim bHash(0 To hashLength - 1)
'Create hash object
Dim hHash As LongPtr
If BCryptCreateHash(hAlg, hHash, bHashObject(0), Length, 0, 0, 0) <> 0 Then GoTo ErrHandler
'Hash data
If BCryptHashData(hHash, ByVal pData, lenData) <> 0 Then GoTo ErrHandler
If BCryptFinishHash(hHash, bHash(0), hashLength, 0) <> 0 Then GoTo ErrHandler
'Return result
NGHash = bHash
ExitHandler:
'Cleanup
If hAlg <> 0 Then BCryptCloseAlgorithmProvider hAlg, 0
If hHash <> 0 Then BCryptDestroyHash hHash
Exit Function
VBErrHandler:
errorMessage = "VB Error " & Err.Number & ": " & Err.Description
ErrHandler:
If errorMessage <> "" Then MsgBox errorMessage
Resume ExitHandler
End Function
Public Function HashBytes(Data() As Byte, Optional HashingAlgorithm As String = "SHA512") As Byte()
HashBytes = NGHash(VarPtr(Data(LBound(Data))), UBound(Data) - LBound(Data) + 1, HashingAlgorithm)
End Function
Public Function HashString(str As String, Optional HashingAlgorithm As String = "SHA512") As Byte()
HashString = NGHash(StrPtr(str), Len(str) * 2, HashingAlgorithm)
End Function
You can now use the HashString function to hash passwords. When someone enters a password, always use HashString(password) to look up the password or store a hashed password. You never store an actual unhashed password.
Of course, this also means that even you can not view passwords of users, only their hashes.
If you want to improve this further, you can use a salt to avoid rainbow table attacks. But only adding a hash will already substantially improve security.

Remove ribbon but keep QAT in Access using VBA

I've successfully removed the ribbon on startup by using this line of code:
DoCmd.ShowToolbar "Ribbon", acToolbarNo
But this also removes the Quick Access Toolbar. I want this to stay so that the user can only see the icons I've selected to be shown (Copy, Paste, Export to Excel, etc,.)
My ultimate goal is to have only these icons appear, and the header to be otherwise empty, thin and out of the way. I'm trying to save as much screen space as possible, and I do not want the user to have to ability to expand the ribobn.
Is there a similar line of code that will force the QAT to be shown. Furthermore, is there a way to select which shortcuts will appear in the QAT with VBA. I know this can be done manually, but I'm trying to automate it.
Looks like it's possible using custom Access XML
Start by going and reading through this
https://support.office.com/en-us/article/Customize-the-Ribbon-45e110b9-531c-46ed-ab3a-4e25bc9413de
and then you'll need to create your own bit of XML code to control everything
here a list of the control idMso to use http://www.ribboncreator2010.de/Onlinehelp/EN/_2el0osmon.htm
the following is a something i whipped together as a proof of concept
<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon startFromScratch="true">
<tabs>
<tab idMso="TabAddIns">
</tab>
</tabs>
<qat>
<documentControls>
<control idMso="SaveObjectAs" />
</documentControls>
</qat>
</ribbon>
</customUI>
this is what is looks like
Not sure how helpful is replying to 5 years old thread, but who knows...
Few days ago I had to (finally) move from Access 2010 to 2021 and was stunned with new UI in general, but mostly with how much space is wasted at the top, especially for my usual scenario for distributed apps - QAT without ribbons and everything else on the right mouse. I didn't take it very seriously at first, believing I'm not the only one with such layout and ready-made solution(s) would be there, in numbers. That was a second surprise, because there are none. "Blank" ribbon is still wasting 40-50px of space with File ribbon not removable. Aside from ruining the concept, everybody will ask "Why is this blank File menu there doing nothing...??"
This is not very difficult to solve, but (like everybody else) I prefer copy/paste to writing own solution from scratch, sadly it was not there to grab :) So here's simple solution for those who want their ~40px back :)
It is assumed you handle all the usual stuff like hiding navigation bar, prevent F11, opening with Shift, hiding Taskbar etc. Or not. It will work even in "design" mode scenarios, but it's not meant for that. For now I simply hardcoded 45px for Window bar/QAT height, can be modified, calculated, but can't be bothered at the moment. Also, this will not work with Access 2010 and earlier, can be made to work if somebody wants to play with it, I'll not
Here's code
Somewhere in your start up routine add these 2 lines of code
moveMDIUp
SubClass_On Access.hWndAccessApp
Create new Code module and insert this code:
'######################################################################
'Module to show only QAT and hide the ribbon
'Miroslav, June 2022
'######################################################################
Option Compare Database
Option Explicit
'Set height of window/QAT bar you wish to keep/see
Private Const QAT_Height = 45
Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWndParent As LongPtr, ByVal hWndChildAfter As LongPtr, ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal nCmdShow As Long) As LongPtr
Private Declare PtrSafe Function GetWindowRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As RECT) As LongPtr
Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hwnd As LongPtr, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As LongPtr
Private Declare PtrSafe Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As LongPtr, ByVal hwnd As LongPtr, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As LongPtr
Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal wNewWord As LongPtr) As LongPtr
Private Const GWL_WNDPROC = (-4)
Private Const WM_WINDOWPOSCHANGED = &H47
Private Const WM_SYSCOMMAND = &H112
Private m_PrevProc As LongPtr
Private m_PrevProcSH As LongPtr
Private SHDhwnd As LongPtr
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Public Sub moveMDIUp()
Dim MDIhwnd, ACChwnd, TASKhwnd, NAVhwnd, RIBhwnd, s
Dim navWidth As Long: Dim taskHeight As Long: Dim ribHeight As Long: Dim accWidth As Long: Dim accHeight As Long: Dim mRECT As RECT
ACChwnd = Application.hWndAccessApp
MDIhwnd = FindWindowEx(ACChwnd, 0, "MDIClient", vbNullString)
SHDhwnd = FindWindowEx(ACChwnd, 0, "DropShadow", vbNullString)
'Hide shadow below the ribbon
ShowWindow SHDhwnd, 0
'Get Access width and height
GetWindowRect ACChwnd, mRECT
accHeight = mRECT.Bottom - mRECT.Top
accWidth = mRECT.Right - mRECT.Left
'Find height of taskbar, if not shown it's 0 height
TASKhwnd = FindWindowEx(ACChwnd, 0, "MsoCommandBarDock", "MsoDockBottom")
TASKhwnd = FindWindowEx(TASKhwnd, 0, "MsoCommandBar", "Status Bar")
GetWindowRect TASKhwnd, mRECT
taskHeight = mRECT.Bottom - mRECT.Top
s = SetWindowPos(MDIhwnd, 0, 0, QAT_Height, accWidth - 2, accHeight - QAT_Height - taskHeight - 1, 0)
End Sub
Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
WindowProc = CallWindowProc(m_PrevProc, hwnd, uMsg, wParam, lParam)
If (uMsg = WM_SYSCOMMAND) And (wParam = 61536) Then
'unhook here...
Call SubClass_Off(Application.hWndAccessApp)
Call SubClassSH_Off(SHDhwnd)
Exit Function
End If
End Function
Public Function SubClass_On(ByVal hwnd As Long)
m_PrevProc = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf WindowProc)
Call SubClassSH_On(SHDhwnd)
End Function
Public Function SubClass_Off(ByVal hwnd As Long)
SetWindowLong hwnd, GWL_WNDPROC, m_PrevProc
End Function
Public Function WindowProcSH(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
WindowProcSH = CallWindowProc(m_PrevProcSH, hwnd, uMsg, wParam, lParam)
If uMsg = WM_WINDOWPOSCHANGED Then
moveMDIUp
End If
End Function
Public Function SubClassSH_On(ByVal hwnd As Long)
m_PrevProcSH = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf WindowProcSH)
End Function
Public Function SubClassSH_Off(ByVal hwnd As Long)
SetWindowLong hwnd, GWL_WNDPROC, m_PrevProcSH
End Function

Form_Open VBA Code not working

I have a access database with some forms and vba code behind the forms.
On Form_Open (on every form) I have this piece of code.
Dim hWindow As Long
Dim nResult As Long
Dim nCmdShow As Long
hWindow = Application.hWndAccessApp
nCmdShow = SW_HIDE
nResult = ShowWindow(ByVal hWindow, ByVal nCmdShow)
Call ShowWindow(Me.hWnd, SW_NORMAL)
This hides Access and shows only the opened form.
My problem is that Access is starting, but the form will not open.
I have to kill the Access task in task manager.
Is there any way to solve the problem?
EDIT: Every form is a PopUp
As I understand, you need to hide main form and leave popup form open. I normally use window opacity settings instead of hiding:
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function SetLayeredWindowAttributes Lib "user32" (ByVal hwnd As Long, ByVal crKey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As Long
Private Const LWA_ALPHA As Long = &H2&
Private Const LWA_COLORKEY As Long = &H1&
Private Const GWL_EXSTYLE As Long = -20
Private Const WS_EX_LAYERED As Long = &H80000
Public Function SetWndOpacity(hwnd As Long, opacity As Byte, clr As Long)
DoCmd.Maximize
SetWindowLong hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) Or WS_EX_LAYERED
SetLayeredWindowAttributes hwnd, 0&, opacity, LWA_ALPHA
End Function
Then call:
SetWndOpacity Access.hWndAccessApp(), 0, 0

use AddressOf in VBA in win x64

I use this program in winx86 without any error but when i try use it in win x64 my problems started.
I use ptrsafe in my code to let me run in win 7 64bit, I'll add that this module have more code but for limitations of this site i had delete that. if need to now that lines of my code tell me to put them here.
please help me and tell me Why my code produce an error:
'UDT for passing data through the hook
Private Type MSGBOX_HOOK_PARAMS
hwndOwner As Long
hHook As Long
End Type
Private MSGHOOK As MSGBOX_HOOK_PARAMS
#If VBA7 Then
Private Declare PtrSafe Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" _
(ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long
#Else
Private Declare Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" _
(ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long
#End If
Public Function MsgBoxHookProc(ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
If uMsg = HCBT_ACTIVATE Then
SetDlgItemText wParam, vbYes, "بلـي"
SetDlgItemText wParam, vbNo, "خـير"
SetDlgItemText wParam, vbIgnore, "انصـراف"
SetDlgItemText wParam, vbOK, "تـــاييد"
UnhookWindowsHookEx MSGHOOK.hHook
End If
MsgBoxHookProc = False
End Function
Public Function MsgBoxFa(Prompt, Optional Buttons As VbMsgBoxStyle = vbOKOnly, Optional Tiltle = "", Optional HelpFile, Optional Context) As Long
'Wrapper function for the MessageBox API
Dim hwndThreadOwner As Long
Dim frmCurrentForm As Form
Set frmCurrentForm = Screen.ActiveForm
hwndThreadOwner = frmCurrentForm.hwnd
Dim hInstance As Long
Dim hThreadId As Long
Dim hwndOwner As Long
hwndOwner = GetDesktopWindow()
hInstance = GetWindowLong(hwndThreadOwner, GWL_HINSTANCE)
hThreadId = GetCurrentThreadId()
With MSGHOOK
.hwndOwner = hwndOwner
'in next line the error produced******************************
.hHook = SetWindowsHookEx(WH_CBT, _
AddressOf MsgBoxHookProc, _
hInstance, hThreadId)
End With
MsgBoxFa = MessageBox(hwndThreadOwner, Prompt, Tiltle, Buttons)
End Function

Passing a LPCTSTR parameter to an API call from VBA in a PTRSAFE and UNICODE safe manner

I've been fighting with this for a good week. I am having difficulties when passing string pointers to a DLL function.
Background
We have just started migrating to Office 2010, from Office 2003. Some people will continue to only have Office 2003 for the next few years. Some people will be using Office 2010 64-bit (why I don't know - but that's a different subject).
For me - I need to make some code that will work on all versions. I had found this function on the internet some years back and was using it. When I went to do a rewrite on my library, I noticed that there was a total mixture of Unicode vs ANSI calls .. and the function outright did not work on Access 2010. So I went to re-write it. I think I'm close - but I notice that the dll calls are not returning the proper values.
What I've done to attempt to solve the problem
I have made sure I read up on ByRef and ByVal parameter passing.
I've read up on the difference between varptr() and strptr(). I believe I am using them correctly.
I've tried declaring the lpctstr as a string but am uncomfortable with this approach since I am unsure how it will play out on a 64-Bit system, or on a Unicode system.
When working with pointers - such oversights will crash and potentially corrupt a DB
Using pointers means I don't have to convert to and from Unicode - its either in Unicode or it isn't - and the conditional compilation statements ensure the proper functions are referenced.
Short Summary Example
Public Sub foo()
 Dim strA As String
 Dim strCB As String
#If VB7 Then
 Dim lptstrA As LongPtr
 Dim lResult As LongPtr
#Else
 Dim lptstrA As Long
 Dim lResult As Long
#End If
 
 strA = "T:\TEST\"
 lptstrA = StrPtr(strA)
 strCB = String$(255, vbNullChar)
 lResult = PathIsNetworkPath(lptstrA)
#If UNICODE Then
 CopyMemory StrPtr(strCB), lptstrA, (Len(strA))
#Else
 CopyMemory StrPtr(strCB), lptstrA, (Len(strA) * 2)
#End If
 Debug.Print "Returned: " & lResult
 Debug.Print "Buffer: " & strCB
 Debug.Print "Result: " & strA
End Sub
This, in my mind should work. I'm passing the pointer to the string. But...
Results
foo
Returned: 0
Buffer: T:\TEST\
Result: T:\TEST\
So the function is returning zero .. it should return 1. But if we examine the contents of the memory at the pointer - it clearly has the data in it.
Full Code
(Doesn't Work)
Option Explicit
'
' WNetGetConnection Return Result Constants
Private Const ERROR_SUCCESS As Long = 0&
Private Const ERROR_BAD_DEVICE As Long = 1200&
Private Const ERROR_NOT_CONNECTED = 2250&
Private Const ERROR_MORE_DATA = 234&
Private Const ERROR_CONNECTION_UNAVAIL = 1201&
Private Const ERROR_NO_NETWORK = 1222&
Private Const ERROR_EXTENDED_ERROR = 1208&
Private Const ERROR_NO_NET_OR_BAD_PATH = 1203&
'
' WNetGetConnection function retrieves the name of the network resource
' associated with a local device.
' > msdn.microsoft.com/en-us/library/windows/desktop/aa385453(v=vs.85).aspx
' - If the function succeeds, the return value is NO_ERROR.
' - If the function fails, the return value is a system error code, such as
' one of the following values.
'
' PathIsUNC function determines if the string is a valid Universal Naming
' Convention (UNC) for a server and share path.
' > msdn.microsoft.com/en-us/library/windows/desktop/bb773712(v=vs.85).aspx
' - Returns TRUE if the string is a valid UNC path, or FALSE otherwise.
'
' PathIsNetworkPath function determines whether a path string represents a
' network resource.
' > msdn.microsoft.com/en-us/library/windows/desktop/bb773640(v=vs.85).aspx
' - Returns TRUE if the string represents a network resource, or FALSE
' otherwise.
'
' PathStripToRoot function removes all parts of the path except for the root
' information.
' > msdn.microsoft.com/en-us/library/windows/desktop/bb773757(v=vs.85).aspx
' - Returns TRUE if a valid drive letter was found in the path, or FALSE
' otherwise.
'
' PathSkipRoot function parses a path, ignoring the drive letter or Universal
' Naming Convention (UNC) server/share path elements.
' > msdn.microsoft.com/en-us/library/windows/desktop/bb773754(v=vs.85).aspx
' - Returns the address of the beginning of the subpath that follows the root
' (drive letter or UNC server/share).
'
' PathRemoveBackslash function removes the trailing backslash from a given
' path.
' > msdn.microsoft.com/en-us/library/windows/desktop/bb773743(v=vs.85).aspx
' - Returns the address of the NULL that replaced the backslash, or the
' address of the last character if it's not a backslash.
' For Access 2010 64-Bit Support, as well as backward compatibility
#If VBA7 Then
#If UNICODE Then
Public Declare PtrSafe Function WNetGetConnection _
Lib "mpr.dll" Alias "WNetGetConnectionW" ( _
ByVal lpLocalName As LongPtr, _
ByVal lpRemoteName As LongPtr, _
lpnLength As Long _
) As Long
Public Declare PtrSafe Function PathIsUNC _
Lib "shlwapi.dll" Alias "PathIsUNCW" ( _
ByVal pszPath As LongPtr _
) As Long
Public Declare PtrSafe Function PathIsNetworkPath _
Lib "shlwapi.dll" Alias "PathIsNetworkPathW" ( _
ByVal pszPath As LongPtr _
) As Long
Public Declare PtrSafe Function PathStripToRoot _
Lib "shlwapi.dll" Alias "PathStripToRootW" ( _
ByVal pPath As LongPtr _
) As Long
Public Declare PtrSafe Function PathSkipRoot _
Lib "shlwapi.dll" Alias "PathSkipRootW" ( _
ByVal pPath As LongPtr _
) As Long
Public Declare PtrSafe Function PathRemoveBackslash _
Lib "shlwapi.dll" Alias "PathRemoveBackslashW" ( _
ByVal strPath As LongPtr _
) As LongPtr
Public Declare PtrSafe Function lStrLen _
Lib "kernel32" Alias "lstrlenW" ( _
ByVal lpString as longptr _
) As Integer
#Else
Public Declare PtrSafe Function WNetGetConnection _
Lib "mpr.dll" Alias "WNetGetConnectionA" ( _
ByVal lpLocalName As LongPtr, _
ByVal lpRemoteName As LongPtr, _
ByVal lpnLength As Long _
) As Long
Public Declare PtrSafe Function PathIsUNC _
Lib "shlwapi.dll" Alias "PathIsUNCA" ( _
ByVal pszPath As LongPtr _
) As Long
Public Declare PtrSafe Function PathIsNetworkPath _
Lib "shlwapi.dll" Alias "PathIsNetworkPathA" ( _
ByVal pszPath As LongPtr _
) As Long
Public Declare PtrSafe Function PathStripToRoot _
Lib "shlwapi.dll" Alias "PathStripToRootA" ( _
ByVal pPath As LongPtr _
) As Long
Public Declare PtrSafe Function PathSkipRoot _
Lib "shlwapi.dll" Alias "PathSkipRootA" ( _
ByVal pPath As LongPtr _
) As Long
Public Declare PtrSafe Function PathRemoveBackslash _
Lib "shlwapi.dll" Alias "PathRemoveBackslashA" ( _
ByVal strPath As LongPtr _
) As LongPtr
Public Declare PtrSafe Function lStrLen _
Lib "kernel32" Alias "lstrlenA" ( _
ByVal lpString As LongPtr _
) As Integer
#End If
Public Declare Sub CopyMemory _
Lib "kernel32" Alias "RtlMoveMemory" ( _
ByVal Destination As LongPtr, _
ByVal Source As LongPtr, _
ByVal Length As Long _
)
#Else
#If UNICODE Then
Public Declare Function WNetGetConnection _
Lib "mpr.dll" Alias "WNetGetConnectionW" ( _
ByVal lpLocalName As Long, _
ByVal lpRemoteName As Long, _
lpnLength As Long _
) As Long
Public Declare Function PathIsUNC _
Lib "shlwapi.dll" Alias "PathIsUNCW" ( _
ByVal pszPath As Long _
) As Long
Public Declare Function PathIsNetworkPath _
Lib "shlwapi.dll" Alias "PathIsNetworkPathW" ( _
ByVal pszPath As Long _
) As Long
Public Declare Function PathStripToRoot _
Lib "shlwapi.dll" Alias "PathStripToRootW" ( _
ByVal pPath As Long _
) As Long
Public Declare Function PathSkipRoot _
Lib "shlwapi.dll" Alias "PathSkipRootW" ( _
ByVal pPath As Long _
) As Long
Public Declare Function PathRemoveBackslash _
Lib "shlwapi.dll" Alias "PathRemoveBackslashW" ( _
ByVal strPath As Long _
) As Long
Public Declare Function lStrLen _
Lib "kernel32" Alias "lstrlenW" ( _
ByVal lpString As Long _
) As Integer
#Else
Public Declare Function WNetGetConnection _
Lib "mpr.dll" Alias "WNetGetConnectionA" ( _
ByVal lpLocalName As Long, _
ByVal lpRemoteName As Long, _
ByVal lpnLength As Long _
) As Long
Public Declare Function PathIsUNC _
Lib "shlwapi.dll" Alias "PathIsUNCA" ( _
ByVal pszPath As Long _
) As Long
Public Declare Function PathIsNetworkPath _
Lib "shlwapi.dll" Alias "PathIsNetworkPathA" ( _
ByVal pszPath As Long _
) As Long
Public Declare Function PathStripToRoot _
Lib "shlwapi.dll" Alias "PathStripToRootA" ( _
ByVal pPath As Long _
) As Long
Public Declare Function PathSkipRoot _
Lib "shlwapi.dll" Alias "PathSkipRootA" ( _
ByVal pPath As Long _
) As Long
Public Declare Function PathRemoveBackslash _
Lib "shlwapi.dll" Alias "PathRemoveBackslashA" ( _
ByVal strPath As Long _
) As Long
Public Declare Function lStrLen _
Lib "kernel32" Alias "lstrlenA" ( _
ByVal lpString As Long _
) As Integer
#End If
Public Declare Sub CopyMemory _
Lib "kernel32" Alias "RtlMoveMemory" ( _
ByVal Destination As Long, _
ByVal Source As Long, _
ByVal Length As Long _
)
#End If
Public Function GetUncPath(tsLocal As String) As String
Dim tsRoot As String
Dim tsPath As String
Dim tsRemoteRoot As String
Dim tsRemote As String
Dim tcbTemp As String
#If VBA7 Then
Dim lptsLocal As LongPtr
Dim lptsRoot As LongPtr
Dim lptsPath As LongPtr
Dim lptsRemote As LongPtr
Dim lptcbTemp As LongPtr
Dim lpResult As LongPtr
#Else
Dim lptsLocal As Long
Dim lptsRoot As Long
Dim lptsPath As Long
Dim lptsRemote As Long
Dim lptcbTemp As Long
Dim lpResult As Long
#End If
Dim lResult As Long
' Initialize strings. Since Strings are essentially a pointer to
' a pointer, we use StrPtr() instead of VarPtr()
'
tsLocal = tsLocal & vbNullChar ' Just in case
tsRoot = String(255, vbNullChar) ' Path Root / Drive Letter
tsPath = String(255, vbNullChar) ' Path Without Root
tsRemote = String(255, vbNullChar) ' Remote Path + Root, Resolved
tcbTemp = String(255, vbNullChar) ' Temporary Copy Buffer
lptsLocal = StrPtr(tsLocal) ' Pointer to Local Path
lptsRoot = StrPtr(tsRoot) ' Pointer to Root
lptsPath = StrPtr(tsPath) ' Pointer to Path
lptsRemote = StrPtr(tsRemote) ' Pointer to Remote
' Check is already in UNC Format
lResult = PathIsUNC(lptsLocal)
If (lResult <> 0) Then
GetUncPath = tsLocal
Exit Function
End If
' Check if its a local path or network. If Local - use that path.
lResult = PathIsNetworkPath(lptsLocal)
>! PathIsNetworkPath(lptsLocal) always returns 0
If lResult = 0 Then
GetUncPath = tsLocal
Exit Function
End If
' Extract our root from path (ie. Drive letter)
' ### lStrLen(lptsLocal returns 1 ?? ###
CopyMemory lptsRoot, lptsLocal, lStrLen(lptsLocal)
>! lStrLen(lptsLocal) always returns 1 -- unsure why
lResult = PathStripToRoot(lptsRoot)
If (lResult = 0) Then
' An error has occurred
GetUncPath = ""
Exit Function
End If
' Strip Backslash
lpResult = PathRemoveBackslash(lptsRoot)
' Find our Path portion
CopyMemory lptsPath, lptsLocal, lStrLen(lptsLocal)
lptsPath = PathSkipRoot(lptsPath)
' Strip Backslash
lpResult = PathRemoveBackslash(lptsPath)
' Convert our Root to a UNC Network format
lResult = WNetGetConnection(lptsRemote, lptsRoot, lStrLen(lptsRoot))
If lResult = ERROR_SUCCESS Then
tsRemote = tsRemote & tsPath ' Add Remote + Path to build UNC path
GetUncPath = tsRemote ' Return resolved path
Else
' Errors have occurred
GetUncPath = ""
End If
End Function
What am I missing?
Here is the final product I came up with - feel free to suggest critiques.
As it was pointed out by Gserg, I don't need to worry about whether strings are stored as single-byte characters within memory, since every modern computer will now be using Unicode. Due to this, I was able to eliminate use of the CopyMemory function and use pointer arithmetic instead.
I opted out from using an object factory wrapper and instead controlled the class initilization myself.
This has been tested on Access 2003 and Access 2010. It is 32-bit and 64-bit compatible.
Module: GetUNC
Option Compare Database
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Function WNetGetConnection Lib "mpr.dll" Alias "WNetGetConnectionW" (ByVal lpLocalName As LongPtr, ByVal lpRemoteName As Long, lpnLength As Long) As Long
Private Declare PtrSafe Function PathIsUNC Lib "shlwapi.dll" Alias "PathIsUNCW" (ByVal pszPath As LongPtr) As Long
Private Declare PtrSafe Function PathIsNetworkPath Lib "shlwapi.dll" Alias "PathIsNetworkPathW" (ByVal pszPath As LongPtr) As Long
Private Declare PtrSafe Function PathStripToRoot Lib "shlwapi.dll" Alias "PathStripToRootW" (ByVal pPath As LongPtr) As LongPtr
Private Declare PtrSafe Function PathSkipRoot Lib "shlwapi.dll" Alias "PathSkipRootW" (ByVal pPath As LongPtr) As Long
Private Declare PtrSafe Function PathRemoveBackslash Lib "shlwapi.dll" Alias "PathRemoveBackslashW" (ByVal strPath As LongPtr) As LongPtr
#Else
Private Declare Function WNetGetConnection Lib "mpr.dll" Alias "WNetGetConnectionW" (ByVal lpLocalName As Long, ByVal lpRemoteName As Long, lpnLength As Long) As Long
Private Declare Function PathIsUNC Lib "shlwapi.dll" Alias "PathIsUNCW" (ByVal pszPath As Long) As Long
Private Declare Function PathIsNetworkPath Lib "shlwapi.dll" Alias "PathIsNetworkPathW" (ByVal pszPath As Long) As Long
Private Declare Function PathStripToRoot Lib "shlwapi.dll" Alias "PathStripToRootW" (ByVal pPath As Long) As Long
Private Declare Function PathSkipRoot Lib "shlwapi.dll" Alias "PathSkipRootW" (ByVal pPath As Long) As Long
Private Declare Function PathRemoveBackslash Lib "shlwapi.dll" Alias "PathRemoveBackslashW" (ByVal strPath As Long) As Long
#End If
Public Function GetUNCPath(sLocalPath As String) As String
Dim lResult As Long
#If VBA7 Then
Dim lpResult As LongPtr
#Else
Dim lpResult As Long
#End If
Dim ASLocal As APIString
Dim ASPath As APIString
Dim ASRoot As APIString
Dim ASRemoteRoot As APIString
Dim ASTemp As APIString
Set ASLocal = New APIString
ASLocal.Value = sLocalPath
If ASLocal.Pointer > 0 Then
lResult = PathIsUNC(ASLocal.Pointer)
End If
If lResult <> 0 Then
GetUNCPath = ASLocal.Value
Exit Function
End If
If ASLocal.Pointer > 0 Then
lResult = PathIsNetworkPath(ASLocal.Pointer)
End If
If lResult = 0 Then
GetUNCPath = ASLocal.Value
Exit Function
End If
' Extract Root
Set ASRoot = New APIString
ASRoot.Value = sLocalPath
If ASRoot.Length = 2 And Mid(ASRoot.Value, 2, 1) = ":" Then
' We have a Root with no Path
Set ASPath = New APIString
ASPath.Value = ""
Else
If ASRoot.Pointer > 0 Then
lpResult = PathStripToRoot(ASRoot.Pointer)
End If
ASRoot.TruncToNull
If ASRoot.Pointer > 0 And Mid(ASRoot.Value, ASRoot.Length) = "\" Then
lpResult = PathRemoveBackslash(ASRoot.Pointer)
ASRoot.TruncToPointer lpResult
End If
' Extract Path
Set ASPath = New APIString
ASPath.Value = sLocalPath
lpResult = PathSkipRoot(ASPath.Pointer)
ASPath.TruncFromPointer lpResult
If ASPath.Length > 0 Then
If ASPath.Pointer > 0 And Mid(ASPath.Value, ASPath.Length) = "\" Then
lpResult = PathRemoveBackslash(ASPath.Pointer)
ASPath.TruncToPointer lpResult
End If
End If
End If
' Resolve Local Root into Remote Root
Set ASRemoteRoot = New APIString
ASRemoteRoot.Init 255
If ASRoot.Pointer > 0 And ASRemoteRoot.Pointer > 0 Then
lResult = WNetGetConnection(ASRoot.Pointer, ASRemoteRoot.Pointer, LenB(ASRemoteRoot.Value))
End If
ASRemoteRoot.TruncToNull
GetUNCPath = ASRemoteRoot.Value & ASPath.Value
End Function
Class Module: APIString
Option Compare Database
Option Explicit
Private sBuffer As String
Private Sub Class_Initialize()
sBuffer = vbNullChar
End Sub
Private Sub Class_Terminate()
sBuffer = ""
End Sub
Public Property Get Value() As String
Value = sBuffer
End Property
Public Property Let Value(ByVal sNewStr As String)
sBuffer = sNewStr
End Property
' Truncates Length
#If VBA7 Then
Public Sub TruncToPointer(ByVal lpNewUBound As LongPtr)
#Else
Public Sub TruncToPointer(ByVal lpNewUBound As Long)
#End If
Dim lpDiff As Long
If lpNewUBound <= StrPtr(sBuffer) Then Exit Sub
lpDiff = (lpNewUBound - StrPtr(sBuffer)) \ 2
sBuffer = Mid(sBuffer, 1, lpDiff)
End Sub
' Shifts Starting Point forward
#If VBA7 Then
Public Sub TruncFromPointer(ByVal lpNewLBound As LongPtr)
#Else
Public Sub TruncFromPointer(ByVal lpNewLBound As Long)
#End If
Dim lDiff As Long
If lpNewLBound <= StrPtr(sBuffer) Then Exit Sub
If lpNewLBound >= (StrPtr(sBuffer) + LenB(sBuffer)) Then
sBuffer = ""
Exit Sub
End If
lDiff = (lpNewLBound - StrPtr(sBuffer)) \ 2
sBuffer = Mid(sBuffer, lDiff)
End Sub
Public Sub Init(Size As Long)
sBuffer = String(Size, vbNullChar)
End Sub
Public Sub TruncToNull()
Dim lPos As Long
lPos = InStr(sBuffer, vbNullChar)
If lPos = 0 Then Exit Sub
sBuffer = Mid(sBuffer, 1, lPos - 1)
End Sub
Public Property Get Length() As Long
Length = Len(sBuffer)
End Property
#If VBA7 Then
Public Property Get Pointer() As LongPtr
#Else
Public Property Get Pointer() As Long
#End If
Pointer = StrPtr(sBuffer)
End Property
Thanks for the assistance.
So what you have done is a little abstraction to pretend strings are always pointers (hmm... actually, that's a reverse abstraction to remove the built-in abstraction that pointers are strings).
You now need an easy way to use that abstraction.
Have a class, WrappedString (not tested, don't have Office 2010):
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private buf() As Byte
Friend Sub Init(s As String)
Dim len_of_s_in_bytes As Long
len_of_s_in_bytes = LenB(s)
If len_of_s_in_bytes = 0 Then Exit Sub
#If UNICODE Then
ReDim b(1 To len_of_s_in_bytes + 2) 'Adding the null terminator
CopyMemory b(LBound(b)), ByVal StrPtr(s), len_of_s_in_bytes
#Else
b = StrConv(s & vbNullChar, vbFromUnicode)
#End If
End Sub
#If VB7 Then
Public Property Get Pointer() As LongPtr
Pointer = VarPtr(b(LBound(b)))
End Property
#Else
Public Property Get Pointer() As Long
Pointer = VarPtr(b(LBound(b)))
End Property
#End If
Why you need a class and not just a conversion function: to avoid memory leaks. An allocated pointer needs to be freed, the class destructor will take care of that.
Then have a construction function in a module:
Public Function ToWrappedString(s As String) As WrappedString
Set ToWrappedString = New WrappedString
ToWrappedString.Init s
End Function
Then you can call your functions:
lResult = PathIsNetworkPath(ToWrappedString("T:\TEST\").Pointer)
Obviously, you can take this abstraction one little step further:
Have a module, put all your declares there and make them private.
Then have public functions in that module, one for each declared function (that is, Public Function PathSkipRoot (...) As String, Public Function PathRemoveBackslash (...) As String etc, and make each of those public wrappers to call the declared functions using WrappedString.
Then the rest of the code will only see plain String versions of the functions.