【问题标题】:VB6 - Windows API call to DnsQuery - Function and Pointer QuestionVB6 - Windows API 调用 DnsQuery - 函数和指针问题
【发布时间】:2010-02-13 03:03:41
【问题描述】:

在 VB6 中,我正在调用 Windows API DnsQuery。

Private Declare Function DnsQuery Lib "dnsapi" Alias "DnsQuery_A" ( _
   ByVal lpstrName As String, _
   ByVal wType As Integer, _
   ByVal Options As Long, _
   ByVal pServers As Long, _
   ppQueryResultsSet As Long, _
   ByVal pReserved As Long) As Long

Private Type VBDnsRecord
   pNext           As Long
   pName           As Long
   'Name            As String ' Commented out, see question
   wType           As Integer
   wDataLength     As Integer
   Flags           As Long
   dwTtl           As Long
   dwReserved      As Long
   ptrData         As Long
   Others(35)      As Byte
End Type

我的结构声明来自here。我假设 Others(35) 是在返回的实际结构大于预期时提供足够大的容器(其中有一些可变长度类型 UNIONed)。有关详细信息,请参阅DNS_RECORD structure。

所以,我有两个问题:

  1. VB 中的字符串在幕后实际上是双字指针(4 个字节,又名 Long)。有一段时间,我想我可以将 Name 声明为 String,因为这只会将指针放在那里并正常工作(就像将 VB 字符串传递给 API 调用时一样)。但是,我猜我得到的应用程序终止是因为它是 C 风格的字符串而不是 VB 风格的字符串,并且 VB 在字符串开始之前的内存位置中查找长度值,并得到一些随机垃圾和爆炸。这是一个合理的猜测吗?

  2. 当我使用返回 DNS_PTR_DATA 或 DNS_A_DATA 的 DNS RR 类型时,我对 DnsQuery 的调用有效,但是当我尝试使用 DNS_TXT_DATA 时,它会爆炸。其他人可以发现我做错了什么吗?查看 Case DNS_TYPE_TEXT,并在下面查看我的 cmets。

    RetVal = DnsQuery(DnsName,
    QueryType, DNS_QUERY_BYPASS_CACHE,
    pServers, pDnsRecord, 0)
        If RetVal = 0 Then
           pNext = pDnsRecord
           Do While pNext <> 0
              CopyMemory DnsRecord, pNext, Len(DnsRecord)
              Select Case DnsRecord.wType
                 Case DNS_TYPE_A
                    Ptr = inet_ntoa(DnsRecord.ptrData)
                    TempString = String(lstrlen(Ptr), 0)
                    CopyMemory ByVal TempString, Ptr, Len(TempString)
                 Case DNS_TYPE_PTR, DNS_TYPE_NS, DNS_TYPE_CNAME,
    DNS_TYPE_DNAME, DNS_TYPE_MB,
    DNS_TYPE_MD, DNS_TYPE_MF,
    DNS_TYPE_MG, DNS_TYPE_MR
                    Ptr = DnsRecord.ptrData
                    TempString = String$(lstrlen(Ptr), 0)
                    CopyMemory ByVal TempString, Ptr, Len(TempString)
                 Case DNS_TYPE_TEXT, DNS_TYPE_HINFO, DNS_TYPE_ISDN,
    DNS_TYPE_TEXT, DNS_TYPE_X25
                    Dim TextData As Dns_Txt_Data
                    Ptr = DnsRecord.ptrData
                    CopyMemory VarPtr(TextData), Ptr, Len(TextData)
                    Stop
                 Case Else
                    TempString = "unhandled resource record type"
             End Select
             If Not FullRecord Then
                DnsLookup = "   " & TempString
                Exit Do
             Else
                DnsLookup = DnsLookup & " " & vbCrLf &
    DnsTypeNameFromCode(DnsRecord.wType)
    & " " & TempString
             End If
             pNext = DnsRecord.pNext
          Loop
    

    现在,当我在 CopyMemory 行设置断点并检查 Ptr 的值时,我预计会有数百万或更高的值,表明它是一个指针,但我得到的值是 1(这解释了为什么所有当我尝试从该内存位置复制时爆炸)。这似乎向我表明,我得到的不是指向预期的 DNS_TXT_DATA 结构的指针,而是字符串的计数。当我检查 Other(0) 到 Other(3) 时,它们都具有使我怀疑接下来的四个字节是指针的值。那么给了什么?为什么这个结构只是“内联”进来,而其他的则作为指向结构开始的指针进来?

