4

我对 DNSServiceRegister 有以下声明:

  function DNSServiceRegister
      (
      var sdRef: TDNSServiceRef;
      const flags: TDNSServiceFlags;
      const interfaceIndex: uint32_t;
      const name: PUTF8String;                    //* may be NULL */
      const regType: PUTF8String;
      const domain: PUTF8String;                  //* may be NULL */
      const host: PUTF8String;                    //* may be NULL */
      const port: uint16_t;
      const txtLen: uint16_t;
      const txtRecord: Pointer;                 //* may be NULL */
      const callBack: TDNSServiceRegisterReply; //* may be NULL */
      const context: Pointer                    //* may be NULL */
      ): TDNSServiceErrorType; stdcall; external DNSSD_DLL;

在我的 Bonjour 框架中,我对激活的已宣布服务有以下响应(即通过 Bonjour 实际开始宣布自己):

  procedure TAnnouncedService.Activate;
  var
    flags: Cardinal;
    name: UTF8String;
    svc: UTF8String;
    pn: PUTF8String;
    ps: PUTF8String;
  begin
    fPreAnnouncedServiceName := ServiceName;

    inherited;

    if AutoRename then
      flags := 0
    else
      flags := kDNSServiceFlagsNoAutoRename;  { - do not auto-rename }

    if (ServiceName <> '') then
    begin
      name  := ServiceName;
      pn    := PUTF8String(name);
    end
    else
      pn := NIL;

    svc := ServiceType;
    ps  := PUTF8String(svc);

    CheckAPIResult(DNSServiceRegister(fHandle,
                                      flags,
                                      0 { interfaceID - register on all interfaces },
                                      pn,
                                      ps,
                                      NIL { domain - register in all available },
                                      NIL { hostname - use default },
                                      ReverseBytes(Port),
                                      0   { txtLen },
                                      NIL { txtRecord },
                                      DNSServiceRegisterReply,
                                      self));
    TBonjourEventHandler.Create(fHandle);
  end;

这比我认为它严格需要的更冗长,当然它在 Delphi 7 中以一种不那么冗长的形式运行得很好。我已将许多操作扩展为显式步骤以促进调试,例如,能够识别可能在 Delphi 2009 中“幕后”发生的字符串有效负载的任何隐式转换。

即使在这种杂乱无章的扩展形式中,此代码在 Delphi 7 中也能很好地编译和运行,但如果我使用 Delphi 2009 编译和运行,我不会收到关于我的服务的通知。

例如,如果我将此代码作为 Delphi 7 应用程序的一部分运行以注册_daap._tcp服务(iTunes 共享库),我会看到它在运行的 iTunes 实例中弹出。如果我在 Delphi 2009 中不加修改地重新编译完全相同的应用程序并运行它,我看不到我的服务出现在 iTunes 中。

使用dns-sd命令行实用程序进行监视时,我得到了相同的行为。也就是说,使用 Delphi 7 编译的服务代码的行为与我预期的一样,在 Delphi 2009 中编译 - 没有。

我没有从 Bonjour API收到任何错误 -正在调用DNSServiceRegisterReply回调,错误代码为 0(零),即成功,如果我提供带有在标志中指定的 AutoRename 的 NIL 名称参数,那么我的服务被分配了正确的默认名称。但该服务仍然没有出现在 iTunes 中。

我不知道发生了什么。

正如您从代码的扩展中可能看出的那样,我一直在寻找由 Delphi 2009 中的 Unicode 实现引入的潜在错误,但这似乎让我无处可去。

该代码最初是针对 Bonjour API/SDK 1.0.3 版开发的。我已经更新到 1.0.6,以防万一涉及到,但没有任何成功。afaict 1.0.6 只是添加了一个用于获取“属性”的新功能,该功能目前仅支持用于获取 Bonjour 版本的“DaemonVersion”属性 - 这非常有效。

注意:我知道 Delphi 7 中的代码在技术上不是 UTF8 安全的 - 我已尽可能消除显式转换,以使 Delphi 2009 应用的自动转换尽可能简单。我现在的目标是让它在 Delphi 2009 中工作,然后从该解决方案向后工作,希望找到与早期版本的 Delphi 兼容的方法。

