处理注册表中的 TypeGuessRows

问题描述

我有一个 VB.net 应用程序,它需要使用 OLEDB 和 MS Office Access 数据库驱动程序读取和写入 Excel 工作簿。随着时间的推移,我在 StackOverflow 上学到了很多东西,这让我可以让一切都按照应有的方式工作。这些主题涵盖了从连接字符串到“神奇”TypeGuessRows 设置/255 个字符限制的所有内容,现在位于注册表中。随着时间的推移,由于微软多年来的发展,这个问题本身已经发生了变化,所以我不会在这里引用那些(成百上千?)的帖子。可以说我能够将它们拼凑在一起并使其正常工作。

供参考:我的连接字符串如下所示:

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

其中“@DBQ”在运行时替换为所选 Excel 工作簿的相应路径名。请注意,它不包含 TypeGuessRows(因为它已移至注册表),并且 IMEX=(whatever) 也不起作用,因此它不会出现。结果是一切正常,只要在注册表中将 TYPEGUESSROWS 设置为零。

使用 regedit 手动搜索并在所有出现的情况下设置 TypeGuessRows=0(这是我在 StackOverflow 上学到的一件事),这已经奏效并完美避免了 255 个字符的截断。

问题是每当有 Windows 更新时,它都会被设置回认值 8。没有警告或公告;当我开始看到“截断为 255 个字符”的症状再次出现时,我才知道它。那么我必须回去再次使用RegEdit。

显而易见的解决方案是应用程序自行进行此设置,但这似乎需要操纵安全功能,这是我宁愿完全避免的事情,因为它似乎比我想要的更复杂立即处理。

所以,我想,“为什么不扫描适当的注册表项,并报告它们是否不为零”,然后向用户发出消息以使用 regedit 对其进行排序。

我编写了以下函数,它查看我发现的所有包含 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

这很有效,除了最后一个,键名中带有“...ClickToRun...”的那个。 无论如何,这总是作为 nothing 返回,因此检查它的尝试失败。即使在第一种方法中,我特别要求它返回一个“BOGUS”对象,它也总是返回为 nothing

问题在于它是由 Windows 更新更新的这个密钥。到目前为止,我从未见过其他三个被重置。

如果我“遍历这个键的树”(即,尝试查看“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office”),它会正常工作,直到我点击 ClickToRun 分支,此时它只返回 nothing。>

所以,我想我有两个问题:

  1. 有没有办法读取这个键,使它不返回为 nothing
  2. 有人可以提供一份(希望不要太混乱/复杂)指南来正确设置安全设置以允许“ReadWriteSubTree”进行权限检查吗?注意如果我现在尝试,我会收到安全异常,告诉我该应用程序没有适当的权限。这就是 aKey.SetValue 行被注释掉的原因。

更多信息: 机器是 Win 10 Pro,64 位,安装了适当的驱动程序。该应用程序设置为“启用 ClickToRun 安全设置”和“完全信任应用程序”。我已经在 app.manifest 中使用了这些设置和适当的条目,但到目前为止,这只会在其他地方引起(很多)其他的悲伤和困难。这就是为什么我不想惹它。

任何帮助将不胜感激。

解决方法

睡了一会儿,想了想,特别是[这里]找到了一些提示

Reading 64bit Registry from a 32bit application 我至少设法获得了读取注册表项的能力。 问题是我试图从我的 32 位应用程序中“读取”64 位注册表项。

现在我可以做我真正想做的事情,即检测何时再次出现 TypeGuessRows 问题,并警告用户采取措施。

修改后的代码如下:

    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

在对此进行测试时,它可以成功检查显示的所有四个键。 try-catch 永远不会被触发,但为了安全起见,我还是把它留在了那里。

我希望其他人可能会觉得这很有用。