感谢您的帮助!

【问题讨论】:

    标签: winapi vb6 pointers dns


    【解决方案1】:

    如果将VBDnsRecord 中的Others 更改为pStringArray(0 To 8) As Long,这样访问数组和字符串指针会更容易

            ...
            ElseIf uRecord.wType = DNS_TYPE_TEXT Then
                For lIdx = 0 To uRecord.prt - 1
                    sName = String(lstrlen(uRecord.pStringArray(lIdx)), 0)
                    Call CopyMemory(ByVal sName, uRecord.pStringArray(lIdx), Len(sName))
                    If LenB(Resolve) <> 0 Then
                        Resolve = Resolve & vbCrLf
                    End If
                    Resolve = Resolve & sName
                Next
            End If
    

    九个Longs 正好是 36 字节,所以 Len(VBDnsRecords) 仍然是 64 字节,正好是 Platform SDK 中 DNS_RECORD 联合体的大小。

    【讨论】:

    • 我离开并自己玩弄了一些东西,最终想出了......基本上就是你在这里所做的(尽管我不得不对你的数据类型的匈牙利符号表示嘘声)。在你和 jdigital 之间,我发现了我的误解。你们中的一个人最好明确地说“UNIONed 元素直接嵌入到父结构中,这里根本不涉及指针。”
    • P.S.,结构不会根据里面的内容改变大小吗?
    • 不,结构(联合)正好是 64 个字节。匈牙利人对变量(数据类型?)感到厌恶是理所当然的,但是花更多时间在 Win32 API 上会让你摆脱困境......
    【解决方案2】:

    1) C 字符串和 VB 字符串不同,不能只用一个替换另一个。您需要将 C 字符串显式转换为 VB 字符串。换句话说,是的,你的猜测是合理的。

    2) 我快速浏览了 Microsoft 文档,其中说 DNS_TXT_DATA 是一个计数,后跟一个指向字符串的指针(这就是您所看到的)。另一方面,DNS_PTR_DATA 是指向结果记录的一个点。所以你所描述的似乎与文档相符。

    【讨论】:

    • 1) VB 字符串不也是空终止的吗?所以 VB 字符串可以像 C 字符串一样工作,但反过来不行,对吧? 2)现在我终于看到了!我被对 inet_ntoa 的调用所迷惑,它返回了一个(指向一个)字符串。但它确实只是一个值。不知何故,我知道它是一个指针。我很清楚现在该做什么(但要到周二才会回来)。
    • 我不相信 VB 字符串是 nul 终止的;此外,他们使用宽字符。您应该双向使用转换例程。
    • 如果他们使用宽字符而 API 函数不使用,那么 CopyMemory 将无法正常工作,但它确实有效。看来您回答的更多是理论而不是实际实践?
    • 此外,DNS_PTR_DATA 内部确实有一个指针,但 DNS_A_DATA 没有……所以事实上,说“DNS_PTR_DATA 是指向结果记录的指针”并不是真的。不管怎样,在你和wqw之间,我发现了我的错误。现在我不知道该将答案授予谁。
    • 这是一篇关于VB字符串和C字符串之间转换的文章:codeproject.com/KB/string/bstrsproject1.aspx。它解释了 VB 字符串是 16 位 unicode。
    猜你喜欢
    • 2017-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多