【问题标题】:Delphi: WSAAddressToString returns error code 10022 (WSAEINVAL)Delphi:WSAAddressToString 返回错误代码 10022 (WSAEINVAL)
【发布时间】:2015-07-04 08:42:49
【问题描述】:

我正在尝试使用 Winsock2 API 接口扫描附近的蓝牙设备的 MAC 地址。 使用下面的代码我可以找到设备。但是,当我尝试使用 WSAAddressToString 获取他们的地址时,会出现 10022 (WSAEINVAL) 错误,提示“提供了无效参数”。

代码是:

uses
   winsock2, bt_helper;

procedure test;
var
  ulFlags: u_long;
  QuerySet: WSAQUERYSET;
  QuerySize: u_long;
  HLookup: THandle;
  Result: Integer;

  pCSAddr: pCSADDR_INFO;
  pDeviceInfo: PBTH_DEVICE_INFO;
  pResults: lpWSAQUERYSET;
  Buffer: array [0..999] of Byte;

  ProtocolInfo: WSAPROTOCOL_INFO;
  ProtocolInfoSize: Integer;

  BufferLength, AddressSize: LongWord;
  addressAsString: array [0..1999] of Char;
begin

WSAStartup ($0202, Data);

ulFlags:=
    LUP_CONTAINERS or   //device inquiry
    LUP_RETURN_NAME or  //Friendly device name (if available) will be returned in lpszServiceInstanceName
    LUP_RETURN_ADDR or  //BTH_ADDR will be returned in lpcsaBuffer member of WSAQUERYSET
    LUP_FLUSHCACHE ;    //Flush the device cache for all inquiries, except for the first inquiry

QuerySize:= SizeOf(WSAQuerySet);
ZeroMemory (@QuerySet, SizeOf(QuerySet));

QuerySet.dwNameSpace:= NS_BTH;
QuerySet.dwSize:= QuerySize;

Result:= WSALookupServiceBegin(@QuerySet, ulFlags, HLookup);

if Result = 0 then
begin
  while true do
  begin    
    bufferLength:= sizeof(buffer);
    pResults:= lpWSAQUERYSET(@buffer);   
    Result:= WSALookupServiceNext (HLOOKUP, ulFlags, bufferLength, pResults);

    if Result = 0 then
    begin
      // Get the device info, name, address, etc.
      Memo1.Lines.Add(Format('The service instance name is %s', [pResults.lpszServiceInstanceName]));
      //pCSAddr.LocalAddr.lpSockaddr.sa_family:= AF_INET;
      pCSAddr:= PCSADDR_INFO(pResults.lpcsaBuffer);
      pDeviceInfo:= PBTH_DEVICE_INFO(pResults.lpBlob);

      // Print the local Bluetooth device address ...
      AddressSize:= sizeof(addressAsString);

      if WSAAddressToString(pCSAddr.LocalAddr.lpSockaddr^, pCSAddr.LocalAddr.iSockaddrLength,
        @ProtocolInfo, @AddressAsString, AddressSize) = 0
      then
        Memo1.Lines.Add(Format ('The localAddress: %s', [AddressAsString]))
      else
        Memo1.Lines.Add(Format ('WSAAddressToString for localAddress failed with error code %d: %s',
            [WSAGetLastError, SysErrorMessage (WSAGetLastError)]));

      // Print the remote Bluetooth device address ...
      AddressSize:= sizeof(addressAsString);
      IF WSAAddressToString(pCSAddr.RemoteAddr.lpSockaddr^, pCSAddr.RemoteAddr.iSockaddrLength,
        @ProtocolInfo, @AddressAsString, Addresssize) = 0
      then
        Memo1.Lines.Add (Format ('The remote device address: %s', [AddressAsString]))
      else
        Memo1.Lines.Add (Format ('WSAAddressToString for remoteAddress failed with error code %d: %s',
            [WSAGetLastError, SysErrorMessage(WSAGetLastError)]));
    end
    else
    begin
        Memo1.Lines.Add(SysErrorMessage(WSAGetLastError));
        break;
    end;
  end;
end;    
WSALookupServiceEnd(HLookup);

