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

最佳答案

如果仔细阅读documentation of WSAAddressToString,您会注意到以下段落:


lpProtocolInfo [in,可选]指向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
   ...

09-27 18:19