Delphi:WSAAddressToString返回错误代码10022(WSAEINVAL)

时间:2015-07-04 08:42:50

标签: delphi bluetooth delphi-xe2 winsock2

我正在尝试使用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.

1 个答案:

答案 0 :(得分:2)

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

  

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

因此,您应该传递nil,而不是提供虚假的WSA_PROTOCOL信息结构。第二个问题是您使用SizeOf()来确定字符串缓冲区的长度,这是不正确的,您应该使用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
   ...