WebBrowser IE 仿真以满足 Google Maps JavaScript API 密钥要求





5.00/5 (8投票s)
"不关心 Google Maps/Routing/StreetView 编程。此控件可为您的 WinForms 应用程序提供 Google Maps API v3 的强大功能。" - 我之前的文章。不幸的是,自 2015 年 12 月起,Google Maps JavaScript API 应用程序需要身份验证,程序停止正常工作。
引言
我写于 2014 年 12 月的文章 GMaps v1.1.12 - Google Maps/Routing/StreetView All-in-1 运行良好。此外,社区还将其评为“2014 年 12 月最佳 VB.NET 文章(一等奖)”。谢谢。
但自 2015 年 12 月起,Google Maps JavaScript API 应用程序需要身份验证,程序停止正常工作(在“路线”视图中,未显示方向蓝色线条和文本方向面板),因为 HTML/JavaScript 文件在 Visual Studio 的 WebBrowser 的控制下运行,默认情况下是 Internet Explorer 7 版本,这不足以满足要求。
必须采取措施。最初,对于我自己的应用程序,我获取了一个 API 密钥并手动修改了注册表等。
但是有必要自动化上述文章组件中的过程,我首先考虑用 Gecko 或 Awesomium 等解决方案替换 WebBrowser。
在这两种情况下,引擎的实现都会导致程序更加沉重,代码转换将是一项巨大的任务。所以我选择仿真 WebBrowser 到更高的 IE 版本。这涉及到
- 向注册表项
HKEY_CURRENT_USER
\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION 写入一个值
可以使用HKEY_CURRENT_USER
而不是HKEY_
LOCAL_MACHINE,但这需要高权限。HKCU 足够了。
值是- 值名称:应用程序项目名称 + ".exe"(或 ".vshost.exe",如果项目在 Visual Studio 中运行)。示例
MyProgram1.exe
MyProgram1.vshost.exe - 值:一个 DWORD,如 此处 定义(参见 Browser Emulation)。值为十进制。我们将使用11000(IE 11 默认)或10000(IE 10 默认)。
- 值名称:应用程序项目名称 + ".exe"(或 ".vshost.exe",如果项目在 Visual Studio 中运行)。示例
- 确保在应用程序开始时写入该值,并在应用程序结束时删除(不要用垃圾填充注册表)。
最好的解决方案是使用My.Application
类的启动和关闭事件,但这不能在类库(如上述文章的用户控件 DLL)中完成,它仅允许在 WinForms 应用程序级别进行。
然后,由于 DLL 组件是用 VB 编写的,对于任何 VB 应用程序,与其使用 DLL 组件,不如只将一个文件 GMapsVB.vb(包含四个类,包括组件)插入 WinForms 项目。重新生成后,该组件将在工具箱中可用。因此,写一篇新文章而不是仅仅更新原来的文章。
对于使用其他语言(包括 VB)的人,我还提供了一个用户控件 DLL 项目(GMapsX)。但是,有一个限制:My.Application 的启动和关闭事件成为 DLL 组件的方法,必须由启动窗体的事件分别手动调用:Load
和 [FormClosing
(VB.NET) 或 OnFormClosing
(C#)]。
必备组件
- Visual Studio 2012 或更高版本。
- 要使用新的 VB 类文件或 DLL,项目必须以.NET Framework 4(客户端配置文件)为目标。
- 必须安装Internet Explorer 10 或11。在 Windows 7 上,安装最新的 IE 版本之前,请先更新到 Service Pack 1 (SP1)。
- 有关用户控件的完整信息,请参阅文章 GMaps v1.1.12 - Google Maps/Routing/StreetView All-in-1,您也可以在那里下载文档。在本篇文章中,我们将只讨论引入的差异和新代码。
- 最小屏幕分辨率为1360x768
Google Maps API 密钥
必须将密钥放入的、应该位于可执行文件文件夹中的文件位于
...\GMaps_VB\GMapsVB_Demo\GMapsVB_Demo\bin\Release\GMaps1.html,或者,
...\GMaps_X\GMapsX_Demo\GMapsX_Demo\bin\Release\GMaps1.html
我不知道 Google Maps API 密钥、其身份验证、与操作系统的交互等的真正机制,因为我迄今为止找到的文档不是非常有帮助。但事实是,在我体验这些项目生成的程序的大多数机器上,一切都与提供的文件GMaps1.html 一起正常工作,即,没有 API 密钥。
“正常工作”意味着在“路线”视图中显示方向蓝色线条和文本方向面板。
因此,首先,尝试按原样运行所选项目。正常工作?太棒了!
但如果不行
- 在此 处 获取 API 密钥。
- 在文件
GMaps1.html
的末尾,找到这个<!--- (1) First attempt <script src="https://maps.googleapis.com/maps/api/js?key=XXXXXXXXXX"></script> ---> <!--- (2) Second attempt <script async defer src="https://maps.googleapis.com/maps/api/js?key=XXXXXXXXXX&callback=Init(38.7438882, -9.1417950)"> </script> ---> <!--- Specifying an API KEY (above attempts): ---> <!--- [(1) or (2)] Replace XXXXXXXXXX by YOUR API KEY ---> <!--- (2) Replace 38.7438882 by YOUR INITIAL LATITUDE ---> <!--- (2) Replace -9.1417950 by YOUR INITIAL LONGITUDE ---> <!--- Uncomment only the chosen <script> ... </script> ---> <!--- (3) Third attempt Without loading the API --->
- 按顺序进行三次尝试并测试,一次一个
- 同步加载 API。
- 异步加载 API(如 Google 推荐)。
- 不加载 API(再次)。
使用代码
1) 对于GMapsVB,在一个新的或现有的 VB 项目中
- 在解决方案资源管理器 -> 应用程序选项卡 -> 将目标框架更改为.NET Framework 4(客户端配置文件更轻)
- 插入文件GMapsVB.vb:Visual Studio 菜单项目 -> 添加现有项... -> GMapsVB.vb(从...\GMaps_VB\GMapsVB_Demo\GMapsVB_Demo\GMapsVB.vb 选择)。选择此项后,还将传输另外两个文件:GMapsVB.Designer.vb 和 GMapsVB.resx
- 将文件GMaps1.html复制到您的项目启动文件夹(...\...\bin\Debug 或 ...\...\bin\Release,取决于配置管理器设置)
- 生成(或重新生成)项目。一个新
GMapsVB
图标将出现在工具箱中。
GMapsVB.vb 包含四个类
1) 类 GMapsVB - 是用户控件,因此必须是第一个类。
与原始v1.1.12 GMaps 唯一的区别是:a) 程序集不是 COM 可见的,以及 b) GMaps.html 文件已更改为 GMaps1.html。所以这些行
WBrowser.ObjectForScripting = Me
WBrowser.Navigate(Application.StartupPath & "\GMaps.html")
已更改为
' WBrowser.ObjectForScripting = Me
WBrowser.Navigate(Application.StartupPath & "\GMaps1.html")
2) 类 MyApplication - 定义在Namespace My 中
控制启动和关闭事件,这些事件在应用程序启动和结束时触发。此外,它还包含执行仿真操作所需的所有代码 -获取信息、写入和删除注册表项值。还调用_InfoBox 窗体(在下一个类中定义)。
- 可以修改配置变量,但建议在一切正常之前保留它们的默认值。
_Process_My_Events
- 在需要仿真时保持为 True(谁知道,也许有一天 Microsoft 会改变 WebBrowser 的默认设置...)。_Default_Emul_Version
- 保持为 0,除非使用以前的版本有什么好处。_Verbose_Mode
- 只有当一切正常时才将其更改为 False,即,“路线”视图显示方向蓝线和文本方向面板。
Namespace My
Partial Friend Class MyApplication
' =========================== CONFIGURATION VARIABLES ============================
Public _Process_My_Events As Boolean = True ' Events Startup and Shutdown
Public _Default_Emul_Version As Byte = 0 ' 0 (Installed version), 10, 11
' Only versions 10 or 11 will properly process the HTML file
Public _Verbose_Mode As Boolean = True ' True = Shows Form _InfoBox
' ================================================================================
内部变量。不要更改。_InfoBox 消息背景色的含义
- Lime:OK。WebBrowser 已为可用的最新版本进行了仿真。
- Yellow:警告。WebBrowser 已为可用的工作版本进行了仿真,但不是最新的。
- Orange:危险。WebBrowser 已为(可预见的)不适用版本进行了仿真。
- Red:错误。WebBrowser 未被仿真。
Public OS_64bit As Boolean
Public App_64bit As Boolean
Public RegistryBase As String
Public RegistrySubKey As String
Public ProgName As String
Public IE_Version As String = ""
Public IE_MajorVersion As Byte = 0
Public Emulation As Byte = 0
Public Error_Color As Byte = 3 ' 0=Lime, 1=Yelow, 2=Orange, 3=Red
Public StartupEvent As Boolean
如果必须处理启动事件
GetInfo()
收集一些信息并设置内部变量。- 如果没有检测到错误,则写入注册表项值。变量
Emulation
包含一个主版本(例如 11、10、..),并乘以 1000... - 并且(如果适用)显示_InfoBox 窗体。
Private Sub MyApplication_Startup(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup
If _Process_My_Events Then
StartupEvent = True
GetInfo()
If Error_Color < 3 Then ' Create Registry Value
Registry.SetValue(RegistryBase & RegistrySubKey, _
ProgName, Emulation * 1000, RegistryValueKind.DWord)
End If
ShowInfoBox()
End If
End Sub
如果必须处理关闭事件
- 如果注册表项值已被写入,则将其删除,
- 并且(如果适用)显示_InfoBox 窗体。
Private Sub MyApplication_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shutdown
If _Process_My_Events Then
StartupEvent = False
If Error_Color < 3 Then ' Delete Registry Value
Dim Key As RegistryKey = Registry.CurrentUser.OpenSubKey(RegistrySubKey, True)
Key.DeleteValue(ProgName)
End If
ShowInfoBox()
End If
End Sub
GetInfo()
获取已安装的 IE 版本并设置一些变量,包括 Emulation
(要仿真的主版本)和 Error_Color
,它控制要执行的操作的质量。
Private Sub GetInfo()
OS_64bit = Environment.Is64BitOperatingSystem
App_64bit = Environment.Is64BitProcess
' Get Internet Explorer version
Dim FileIE As String = Environment.GetFolderPath(Environment.SpecialFolder.System) & "\ieframe.dll"
If File.Exists(FileIE) Then
Dim V = FileVersionInfo.GetVersionInfo(FileIE)
IE_Version = V.ProductVersion
IE_MajorVersion = V.FileMajorPart
Error_Color = 0
End If
' Set Registry Base, SubKey and Value Name (EXE program name)
RegistryBase = Registry.CurrentUser.Name & "\"
RegistrySubKey = "SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION"
ProgName = My.Application.Info.AssemblyName & If(Debugger.IsAttached, ".vshost", "") & ".exe"
' Select IE Emulation version to apply
If IE_MajorVersion > 0 Then ' IE detected
If IE_MajorVersion <= 9 Then ' Unsuitable version
Error_Color = 2
End If
End If
If _Default_Emul_Version = 0 OrElse _Default_Emul_Version = IE_MajorVersion Then
Emulation = IE_MajorVersion
Else
If _Default_Emul_Version > IE_MajorVersion OrElse _Default_Emul_Version < 7 Then
Error_Color = 3
Else
Emulation = _Default_Emul_Version
Error_Color = If(_Default_Emul_Version < 10, 2, 1)
End If
End If
End Sub
_InfoBox 窗体将在请求时显示,在检测到错误或危险状态时也会显示。
Private Sub ShowInfoBox()
If _Verbose_Mode OrElse Error_Color > 1 Then
Dim F1 As New _InfoBox
F1.ShowDialog()
F1.Dispose()
End If
End Sub
End Class
End Namespace ' My
3) 类 _InfoBox - 是一个窗体,有两个主要部分:DESIGNER 和 CODE。
- DESIGNER 由 IDE 生成。关于这个没有什么可说的。
- CODE 根据
Class MyApplication
中定义的内部变量准备要显示的信息,在类中,它作为Application
可见,类型为Friend ReadOnly Property Application
,因此变量必须带有前缀My.Application
。
变量.StartupEvent
指示调用事件:Startup(=True
)或 Shutdown(=False
)。
' ============================================================
' == CODE ==
' ============================================================
Private Tab_Colors() As Color = {Color.Lime, Color.Yellow, Color.Orange, Color.Red}
Private Sub _InfoBox_Load(sender As Object, e As EventArgs) Handles MyBase.Load
With My.Application
If .StartupEvent Then
A2.Text = "Startup"
End If
If .OS_64bit = False Then
B2.Text = "32 bits"
End If
If .App_64bit = False Then
C2.Text = "32 bits"
End If
D2.Text = .RegistryBase & .RegistrySubKey
E2.Text = .ProgName
If .IE_MajorVersion > 0 Then
F2.Text = .IE_MajorVersion.ToString
F3.Text = .IE_Version
End If
If ._Default_Emul_Version > 0 Then
G2.Text = ._Default_Emul_Version
End If
H2.BackColor = Tab_Colors(.Error_Color)
If .Error_Color = 3 Then
H2.ForeColor = Color.Yellow
If .IE_MajorVersion = 0 Then
H2.Text = "Internet Explorer not detected..."
Else
H2.Text = "Invalid Default version (" & _
._Default_Emul_Version.ToString & ")"
End If
Else
If .StartupEvent Then
H2.Text &= " " & .Emulation.ToString
Else
H2.BackColor = Tab_Colors(0)
H2.Text = "Registry Value deleted..."
End If
End If
End With
End Sub
Private Sub bClose_Click(sender As Object, e As EventArgs) Handles bClose.Click
Me.Close()
End Sub
输出示例
4) 类 _List - 是原始 GMaps 用户控件的列表窗体,有两个主要部分:DESIGNER 和 CODE。
- 唯一的区别是窗体的
FormBorderStyle
属性从FixedToolWindow
更改为SizableToolWindow
。
输出看起来像这样
2) 对于GMapsX - 其他语言(包括 VB)
项目GMapsX 生成一个用户控件 DLL(GMapsX.dll),必须将其复制到应用程序的启动文件夹,并通过 WinForms 项目进行引用。
文件GMaps1.html 也必须复制到应用程序的启动文件夹。
与GMapsVB相比,GMapsX 在 Startup
和 Shutdown
事件的实现上有所不同,这些事件不能在类库项目中定义为 My.Application
事件。因此,类 MyApplication
被省略,事件由类 GMapsX
中的用户控件控制
1) 添加了新变量
'
' New Public Shared Variables (GMapsX)
'
' =========================== CONFIGURATION VARIABLES ============================
Public Shared _Process_My_Events As Boolean = True ' Events Startup and Shutdown
Public Shared _Default_Emul_Version As Byte = 0 ' 0 (Installed version), 10, 11
' Only versions 10 or 11 will properly process the HTML file
Public Shared _Verbose_Mode As Boolean = True ' True = Shows Form _InfoBox
' ================================================================================
Public Shared OS_64bit As Boolean
Public Shared App_64bit As Boolean
Public Shared RegistryBase As String
Public Shared RegistrySubKey As String
Public Shared ProgName As String
Public Shared IE_Version As String = ""
Public Shared IE_MajorVersion As Byte = 0
Public Shared Emulation As Byte = 0
Public Shared Error_Color As Byte = 3 ' 0=Lime, 1=Yelow, 2=Orange, 3=Red
Public Shared StartupEvent As Boolean
2) 创建了新属性,以允许更改配置变量
'
' New Properties (GMapsX)
'
Public WriteOnly Property Process_My_Events As Boolean
Set(value As Boolean)
_Process_My_Events = value
End Set
End Property
Public WriteOnly Property Default_Emul_Version As Byte
Set(value As Byte)
_Default_Emul_Version = value
End Set
End Property
Public WriteOnly Property Verbose_Mode As Boolean
Set(value As Boolean)
_Verbose_Mode = value
End Set
End Property
3) 并且 Startup
和 Shutdown
事件变为方法(重命名为 Application_Startup
和 Application_Shutdown
)
'
' New Methods (GMapsX)
'
Public Sub Application_Startup()
If _Process_My_Events Then
StartupEvent = True
GetInfo()
If Error_Color < 3 Then ' Create Registry Value
Registry.SetValue(RegistryBase & RegistrySubKey, _
ProgName, Emulation * 1000, RegistryValueKind.DWord)
End If
ShowInfoBox()
End If
End Sub
Public Sub Application_Shutdown()
If _Process_My_Events Then
StartupEvent = False
If Error_Color < 3 Then ' Delete Registry Value
Dim Key As RegistryKey = Registry.CurrentUser.OpenSubKey(RegistrySubKey, True)
Key.DeleteValue(ProgName)
End If
ShowInfoBox()
End If
End Sub
- 请注意,上述方法必须由启动窗体的事件分别调用:
Load
和 [FormClosing
(VB.NET) 或OnFormClosing
(C#)]。请参阅示例
private void Form1_Load(object sender, EventArgs e)
{
GMapsX1.Process_My_Events = true; // (Default) - Required to perform Emulation
GMapsX1.Default_Emul_Version = 0; // (Default) - Emulates IE installed version
GMapsX1.Verbose_Mode = true; // When "Route" views are running OK,
// change to false, to stop displaying InfoBox
//
GMapsX1.Application_Startup(); // This is mandatory
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
GMapsX1.Application_Shutdown(); // This is mandatory
}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
GMapsX1.Process_My_Events = True ' (Default) - Required to perform Emulation
GMapsX1.Default_Emul_Version = 0 ' (Default) - Emulates IE installed version
GMapsX1.Verbose_Mode = True ' When "Route" views are running OK,
' change to False, to stop displaying InfoBox
'
GMapsX1.Application_Startup() ' This is mandatory
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
GMapsX1.Application_Shutdown() ' This is mandatory
End Sub
将组件放置在工具箱中
如果右键单击工具箱 -> 选择项... -> 浏览(...\...\GMapsX.dll)-> 打开,然后收到一条消息 [‘...\...\GmapsX.dll’中没有可以放置在工具箱上的组件。],那么,
- 使用文件资源管理器,打开GMapsX.dll 所在的文件夹。
- 将GMapsX.dll拖动并放入您想放置它的选项卡(在工具箱中)。
历史
2016 年 7 月 4 日 - 首次发布。