Read Registry Keys from within VBA using Windows Shell - vba

I have been searching around on the internet and am having problems finding a solution to this issue.
Basically I am trying to execute a registry query with administrator privileges using Shell.Application from within VBA to read the value of TypeGuessRows (and eventually modify it to 0 aswell so that excel data can be correctly queried using ADOdb). I have come up with the following sub routine:
Sub Read_Registry_Value()
'Declare variables
Dim reg_key_location As String
Dim reg_key_name As String
Dim wsh As Object
'Define registry key path and name
reg_key_location = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\15.0\ClickToRun\REGISTRY\MACHINE\Software\Wow6432Node\Microsoft\Office\15.0\Access Connectivity Engine\Engines\Excel"
reg_key_name = "TypeGuessRows"
'Create instance of windows shell
Set wsh = VBA.CreateObject("Shell.Application")
'Execute registry query with administrative privileges
wsh.ShellExecute "cmd", _
"/K REG QUERY " & Chr(34) & reg_key_location & Chr(34) & " /v " & reg_key_name, _
"", _
"runas", _
1
End Sub
All that is returned from this routine is:
ERROR:
The system was unable to find the specified registry key or value.
However the registry key most definitely exists. Refer to screenshot below. Additionally the command prompt should also be running with admin rights according to my code above.
Registry Key Screenshot:
Furthermore executing the command...
REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\15.0\ClickToRun\REGISTRY\MACHINE\Software\Wow6432Node\Microsoft\Office\15.0\Access Connectivity Engine\Engines\Excel" /v TypeGuessRows
Directly in command prompt works without any Administrator Rights.
REG EDIT Manually in CMD:
So I'm lost on how to get this function working correctly and any help on this issue would be much appreciated!
**** UPDATE ****
Ok so i've implemented the code suggested by Dinotom in the first answer. See extract of code below.
Sub Read_Registry()
Dim entryArray() As Variant
Dim valueArray() As Variant
Dim reg_key_location As String
Dim x As Integer
reg_key_location = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\15.0\ClickToRun\REGISTRY\MACHINE\Software\Wow6432Node\Microsoft\Office\15.0\Access Connectivity Engine\Engines\Excel"
Call EnumerateRegEntries(reg_key_location, entryArray, valueArray)
For x = 0 To UBound(entryArray)
'Do something here
Next x
End Sub
Public Sub EnumerateRegEntries(keyPath As String, arrEntryNames As Variant, arrValueTypes As Variant)
Dim registryObject As Object
Dim rootDirectory As String
rootDirectory = "."
Set registryObject = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
rootDirectory & "\root\default:StdRegProv")
registryObject.EnumValues HKEY_LOCAL_MACHINE, keyPath, arrEntryNames, arrValueTypes
End Sub
However the following error is returned on the For x = 0 ... line...
ERROR:
Run-time error '9' Subscript out of range.
It doesn't look like the arrays are being populated with the registry data as suggested below. Any more ideas?

Do you have to use Shell?
This will enumerate your registry entries, manipulate as you need.
Set up empty arrays to pass as the parameters, and the keypath is the local file path to your registry to enumerate. the sub will fill the arrays.
Dim entryArray() As Variant, valueArray() As Variant
Call EnumerateRegEntries("pathtokey",entryArray, valueArray)
The sub below will run and entryArray and valueArray will be populated.
Then you can iterate over the arrays
For x = 0 to UBound(yourarrayhere)
'Do something here
Next x
Enumerate method:
Public Sub EnumerateRegEntries(keyPath As String, arrEntryNames As Variant, arrValueTypes As Variant)
Dim registryObject As Object
Dim rootDirectory As String
rootDirectory = "."
Set registryObject = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
rootDirectory & "\root\default:StdRegProv")
registryObject.EnumValues HKEY_LOCAL_MACHINE, keyPath, arrEntryNames, arrValueTypes
End Sub
if you are unable to alter or use this sub, then look here
Chip Pearsons registry page
or, if you have some requirement to use Shell, then look here for how to run as Admin
run shell as admin

The path is wrong.
Set the path like this:
reg_key_location = "SOFTWARE\Microsoft\Office\15.0\ClickToRun\REGISTRY\MACHINE\Software\Wow6432Node\Microsoft\Office\15.0\Access Connectivity Engine\Engines\Excel"
The HKEY_LOCAL_MACHINE is placed when calling the object:
registryObject.EnumValues HKEY_LOCAL_MACHINE, keyPath, arrEntryNames, arrValueTypes
EDIT: Also remind that if you are running windows 64 bits and office 32 bits, the stdregprov only reads inside Wow6432Node.

Related

Trying to Open a File with a Button from Access using ShellExecute

