Obtaining wi-fi connection status from Connection Manager - c++

I have some code which successfully iterates over a list of wi-fi networks, and provides feedback about available networks. The essential calls shown here...
WlanOpenHandle(WLAN_API_VERSION, NULL, &dwVersion, &hSession);
PWLAN_INTERFACE_INFO_LIST pInterfaceInfoList = NULL;
WlanEnumInterfaces(hSession, NULL, &pInterfaceInfoList);
for(int i ...)
{
PWLAN_AVAILABLE_NETWORK_LIST pAvailableNetworkList = NULL;
WlanGetAvailableNetworkList(hSession, &interfaceGUID,
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES |
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES,
NULL, &pAvailableNetworkList);
for(int j ...)
{
WLAN_AVAILABLE_NETWORK network = pAvailableNetworkList->Network[j];
:
}
}
This all works fine, and inside the inner loop I'm able to access all of the attributes that I need, such as signal strength, security flags, etc via the network data structure.
One thing that I am not able to obtain is information regarding connection status, such as AUTHENTICATING or AUTHENTICATION_FAILED, etc, so I have tried to introduce another call inside the loop as follows...
CM_CONNECTION_DETAILS connectionDetails;
memset(&connectionDetails, 0, sizeof(CM_CONNECTION_DETAILS));
connectionDetails.Version = CM_CURRENT_VERSION;
const char* ccp = reinterpret_cast<const char*>(network.dot11Ssid.ucSSID);
mbstowcs(connectionDetails.szName, &ccp[0], network.dot11Ssid.uSSIDLength);
DWORD dwCount = sizeof(CM_CONNECTION_DETAILS);
CM_RESULT cmr = CmGetConnectionDetailsByName(connectionDetails.szName,
&connectionDetails, &dwCount);
if (cmr == CMRE_SUCCESS)
{
:
}
Upon calling the CmGetConnectionDetailsByName() function, the details inside the CM_CONNECTION_DETAILS structure look correct (name and version), but the function returns with CMRE_INVALID_CONNECTION and the structure is not populated.
I haven't been able to find any examples of this call being successful (only a couple of references to the call returning the same CMRE_INVALID_CONNECTION code).
Does anyone have any experience of using the call successfully, or alternatively suggest a better way to find out the connection status of a network (ie if AUTHENTICATION is in progress or if AUTHENTICATION failed, etc)?
[I'm using Visual Studio 2013 C++ (native Windows app, not MFC), the target is 32-bit and Unicode, running on Windows Compact 2013]

The function below doesn't quite give me the fine control that I was looking for, but it does at least give me the opportunity to find out the state a particular interface. This means that I can find out if the interface is currently in the process or authenticating, and depending whether the final state is connected or disconnected, I can find out if authentication was successful or not.
WLAN_INTERFACE_STATE getNetworkState(HANDLE hSession, GUID* pGUID, std::wstring& wsState, bool bReportState=true)
{
WLAN_INTERFACE_STATE result = wlan_interface_state_not_ready;
DWORD dwDataSize;
void* pData;
DWORD dwErrorCode = WlanQueryInterface(hSession, pGUID, wlan_intf_opcode_interface_state, NULL, &dwDataSize, &pData, NULL);
if (dwErrorCode == ERROR_SUCCESS && pData != NULL)
{
WLAN_INTERFACE_STATE* pState = reinterpret_cast<WLAN_INTERFACE_STATE*>(pData);
if (pState != NULL)
{
switch (*pState)
{
case wlan_interface_state_not_ready: wsState = L"NOT_READY"; break;
case wlan_interface_state_connected: wsState = L"CONNECTED"; break;
case wlan_interface_state_ad_hoc_network_formed: wsState = L"AD_HOC_NETWORK_FORMED"; break;
case wlan_interface_state_disconnecting: wsState = L"DISCONNECTING"; break;
case wlan_interface_state_disconnected: wsState = L"DISCONNECTED"; break;
case wlan_interface_state_associating: wsState = L"ASSOCIATING"; break;
case wlan_interface_state_discovering: wsState = L"DISCOVERING"; break;
case wlan_interface_state_authenticating: wsState = L"AUTHENTICATING"; break;
}
result = *pState;
}
WlanFreeMemory(pData);
}
return result;
}
A limitation of this check, is that it doesn't readily support multiple connections on the same interface, This query doesn't allow us to query to which of the connections the status refers.
If I arrive at a better solution, I will report it here.

Related

How to get Game Controller name (Windows 10 / C++)

I've seen lots of info on how to read game controller input using XInput but I really want to know the name of the controller that is connected.
How can I find out the name of connected controllers on a PC or more specifically the name of the controller I am reading XInput from?
You can do this by calling the joyGetDevCaps function which returns a JOYCAPS structure containing all information (including name) of the connected controller.
You can use DirectInput to get the name of the device. You need to do that using a callback:
pDirectInput->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoystickCallbackStatus, &joynum, DIEDFL_ATTACHEDONLY);
Then you have to be a bit creative: on Startup detect all devices using the callback and store their name/GUID... and then when a device is hot-plugged (which you detect with XInputGetState) look for the device which you don't know about yet, with a modified version of that earlier callback, something similar to this:
BOOL CALLBACK EnumJoystickCallbackStatus(LPCDIDEVICEINSTANCE pdevinst, LPVOID pref)
{
DWORD devtype = GET_DIDEVICE_TYPE(pdevinst->dwDevType);
DWORD subtype = GET_DIDEVICE_SUBTYPE(pdevinst->dwDevType);
if (devtype == DI8DEVTYPE_KEYBOARD || (devtype == DI8DEVTYPE_SUPPLEMENTAL && subtype == DI8DEVTYPESUPPLEMENTAL_UNKNOWN)) {
return DIENUM_CONTINUE;
}
ULONG* pjoynum = reinterpret_cast<ULONG*>(pref);
if (IsXInputDevice(&pdevinst->guidProduct)) {
// loop through your known devices and see if this GUI already exists
// we are looking for one which we don't know about yet.
if (!found) {
// store GUI / Name / ... in some global controllers-array
return DIENUM_STOP; // done
}
}
DEBUG_INFO(Debug::XDF_General, "continue");
return DIENUM_CONTINUE;
}
Note that if you have multiple xbox-controllers, you'll get a callback for each one separately.
Implementation of IsXInputDevice can be found in the MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx

