通过WinAPI套接字连接到蓝牙设备时崩溃(访问冲突读取位置0x00000004)

时间:2011-07-02 12:01:11

标签: c++ winapi sockets visual-c++ bluetooth

我认为你是我最后的希望。我有一个蓝牙设备(它更准确地说是一个传感器),我想连接到它并从中读取数据。该设备提供SPP(串行端口配置文件)。为了避免从蓝牙地址和虚拟串行端口(COM端口)可靠映射的问题,我将使用套接字。

不幸的是,在从WinAPI函数 connect (...)返回之前,应用程序总是崩溃: 0xC0000005:访问冲突读取位置0x00000004 ,所以我得到否错误代码

但是,这很奇怪,当我右键单击蓝牙系统托盘图标以显示可用设备时,我的设备显示已经过身份验证和连接。此列表之前是空的,课程。

我的操作系统是Windows 7 64位,IDE是Visual Studio 2010,Microsoft蓝牙堆栈。找到并连接到我唯一设备的代码:

#include <iostream>
#include <string>
#include <algorithm>
#include <cassert>

#define WIN32_LEAN_AND_MEAN

#include <Windows.h>
#include <BluetoothAPIs.h>
#include <Winsock2.h>
#include <Ws2bth.h>



BOOL auth_callback_ex(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS authParams)
{
    BLUETOOTH_AUTHENTICATE_RESPONSE response;
    response.bthAddressRemote = authParams->deviceInfo.Address;
    response.authMethod = authParams->authenticationMethod; // == BLUETOOTH_AUTHENTICATION_METHOD_LEGACY

    UCHAR pin[] = "1234";
    std::copy(pin, pin+sizeof(pin), response.pinInfo.pin);
    response.pinInfo.pinLength = sizeof(pin)-1; //excluding '\0'

    response.negativeResponse = false;


    HRESULT err = BluetoothSendAuthenticationResponseEx(NULL, &response);
    if (err)
    {
        std::cout << "BluetoothSendAuthenticationResponseEx error = " << err << std::endl;
    }

    return true;
}


int main()
{
    BLUETOOTH_DEVICE_SEARCH_PARAMS btSearchParams;

    btSearchParams.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
    btSearchParams.cTimeoutMultiplier = 5;  //5*1.28s search timeout
    btSearchParams.fIssueInquiry = true;    //new inquiry

    //return all known and unknown devices
    btSearchParams.fReturnAuthenticated = true;
    btSearchParams.fReturnConnected = true;
    btSearchParams.fReturnRemembered = true;
    btSearchParams.fReturnUnknown = true;

    btSearchParams.hRadio = NULL;   //search on all local radios



    BLUETOOTH_DEVICE_INFO btDeviceInfo;
    ZeroMemory(&btDeviceInfo, sizeof(BLUETOOTH_DEVICE_INFO));   //"initialize"

    btDeviceInfo.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);

    HBLUETOOTH_DEVICE_FIND btDeviceFindHandle = NULL;

    btDeviceFindHandle = BluetoothFindFirstDevice(&btSearchParams, &btDeviceInfo);
    if(btDeviceFindHandle)
    {

        HBLUETOOTH_AUTHENTICATION_REGISTRATION authCallbackHandle = NULL;

        DWORD err = BluetoothRegisterForAuthenticationEx(&btDeviceInfo, &authCallbackHandle, &auth_callback_ex, NULL);


        if (err != ERROR_SUCCESS)
        {
            DWORD err = GetLastError();
            std::cout << "BluetoothRegisterForAuthentication Error" << err << std::endl; 
        }

        /////////////// Socket
        WSADATA wsaData;
        err = WSAStartup(MAKEWORD(2,2), &wsaData);
        if (err)
        {
            std::cout << "WSAStartup error = " << err << std::endl;
        }


        // create BT socket
        SOCKET s = socket (AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
        assert(s != INVALID_SOCKET);    //WSAGetLastError //throw // runtime check release?

        SOCKADDR_BTH btSockAddr;
        btSockAddr.addressFamily = AF_BTH;
        btSockAddr.btAddr = btDeviceInfo.Address.ullLong;
        btSockAddr.serviceClassId = RFCOMM_PROTOCOL_UUID; //SerialPortServiceClass_UUID (no difference)
        btSockAddr.port = BT_PORT_ANY;


        err = connect(s, reinterpret_cast<SOCKADDR*>(&btSockAddr), sizeof(SOCKADDR_BTH));

        /* <--- never got so far --> */

        if (err)
        {
            DWORD wsaErr = WSAGetLastError();
            std::cout << "connect error = " << wsaErr << std::endl;

        }
        else
        {
            //err = shutdown(s, SD_BOTH);

            err = closesocket(s);
            if (err)
            {
                std::cout << "closesocket error = " << err << std::endl;
            }
        }

        WSACleanup();
        ///////////////Socket


        BOOL ok = BluetoothUnregisterAuthentication(authCallbackHandle);
        if (!ok)
        {
            DWORD err = GetLastError();
            std::cout << "BluetoothUnregisterAuthentication Error" << err << std::endl; 
        }



        ok = BluetoothFindDeviceClose(btDeviceFindHandle);
        if (!ok)
        {
            DWORD err = GetLastError();
            std::cout << "BluetoothDeviceClose Error" << err << std::endl; 
        }
    }
    else
    {
        DWORD err = GetLastError();
        std::cout << "BluetoothFindFirstDevice Error" << err << std::endl; 
    }


    std::cin.get();
}

