M
Markus Humm
Hello,
we're currently implementing a device search for Bluetooth devices to
find our device in order to be able to open it. This device search
source code has been put in a C/C++ DLL to avoid all those P/Invoke
declarations in our VB.net compact framework application. So it might
not have to do much with CF, but since the code is called from VB.net
(CF 1.0) it's not entirely out of the equation.
Normally the code just works and gives us a list of all devices
currently visible on the location the PDA is. But I found out during
testing that it crashes with a 0xC0000005 exception if it's run in a
environment where many (up to a dozen and more) Bluetooth devices are
reachable. My developper then said he couldn't catch that exception
(says eVC 4.x can't do it) and we started further research by logging
what the DLL did into a file. To cut it short, we found out that the
crash happened after a WSALookupServiceNext call and further research
led us to the conclusion it must happen if a (null) (means empty) device
name is delivered. What we do not understand is who puts that (null)
text into our log as we don't do it (we've a empty string in this
situation).
Looking at all those examples on the internet didn't help much until my
developper noticed one example where a sleep was included in this
WSALookupServiceNext-Loop. All other examples didn't have that sleep.
So we included a 500ms sleep and now it works, even if it still returns
(null) names. So the main question is: why does it crash without that
sleep call? What goes on in the background? Obviously the (null) names
appear if a device can still answer the inquiry scan but not the "full
connect" being made to get the friednly name. I think mostly due to
getting out of range (e.g. mobile phone).
We then also found a MS example (with VS2005/2008) which would simply
list all devices. This example didn't use sleep but also didn't crash!
It seemd simply to leave out those (null) devices and investigation did
show that this are devices which sometimes do report a name and
sometimes don't. The Windows Mobile GUI in this case displays the
Bluetooth MAC address instead of the name. Nice trick!
I'm asking you now since I'd like to understand why our solution is one
or if there still lurks some bug in it (missed in my tests so far but
comming up later).
Here's our source used now:
/*
Bluetooth Device Discovery by Name
In WCHAR* myDeviceName
Out BTH_ADDR Bluetooth Device Address
*/
BTH_ADDR CMicrosoftStack::FindDeviceEBM(WCHAR* myDeviceName) {
BTH_ADDR retVal = 0;
WSAQUERYSET wsaQuerySet;
LPWSAQUERYSET pQuerySet;
HANDLE hLookup;
int nRet;
WCHAR szError[1024]; //Debug Message
//
// Initialize the query
//
memset(&wsaQuerySet, 0, sizeof(wsaQuerySet));
wsaQuerySet.dwSize = sizeof(WSAQUERYSET);
wsaQuerySet.dwNameSpace = NS_BTH;
wsaQuerySet.lpBlob = NULL;
// Start looking for devices
nRet = WSALookupServiceBegin(&wsaQuerySet,
LUP_CONTAINERS, &hLookup);
if (nRet == 0){
DebugMessage(_T("Success WSALookupServiceBegin"));
}else{
swprintf(szError,_T("Failed
WSALookupServiceBegin %d"),WSAGetLastError());
DebugMessage(szError);
}
if (nRet == 0){
union{
CHAR buf[5000];
SOCKADDR_BTH __unused;
};
for(int i = 0; i < 255; ++i)
{
pQuerySet = (LPWSAQUERYSET)buf;
DWORD dwLen = sizeof(buf);
memset(pQuerySet,0,sizeof(WSAQUERYSET));
pQuerySet->dwSize = sizeof(WSAQUERYSET);
pQuerySet->dwNameSpace = NS_BTH;
// NS_BTH for bluetooth queries
pQuerySet->lpBlob = NULL;
DWORD dwFlags =
LUP_RETURN_NAME | LUP_RETURN_ADDR;
// only name and address
nRet = WSALookupServiceNext(hLookup,
dwFlags, &dwLen, pQuerySet);
Sleep(500);
if(nRet != 0) // ERROR or WSA_E_NO_MORE
{
swprintf(szError,_T(
"WSALookupServiceNext :: Error
%d"),WSAGetLastError());
DebugMessage(szError);
break;
} else {
swprintf(szError,_T(
"WSALookupServiceNext :: Success
WSAGetLastError: %d"),WSAGetLastError());
DebugMessage(szError);
}
SOCKADDR_BTH *pBtAddr = (SOCKADDR_BTH*)
pQuerySet->lpcsaBuffer->RemoteAddr.lpSockaddr;
BT_ADDR btAddr = pBtAddr->btAddr;
swprintf(szError,_T("SOCKADDR_BTH: %x"),btAddr);
DebugMessage(szError);
swprintf(szError,_T("%s"),
pQuerySet->lpszServiceInstanceName);
DebugMessage(szError);
if (wcscmp(szError, myDeviceName) == 0){
retVal = btAddr; // found what we are
// looking for
break;
}
} // End For
}
// Cleanup
WSALookupServiceEnd(hLookup);
DebugMessage(_T("Exit"));
return retVal;
}
Summary of my questions:
1. why does it log (null) as name even if we don't assign it to out
log string? Who puts it there?
2. what goes on in the background of the MS stack implementation that a
sleep aviods a crash?
3. why does the above source crash with a general protection exception
sometimes (0xC000005 is the GPF) if the sleep is not done?
Greetings
Markus
we're currently implementing a device search for Bluetooth devices to
find our device in order to be able to open it. This device search
source code has been put in a C/C++ DLL to avoid all those P/Invoke
declarations in our VB.net compact framework application. So it might
not have to do much with CF, but since the code is called from VB.net
(CF 1.0) it's not entirely out of the equation.
Normally the code just works and gives us a list of all devices
currently visible on the location the PDA is. But I found out during
testing that it crashes with a 0xC0000005 exception if it's run in a
environment where many (up to a dozen and more) Bluetooth devices are
reachable. My developper then said he couldn't catch that exception
(says eVC 4.x can't do it) and we started further research by logging
what the DLL did into a file. To cut it short, we found out that the
crash happened after a WSALookupServiceNext call and further research
led us to the conclusion it must happen if a (null) (means empty) device
name is delivered. What we do not understand is who puts that (null)
text into our log as we don't do it (we've a empty string in this
situation).
Looking at all those examples on the internet didn't help much until my
developper noticed one example where a sleep was included in this
WSALookupServiceNext-Loop. All other examples didn't have that sleep.
So we included a 500ms sleep and now it works, even if it still returns
(null) names. So the main question is: why does it crash without that
sleep call? What goes on in the background? Obviously the (null) names
appear if a device can still answer the inquiry scan but not the "full
connect" being made to get the friednly name. I think mostly due to
getting out of range (e.g. mobile phone).
We then also found a MS example (with VS2005/2008) which would simply
list all devices. This example didn't use sleep but also didn't crash!
It seemd simply to leave out those (null) devices and investigation did
show that this are devices which sometimes do report a name and
sometimes don't. The Windows Mobile GUI in this case displays the
Bluetooth MAC address instead of the name. Nice trick!
I'm asking you now since I'd like to understand why our solution is one
or if there still lurks some bug in it (missed in my tests so far but
comming up later).
Here's our source used now:
/*
Bluetooth Device Discovery by Name
In WCHAR* myDeviceName
Out BTH_ADDR Bluetooth Device Address
*/
BTH_ADDR CMicrosoftStack::FindDeviceEBM(WCHAR* myDeviceName) {
BTH_ADDR retVal = 0;
WSAQUERYSET wsaQuerySet;
LPWSAQUERYSET pQuerySet;
HANDLE hLookup;
int nRet;
WCHAR szError[1024]; //Debug Message
//
// Initialize the query
//
memset(&wsaQuerySet, 0, sizeof(wsaQuerySet));
wsaQuerySet.dwSize = sizeof(WSAQUERYSET);
wsaQuerySet.dwNameSpace = NS_BTH;
wsaQuerySet.lpBlob = NULL;
// Start looking for devices
nRet = WSALookupServiceBegin(&wsaQuerySet,
LUP_CONTAINERS, &hLookup);
if (nRet == 0){
DebugMessage(_T("Success WSALookupServiceBegin"));
}else{
swprintf(szError,_T("Failed
WSALookupServiceBegin %d"),WSAGetLastError());
DebugMessage(szError);
}
if (nRet == 0){
union{
CHAR buf[5000];
SOCKADDR_BTH __unused;
};
for(int i = 0; i < 255; ++i)
{
pQuerySet = (LPWSAQUERYSET)buf;
DWORD dwLen = sizeof(buf);
memset(pQuerySet,0,sizeof(WSAQUERYSET));
pQuerySet->dwSize = sizeof(WSAQUERYSET);
pQuerySet->dwNameSpace = NS_BTH;
// NS_BTH for bluetooth queries
pQuerySet->lpBlob = NULL;
DWORD dwFlags =
LUP_RETURN_NAME | LUP_RETURN_ADDR;
// only name and address
nRet = WSALookupServiceNext(hLookup,
dwFlags, &dwLen, pQuerySet);
Sleep(500);
if(nRet != 0) // ERROR or WSA_E_NO_MORE
{
swprintf(szError,_T(
"WSALookupServiceNext :: Error
%d"),WSAGetLastError());
DebugMessage(szError);
break;
} else {
swprintf(szError,_T(
"WSALookupServiceNext :: Success
WSAGetLastError: %d"),WSAGetLastError());
DebugMessage(szError);
}
SOCKADDR_BTH *pBtAddr = (SOCKADDR_BTH*)
pQuerySet->lpcsaBuffer->RemoteAddr.lpSockaddr;
BT_ADDR btAddr = pBtAddr->btAddr;
swprintf(szError,_T("SOCKADDR_BTH: %x"),btAddr);
DebugMessage(szError);
swprintf(szError,_T("%s"),
pQuerySet->lpszServiceInstanceName);
DebugMessage(szError);
if (wcscmp(szError, myDeviceName) == 0){
retVal = btAddr; // found what we are
// looking for
break;
}
} // End For
}
// Cleanup
WSALookupServiceEnd(hLookup);
DebugMessage(_T("Exit"));
return retVal;
}
Summary of my questions:
1. why does it log (null) as name even if we don't assign it to out
log string? Who puts it there?
2. what goes on in the background of the MS stack implementation that a
sleep aviods a crash?
3. why does the above source crash with a general protection exception
sometimes (0xC000005 is the GPF) if the sleep is not done?
Greetings
Markus