WTS_CONNECTSTATE_CLASS enumeration class

I have to determine what state the user session is in. i.e if the the user is logged on and active or if the user is not logged on. To do this im using the function WTSEnumerateSessions and then I am checking the WTS_SESSION_INFOwhich is return from this function which has a state member.
According to the online documentation.
WTSActive A user is logged on to the WinStation
WTSDisconnected
The WinStation is active but the client is disconnected.
The problem is that im currently logged onto my system but the state returned for my current session is WTSDisconnected im expecting the state to be WTSActive is there any reason as to why this is happening?
Code below
PWTS_SESSION_INFO pSessionInfo(nullptr);
DWORD countOfSessions(0);
DWORD sessionIndex(0);
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, // local machine
0, // reserved, must be zero
1, // Version, must be one
&pSessionInfo,
&countOfSessions) == FALSE)
{
return false;
}
if (countOfSessions == 0 || !pSessionInfo)
{
return false;
}
const DWORD currentSession = GetRealSessionID(CurrentSession); //This case we are in session 0
for (; sessionIndex < countOfSessions; ++sessionIndex)
{
WTS_SESSION_INFO& sessionInfo = pSessionInfo[sessionIndex];
if (currentSession == sessionInfo.SessionId)
{
if (sessionInfo.State == WTSActive)
{
return true;
}
}
}
return false;
Thanks
This doesn't make much sense. The most likely explanation is that currentSession is being assigned a value of 0, which is the session ID for the non-interactive isolated session in which services run.

How to change the Network name shown in Windows's ncpa.cpl using C++?