我做了一些更多的观察:

  • 身份验证回调和 BluetoothSendAuthenticationResponseEx 功能正常运行,没有给出错误。
  • 如果我没有安装身份验证回调( BluetoothRegisterForAuthenticationEx ),因此必须手动输入PIN(尝试连接时UI会自动显示),连接功能返回正常,一切正常。我甚至得到了数据(在这个片段中省略了recv部分)。
  • 如果我手动搜索并配对(蓝牙托盘图标 - >添加设备),一切都很好。安装了服务和虚拟串行端口。数据来自putty。

因此,在调用身份验证回调和连接函数结束之间出现问题。也许当试图通过指针获取某个结构数据时,该指针不应该是NULL,加上偏移量。

或者我做错了什么?有什么遗失的吗? 谢谢......

4 个答案:

答案 0 :(得分:3)

问题是你的函数使用了错误的调用约定。 According to MSDN, you need to use the CALLBACK macro,如:

BOOL CALLBACK auth_callback_ex(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS authParams)

具有错误的调用约定将导致返回时堆栈不匹配,这可能导致MS蓝牙代码内无法找到其本地变量时发生访问冲突。

或者它可能导致您的函数的参数全部混乱。如果authParamspvParam被交换,因为cdecl调用约定期望args从右向左推,而stdcall从左向右推,所以{{1}在NULL中,然后authParams会尝试读取地址authParams->deviceInfo.Address

编译器应该抓住这个。在启用最大警告(0x04)时进行编译。您将不得不忽略有关未知编译指示的警告,这是bug in the header which I'm reporting to Microsoft (misspelled #pragma deprecated)

不幸的是有a second bug in the header, much more serious, of not specifying the calling convention explicitly,结果是只有在使用/W4时它才能在x86(32位代码)上正常工作。呸!


跟进:在VS2013附带的SDK标头中,两个问题都已修复。

答案 1 :(得分:0)

您在某处有空指针访问权限。 “访问冲突读取位置 0x00000004 ”表示,因为距离零只有4个字节。

答案 2 :(得分:0)

我有几点想与你分享,但请注意,这些都是预感。我没有编译和调试你的代码,虽然我赞扬你发布了一个完整的样本。

我认为崩溃可能在您的身份验证回调函数中,因为''''NULL'''指针取消引用。

这些行:

response.bthAddressRemote = authParams->deviceInfo.Address;
response.authMethod = authParams->authenticationMethod; // == BLUETOOTH_AUTHENTICATION_METHOD_LEGACY

将导致您描述的消息,如果您在32位Windows上运行,并且'''authParams'''可能'''NULL''' - 在这种情况下,'''deviceInfo'''贡献一个零偏移量(它位于''''BLUETOOTENTICATION_CALLBACK_PARAMS'''的开头),'''。地址'''的偏移量为4('''NULL + 4 == 0x00000004''') ,因为它遵循'''DWORD'''并且在'''BLUETOOTH_DEVICE_INFO'''布局中没有其他内容。

调用回调时'''authParams'''是否可能为NULL?

正如另一张海报已经提到的,这可能是由于错误的调用约定(缺少'''CALLBACK'''宏) - 导致其他正确的参数与编译代码正在读取的位置不一致。 / p>

第二个想法是:

BLUETOOTH_DEVICE_INFO btDeviceInfo;
ZeroMemory(&btDeviceInfo, sizeof(BLUETOOTH_DEVICE_INFO));   //"initialize"

btDeviceInfo.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);

可以表示:

BLUETOOTH_DEVICE_INFO btDeviceInfo = {sizeof(BLUETOOTH_DEVICE_INFO)};

根据标准,这将使'''btDeviceInfo'''的其他字段为零。

答案 3 :(得分:-1)

或者编写托管代码并使用我的蓝牙库32feet.NET超级简单。 http://32feet.codeplex.com/

它会崩溃吗 - 如果是这样,你的电脑就出现了问题......