在Delphi 7中工作的ZeroConf / Bonjour Code在2009年不起作用

时间:2009-09-27 03:06:20

标签: delphi delphi-2009 bonjour zeroconf

我有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中获取 任何 错误 - 使用ErrorCode为0(零)调用 DNSServiceRegisterReply 回调,即成功,如果我提供NIL名称参数与标志中指定的AutoRename,那么我的服务被分配正确的默认名称。但是该服务仍然没有出现在iTunes中。

我对发生的事情感到茫然。

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

该代码最初是针对Bonjour API / SDK的1.0.3版开发的。我已经更新到1.0.6,如​​果涉及到某种程度,没有任何成功。 afaict 1.0.6只添加了一个新的函数来获取“属性”,它目前仅支持“DaemonVersion”属性来获取Bonjour版本 - 这是完美的。

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

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

我必须错过一些愚蠢而明显的东西。

有没有人有任何想法?!

更新

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

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

  • 在2006年建立并运行:它有效 - 我的服务公告由iTunes
  • 选择
  • 切换到D2010并运行(不构建):它执行最小的编译,运行并运行。
  • 在D2010中完整构建:它停止工作

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

  • 在D2006中完整构建:它再次运行

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

4 个答案:

答案 0 :(得分:4)

对此的回答令人难以置信。一方面,我做了一个完全愚蠢,非常简单的错误,但另一方面,它应该永远 - 我可以看到 - 在任何版本的Delphi中都有效!

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

我传递了 ReverseBytes(端口)的结果 - 该参数需要 uint16_t ,即 Word 值。然而,我的端口属性(懒惰地)被声明为整数 !!

我修复此问题并将端口声明为 Word 后,它现在可用于D2007和D2009 +版本的Delphi。

非常奇怪。

我只能认为在引入Unicode支持时,可能会以某种方式影响这种情况的编译器的其他一些边缘情况行为会被更改。

答案 1 :(得分:1)

根据我们在此提供的信息,情况如下:

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

逻辑上,差异必须是,Delphi 2009将不同​​的值作为参数发送。因此,为了使调试真正独立于Delphi,您需要创建一个虚拟DLL,它会报告它获得的值。可以应用其他依赖于Delphi的方法,例如查看函数调用到DLL中的反汇编,并调试它以便我们确切地知道在两个编译器中传递了什么值,以及如何在DLL中。

答案 2 :(得分:0)

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

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

name  := ServiceName;

为什么不使用以下内容?

name  := PAnsiChar(AnsiString(ServiceName)) 

无论如何......只是我的2个人。

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

if (ServiceName <> EmptyStr) then

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

另一方面,德尔福可能会像以下声明一样将''解释为ANSIChar:

const
  MyParagraphChar = '§';

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

答案 3 :(得分:-1)

如果DLL不是使用Delphi 2009编写的,您可能希望使用除PUTF8String之外的其他内容。 Delphi 2009 Utf8String类型与Delphi 2007的UTF8String类型不同。

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