Windows' "Control Panel\Network and Internet\Network Connections" dialog (also by running ncpa.cpl) is used to show the Networks. A user can rename the name of Network by right-clicking and choosing "rename". This name is different with the adapter name, for instance, my ethernet card has the Network name being "Ethernet", but with the adapter name called "Qualcomm Atheros AR8171/8175 PCI-E Gigabit Ethernet Controller (NDIS 6.30)". I know I can change the latter one using NetCfg API, but I don't know how to change the former one, the "Ethernet".
I know that netsh command seems to do this work. But I want to implement this renaming using C++. Is there a way?
SOLUTION:
I'm so happy that got problem SOLVED using NetShareManager API! NETCON_PROPERTIES structure saves the GUID of the device, used to search the device, INetConnection::Rename can be used to rename the Network Connection name!
Here's the code:
BOOL DoTheWork(INetSharingManager *pNSM, wchar_t strDeviceName[])
{ // add a port mapping to every firewalled or shared connection
BOOL bFound = FALSE;
INetSharingEveryConnectionCollection * pNSECC = NULL;
HRESULT hr = pNSM->get_EnumEveryConnection (&pNSECC);
if (!pNSECC)
wprintf (L"failed to get EveryConnectionCollection!\r\n");
else {
// enumerate connections
IEnumVARIANT * pEV = NULL;
IUnknown * pUnk = NULL;
hr = pNSECC->get__NewEnum (&pUnk);
if (pUnk) {
hr = pUnk->QueryInterface (__uuidof(IEnumVARIANT),
(void**)&pEV);
pUnk->Release();
}
if (pEV) {
VARIANT v;
VariantInit (&v);
while ((S_OK == pEV->Next (1, &v, NULL)) && (bFound == FALSE)) {
if (V_VT (&v) == VT_UNKNOWN) {
INetConnection * pNC = NULL;
V_UNKNOWN (&v)->QueryInterface (__uuidof(INetConnection),
(void**)&pNC);
if (pNC) {
NETCON_PROPERTIES *pNETCON_PROPERTIES;
pNC->GetProperties(&pNETCON_PROPERTIES);
wchar_t currentGUID[BUF_SIZE];
GUID guid = pNETCON_PROPERTIES->guidId;
wsprintf(currentGUID, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
if (wcscmp(currentGUID, strDeviceName) == 0)
{
pNC->Rename(NPCAP_LOOPBACK_INTERFACE_NAME);
bFound = TRUE;
}
pNC->Release();
}
}
VariantClear(&v);
}
pEV->Release();
}
pNSECC->Release();
}
return bFound;
}
From a cursory look at the code for
netsh interface set interface name "oldname" newname="newname"
It looks to be using INetworkConnection::Rename. In order to get the connection you want, I believe you would have to use the INetSharingManager to enumerate all the connections available. Then you would have to iterate through them to find the one you want and then call the Rename function.
All that being said, as #Alexander Balabin said, the NLM API would probably be easier, but I don't know for sure if it does what you want. I can just tell you what netsh does.
I have not tried this, but NLM API's INetwork has a SetName method which looks like it should do what you want.

How do I get the Service Display name in C++?

I am trying to get the display name of the running service using c++. I was trying to use the GetServiceDisplayName function but it does not seem to be working, not sure why.
TTServiceBegin( const char *svcName, PFNSERVICE pfnService, bool *svc, PFNTERMINATE pfnTerm,
int flags, int argc, char *argv[], DWORD dynamiteThreadWaitTime )
{
SC_HANDLE serviceStatusHandle;
DWORD dwSizeNeeded = 0 ;
TCHAR* szKeyName = NULL ;
serviceStatusHandle=OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE ,SC_MANAGER_ALL_ACCESS);
GetServiceDisplayName(serviceStatusHandle,svcName, NULL, &dwSizeNeeded);
if(dwSizeNeeded)
{
szKeyName = new char[dwSizeNeeded+1];
ZeroMemory(szKeyName,dwSizeNeeded+1);
if(GetServiceDisplayName(serviceStatusHandle ,svcName,szKeyName,&dwSizeNeeded)!=0)
{
MessageBox(0,szKeyName,"Got the key name",0);
}
}
When i run this code, i can never see the value of szKeyName in my debugger and it goes into the if block for the message box but never displays the message box. Not sure why?
Anyway to get this to work to get the display name of the service or any other/easier way to accomplish that task?
You need to use the WTSSendMessage instead of the MessageBox to interact with the active session.
WTS_SESSION_INFO* pSessionInfo = NULL;
DWORD dwSessionsCount = 0;
if(WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwSessionsCount))
{
for(int i=0; i<(int)dwSessionsCount; i++)
{
WTS_SESSION_INFO &si = pSessionInfo[i];
if(si.State == WTSActive)
{
DWORD dwIdCurrentSession = si.SessionId;
std::string strTitle = "Hello";
std::string strMessage = "This is a message from the service";
DWORD dwMsgBoxRetValue = 0;
if(WTSSendMessage(
WTS_CURRENT_SERVER_HANDLE,
dwIdCurrentSession,
(char*)strTitle.c_str(),
strTitle.size(),
(char*)strMessage.c_str(),
strMessage.size(),
MB_RETRYCANCEL | MB_ICONINFORMATION | MB_TOPMOST,
60000,
&dwMsgBoxRetValue,
TRUE))
{
switch(dwMsgBoxRetValue)
{
case IDTIMEOUT:
// Deal with TimeOut...
break;
case IDCANCEL:
// Deal With Cancel....
break;
}
}
else
{
// Deal With Error
}
break;
}
}
WTSFreeMemory(pSessionInfo);
}
The message box will not be visible on Windows Vista and later due to a change that has services running in a separate session (Session 0 Isolation) that does not have access to a desktop so the message box would not be visible to you, the logged on user.
On Window XP and earlier, you need to tick the Allow service to interact with desktop checkbox under the Log On tab in the service's properties dialog for your service to make message box appear.
Instead, you could write the service name out to a file or run a user application that accepts the name of the service to query and have it query and display the service name (I just tried with the posted code and it works correctly, displaying the message box).

Programmatically allow write access for a Registry key

