【问题标题】:Using browscap.ini with VB.Net在 VB.Net 中使用 browscap.ini
【发布时间】:2017-07-09 23:50:47
【问题描述】:

自 2013 年以来(超过 3 年),我一直在我的主要 VB.Net 项目中使用 http://www.useragentstring.com/ 从用户代理字符串中获取浏览器名称/版本和操作系统名称/版本,以向我的本地 Web 应用程序添加统计信息.

但是,最近,在过去的几个月里,这个网站一直不可靠,停机时间很多。因此,为了避免在我的统计数据中丢失数据,我搜索了本地解决方案而不是在线解决方案。我发现http://browscap.org/ 是一个旧网站(自 1998 年以来),至今仍在上传更新的用户代理信息(browscap.ini)。它是为 PHP 设计的,但我在那里找到了一个 C# 实现:https://www.gocher.me/C-Sharp-Browscap

但作为 VB.Net 开发人员,我没有找到任何 VB 实现。我用谷歌搜索了很多但没有成功。有人会为 VB.NET 买一个吗?

【问题讨论】:

    标签: asp.net vb.net user-agent browser-detection browscap


    【解决方案1】:

    我终于可以将 C# 解决方案转换为 VB.NET,但有点头疼。

    Public Class CompareByLength
    Implements IComparer(Of String)
        Private Function Compare(ByVal x As String, ByVal y As String) as Integer _
            Implements IComparer(Of String).Compare
                If x Is Nothing Then
                    If y Is Nothing Then
                        Return 0
                    Else
                        Return 1
                    End If
                Else
                    If y Is Nothing Then
                        Return -1
                    Else
                        Dim retval As Integer = x.Length.CompareTo(y.Length)
                        If retval <> 0 Then
                            Return -retval
                        Else
                            return -x.CompareTo(y)
                        End If
    
                    End If
                End If
            End Function
    End Class
    
    Public Class BrowsCap
        Private Declare Function GetPrivateProfileSectionNames Lib "kernel32.dll" Alias "GetPrivateProfileSectionNamesA" (ByVal lpReturnedString As Byte(), ByVal nSize As Integer, ByVal lpFileName As String) As Integer
        Private Declare Function GetPrivateProfileSection Lib "kernel32.dll" Alias "GetPrivateProfileSectionA" (ByVal lpAppName As String, ByVal lpReturnedBuffer As Byte(), ByVal nSize As Integer, ByVal lpFileName As String) As Integer
        Private Declare Function GetPrivateProfileString Lib "kernel32.dll" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedBuffer As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer
    
        Private path As String
        Private sections As String()
    
        Private Function GetSectionNames() As String()
            Dim maxsize As Integer = 500
            Do
                Dim bytes(maxsize) As Byte
                Dim size As Integer = GetPrivateProfileSectionNames(bytes, maxsize, path)
                If size < maxsize - 2 Then
                    Dim Selected As String = Encoding.ASCII.GetString(bytes, 0, size - (IIf(size > 0, 1, 0)))
                    Return Selected.Split(New Char() {ControlChars.NullChar})
                End If
                maxsize = maxsize * 2
            Loop
        End Function
    
        Public Sub IniFileName(ByVal INIPath As String)
            path = INIPath
            sections = GetSectionNames()
            Array.Sort(sections, New CompareByLength())
        End Sub
    
        public Function IniReadValue(ByVal Section As String, ByVal Key As String) As String
          Dim temp As New StringBuilder(255)
          Dim i As Integer = GetPrivateProfileString(Section, Key, "", temp.ToString(), 255, path)
          Return temp.ToString()
        End Function
    
        Private Function findMatch(ByVal Agent As String) As String
          If sections IsNot Nothing Then
            For Each SecHead As String In sections
              If (SecHead.IndexOf("*", 0) = -1) And (SecHead.IndexOf("?", 0) = -1) And (SecHead = Agent) Then
                If IniReadValue(SecHead, "parent") <> "DefaultProperties" Then
                  Return SecHead
                End If
              End If
            Next
            For Each SecHead As String In sections
              Try
                If (SecHead.IndexOf("*", 0) > -1) Or (SecHead.IndexOf("?", 0) > -1) Then
                  if Regex.IsMatch(Agent, "^" + Regex.Escape(SecHead).Replace("\*", ".*").Replace("\?", ".") + "$") Then
                    Return SecHead
                  End If
                End If
              Catch ex As Exception
                'Console.WriteLine(ex)
              End Try
            Next
            Return "*"
          End If
          Return ""
        End Function
    
        Public Function getValues(ByVal Agent As String) As NameValueCollection 
          Dim match As String = findMatch(Agent)
          Dim col As NameValueCollection = New NameValueCollection()
          Do
            Dim entries() As string
            Dim goon As Boolean = true
            Dim maxsize As Integer = 500
            While goon
              Dim bytes(maxsize) As Byte
              Dim size As Integer = GetPrivateProfileSection(match, bytes, maxsize, path)
              If size < maxsize - 2
                Dim section As String = Encoding.ASCII.GetString(bytes, 0, size - IIf(size > 0, 1, 0))
                entries = section.Split(New Char() {ControlChars.NullChar})
                goon = False
              End If
              maxsize = maxsize * 2
            End While
            match = ""
            If entries.Length > 0 Then
              For Each entry As String In entries
                Dim ent As String() = entry.Split(New Char() {"="C})
                If ent(0) = "Parent" Then
                  match = ent(1)
                else if col(ent(0)) is nothing Then
                  col.Add(ent(0), ent(1))
                End If
              Next
            End If
          Loop While match <> ""
          Return col
        End Function
    End Class
    

    下面是如何使用它:

    Dim dict As Dictionary(Of String, Object) = New Dictionary(Of String, Object)
    Dim bc As New BrowsCap
    bc.IniFileName(Server.MapPath("/App_Data/lite_asp_browscap.ini"))
    Dim Entry As NameValueCollection = bc.getValues(Request.UserAgent)
    For Each s As String In Entry.AllKeys
        dict.Add(s, Entry(s))
    Next
    ' dict("Browser") will contains browser name like "IE" or "Chrome".
    ' dict("Version") will contains browser version like "11.0" or "56.0".
    ' dict("Platform") will contains OS name and version like "Win7".
    

    剩下要做的就是有时(比如每周一次)刷新我的 browscap.ini(或 lite_asp_browscap.ini)。

    【讨论】:

    • 您应该知道,自己读取和解析 INI 比使用 PInvoke 快大约 6 倍。也许也更简单。
    • 你一定是对的,我觉得我在其他地方看到过这个,但我没有时间自己编写解析+比较+正则表达式/匹配逻辑。一切都已经用 C# 编写了。此外,我的代码每天只执行 100-200 次。对我来说不是什么大问题。但是,如果您已经在没有 PInvoke 的情况下编写了这种逻辑,请随时发布。感谢您指出这一点。
    猜你喜欢
    • 2015-04-03
    • 2012-11-01
    • 2013-12-22
    • 2011-02-02
    • 1970-01-01
    • 2015-11-04
    • 2013-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多