1

I have a VB.net application which needs to read and write Excel workbooks, using OLEDB and the MS Office Access Database driver. Over time, I have learned a vast amount here on StackOverflow which allowed me to get everything working the way it should be. Those topics covered everything from connectionstrings to the "magic" TypeGuessRows setting / 255 character limitation, now located in the registry. Over time, that issue by itself has morphed due to the evolutions by Microsoft over the years, so I won't reference those (hundreds? thousands?) of posts here. Suffice it to say that I was able to piece it all together and get it working.

For reference: my connectionstring looks like this:

"Provider=Microsoft.ACE.OLEDB.12.0;Data Source='@DBQ';Extended Properties='Excel 12.0 Xml;HDR=YES';"

where '@DBQ' is replaced at runtime by the appropriate pathname of the selected Excel workbook. Note that it does not contain TypeGuessRows (since that has moved to the registry), and IMEX=(whatever) has not effect either, so it does not appear. The result is that everything works quite well, AS LONG AS TYPEGUESSROWS is set to zero in the registry.

Using regedit to manually hunt down and set TypeGuessRows=0 in all occurrences (one of the things I learned about here on StackOverflow), this has worked and avoids the 255-char truncation perfectly.

The problem is that whenever there is a Windows update, it gets set back to the default value, 8. There is no warning or announcement of this; I only know about it when I start seeing the "truncated at 255 characters" symptom showing up again. So then I have to go back and use RegEdit again.

The obvious solution would be that the app does this setting on its own, but that seems to require manipulation of the Security features, which is something I'd prefer to avoid entirely as it seems to be more complex than I want to deal with presently.

So, I thought, "why not just scan the appropriate registry keys, and report if they are not zero", then issue a message to the user to get it sorted using regedit.

I wrote the following function, which looks at all of the various registry keys I have found which contain TypeGuessRows:

    Public Function CheckRegistry() As Integer
    Dim Val1 As Integer = 0, Val2 As Integer = 0
    Dim KeysToCheck As String() = New String() {
       "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Jet\4.0\Engines\Excel",
       "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Jet\4.0\Engines\Lotus",
       "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Office\14.0\Access Connectivity Engine\Engines\Excel",
       "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\ClickToRun\REGISTRY\MACHINE\Software\Wow6432Node\Microsoft\Office\16.0\Access Connectivity Engine\Engines\Excel"
        }
    '
    '   try two methods for checking the registry values.
    '
    '   method 1 uses the My.Computer class
    '
    For Each Key As String In KeysToCheck
        Dim aVal As Object = My.Computer.Registry.GetValue(Key, "TypeGuessRows", CType("BOGUS", Object))
        If IsNothing(aVal) Then                         ' did we hit Nothing?
            Continue For                                ' have to skip it
        ElseIf aVal.Equals(CType("BOGUS", Object)) Then ' did we hit one not exist?
            Val1 = -9999                                ' set a crazy value
        End If
        Val1 += CInt(aVal)                              ' otherwise, get its value and add to total
    Next
    '
    '   method 2 uses the RegistryKey class
    '
    Dim aKey As Microsoft.Win32.RegistryKey
    For Each Key As String In KeysToCheck
        Dim LM As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.LocalMachine   ' point to HKLM
        Key = Key.TrimStart("HKEY_LOCAL_MACHINE\".ToCharArray)                          ' peel off the start (a convenience)
        aKey = LM.OpenSubKey(Key, Microsoft.Win32.RegistryKeyPermissionCheck.ReadSubTree)   ' try to read the subkey
        If IsNothing(aKey) Then Continue For                                            ' did we hit Nothing?  have to skip it
        'If CInt(aKey.GetValue("TypeGuessRows", 0)) <> 0 Then aKey.SetValue("TypeGuessRows", 0)
        Val2 += CInt(aKey.GetValue("TypeGuessRows", 0))                                 ' otherwise, get its value and add to total
    Next
    Return Val1 + Val2                                  ' return the sum of both methods
End Function

This works well enough, EXCEPT FOR THE LAST ONE, the one with "...ClickToRun..." in the key name. No matter what, this ALWAYS returns as Nothing, so the attempt to check it fails. Even in the first method, where I specifically ask it to return a "BOGUS" object, it always returns as Nothing.

The problem is that it is THIS KEY which gets updated by Windows Update. Thus far I have never seen any of the other three get reset.

If I "walk the tree" of this key (i.e., try looking at "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office"), it works fine until I hit the ClickToRun branch, at which point it just returns Nothing.

So, I guess I have two questions:

  1. Is there a way to read this key so that it does not return as Nothing?
  2. Can someone provide a (hopefully not too messy / complex) guide to getting the Security settings correct to allow "ReadWriteSubTree" for the permission check? N.B. if I try that now, I get Security exception, telling me that the app does not have the appropriate permission. That's why the aKey.SetValue line is commented out.

Further info: the machine is Win 10 Pro, 64-bit, with appropriate drivers installed. The app is set to "Enable ClickToRun security settings", and "full trust application". I have played with those settings and the appropriate entries in app.manifest, but thus far that only causes (much) other grief and difficulties elsewhere. That's why I'd prefer to not mess with it.

Any help would be really appreciated.

1 Answers1

1

After some sleep, some more thought and in particular, some hints found [here]

Reading 64bit Registry from a 32bit application I managed to at least get the ability to read the registry keys. The problem was that I was trying to "read" 64-bit registry keys from my 32-bit app.

Now I can do what I really wanted to do, which was to detect when the TypeGuessRows issue has cropped up again, and warn the user to do something about it.

Here is the modified code:

    Public Function CheckRegistry() As Integer
    Dim CheckVal As Integer = 0
    Dim KeysToCheck As String() = New String() {
       "SOFTWARE\WOW6432Node\Microsoft\Jet\4.0\Engines\Excel",
       "SOFTWARE\WOW6432Node\Microsoft\Jet\4.0\Engines\Lotus",
       "SOFTWARE\WOW6432Node\Microsoft\Office\14.0\Access Connectivity Engine\Engines\Excel",
       "SOFTWARE\Microsoft\Office\ClickToRun\REGISTRY\MACHINE\Software\Wow6432Node\Microsoft\Office\16.0\Access Connectivity Engine\Engines\Excel"
        }

    Dim LMachine As Microsoft.Win32.RegistryKey =
        Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine,
        Microsoft.Win32.RegistryView.Registry64)

    Dim LKey As Microsoft.Win32.RegistryKey
    For Each Key As String In KeysToCheck
        Try
            LKey = LMachine.OpenSubKey(Key, Microsoft.Win32.RegistryKeyPermissionCheck.ReadSubTree)
            CheckVal += CInt(LKey.GetValue("TypeGuessRows", 0))
        Catch ex As Exception
            LMachine.Close()
            Return -9999
        End Try
    Next
    LMachine.Close()
    Return CheckVal
End Function

In testing this out, it can successfully check all four of the keys shown. The try-catch is never fired, but I left it there for safety anyway.

I hope that someone else might find this useful.