In Windows 7, the following Excel VBA 7.1 snippet successfully launches the On-Screen Keyboard (OSK.EXE) but the value of the dwProcessID member retrieved from the 'proc' (process information) parameter (passed ByRef) does not match that, or any other PID, displayed by task manager.
Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long ' Integer doesn't work either
dwThreadID As Long
End Type
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, _
ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInformation As _
PROCESS_INFORMATION) As Long
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
start.cb = Len(start)
If CreateProcessA(0, "OSK.EXE", 0, 0, 1, NORMAL_PRIORITY_CLASS, 0, 0, start,
proc) <> 0 Then
WaitForInputIdle proc.hProcess, INFINITE
MsgBox CStr(proc.dwProcessID), vbInformation, "Process ID" ' Wrong for
OSK, but correct for Notepad and Calc
CloseHandle (proc.hProcess)
CloseHandle (proc.hThread)
End If
For OSK, it seems that the proc.hProcess value is incorrect. I checked the process ID value of proc.dwProcessID against the PID listed for OSK in Task Manager, and they don't match. Indeed, proc.dwProcessID isn't listed for any process (even in Process Explorer), so it seems that the PROCESS_INFORMATION Type is not receiving correct outputs.
Everything works fine for NOTEPAD and CALC though. Equivilent code compiled in VB.NET behaves properly with OSK, so what's different about VBA that causes CreateProcessA to work incorrectly with OSK?
Thanks in advance,
John.
It could be that the executable you are launching (OSK.EXE) execute another process or does some job and exit very quickly so when you look in process explorer you cannot find anything.
If the code inside the "if" is not executed I would try to get the error with GetLastError call.
I think you are encountering issues like https://learn.microsoft.com/en-us/office/vba/language/concepts/getting-started/64-bit-visual-basic-for-applications-overview.
Long is not a good replacement for pointers, in this case should be
Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessID As Long
dwThreadID As Long
End Type
Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As LongPtr, ByVal lpCommandLine As String, ByVal _
lpProcessAttributes As LongPtr, ByVal lpThreadAttributes As LongPtr, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As LongPtr, _
ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInformation As _
PROCESS_INFORMATION) As Long
See also https://www.jkp-ads.com/articles/apideclarations.asp
Related
I'm new to VBA and use excel 2010 64bit VBA v6.0 compatible. I pasted the code, trying to download files through VBA.
Option Explicit
'Tutorial link: https://youtu.be/H4-w6ULc_qs
#If VBA7 Then
Private Declare Function URLDownloadToFile Lib "urlmon" Alias _
"URLDownloadToFileA" (ByVal pCaller As LongPtr, ByVal szURL As String, ByVal _
szFileName As String, ByVal dwReserved As LongPtr, ByVal lpfnCB As LongPtr) As LongPtr
#Else
Private Declare Function URLDownloadToFile Lib "urlmon" Alias _
"URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, ByVal _
szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
#End If
Sub download_file()
'-----------------------------
'Thanks for downloading the code.
'Please visit our channel for a quick explainer on how to use this code.
'Feel free to update the code as per your need and also share with your friends.
'Download free codes from http://vbaa2z.blogspot.com
'Support our channel: youtube.com/vbaa2z
'Author: L Pamai (vbaa2z.team#gmail.com)
'-----------------------------
Dim downloadStatus As Variant
Dim url As String
Dim destinationFile_local As String
url = [D3]
destinationFile_local = "C:\Users\myUserName\Downloads\" & fileName([D3])
downloadStatus = URLDownloadToFile(0, url, destinationFile_local, 0, 0)
If downloadStatus = 0 Then
MsgBox "Downloaded Succcessfully!"
Else
MsgBox "Download failed"
End If
End Sub
Function fileName(file_fullname) As String
fileName = Mid(file_fullname, InStrRev(file_fullname, "/") + 1)
End Function
However, a pop-up window says it can only run on 64-bit systems as follow:
Compile error:
The code in this project must be updated for use on 64-bit systems. Please review and update Declare statements and then mark them with the PtrSafe attribute.
My questions are:
I do use window and office 64-bit system. Why the window keeps popping up?
Is there any way to solve this problem?
Thanks in advance.
As the error tells you, add the PtrSafe keyword to the VBA7 branch
#If VBA7 Then
Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" Alias _
"URLDownloadToFileA" (ByVal pCaller As LongPtr, ByVal szURL As String, ByVal _
szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As LongPtr) As Long
#Else
Private Declare Function URLDownloadToFile Lib "urlmon" Alias _
"URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, ByVal _
szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
#End If
You need to add this keyword anywhere you are using LongPtr, or LongLong.
Here is the MS Documentation on PtrSafe
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/ptrsafe-keyword
Adding the PtrSafe keyword to a Declare statement only signifies that 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 by using either LongLong for 64-bit integrals or LongPtr for pointers and handles.
I'm having some problems with converting this API call into 64 bit accessible call from VBA.
API declaration
Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" ( _
ByVal pvInstance As Long, _
ByVal oVft As Long, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByVal prgvt As Long, _
ByVal prgpvarg As Long, _
ByVal pvargResult As Long _
) As Long
Client code
Public Sub Main()
' On this line I get "compile error: type mismatch" because AddressOf method
' returns LongPtr but DispCallFunc expects Long.
DispCallFunc 0, AddressOf Foo, CLng(4), VbVarType.vbEmpty, 0, 0, 0, 0
End Sub
Private Sub Foo()
Debug.Print 100
End Sub
I tried to change Long to LongPtr in DispCallFunc but every time I make that change to the API and run macro, Excel freezes.
The DispCallFunc function is declared like this:
HRESULT DispCallFunc(
void *pvInstance,
ULONG_PTR oVft,
CALLCONV cc,
VARTYPE vtReturn,
UINT cActuals,
VARTYPE *prgvt,
VARIANTARG **prgpvarg,
VARIANT *pvargResult
);
pvInstance is a pointer [input]
oVft is a pointer [input]
cc is a 32-bit integer [input]
vtReturn is a 16-bit integer [input]
cActuals is a 32-bit integer [input]
prgvt is an array of 16-bit integers (so a pointer) [input]
prgpvarg is an array of pointer on VARIANTs (so a pointer) [input]
pvargResult is a pointer on a VARIANT, so a byref VBA's Variant [output]
So, for VBA:
Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" ( _
ByVal pvInstance As LongPtr, _
ByVal oVft As LongPtr, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByVal prgvt As LongPtr, _
ByVal prgpvarg As LongPtr, _
ByRef pvargResult As Variant) As Long
I'm trying to use Waitable Timer objects in VBA, as I want to call something asynchronously with a delay of under 1 second (so no Application.OnTime) and with arguments (so no SetTimer API)
I haven't found someone attempting this anywhere else so I'm having to do it all from scratch, but I think it should be feasible. Here are the API declarations:
Public Declare Function CreateWaitableTimer Lib "kernel32" Alias "CreateWaitableTimerA" ( _
ByVal lpTimerAttributes As Long, _
ByVal manualReset As Boolean, _
ByVal lpTimerName As Long) As Long
'The A meaning Ansi not Unicode https://jeffpar.github.io/kbarchive/kb/145/Q145727/
Public Declare Function SetWaitableTimer Lib "kernel32" ( _
timerHandle As Long, _
lpDueTime As fileTime, _
lPeriod As Long, _
pfnCompletionRoutine As Long, _
lpArgToCompletionRoutine As Long, _
fResume As Boolean) As Boolean
Which references a fileTime (struct)
'see https://social.msdn.microsoft.com/Forums/sqlserver/en-US/a28a32c6-df4e-41b9-94ce-6260812dd92f/problem-trying-to-run-32-bit-vba-program-on-a-64-bit-machine?forum=exceldev
Public Type fileTime
dwLowDateTime As Long
dwHighDateTime As Long
End Type
I am calling the API like this:
'[...]
args = 1234 'public args As Long so it doesn't go out of scope while the timer is waiting
Dim timerHandle As Long
timerHandle = CreateWaitableTimer(0, False, 0)
Debug.Print GetSystemErrorMessageText(Err.LastDllError)
If Not SetWaitableTimer(timerHandle, absoluteDueTime, 0, AddressOf TimerCallbacks.pointerProc, VarPtr(args), False) Then
Debug.Print "Error: "; GetSystemErrorMessageText(Err.LastDllError)
End If
GetSystemErrorMessageText comes from Chip Pearson. absoluteDueTime is a fileTime variable which is set to Now + 1 second earlier in the procedure.
I'm getting in the immediate window:
0 - The operation completed successfully.
Error: 6 - The handle is invalid.
Meaning that CreateWaitableTimer appears to work but SetWaitableTimer does not.
FWIW TimerCallbacks.pointerProc looks like:
Public Sub pointerProc(ByVal argPtr As Long, ByVal timerLowValue As Long, ByVal timerHighValue As Long)
Debug.Print "pointerProc called"; Time
End Sub
(but I don't think that's where the error is...)
Oh, the problem is with the implicit byRef of everything:
Public Declare Function SetWaitableTimer Lib "kernel32" ( _
timerHandle As Long, _
lpDueTime As fileTime, _
lPeriod As Long, _
pfnCompletionRoutine As Long, _
lpArgToCompletionRoutine As Long, _
fResume As Boolean) As Boolean
Pointers must be passed byVal or they are immediately deferenced? Is this correct:
Public Declare Function SetWaitableTimer Lib "kernel32" ( _
byVal timerHandle As Long, _
byRef lpDueTime As fileTime, _
byRef lPeriod As Long, _
byVal pfnCompletionRoutine As Long, _
byVal lpArgToCompletionRoutine As Long, _
byRef fResume As Boolean) As Boolean
Not sure if all the byRefs are necessary
I am currently making some VBA code applicable for the new win7 machines on the job. This means that many function has to be added with the new 64-bit compatibility. I am currently following the guidelines from this homepage. I find them very useful and implemented following:
#If VBA7 Then
Declare PtrSafe Function CreateProcess Lib "kernel32" 'More code, see below.
#Else
Declare Function CreateProcess Lib "kernel32" 'More code, see below.
#EndIf
however, currently VBA is giving me following error message when I save my code:
NOTE: The code above contains the code described in the previous mentioned link: from jkp-ads. The error appears in the Declare PtrSafe Function CreateProcess Lib "kernel32" Alias "CreateProcessA". The full code can be found here.
What am I missing here?
Thanks!
Update
My full code:
Option Explicit
Const INFINITE = &HFFFF
Const STARTF_USESHOWWINDOW = &H1
Private Enum enSW
SW_HIDE = 0
SW_NORMAL = 1
SW_MAXIMIZE = 3
SW_MINIMIZE = 6
End Enum
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId 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 SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Private Enum enPriority_Class
NORMAL_PRIORITY_CLASS = &H20
IDLE_PRIORITY_CLASS = &H40
HIGH_PRIORITY_CLASS = &H80
End Enum
#If VBA7 Then
private Declare PtrSafe Function CreateProcess Lib "kernel32" _
Alias "CreateProcessA" (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
#Else
Declare Function CreateProcess Lib "kernel32" _
Alias "CreateProcessA" (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
#End If
Now, I want to emphasize a few things. First, I am running Excel 2003 and Windows XP 32bit. In addition to this I have not attempted to run the code yet. Why you may ask. The reason is simple. The editor comes up with a compile error before I am able to do anything. This Compile error can be seen in the picture above this piece of code. I have taken a screenshot of some of the code and especially where the compile error occurs. This is how it looks:
UpdateUpdate: Just found this article from MS support. It appears that the resolution is following: "To resolve this issue, ignore the "Compile error" and run the VBA code in the 64-bit version of the Office 2010 program." I still hope some solution exist though.
Further to my comment below your question (which you have ignored... by mistake I guess?) I believe you are getting that error because you have not specified all the parameters correctly.
Try this and it will compile just fine.
Const INFINITE = &HFFFF
Const STARTF_USESHOWWINDOW = &H1
Private Enum enSW
SW_HIDE = 0
SW_NORMAL = 1
SW_MAXIMIZE = 3
SW_MINIMIZE = 6
End Enum
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId 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 SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Private Enum enPriority_Class
NORMAL_PRIORITY_CLASS = &H20
IDLE_PRIORITY_CLASS = &H40
HIGH_PRIORITY_CLASS = &H80
End Enum
#If VBA7 Then
Declare PtrSafe Function CreateProcess Lib "kernel32" _
Alias "CreateProcessA" (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
#Else
Declare Function CreateProcess Lib "kernel32" _
Alias "CreateProcessA" (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
#End If
This is not a solution, but a path of investigation.
It looks like the compiler is looking at the stuff under #If VBA7 Then. But this shouldn't be happening at all in Excel 2003.
Try replacing whatever is under #If VBA7 Then with garbage, and add one declaration in the #Else part, e.g.
#If VBA7 Then
ksjdhfg lkjsdh fgkjh,dlgf sldjkgflsd g 'obvious syntax error
#Else
Const vOlder As String = "older VBA"
Declare Function CreateProcess Lib "kernel32" '... keep this part as it was
#End If
Then try to run this to see if it compiles:
Sub tester()
Debug.Print vOlder
End Sub
This should compile and run fine in Excel 2003 since it has an older version of VBA than 7.0. The stuff below #If VBA7 Then should get completely ignored (regardless of any syntax errors or anything else) and the stuff under #Else, e.g. vOlder, should be accessible.
Let us know if this helps you trace the error.
Is there any option to change the excel icon in taskbar for Excel?
Disclaimer: I usually do not answer questions where the OP has shown no efforts but this was way too interesting to pass on. But do not be surprised if this question gets closed :)
Is there any option to change the excel icon in taskbar for Excel
Yes there is. You have to use 3 APIs for this
ExtractIcon32, GetActiveWindow32 and SendMessage32
To read about them see THIS LINK. This is my favorite one stop for APIs :)
Declare Function ExtractIcon32 Lib "shell32.dll" Alias _
"ExtractIconA" (ByVal hInst As Long, _
ByVal lpszExeFileName As String, ByVal nIconIndex As Long) As Long
Declare Function GetActiveWindow32 Lib "user32" _
Alias "GetActiveWindow" () As Integer
Declare Function SendMessage32 Lib "user32" Alias _
"SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Sub Sample()
Dim myIcoFile As String
Dim NewIco
'~~> Change this to the relevant icon file
myIcoFile = "D:\Temp\icons\CHARACT\$SIGN1.ico"
NewIco = ExtractIcon32(0, myIcoFile, 0)
SendMessage32 GetActiveWindow32(), &H80, 1, NewIco
End Sub