I need to check the Internet connection when an Excel workbook opens. For that, I'm trying the following code:
Private Declare PtrSafe Function InternetGetConnectedState _
Lib "wininet.dll" (ByRef dwflags As Long, _
ByVal dwReserved As Long) As Long
Private Const INTERNET_CONNECTION_MODEM As Long = &H1
Private Const INTERNET_CONNECTION_LAN As Long = &H2
Private Const INTERNET_CONNECTION_PROXY As Long = &H4
Private Const INTERNET_CONNECTION_OFFLINE As Long = &H20
Function IsInternetConnected() As Boolean
Dim L As Long
Dim R As Long
R = InternetGetConnectedState(L, 0&)
If R = 0 Then
IsInternetConnected = False
Else
If R <= 4 Then
IsInternetConnected = True
Else
IsInternetConnected = False
End If
End If
End Function
It works, but some users are reporting this is breaking Excel (it freezes Excel forever).
Is there a way to throw an error or somehow correct this code in order to find out what is the cause of the problem (Firewall, etc.)?
Thank you so much for helping!
Here are the proper imports with compiler directives.
Option Explicit
#If Win64 Then
Public Flg As LongPtr
Public Declare PtrSafe Function InternetGetConnectedState _
Lib "wininet.dll" (lpdwFlags As LongPtr, _
ByVal dwReserved As Long) As Boolean
#Else
Public Flg As Long
Public Declare Function InternetGetConnectedState _
Lib "wininet.dll" (lpdwFlags As Long, _
ByVal dwReserved As Long) As Boolean
#End If
Private Const INTERNET_CONNECTION_MODEM As Long = &H1
Private Const INTERNET_CONNECTION_LAN As Long = &H2
Private Const INTERNET_CONNECTION_PROXY As Long = &H4
Private Const INTERNET_CONNECTION_OFFLINE As Long = &H20
Function IsInternetConnected() As Boolean
Dim R As Long
R = InternetGetConnectedState(Flg, 0&)
If Flg >= INTERNET_CONNECTION_OFFLINE Then
Debug.Print "INTERNET_CONNECTION_OFFLINE"
End If
If CBool(R) Then
IsInternetConnected = True
Else
IsInternetConnected = False
End If
End Function
Sub main()
Dim mssg As String
If IsInternetConnected Then
mssg = "Connected"
Else
mssg = "Not connected"
End If
MsgBox mssg
End Sub
I believe at least one of the problems was failing to provide the correct var-type to receive the lpdwFlags. I've added a public var within the compiler directives. The Flg var receives the flags from the function. These can be parsed bitwise against your constants to determine the state. See InternetGetConnectedState for more information (and a full set of flags).
This has been tested on both 32-bit and 64-bit xl2010.
Related
Trying to get HKEY name using NtQueryObject
Private Const MAX_PATH As Long = 260
Private Const HKEY_CLASSES_ROOT As Long = &H80000000
Private Const HKEY_CURRENT_USER As Long = &H80000001
Private Const HKEY_LOCAL_MACHINE As Long = &H80000002
Private Const HKEY_USERS As Long = &H80000003
Private Const HKEY_PERFORMANCE_DATA As Long = &H80000004
Private Const HKEY_CURRENT_CONFIG As Long = &H80000005
Private Const KEY_READ As Long = &H20019
Private Const ERROR_SUCCESS As Long = 0
Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Private Declare PtrSafe Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" ( _
ByVal hKey As Long, _
ByVal pSubKey As String, _
ByVal ulOptions As Long, _
ByVal samDesired As Long, _
phkResult As Long) As Long
Private Enum OBJECT_INFORMATION_CLASS
ObjectBasicInformation ' // 0
ObjectNameInformation ' // 1
ObjectTypeInformation ' // 2
ObjectAllTypesInformation ' // 3
ObjectHandleInformation ' // 4
End Enum
Type UNICODE_STRING
Name As String
Length As LongPtr
MaximumLength As LongPtr
End Type
Type PUBLIC_OBJECT_NAME_INFORMATION
Name As UNICODE_STRING
End Type
Private Declare PtrSafe Function NtQueryObject Lib "Ntdll.dll" _
(ByVal Handle As Long, _
ByVal ObjectInformationClass As OBJECT_INFORMATION_CLASS, _
ByRef ObjectInformation As Any, _
ByVal ObjectInformationLength As Long, _
ByRef ReturnLength As Long) As Long
Public Enum NTSTATUS
Successful = 0
End Enum
Function findreg()
If RegOpenKeyEx(HKEY_USERS, ".DEFAULT\Console", 0, KEY_READ, hKey) <> ERROR_SUCCESS Then Exit Function
Dim buffer_STRUCT As PUBLIC_OBJECT_NAME_INFORMATION
Dim lngVarAddress As LongPtr
lngVarAddress = VarPtr(buffer_STRUCT)
cbBuffer = 80
ObjectNameStatus = NtQueryObject(ByVal hKey, ObjectNameInformation, lngVarAddress, cbBuffer, ReturnLength)
end function
I get this line
buffer_STRUCT.Name.Name = "\REGISTRY\USER.DEFAULT\Console???a?A??????PQ?????????????????????????????"
how to create a structure correctly?
I have a macro that send me a text from outlook when a meeting notification pops up. I would like to figure out a way to make that macro only run if I am not at my computer. I have looked for a way to pull my status from Skype for Business, determine if the PC is locked or not, and see if a smart card is inserted. All without much luck. Looking for a simple solution that works in VBA.
I used the code from here Determine if application is running with Excel
Function IsProcessRunning(process As String)
Dim objList As Object
Set objList = GetObject("winmgmts:") _
.ExecQuery("select * from win32_process where name='" & process & "'")
If objList.Count > 0 Then
IsProcessRunning = True
Else
IsProcessRunning = False
End If
End Function
Based on the answer here In Python 3, how can I tell if Windows is locked?
I called
IsProcessRunning("LogonUI.exe")
and it seems to work.
Maybe this is of any help
Option Explicit
Private Declare Function SwitchDesktop Lib "User32" (ByVal hDesktop As Long) As Long
Private Declare Function OpenDesktop Lib "User32" Alias "OpenDesktopA" (ByVal lpszDesktop As String, ByVal dwFlags As Long, ByVal fInherit As Long, ByVal dwDesiredAccess As Long) As Long
Private Declare Function CloseDesktop Lib "User32" (ByVal hDesktop As Long) As Long
Private Const DESKTOP_SWITCHDESKTOP As Long = &H100
Function desktopLocked() As String
Dim p_lngHwnd As Long
Dim p_lngRtn As Long
Dim p_lngErr As Long
Dim System As String
p_lngHwnd = OpenDesktop(lpszDesktop:="Default", dwFlags:=0, fInherit:=False, dwDesiredAccess:=DESKTOP_SWITCHDESKTOP)
If p_lngHwnd = 0 Then
System = "Error"
Else
p_lngRtn = SwitchDesktop(hDesktop:=p_lngHwnd)
p_lngErr = Err.LastDllError
If p_lngRtn = 0 Then
If p_lngErr = 0 Then
System = "Locked"
Else
System = "Error"
End If
Else
System = "Unlocked"
End If
p_lngHwnd = CloseDesktop(p_lngHwnd)
End If
desktopLocked = System
End Function
Update: Example how one could use the function above
Option Explicit
#If VBA7 Then
Declare PtrSafe Function LockWorkStation Lib "user32.dll" () As Long
#Else
Declare Function LockWorkStation Lib "user32.dll" () As Long
#End If
Dim iTimerSet As Double
Public Sub SaveAndClose()
If desktopLocked = "Locked" Then
ThisWorkbook.Close True
Else
iTimerSet = Now + TimeValue("00:00:03")
Application.OnTime iTimerSet, "SaveAndClose"
End If
End Sub
Sub LockPC()
SaveAndClose
LockWorkStation
End Sub
Just run LockPC and wait 3 seconds before you unlock the workstation. The file has been closed in the meantime.
Hello VBA & Windows API experts ! I have the following code which is supposed to set the color of the current selected text in a rich text Textbox, but it works only partially. For example, it puts the whole text in Blue instead of just the selection. Can anyone spot where the problem can be ? I am using VBA 7 - 64 bits - Windows 10
Form module
Private Sub cmdButton_Click() 'Selected Text in Blue
Dim lngHandle As Long
lngHandle = fhWnd(Me.txtITMrichtxt)
RTBSetTextColor lngHandle, vbBlue
End Sub
Code Module
Option explicit
Public Enum ATTDEFINI
ATTUNDEF = -3
ATTDEFAULT = -2
End Enum
Private Type CHARFORMAT2
cbSize As Long
dwMask As Long
dwEffects As Long
yHeight As Long
yOffset As Long
crTextColor As OLE_COLOR
bCharSet As Byte
bPitchAndFamily As Byte
szFaceName As String * LF_FACESIZE
wWeight As Integer
sSpacing As Integer
crBackColor As OLE_COLOR
lcid As Long
dwReserved As Long
sStyle As Integer
wKerning As Integer
bUnderLineType As Byte
bAnimation As Byte
bRevAuthor As Byte
bReserved1 As Byte
End Type
Public Enum RTBC_FLAGS 'CharFormat (SCF_) flags for EM_SETCHARFORMAT message.
RTBC_DEFAULT = 0
RTBC_SELECTION = 1
RTBC_WORD = 2 'Combine with RTBC_SELECTION!
RTBC_ALL = 4
End Enum
Private Const WM_USER As Long = &H400
Private Const EM_SETCHARFORMAT = WM_USER + 68
Private Const CFM_COLOR As Long = &H40000000 '<-> Membre de la structure CHARFORMAT2 ou Attribut de dwEffects
Private Const CFE_AUTOCOLOR = CFM_COLOR
Declare PtrSafe Function apiGetFocus Lib "user32" Alias "GetFocus" () As LongPtr
Declare PtrSafe Function SendMessageWLng Lib "user32" Alias "SendMessageW" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As Any) As LongPtr
Function fhWnd(iaControl As Control) As LongPtr
On Error Resume Next
iaControl.SetFocus
If Err Then
fhWnd = 0
Else
fhWnd = apiGetFocus
End If
On Error GoTo 0
End Function
Public Sub RTBSetTextColor(ByVal ilngHWND As LongPtr, Optional ByVal ilngTextColor As OLE_COLOR = -1, _
Optional ByVal Scope As RTBC_FLAGS = RTBC_SELECTION)
Dim cf2Colors As CHARFORMAT2
With cf2Colors
.cbSize = LenB(cf2Colors)
.dwMask = CFM_COLOR
If ilngTextColor = ATTDEFAULT Then
.dwEffects = CFE_AUTOCOLOR
Else
.dwEffects = 0
.crTextColor = ilngTextColor
End If
End With
SendMessageWLng ilngHWND, EM_SETCHARFORMAT, Scope, VarPtr(cf2Colors)
End Sub
My base problem is that I have a spreadsheet with 10's of thousands of FQDN (fully qualified domain name) entries that I need to check if the FQDN is a valid DNS entry on the public internet. I am doing a DNS lookup of each FQDN and would like to specify a public DNS server. If the call to the DNS returns an IP address, I will assume the FQDN is valid. I am working in excel 64-bit, but need a solution that will also compile and work in 32-bit, so I want the same source code to be able to be compiled in both. Since there are so many rows in the spreadsheet, I don't want to use a function that creates a temporary file for each lookup. (I am OCD about unneeded temporary files when a system call is available).
I believe that the function "getaddrinfoex" provides the ability to specify what name server is queried, but I have not been able to find any VBA snippets that use getaddrinfoex or the lesser version of getaddrinfo (which does not allow specifying the DNS server). I have found several examples of calls to gethostbyname, but all are for 32-bit Excel. Also, Microsoft has published that gethostbyname has been deprecated (https://msdn.microsoft.com/en-us/library/windows/desktop/ms738524(v=vs.85).aspx), so I was trying to use the recommended replacement getaddrinfo
How can I make a network connection with Visual Basic from Microsoft Access?
The snippet posted in the answer by #david in the question I linked above looks to have the proper syntax to be both 32-bit and 64-bit compatible. But the example did not include the call to gethostbyname, it only provided the declaration of the function.
Is getaddrinfoex available in VBA? Does someone have an example of using getaddrinfoex which will work in both 32-bit and 64-bit?
I would appreciate any help. I have not coded in MANY years, so my skills are very dated. Thus I am doing a lot of searches to find what I need.
Here is the code I have created from combining various searches on-line.
Private Type HOSTENT
hName As LongPtr
hAliases As LongPtr
hAddrType As Integer
hLen As Integer
hAddrList As LongPtr
End Type
#if Not VBA7 then
' used by 32-bit compiler
Private Declare Function gethostbyname Lib "wsock32.dll" _
(ByVal HostName As String) As LongPtr
Private Declare Function getaddrinfo Lib "wsock32.dll" _
(ByVal HostName As String) As LongPtr
Public Declare Function WSAStartup Lib "wsock32.dll" _
(ByVal wVersionRequired As Long, lpWSADATA As WSADATA) As LongPtr
#else
' used by 64-bit compiler
Private Declare PtrSafe Function gethostbyname Lib "wsock32.dll" _
(ByVal HostName As String) As LongPtr
Private Declare PtrSafe Function getaddrinfo Lib "wsock32.dll" _
(ByVal HostName As String) As LongPtr
Public Declare PtrSafe Function WSAStartup Lib "wsock32.dll" _
(ByVal wVersionRequired As Long, lpWSADATA As WSADATA) As LongPtr
#endif
Public Function GetIPAddressFromHostName(ByVal HostName As String) _
As LongPtr
Dim HostEntry As HOSTENT
Dim HostEntry2 as HOSTENT
Dim HostEntryPtr As LongPtr
Dim HostEntryPtr2 As LongPtr
Dim IPAddressesPtr As LongPtr
Dim Result As Long
If InitializeSockets Then
' I added the call do getaddrinfo as an example
' I have been able to get it to work at all
HostEntryPtr2 = getaddrinfo(HostName & vbNullChar)
HostEntryPtr = gethostbyname(HostName & vbNullChar)
If HostEntryPtr > 0 Then
CopyMemory HostEntry, ByVal HostEntryPtr, Len(HostEntryPtr)
CopyMemory IPAddressesPtr, ByVal HostEntry.hAddrList, _
Len(IPAddressesPtr)
CopyMemory Result, ByVal IPAddressesPtr, Len(Result)
GetIPAddressFromHostName = Result
End If
End If
End Function
Public Function InitializeSockets() As Boolean
' Initialize Windows sockets.
Dim WinSockData As WSADATA
InitializeSockets = WSAStartup(WS_VERSION_REQD, WinSockData) = 0
End Function
I have it working now as long as it is not moved to an add-in (.xlam). If I move it to an add-in this exact same code crashes on the call to getaddrinfo. I will continue to work on that.
The procedure requires one argument (hostname passed as a string). The second argument is the maximum number of IP addresses to return (passed as an integer), but is optional. If the second argument is blank, all IP address are returned. When set to a value other than zero, that value will be the maximum number of ip addresses for the host.
Private Const AF_UNSPEC As Long = 0
Private Const AF_INET As Long = 2
Private Const AF_INET6 As Long = 23
Private Const SOCK_STREAM As Long = 1
Private Const INADDR_ANY As Long = 0
Private Const IPPROTO_TCP As Long = 6
' Getaddrinfo return status codes
Private Const WAS_NOT_ENOUGH_MEMORY = 8 ' Insufficient memory available.
Private Const WASEINVAL = 10022 ' Invalid argument.
Private Const WASESOCKTNOSUPPORT = 10044 ' Socket type not supported.
Private Const WASEAFNOSUPPORT = 10047 ' Address family not supported by protocol family.
Private Const WASNOTINITIALISED = 10093 ' Successful WSAStartup not yet performed.
Private Const WASTYPE_NOT_FOUND = 10109 ' Class type not found.
Private Const WASHOST_NOT_FOUND = 11001 ' Host not found.
Private Const WASTRY_AGAIN = 11002 ' Nonauthoritative host not found.
Private Const WASNO_RECOVERY = 11003 ' This is a nonrecoverable error.
Private Const WASNO_DATA = 11004 ' Valid name, no data record of requested type.
'AI_flags
Private Const AI_PASSIVE As Long = &H1
Private Const ai_canonName As Long = &H2
Private Const AI_NUMERICHOST As Long = &H4
Private Const AI_ALL As Long = &H100
Private Const AI_ADDRCONFIG As Long = &H400
Private Const AI_V4MAPPED As Long = &H800
Private Const AI_NON_AUTHORITATIVE As Long = &H4000
Private Const AI_SECURE As Integer = &H8000
Private Const AI_RETURN_PREFERRED_NAMES As Long = &H10000
Private Const AI_FQDN As Long = &H20000
Private Const AI_FILESERVER As Long = &H40000
Dim hSocket As Long
Dim sServer As String
' To initialize Winsock.
Private Type WSADATA
wVersion As Integer
wHighVersion As Integer
szDescription(256 + 1) As Byte
szSystemstatus(128 + 1) As Byte
iMaxSockets As Integer
iMaxUpdDg As Integer
lpVendorInfo As Long
End Type
Private Type in_addr
s_addr As LongPtr
End Type
Private Type sockaddr_in
sin_family As Integer '2 bytes
sin_port As Integer '2 bytes
sin_addr As in_addr '4 bytes or 8 bytes
sin_zero(7) As Byte '8 bytes
End Type 'Total 16 bytes or 24 bytes
Private Type sockaddr
sa_family As Integer '2 bytes
sa_data(25) As Byte '26 bytes
End Type 'Total 28 bytes
Private Type addrinfo
ai_flags As Long
ai_family As Long
ai_socktype As Long
ai_protocol As Long
ai_addrlen As Long
ai_canonName As LongPtr 'strptr
ai_addr As LongPtr 'p sockaddr
ai_next As LongPtr 'p addrinfo
End Type
Private Declare PtrSafe Function API_Socket Lib "ws2_32.dll" Alias "socket" (ByVal af As Long, ByVal stype As Long, ByVal Protocol As Long) As Long
Private Declare PtrSafe Function API_GetAddrInfo Lib "ws2_32.dll" Alias "getaddrinfo" (ByVal NodeName As String, ByVal ServName As String, ByVal lpHints As LongPtr, lpResult As LongPtr) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare PtrSafe Function ntohs Lib "ws2_32.dll" (ByVal netshort As Long) As Integer
Public Function NameToIPaddress(hostname As String, Optional MaxReturn As Integer = 0) As String
Dim sa_local As sockaddr_in
Dim sa_dest As sockaddr
Dim lRet As Long
Dim Hints As addrinfo
Dim ptrResult As LongPtr
Dim IPaddress As String
Dim AddressList As String
Dim AddressType As Long
Dim Cnt As Integer
AddressType = AF_INET
If hostname = "" Then
NameToIPaddress = ""
Exit Function
End If
'Create TCP socket
hSocket = API_Socket(AddressType, SOCK_STREAM, IPPROTO_TCP)
If hSocket = 0 Then
MsgBox ("Failed to create socket!")
Exit Function
End If
'Populate the local sockaddr
sa_local.sin_family = AddressType
sa_local.sin_port = ntohs(0&)
sa_local.sin_addr.s_addr = INADDR_ANY
'Recover info about the destination.
'Hints.ai_flags = AI_NON_AUTHORITATIVE
Hints.ai_flags = 0
Hints.ai_family = AddressType
sServer = hostname & vbNullChar 'Null terminated string
sServer = hostname
lRet = API_GetAddrInfo(sServer, 0, VarPtr(Hints), ptrResult)
If lRet <> 0 Then
If lRet = WASHOST_NOT_FOUND Then
NameToIPaddress = "not found"
Exit Function
End If
Dim errorText As String
Select Case lRet
Case WAS_NOT_ENOUGH_MEMORY
errorText = "Insufficient memory available"
Case WASEINVAL
errorText = "Invalid argument"
Case WASESOCKTNOSUPPORT
errorText = "Socket type not supported"
Case WASEAFNOSUPPOR
errorText = "Address family not supported by protocol family"
Case WASNOTINITIALISED
errorText = "Successful WSAStartup not yet performed"
Case WASTYPE_NOT_FOUND
errorText = "Class type not found"
Case WASHOST_NOT_FOUND
errorText = "Host not found"
Case WASTRY_AGAIN
errorText = "Nonauthoritative host not found"
Case WASNO_RECOVERY
errorText = "This is a nonrecoverable error"
Case WASNO_DATA
errorText = "Valid name, no data record of requested type"
Case Else
errorText = "unknown error condition"
End Select
'MsgBox ("Error in GetAddrInfo: " & lRet & " - " & errorText)
NameToIPaddress = "#Error in lookup"
Exit Function
End If
Cnt = 0
Hints.ai_next = ptrResult 'Pointer to first structure in linked list
Do While Hints.ai_next > 0 And (Cnt < MaxReturn Or MaxReturn = 0)
CopyMemory Hints, ByVal Hints.ai_next, LenB(Hints) 'Copy next address info to Hints
CopyMemory sa_dest, ByVal Hints.ai_addr, LenB(sa_dest) 'Save sockaddr portion
Select Case sa_dest.sa_family
Case AF_INET
IPaddress = sa_dest.sa_data(2) & "." & sa_dest.sa_data(3) & "." & sa_dest.sa_data(4) & "." & sa_dest.sa_data(5)
Case AF_INET6
IPaddress = sa_dest.sa_data(0) & ":" & sa_dest.sa_data(1) & ":" & sa_dest.sa_data(2) & "::" & sa_dest.sa_data(3) & ":" & sa_dest.sa_data(4)
Case Else
IPaddress = ""
End Select
Cnt = Cnt + 1
If AddressList = "" Then
AddressList = IPaddress
Else
AddressList = AddressList & "," & IPaddress
End If
Loop
NameToIPaddress = AddressList
End Function
Today im using this code to copy a file to clipboard with excel 2010 (32 bit).
Im trying to get this to work with office 2016 (64 bit), but everytime the function is used excel crashes.
Is it possible to get this code to work with both excel 2016 (64 bit) and office 2010 (32 bit)?
Option Explicit
' Required data structures
Private Type POINTAPI
x As Long
y As Long
End Type
' Clipboard Manager Functions
Private Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long
Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
Private Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As Long) As Long
Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As Long
Private Declare PtrSafe Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Long) As Long
' Other required Win32 APIs
Private Declare PtrSafe Function DragQueryFile Lib "shell32.dll" Alias "DragQueryFileA" (ByVal hDrop As Long, ByVal UINT As Long, ByVal lpStr As String, ByVal ch As Long) As Long
Private Declare PtrSafe Function DragQueryPoint Lib "shell32.dll" (ByVal hDrop As Long, lpPoint As POINTAPI) As Long
Private Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare PtrSafe Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare PtrSafe Sub CopyMem Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
' Predefined Clipboard Formats
Private Const CF_TEXT = 1
Private Const CF_BITMAP = 2
Private Const CF_METAFILEPICT = 3
Private Const CF_SYLK = 4
Private Const CF_DIF = 5
Private Const CF_TIFF = 6
Private Const CF_OEMTEXT = 7
Private Const CF_DIB = 8
Private Const CF_PALETTE = 9
Private Const CF_PENDATA = 10
Private Const CF_RIFF = 11
Private Const CF_WAVE = 12
Private Const CF_UNICODETEXT = 13
Private Const CF_ENHMETAFILE = 14
Private Const CF_HDROP = 15
Private Const CF_LOCALE = 16
Private Const CF_MAX = 17
' New shell-oriented clipboard formats
Private Const CFSTR_SHELLIDLIST As String = "Shell IDList Array"
Private Const CFSTR_SHELLIDLISTOFFSET As String = "Shell Object Offsets"
Private Const CFSTR_NETRESOURCES As String = "Net Resource"
Private Const CFSTR_FILEDESCRIPTOR As String = "FileGroupDescriptor"
Private Const CFSTR_FILECONTENTS As String = "FileContents"
Private Const CFSTR_FILENAME As String = "FileName"
Private Const CFSTR_PRINTERGROUP As String = "PrinterFriendlyName"
Private Const CFSTR_FILENAMEMAP As String = "FileNameMap"
' Global Memory Flags
Private Const GMEM_FIXED = &H0
Private Const GMEM_MOVEABLE = &H2
Private Const GMEM_NOCOMPACT = &H10
Private Const GMEM_NODISCARD = &H20
Private Const GMEM_ZEROINIT = &H40
Private Const GMEM_MODIFY = &H80
Private Const GMEM_DISCARDABLE = &H100
Private Const GMEM_NOT_BANKED = &H1000
Private Const GMEM_SHARE = &H2000
Private Const GMEM_DDESHARE = &H2000
Private Const GMEM_NOTIFY = &H4000
Private Const GMEM_LOWER = GMEM_NOT_BANKED
Private Const GMEM_VALID_FLAGS = &H7F72
Private Const GMEM_INVALID_HANDLE = &H8000
Private Const GHND = (GMEM_MOVEABLE Or GMEM_ZEROINIT)
Private Const GPTR = (GMEM_FIXED Or GMEM_ZEROINIT)
Private Type DROPFILES
pFiles As Long
pt As POINTAPI
fNC As Long
fWide As Long
End Type
Public Function ClipboardCopySingleFile(File As String) As Boolean
Dim Files(0) As String
Files(0) = File
ClipboardCopyFiles Files()
End Function
Public Function ClipboardCopyFiles(Files() As String) As Boolean
Dim data As String
Dim df As DROPFILES
Dim hGlobal As Long
Dim lpGlobal As Long
Dim I As Long
' Open and clear existing crud off clipboard.
If OpenClipboard(0&) Then
Call EmptyClipboard
' Build double-null terminated list of files.
For I = LBound(Files) To UBound(Files)
data = data & Files(I) & vbNullChar
Next
data = data & vbNullChar
' Allocate and get pointer to global memory,
' then copy file list to it.
hGlobal = GlobalAlloc(GHND, Len(df) + Len(data))
If hGlobal Then
lpGlobal = GlobalLock(hGlobal)
' Build DROPFILES structure in global memory.
df.pFiles = Len(df)
Call CopyMem(ByVal lpGlobal, df, Len(df))
Call CopyMem(ByVal (lpGlobal + Len(df)), ByVal data, Len(data))
Call GlobalUnlock(hGlobal)
' Copy data to clipboard, and return success.
If SetClipboardData(CF_HDROP, hGlobal) Then
ClipboardCopyFiles = True
End If
End If
' Clean up
Call CloseClipboard
End If
End Function
Public Function ClipboardPasteFiles(Files() As String) As Long
Dim hDrop As Long
Dim nFiles As Long
Dim I As Long
Dim desc As String
Dim filename As String
Dim pt As POINTAPI
Const MAX_PATH As Long = 260
' Insure desired format is there, and open clipboard.
If IsClipboardFormatAvailable(CF_HDROP) Then
If OpenClipboard(0&) Then
' Get handle to Dropped Filelist data, and number of files.
hDrop = GetClipboardData(CF_HDROP)
nFiles = DragQueryFile(hDrop, -1&, "", 0)
' Allocate space for return and working variables.
ReDim Files(0 To nFiles - 1) As String
filename = Space(MAX_PATH)
' Retrieve each filename in Dropped Filelist.
For I = 0 To nFiles - 1
Call DragQueryFile(hDrop, I, filename, Len(filename))
Files(I) = TrimNull(filename)
Next
' Clean up
Call CloseClipboard
End If
' Assign return value equal to number of files dropped.
ClipboardPasteFiles = nFiles
End If
End Function
Private Function TrimNull(ByVal sTmp As String) As String
Dim nNul As Long
' Truncate input sTmpg at first Null.
' If no Nulls, perform ordinary Trim.
nNul = InStr(sTmp, vbNullChar)
Select Case nNul
Case Is > 1
TrimNull = Left(sTmp, nNul - 1)
Case 1
TrimNull = ""
Case 0
TrimNull = Trim(sTmp)
End Select
End Function
Have you checked the need of compile directives?
https://msdn.microsoft.com/en-us/library/office/gg264731.aspx
I have used before, for similar problems, something like the example below:
#If VBA7 Then
Private Declare PtrSafe Function apiGetComputerName Lib "kernel32" Alias _
"GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long
#Else
Private Declare Function apiGetComputerName Lib "kernel32" Alias _
"GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long
#End If