还请注意:我最初在浏览广告服务时也遇到了问题,即识别网络上的实际 iTunes 共享库。这些问题是由 Delphi 2009 中的 Unicode 处理引起的,并且已得到解决。我的 Delphi 2009 代码同样能够识别实际的 iTunes 共享库并查询它的 TXT 记录。只有这个服务注册不起作用。

我一定错过了一些愚蠢而明显的东西。

有没有人有什么想法?!

更新

回到这个问题后,我现在发现了以下内容:

如果我打开了一个 pre-D2009 和一个 D2009+ IDE(例如 D2006 和 D2010),同时将同一个项目加载到两个 IDE 中:

  • 在 2006 年构建和运行:它有效 - 我的服务公告被 iTunes 接收
  • 切换到 D2010 并运行(无需构建):它进行了最小的编译、运行和工作。
  • 在 D2010 中进行完整构建:它停止工作

  • 切换回 D2006 并运行(不构建):它不起作用

  • 在 D2006 中进行完整构建:它再次工作

这会给任何人其他想法吗?

4

4 回答 4

5

这个问题的答案令人难以置信。一方面,我犯了一个完全愚蠢、非常简单的错误,但另一方面,据我所知,它不应该在任何版本的 Delphi 中工作!

问题与任何字符串的 Unicode/非 unicodeness 无关,但实际上是由于 PORT 参数中的类型不匹配造成的。

我传入了ReverseBytes(Port)的结果——该参数需要一个uint16_t,即一个Word值。然而,我的Port属性被(懒惰地)声明为Integer

一旦我解决了这个问题并将Port声明为Word,它现在可以在 Delphi 的 D2007- 和 D2009+ 版本上运行。

很奇怪。

我只能认为,当引入 Unicode 支持时,编译器的其他一些可能以某种方式影响这一点的边缘情况行为发生了变化。

于 2010-08-20T03:20:09.427 回答
1

根据我们在此处获得的信息,情况是这样的:

  • 在 Delphi 2007 中使用您的代码调用 DLL 时,它会给出一个结果。
  • 在 Delphi 2009 中使用您的代码调用相同的 DLL 时,它会给出另一个结果。
  • 怀疑是,它与 Delphi 2009 编译器有关。

因此,从逻辑上讲,区别在于 Delphi 2009 发送不同的值作为参数。为了使调试真正独立于 Delphi,因此您需要创建一个虚拟 DLL,它报告它获得的值。可以应用其他依赖于 Delphi 的方法,例如查看将函数调用反汇编到 DLL 中,并对其进行调试,以便我们准确地知道在两个编译器中传递了哪些值以及如何传递给 DLL。

于 2009-09-28T07:03:07.877 回答
0

我在您的代码示例中找不到变量“ServiceName”和“ServiceType”的声明指令。

假设一个 String 类型(因此是一个 unicode 字符串),我猜(是的......没有 D2009 可用于测试这个)惰性类型转换可能是一个问题:

name  := ServiceName;

为什么不使用以下内容?

name  := PAnsiChar(AnsiString(ServiceName)) 

无论如何...只是我的 2 cts。

顺便说一句:我总是使用预定义的“ EmptyStr ”、“ EmptyWideStr ”......所以测试看起来像:

if (ServiceName <> EmptyStr) then

这应该是安全的并避免类型混淆。

另一方面,Delphi 可能将 '' 解释为 ANSIChar,如下面的声明:

const
  MyParagraphChar = '§';

不确定......我很困惑 - 现在应该回家;)

于 2009-10-14T16:18:21.730 回答
-1

如果 DLL 不是使用 Delphi 2009 编写的,您可能想要使用 PUTF8String 以外的其他东西。Delphi 2009 的 Utf8String 类型不同于 Delphi 2007 的 UTF8String 类型。

如果 DLL 是使用 C/C++ 编写的,我强烈建议使用 PAnsiChar() 而不是 PUtf8String。

于 2009-09-27T17:37:03.730 回答