I need to programmatically modify the Access Descriptors on a known Registry key during product installation. The way I want it to work is:
The installer is run in Administrative mode.
A Registry key is created.
A function (the one I need) queries the ACL from the key.
If this function finds that the group 'Users' already has write access, nothing should be done.
If not, it should add a new permission allowing write access to the 'Users' group.
The permissions are saved for the Registry key.
This question is similar to Setting Registry key write permissions using .NET, however, I need a C++/Win32 implementation.
Thanks in advance
For getting and setting the ACL of the key you need to use RegGetKeySecurity and RegSetKeySecurity. Then you need to iterate through the ACEs, examining any that apply to the "Users" group SID. Then you'll either modify/remove the existing one and/or add a new one. Be advised that working with ACLs in plain old Win32 C is a pain.
The smallest code to grant access consists of 3 API calls. It gives full access to the given hkey for all authenticated users and administrators.
This snippet does not contain proper error handling and reporting. Do not copy/paste it into the production code.
PSECURITY_DESCRIPTOR sd = nullptr;
ULONG sd_size = 0;
TCHAR* rights = TEXT( "D:" ) // Discretionary ACL
TEXT( "(A;OICI;GA;;;AU)" ) // Allow full control to all authenticated users
TEXT( "(A;OICI;GA;;;BA)" ); // Allow full control to administrators
ConvertStringSecurityDescriptorToSecurityDescriptor( rights, SDDL_REVISION_1, &sd, &sd_size );
RegSetKeySecurity( hkey, DACL_SECURITY_INFORMATION, sd );
LocalFree( sd );
Detecting if "Users" have write access to the key might be more difficult than expected. I ended up with writing a test value to the registry and checking the result of that write.
Just to expand on Mikhail Vorotilov's answer, and also drawing inspiration from the example code at
https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl
bool RegistryGrantAll(HKEY hKey)
{
bool bResult = false;
PSECURITY_DESCRIPTOR sd = nullptr;
const TCHAR* szSD =
TEXT("D:") // Discretionary ACL
TEXT("(D;OICI;KA;;;BG)") // Deny access to built-in guests
TEXT("(D;OICI;KA;;;AN)") // Deny access to anonymous logon
TEXT("(A;OICI;KRKW;;;AU)") // Allow KEY_READ and KEY_WRITE to authenticated users ("AU")
TEXT("(A;OICI;KA;;;BA)"); // Allow KEY_ALL_ACCESS to administrators ("BA" = Built-in Administrators)
if (ConvertStringSecurityDescriptorToSecurityDescriptor((LPCTSTR)szSD, SDDL_REVISION_1, &sd, 0))
{
auto result = RegSetKeySecurity(hKey, DACL_SECURITY_INFORMATION, sd);
if (ERROR_SUCCESS == result)
bResult = true;
else
SetLastError(result);
// Free the memory allocated for the SECURITY_DESCRIPTOR.
LocalFree(sd);
}
return bResult;
}
If the function returns false then call GetLastError() for more information on the failure cause.
Code compiles on VS2019 and appears to work.
I have not added code to check that hKey is a valid registry handle.
Edit: I've edited this a few times following testing. Sorry about all the edits. What I ended up with was far closer to Mikhail's answer than I started with.
Links to further info:
https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl
https://learn.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptorw
https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings
Sup, hope OP is still interested in the answer. Here is the working code adding ACEs to ACLs, it may be used to add ACEs to registry or filesystem DACLs. I haven't tried it with anything else yet. As you may notice, no nasty RegGetKeySecurity or manual ACL composing needed. There's even no need to RegOpenKeyEx. For more info, check this MS doc.
UPD Of course it will need admin rights for execution.
// sk - alloced string / path to needed key
// common look: MACHINE\\Software\\... where MACHINE == HKEY_LOCAL_MACHINE
// google for more address abbrevations
PSECURITY_DESCRIPTOR pSD = 0;
EXPLICIT_ACCESS ea;
PACL pOldDACL = 0, pNewDACL = 0;
if (ERROR_SUCCESS == GetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, &pOldDACL, 0, &pSD)) {
memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = KEY_ALL_ACCESS;
ea.grfAccessMode = GRANT_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.ptstrName = <USERNAME HERE>; //DOMAIN\\USERNAME
if (ERROR_SUCCESS == SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL)) {
if (ERROR_SUCCESS == SetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, pNewDACL, 0)) {
if (pSD != 0) LocalFree((HLOCAL)pSD);
if (pNewDACL != 0) LocalFree((HLOCAL)pNewDACL);
SAFE_FREE(sk);
// WE'RE GOOD!
return ... ;
} else {
if (pSD) LocalFree((HLOCAL)pSD);
if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
SAFE_FREE(sk);
// SetNamedSecurityInfo failed
return ... ;
}
} else {
if (pSD) LocalFree((HLOCAL)pSD);
if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
SAFE_FREE(sk);
// SetEntriesInAcl failed
return ... ;
}
} else {
if (pSD) LocalFree((HLOCAL)pSD);
SAFE_FREE(sk);
// GetNamedSecurityInfo failed
return ... ;
}