In my Access database, I have a button on a form to open an external file.
Here is the code that I am using for that.
Private Sub btn_OpenFile_Click()
Dim a As New Shell32.Shell
Dim strPath As String
strPath = Me.Attachment
strPath = Chr(34) & strPath & Chr(34)
Call a.ShellExecute(Me.Attachment)
'Call CreateObject("Shell.Application").ShellExecute(strPath)
'MsgBox strPath
End Sub
The problem that I have is if I actually put in the value of the variable (Me.Attachment) it works fine and opens the program and the file.
For Example, If I put in Call a.ShellExecute("C:\Docs\Some File.pdf") it will open. But if I use the variable in it's place it won't open and tells me it cannot find the file. I have verified with the msgbox that it is receiving the correct information. I have tried to wrap it in quotes and have used the Chr(34) as shown above but nothing works.
How can I get that variable to work in the ShellExcute command?
I have looked through all the forums and it seems like everyone is using a string but not a variable. I don't want to use just the shell command as I don't want to track down all of the different apps people use to open different types of files. There will be different file types that will need to be opened and I thought this would be easier than it actually is.
Thank you for the help.
I'm pretty sure that ShellExecute expects a Variant parameter, not a String.
So try this:
Call a.ShellExecute(CVar(strPath))
or use a Variant variable from the start.
I had the same problem here.
Both of the following work for me:
Dim a As New Shell32.Shell
Dim strPath As String
strPath = Me.Attachment
Call a.ShellExecute(strPath)
Dim a As Shell
Dim strPath As String
strPath = Me.Attachment
Set a = CreateObject("Shell.Application")
a.ShellExecute strPath
Even referencing Attachment directly.
a.ShellExecute(Me.Attachment)

vba RegRead returning different key string than is in the registry