这是备忘录中的结果:

The service instance name is BTDevice1
WSAAddressToString for localAddress failed with error code 10022: An invalid argument was supplied
WSAAddressToString for remoteAddress failed with error code 10022: An invalid argument was supplied
---------------------------------
No more results can be returned by WSALookupServiceNext

使用以下单元进行编译:

unit bt_helper;

interface

uses
    winsock2, Winapi.Windows;

const
    BTH_MAX_NAME_SIZE = 248;
    BTHPROTO_RFCOMM= 3;
  BT_PORT_ANY = -1;

type
  BTH_ADDR = int64;

  SOCKADDR_BTH = packed record
    addressFamily       :word;            // Always AF_BTH
    btAddr              :BTH_ADDR;        // Bluetooth device address
    serviceClassId      :TGUID;           // [OPTIONAL] system will query SDP for port
    port                :dword;           // RFCOMM channel or L2CAP PSM
  end;

  BTH_COD = ULONG;

  _BTH_DEVICE_INFO = record
    flags: ULONG;                      // Combination BDIF_Xxx flags
    address: BTH_ADDR;                 // Address of remote device.
    classOfDevice: BTH_COD;            // Class Of Device.
    name: array [0..BTH_MAX_NAME_SIZE - 1] of CHAR;    // name of the device
  end;
  {$EXTERNALSYM _BTH_DEVICE_INFO}
  BTH_DEVICE_INFO = _BTH_DEVICE_INFO;
  {$EXTERNALSYM BTH_DEVICE_INFO}
  PBTH_DEVICE_INFO = ^BTH_DEVICE_INFO;
  {$EXTERNALSYM PBTH_DEVICE_INFO}
  TBthDeviceInfo = BTH_DEVICE_INFO;
  PBthDeviceInfo = PBTH_DEVICE_INFO;    

implementation

end.

【问题讨论】:

  • 使用 length() 代替 sizeof()
  • 请附上一个可编译的例子?
  • WSAEINVAL 的文档说 传递了一个无效参数。如果 lpsaAddress、dwAddressLength 或 lpdwAddressStringLength 参数为 NULL,则返回此错误。如果指定的地址不是有效的套接字地址,或者没有传输提供程序支持指定的地址族,也会返回此错误。您是否检查了这些条件?
  • 我不明白第二个问题(有效的套接字地址,传输提供商支持),但lpsaAddress 为零,lpsaAddressString 有它的默认值(随机数据)。
  • 我无法理解该评论。在我看来,文档告诉你问题的答案是什么。

标签: delphi bluetooth delphi-xe2 winsock2


【解决方案1】:

如果您仔细阅读documentation of WSAAddressToString,您会注意到这一段:

lpProtocolInfo [in, optional] 指向 WSAPROTOCOL_INFO 的指针 特定提供商的结构。如果 this 参数为 NULL,则 呼叫被路由到支持第一个协议的提供者 lpsaAddress 参数中指示的地址族。

因此,您应该传入 nil,而不是提供虚假的 WSA_PROTOCOL 信息结构。第二个问题是你使用SizeOf()来确定String缓冲区的长度,这是不正确的,你应该使用Length()

AddressSize:= Length(addressAsString);

if WSAAddressToString(pCSAddr.LocalAddr.lpSockaddr^, pCSAddr.LocalAddr.iSockaddrLength,
    nil, @AddressAsString, AddressSize) = 0 then
 begin
   SetLength(AddressAsString, AddressSize-1);// resize to returned length minus last null character
   ...

【讨论】:

  • 您的解决方案有效。非常感谢。我可以使用pCSAddr.RemoteAddr.lpSockaddr 获取远程蓝牙mac 地址。这就是我想要的。但是第一部分 LocalAddr 还返回 10022 错误。
  • @SAMPro,是的,由于某种原因,本地地址是一个空字符串,我认为这对于蓝牙来说是正常的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-22
  • 2020-08-25
  • 1970-01-01
  • 1970-01-01
  • 2011-12-04
  • 2014-07-03
  • 1970-01-01
相关资源
最近更新 更多