If I run the below code I get a different registry string then when I open RegEdit and view the key directly. I an running the code using MSACCESS 2016 starting it, and also RegEdit, in Admin mode. Is there some kind a cache storing the value.
When I look at the value using RegEdit I get:
"|FromSrtCut|||FALSE|0000000|CFI|Check-4-It.accde||C:\Prog...."
When I use the code below, I get :
"|FromSrtCut|||FALSE|0000000|CFI_INV|Check-4-It.accde||C:\Prog...."
enter code here
Dim WshShell, bKey
Dim strKeyPath As String
strKeyPath = "SOFTWARE\Corporate Data Design\Check-4-It"
Set WshShell = CreateObject("WScript.Shell")
Lg_strTemp = WshShell.RegRead("HKEY_CURRENT_USER\" & strKeyPath & "\LiveUpdate")
Debug.Print Lg_strTemp

Connecting to FTP from Excel to automate file sharing (VBA Beginner)

I'm a beginner and new to Excel VBA, but I'm trying to automate some file sharing in FTP (WinSCP) by connecting to Excel and maybe creating a macro that will help. In FTP I went to Session > Generate Session URL/code > Script (script file) and the following code is there:
open ftp://myUsername:myPassword#theHostname/
# Your command 1
# Your command 2
exit
I'm assuming the open line would connect Excel to FTP. I'm referencing code from this site to put into the '# command' area: https://www.mrexcel.com/forum/excel-questions/261043-connecting-ftp-excel.html
open ftp://myUsername:myPassword#theHostname/
Option Explicit
Sub FtpTest()
MsgBox fnDownloadFile("ftp://yoursite", "username", "password", _
"The name of your file", _
"C:\The name of your file to save as")
End Sub
Function fnDownloadFile(ByVal strHostName As String, _
ByVal strUserName As String, _
ByVal strPassWord As String, _
ByVal strRemoteFileName As String, _
ByVal strLocalFileName As String) As String
'// Set a reference to: Microsoft Internet Transfer Control
'// This is the Msinet.ocx
Dim FTP As Inet 'As InetCtlsObjects.Inet
Set FTP = New Inet 'InetCtlsObjects.Inet
On Error GoTo Errh
With FTP
.URL = strHostName
.Protocol = 2
.UserName = strUserName
.Password = strPassWord
.Execute , "Get " + strRemoteFileName + " " + strLocalFileName
Do While .StillExecuting
DoEvents
Loop
fnDownloadFile = .ResponseInfo
End With
Xit:
Set FTP = Nothing
Exit Function
Errh:
fnDownloadFile = "Error:-" & Err.Description
Resume Xit
End Function
exit
I did as this site said to go to VBA Editor > Tools > reference and check off Microsoft Internet Control.
1) Am I using the code right? Did I place it in the right area (in the '# command' area)? And right now I put the entire code in a Command Button, but when I click it it just gives me a Syntax Error highlighting the first line:
Private Sub CommandButton1_Click())
2) Do I leave the Msgbox on the 3rd line as is to wait for user input or do I fill out with my username/password/hostname? (I'm not very good with functions in VBA yet) If I do fill it out in the code, what do I put for the "yoursite" value since I'm not accessing a website?
I'm sorry I'm so confused :( Any help would be great and thank you in advance!
I think that You should take a look here - Excel VBA reference for Inet objects
it is shown here how to add refernce for INet objects in vba. Furthermore when You just want to test if the code works, instead of assigning macro to button and so on, if You use "Function" then when You go to worksheet cell and start to type =fnDown ... You should see Your macro - there You can put Your function parameters. However first of all You have to take care of the reference to Inet.
This link might also be helpful: VBA Excel and FTP with MSINET.OCX and Inet type

How to use VBA to open a file in a new window?

I have a OSI PI Processbook file that I am using VBA on to open a new instance of PI Processbook. Basically I have a text element (Text35) on which I have a vba click event:
Private Sub Text35_Click(ByVal lvarX As Long, ByVal lvarY As Long)
Dim filePathAndName As String
Dim exeLocation As String
Dim PID As Variant
On Error GoTo errHandle
filePathAndName = "C:\Users\myuser\Desktop\TEST.PDI"
exeLocation = "C:\Program Files (x86)\PIPC\Procbook\Procbook.exe"
PID = Shell("""" & exeLocation & """", vbNormalFocus)
'How can I use the above process id to open a PDI file??
Exit Sub
errHandle:
End Sub
Basically I want to open the TEST.PDI file on my desktop in an entirely new copy of PI Processbook. I've tried pplication.Displays.Open(filePathAndName, True) but this opens my TEST.PDI into the same instance of Processbook, not a new application instance like I want.
Is there some Shell command or command like switch argument I can use to both open a new instance of the .exe and open up a file at the same time? I, at least, have the process id of the new instance stored in the "PID" variable, so I am thinking this might help.

Excel VBA bug accessing HelpFile property from macro-disabled instance?

I think I've stumbled upon a bug in Excel - I'd really like to verify it with someone else though.
The bug occurs when reading the Workbook.VBProject.HelpFile property when the workbook has been opened with the opening application's .AutomationSecurity property set to ForceDisable. In that case this string property returns a (probably) malformed Unicode string, which VBA in turn displays with question marks. Running StrConv(..., vbUnicode) on it makes it readable again, but it sometimes looses the last character this way; this might indicate that the unicode string is indeed malformed or such, and that VBA therefore tries to convert it first and fails.
Steps to reproduce this behaviour:
Create a new Excel workbook
Go to it's VBA project (Alt-F11)
Add a new code module and add some code to it (like e.g. Dim a As Long)
Enter the project's properties (menu Tools... properties)
Enter "description" as Project description and "abc.hlp" as Help file name
Save the workbook as a .xlsb or .xlsm
Close the workbook
Create a new Excel workbook
Go to it's VBA project (Alt-F11)
Add a fresh new code module
Paste the code below in it
Adjust the path on the 1st line so it points to the file you created above
Run the Test routine
The code to use:
Const csFilePath As String = "<path to your test workbook>"
Sub TestSecurity(testType As String, secondExcel As Application, security As MsoAutomationSecurity)
Dim theWorkbook As Workbook
secondExcel.AutomationSecurity = security
Set theWorkbook = secondExcel.Workbooks.Open(csFilePath)
Call MsgBox(testType & " - helpfile: " & theWorkbook.VBProject.HelpFile)
Call MsgBox(testType & " - helpfile converted: " & StrConv(theWorkbook.VBProject.HelpFile, vbUnicode))
Call MsgBox(testType & " - description: " & theWorkbook.VBProject.Description)
Call theWorkbook.Close(False)
End Sub
Sub Test()
Dim secondExcel As Excel.Application
Set secondExcel = New Excel.Application
Dim oldSecurity As MsoAutomationSecurity
oldSecurity = secondExcel.AutomationSecurity
Call TestSecurity("enabled macros", secondExcel, msoAutomationSecurityLow)
Call TestSecurity("disabled macros", secondExcel, msoAutomationSecurityForceDisable)
secondExcel.AutomationSecurity = oldSecurity
Call secondExcel.Quit
Set secondExcel = Nothing
End Sub
Conclusion when working from Excel 2010:
.Description is always readable, no matter what (so it's not like all string properties behave this way)
xlsb and xlsm files result in an unreadable .HelpFile only when macros are disabled
xls files result in an unreadable .HelpFile in all cases (!)
It might be even weirder than that, since I swear I once even saw the questionmarks-version pop up in the VBE GUI when looking at such a project's properties, though I'm unable to reproduce that now.
I realize this is an edge case if ever there was one (except for the .xls treatment though), so it might just have been overlooked by Microsoft's QA department, but for my current project I have to get this working properly and consistently across Excel versions and workbook formats...
Could anyone else test this as well to verify my Excel installation isn't hosed? Preferably also with another Excel version, to see if that makes a difference?
Hopefully this won't get to be a tumbleweed like some of my other posts here :) Maybe "Tumbleweed generator" might be a nice badge to add...
UPDATE
I've expanded the list of properties to test just to see what else I could find, and of all the VBProject's properties (BuildFileName, Description, Filename, HelpContextID, HelpFile, Mode, Name, Protection and Type) only .HelpFile has this problem of being mangled when macros are off.
UPDATE 2
Porting the sample code to Word 2010 and running that exhibits exactly the same behaviour - the .HelpFile property is malformed when macros are disabled. Seems like the code responsible for this is Office-wide, probably in a shared VBA library module (as was to be expected TBH).
UPDATE 3
Just tested it on Excel 2007 and 2003, and both contain this bug as well. I haven't got an Excel XP installation to test it out on, but I can safely say that this issue already has a long history :)
I've messed with the underlying binary representation of the strings in question, and found out that the .HelpFile string property indeed returns a malformed string.
The BSTR representation (underwater binary representation for VB(A) strings) returned by the .HelpFile property lists the string size in the 4 bytes in front of the string, but the following content is filled with the ASCII representation and not the Unicode (UTF16) representation as VBA expects.
Parsing the content of the BSTR returned and deciding for ourselves which format is most likely used fixes this issue in some circumstances. Another issue is unfortunately at play here as well: it only works for even-length strings... Odd-length strings get their last character chopped off, their BSTR size is reported one short, and the ASCII representation just doesn't include the last character either... In that case, the string cannot be recovered fully.
The following code is the example code in the question augmented with this fix. The same usage instructions apply to it as for the original sample code. The RecoverString function performs the needed magic to, well, recover the string ;) DumpMem returns a 50-byte memory dump of the string you pass to it; use this one to see how the memory is layed out exactly for the passed-in string.
Const csFilePath As String = "<path to your test workbook>"
Private Declare Sub CopyMemoryByte Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Byte, ByVal Source As Long, ByVal Length As Integer)
Private Declare Sub CopyMemoryWord Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Integer, ByVal Source As Long, ByVal Length As Integer)
Private Declare Sub CopyMemoryDWord Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Long, ByVal Source As Long, ByVal Length As Integer)
Function DumpMem(text As String) As String
Dim textAddress As LongPtr
textAddress = StrPtr(text)
Dim dump As String
Dim offset As Long
For offset = -4 To 50
Dim nextByte As Byte
Call CopyMemoryByte(nextByte, textAddress + offset, 1)
dump = dump & Right("00" & Hex(nextByte), 2) & " "
Next
DumpMem = dump
End Function
Function RecoverString(text As String) As String
Dim textAddress As LongPtr
textAddress = StrPtr(text)
If textAddress <> 0 Then
Dim textSize As Long
Call CopyMemoryDWord(textSize, textAddress - 4, 4)
Dim recovered As String
Dim foundNulls As Boolean
foundNulls = False
Dim offset As Long
For offset = 0 To textSize - 1
Dim nextByte As Byte
Call CopyMemoryByte(nextByte, textAddress + offset, 1)
recovered = recovered & Chr(CLng(nextByte) + IIf(nextByte < 0, &H80, 0))
If nextByte = 0 Then
foundNulls = True
End If
Next
Dim isNotUnicode As Boolean
isNotUnicode = isNotUnicode Mod 2 = 1
If foundNulls And Not isNotUnicode Then
recovered = ""
For offset = 0 To textSize - 1 Step 2
Dim nextWord As Integer
Call CopyMemoryWord(nextWord, textAddress + offset, 2)
recovered = recovered & ChrW(CLng(nextWord) + IIf(nextWord < 0, &H8000, 0))
Next
End If
End If
RecoverString = recovered
End Function
Sub TestSecurity(testType As String, secondExcel As Application, security As MsoAutomationSecurity)
Dim theWorkbook As Workbook
secondExcel.AutomationSecurity = security
Set theWorkbook = secondExcel.Workbooks.Open(csFilePath)
Call MsgBox(testType & " - helpfile: " & theWorkbook.VBProject.HelpFile & " - " & RecoverString(theWorkbook.VBProject.HelpFile))
Call MsgBox(testType & " - description: " & theWorkbook.VBProject.Description & " - " & RecoverString(theWorkbook.VBProject.Description))
Call theWorkbook.Close(False)
End Sub
Sub Test()
Dim secondExcel As Excel.Application
Set secondExcel = New Excel.Application
Dim oldSecurity As MsoAutomationSecurity
oldSecurity = secondExcel.AutomationSecurity
Call TestSecurity("disabled macros", secondExcel, msoAutomationSecurityForceDisable)
Call TestSecurity("enabled macros", secondExcel, msoAutomationSecurityLow)
secondExcel.AutomationSecurity = oldSecurity
Call secondExcel.Quit
Set secondExcel = Nothing
End Sub