I'm trying to get the strength of the connected wifi signal using c++ on a Windows 7 machine.
I can get a value for the strength of the signal using the command WlanGetAvailableNetworkList but the value returned is not granular enough for our requirements. Basically as you move away from the Wifi router the value jumps in increments of 20% (99% -> 80% -> 60% etc).
For the application we are developing we really need a more accurate value. I know it's possible as I have seen apps in windows displaying accurate dBm values for signal strength...
If anyone has any suggestions they would be greatly appreciated!
dwResult = WlanGetAvailableNetworkList(hClient,&pIfInfo->InterfaceGuid,0,NULL,&pBssList);
if (dwResult != ERROR_SUCCESS) {
wprintf(L"WlanGetAvailableNetworkList failed with error: %u\n", dwResult);
dwRetVal = 1;
} else {
for (j = 0; j < pBssList->dwNumberOfItems; j++) {
pBssEntry = (WLAN_AVAILABLE_NETWORK *) & pBssList->Network[j];
if ((pBssEntry->dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED ) != 0 ){
if (pBssEntry->wlanSignalQuality == 0)
iRSSI = -100;
else if (pBssEntry->wlanSignalQuality == 100)
iRSSI = -50;
else
iRSSI = -100 + (pBssEntry->wlanSignalQuality/2);
wprintf(L" Signal Quality[%u]:\t %u (RSSI: %i dBm)\n", j,
pBssEntry->wlanSignalQuality, iRSSI);
}
}
Ok after continuing research online I managed to patch together a way that works for me. From what I've read - there are many different ways of obtaining the RSSI - but this method, while maybe a little cumbersome, worked well for our needs...
I'm using the command WlanGetNetworkBssList, and then getting the RSSI value directly from the returned PWLAN_BSS_ENTRY.
I found it is important to call WlanScan each time before querying WlanGetNetworkBssList, otherwise the returned value doesn't change with any sort of regularity.
HANDLE hClient;
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
PWLAN_INTERFACE_INFO pIfConnInfo = NULL;
PWLAN_CONNECTION_ATTRIBUTES pConnectInfo = NULL;
PWLAN_BSS_LIST pBssList=NULL;
PWLAN_BSS_ENTRY pBssEntry=NULL;
WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_invalid;
DWORD dwResult = 0;
DWORD dwMaxClient = 2;
DWORD dwCurVersion = 0;
DWORD connectInfoSize = sizeof(WLAN_CONNECTION_ATTRIBUTES);
int i;
// Initialise the Handle
dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);
if (dwResult != ERROR_SUCCESS)
{
return 0;
}
// Get the Interface List
dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);
if (dwResult != ERROR_SUCCESS)
{
return 0;
}
//Loop through the List to find the connected Interface
PWLAN_INTERFACE_INFO pIfInfo = NULL;
for (i = 0; i < (int) pIfList->dwNumberOfItems; i++)
{
pIfInfo = (WLAN_INTERFACE_INFO *) & pIfList->InterfaceInfo[i];
if (pIfInfo->isState == wlan_interface_state_connected)
{
pIfConnInfo = pIfInfo;
break;
}
}
if ( pIfConnInfo == NULL )
return 0;
// Query the Interface
dwResult = WlanQueryInterface(hClient,&pIfConnInfo->InterfaceGuid,wlan_intf_opcode_current_connection,NULL,&connectInfoSize,(PVOID *) &pConnectInfo,&opCode);
if (dwResult != ERROR_SUCCESS)
{
return 0;
}
// Scan the connected SSID
dwResult = WlanScan(hClient,&pIfConnInfo->InterfaceGuid,&pConnectInfo->wlanAssociationAttributes.dot11Ssid,NULL,NULL);
if (dwResult != ERROR_SUCCESS)
{
return 0;
}
// Get the BSS Entry
dwResult = WlanGetNetworkBssList(hClient,&pIfConnInfo->InterfaceGuid,&pConnectInfo->wlanAssociationAttributes.dot11Ssid,dot11_BSS_type_infrastructure,TRUE,NULL,&pBssList);
if (dwResult != ERROR_SUCCESS)
{
return 0;
}
// Get the RSSI value
pBssEntry=&pBssList->wlanBssEntries[0];
return pBssEntry->lRssi;
wlanSignalQuality is an interpolation of the RSSI. Take a look at this answer question for how to get the RSSI directly:
Get Rssi Value From Windows
Related
I'm writing a program that processes USB drives, to get information about connecting a new device, I signed up for a window message WM_DEVICECHANGE. But I, of course, do not receive messages about the devices that were connected before my program was launched. To process such devices, I wrote a search function, but I get a strange result. It finds my flash drive, but does not recognize it to be removable. Why?
Function
bool FindConnectedRemovableUsbstorDevices(std::list<std::wstring>& UsbList)
{
std::wstring ClassGuidString(L"{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}");
GUID ClassGuid;
BYTE buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA_W pspdidd = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(buf);
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD size;
SP_DEVINFO_DATA dev_data;
DWORD properties;
if(NOERROR != CLSIDFromString(ClassGuidString.c_str(), &ClassGuid))
return false;
HDEVINFO dev_info = INVALID_HANDLE_VALUE;
dev_info = SetupDiGetClassDevs(&ClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (dev_info == INVALID_HANDLE_VALUE)
return false;
DWORD index = 0;
BOOL ret = FALSE;
spdid.cbSize = sizeof(spdid);
while (true)
{
ret = SetupDiEnumDeviceInterfaces(dev_info, NULL, &ClassGuid, index, &spdid);
if (!ret)
break;
size = 0;
SetupDiGetDeviceInterfaceDetail(dev_info, &spdid, NULL, 0, &size, NULL);
//Get required size
if (size == 0 || size >= sizeof(buf))
continue;
//prepare structs
ZeroMemory(reinterpret_cast<PVOID>(pspdidd), 1024);
pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
ZeroMemory(reinterpret_cast<PVOID>(&spdd), sizeof(spdd));
spdd.cbSize = sizeof(spdd);
BOOL res = SetupDiGetDeviceInterfaceDetail(dev_info, &spdid, pspdidd, size, &size, &spdd);
//Get info
if (!res)
continue;
HANDLE drive = CreateFileW(pspdidd->DevicePath, FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);//open device
if (drive == INVALID_HANDLE_VALUE)
continue;
printf("\n%S\r\n", pspdidd->DevicePath);
DWORD bytes_returned = 0;
BOOL b = DeviceIoControl(drive, IOCTL_STORAGE_CHECK_VERIFY2, NULL, 0, NULL, 0, &bytes_returned, NULL);
if (!b) //check is card reader?
{
printf("IOCTL_STORAGE_CHECK_VERIFY2 error = %d\r\n", GetLastError());
goto stop_process_device;
}
bytes_returned = 0;
STORAGE_DEVICE_NUMBER sdn;
//Get Drive number
b = DeviceIoControl(drive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &bytes_returned, NULL);
if (!b)
goto stop_process_device;
RtlZeroMemory(&dev_data, sizeof(SP_DEVINFO_DATA));
dev_data.cbSize = sizeof(dev_data);
if (SetupDiEnumDeviceInfo(dev_info, sdn.DeviceNumber, &dev_data))
{
//check property
b = SetupDiGetDeviceRegistryProperty(dev_info, &dev_data, SPDRP_REMOVAL_POLICY, NULL,
reinterpret_cast<PBYTE>(&properties), sizeof(properties), NULL);
if (b && properties != CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL)
{
UsbList.push_back(pspdidd->DevicePath);
printf("REMOVAL\r\n");
}
}
stop_process_device:
CloseHandle(drive);
index++;
}
SetupDiDestroyDeviceInfoList(dev_info);
return true;
}
And output
\\?\usbstor#disk&ven_generic-&prod_ms#ms-pro#hg&rev_1.00#20090703819900000&1#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
IOCTL_STORAGE_CHECK_VERIFY2 error = 21
\\?\ide#diskst3500418as_____________________________cc38____#5&5c6cfd6&0&1.0.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
REMOVAL
\\?\usbstor#disk&ven_generic-&prod_sd#mmc&rev_1.00#20090703819900000&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
IOCTL_STORAGE_CHECK_VERIFY2 error = 21
\\?\scsi#disk&ven_ocz&prod_revodrive3_x2#5&19ad1f72&0&000000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
\\?\ide#diskst1000lm014-1ej164______________________sm30____#5&2ea7e938&0&0.1.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
\\?\usbstor#disk&ven_sandisk&prod_extreme&rev_0001#aa010823150434152862&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
\\?\ide#diskwdc_wd1002fbys-02a6b0___________________03.00c06#5&2ea7e938&0&0.0.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
REMOVAL
Error 21 is empty card reader.
Sandisk is my flash. In Debug i saw that SetupDiGetDeviceRegistryProperty return property CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL, but i really don't know why...
If you just want to find the connected removable devices, there's a much simpler alternative using GetLogicalDriveStrings() and GetDriveType():
#define MAX_DRIVES 256
bool FindConnectedRemovableUsbstorDevices(std::list<std::wstring>& UsbList)
{
wchar_t drives[MAX_DRIVES];
wchar_t* temp = drives;
if (GetLogicalDriveStringsW(MAX_DRIVES, drives) == 0)
return false;
while (*temp != NULL)
{
if (GetDriveTypeW(temp) == 2 /* DRIVE_REMOVABLE */)
UsbList.push_back(temp);
// Go to the next drive
temp += lstrlenW(temp) + 1;
}
return true;
}
I don't know why my fisrt method of detection removable media works so strange, but method of RbMm works great. I send IOCTL query to every found device with IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceProperty and look for STORAGE_DEVICE_DESCRIPTOR.RemovableMedia field. All my devices recognized successfully and right.
I am calling a function that operates an I/o board through a serial port to check that it is communicating in an instance of my main class.
I know that this is risky but unfortunately this is an old section of code that has been used for a while so I am unable to alter the functionality while I have been asked to improve it.
If there is no communication issue the application will start up, use the function and continue with no issue.
The problem arises when there is a communication fault with the I/o board, I have found that the read function is hanging and stopping the app from starting for the majority of the time. On occasion the app will load and will report that there is a communication fault.
What I am trying to achieve is for the application to load successfully every time when there is a communication fault.
The comport is set up with COMMTIMEOUTs originally which I expected would timeout the port when there has been nothing to read. I have attempted to alter the timings but with no avail.
I have also attempted to use a thread for the read function so that it would not block the start up but still it hangs.
Currently the port is set up synchronously.
Has anybody got any suggestions? I can put some code examples up if required.
Main.cpp
extern COMPort comPort;
BOOL Main::InitInstance()
{
int i = comPort.DoorLatchOff();
If(i<0) printf("Error checksum. COM port?\n");
else printf("checksum ok.\n");
}
COMPort.h
class CCOMPort
{
public:
CCOMPort (COM_PORT port = NULL_COM, DCB * state = NULL);
BOOL SetPortNumber (COM_PORT port = NULL_COM, DCB * state = NULL);
void Read(BYTE* buff, int count);
int DoorLatchOff(void);
protected:
HANDLE m_hPort;
};
static HANDLE m_hPortThreaded;
typedef struct readParam{BYTE* readBuff;int readCount;}RP, *PRP;
DWORD WINAPI ThreadedRead( LPVOID lpParam );
COMPort.cpp
CCOMPort::CCOMPort (COM_PORT port, DCB * state) : m_portNum (port), m_hPort(INVALID_HANDLE_VALUE)
{
SetPortNumber (port, state);
}
BOOL CCOMPort::SetPortNumber (COM_PORT port, DCB * state)
{
if (m_hPort != INVALID_HANDLE_VALUE){
::CloseHandle (m_hPort);
m_hPort = INVALID_HANDLE_VALUE;
}
m_portNum = port;
m_currState = m_defState;
m_originState = m_defState;
if (m_portNum != NULL_COM){
stringstream ssPortName;
ssPortName << "COM" << (m_portNum + 1) << ":" << flush;
m_hPort = ::CreateFile (ssPortName.str().c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
NULL);
if (m_hPort == INVALID_HANDLE_VALUE)
return FALSE;
else
{
GetState (& m_originState);
if (state)
m_currState = * state;
SetState (& m_currState);
GetCommTimeouts(m_hPort, &timeouts);
timeouts.ReadIntervalTimeout = 75; //15
timeouts.ReadTotalTimeoutMultiplier = 5; //1
timeouts.ReadTotalTimeoutConstant = 1250; //250
timeouts.WriteTotalTimeoutMultiplier = 5; //1
timeouts.WriteTotalTimeoutConstant = 1250; //250
SetCommTimeouts(m_hPort, &timeouts);
FlushOutput ();
FlushInput ();
PurgeOutput ();
PurgeInput ();
}
}
return TRUE;
}
void CCOMPort::Read(BYTE* buff, int count)
{
PRP pReadArray[1];
DWORD dwThreadArray[1];
HANDLE hThreadArray[1];
m_hPortThreaded = m_hPort;
pReadArray[0] = (PRP) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RP));
if(pReadArray[0] == NULL){
ExitProcess(2);
}
pReadArray[0]->readBuff = buff;
pReadArray[0]->readCount = count;
hThreadArray[0] = CreateThread(NULL,
0,
ThreadedRead,
pReadArray[0],
0,
&dwThreadArray[0]);
if(hThreadArray[0] == NULL){
ExitProcess(3);
}
WaitForSingleObject(hThreadArray[0],500/*INFINITE*/);
CloseHandle(hThreadArray[0]);
if(pReadArray[0] != NULL){
HeapFree(GetProcessHeap(), 0, pReadArray[0]);
pReadArray[0] = NULL;
}
}
DWORD WINAPI ThreadedRead(LPVOID lpParam)
{
BOOL bDone = FALSE, bResult;
//int buff_idx = 0;
DWORD dwCommModemStatus;
DWORD dwBytesTransfered;
PRP pReadArray;
pReadArray = (PRP)lpParam;
SetCommMask(m_hPortThreaded, EV_RXCHAR);
while(!bDone){
WaitCommEvent(m_hPortThreaded, &dwCommModemStatus, 0);
if(dwCommModemStatus == 0){
bDone = TRUE;
break;
}
if(dwCommModemStatus & EV_RXCHAR){
bResult = ReadFile(m_hPortThreaded, pReadArray[0].readBuff, pReadArray[0].readCount, &dwBytesTransfered, 0);
bDone = TRUE;
}
}
return(bResult);
}
int COMPort::DoorLatchOff(void)
{
unsigned char comm_str[10];
int chksum, chksum1;
DWORD count = 6;
WriteComm(21, 7, 0);
comm.Read(comm_str, count);
chksum = comm_str[0] + comm_str[2] + comm_str[3];
chksum1 = comm_str[4];
chksum1 = (chksum1 << 8) | comm_str[5];
if(chksum == chksum1)
return(0);
else
return(-1);
}
Recently I stuck at the same problem, but I have solved it.
There are two ways:
On forums some people recomend to set both ReadIntervalTimeout and ReadTotalTimeoutMultiplier to MAXDWORD, as recomened in MSDN documentation in the REMARKS section. But in this case the funtion returns each time when there is at least one character in the input buffer.
The most robust decision I have found is just to set ReadIntervalTimeout and ReadTotalTimeoutMultiplier to 0, and ReadTotalTimeoutConstant to your timeout value, as below. It works pretty fine for me.
COMMTIMEOUTS commtimeouts;
GetCommTimeouts (hCommFile, &commtimeouts);
commtimeouts.ReadIntervalTimeout = 0;
commtimeouts.ReadTotalTimeoutMultiplier = 0;
commtimeouts.ReadTotalTimeoutConstant = timeout;
commtimeouts.WriteTotalTimeoutMultiplier = 0;
commtimeouts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hCommFile, &commtimeouts);
Please, could you try to remove the WaitCommEvent function from ThreadedRead and see if it still hangs?
DWORD WINAPI ThreadedRead(LPVOID lpParam)
{
BOOL bResult;
DWORD dwBytesTransfered = 0;
PRP pReadArray;
pReadArray = (PRP)lpParam;
while (dwBytesTransfered == 0) {
bResult = ReadFile(m_hPortThreaded, pReadArray[0].readBuff, pReadArray[0].readCount, &dwBytesTransfered, 0);
Sleep(250);
}
return(bResult);
}
When dealing with hw I/O it is a best practice to decouple the Application (GUI) thread from the command-execution thread.
If you are developing a C++ Win32 app you could use SerialLib. It is an old but stable Win32 event-driven serial library.
How to tell from within my interactive GUI process that its user token is the same as the user token for logon session that the process is running under?
PS. I cannot assume that my process is running elevated.
Cutting through demagoguery I think I got it.
It's somewhat of a hack, as I can't seem to find a way to retrieve logon user token from a session ID to compare it's SID to the user token SID from my process. The only API that I know of that can do this (WTSQueryUserToken) requires SE_TCB_NAME privilege which my "little process" will most certainly not have.
So assuming that no two user accounts can have the same name on the same domain (or workgroup), this seems to do the trick so far:
#define SIZEOF(f) (sizeof(f) / sizeof(f[0]))
int IsRunningInOwnLogonUserSession()
{
//Checks if our process is running in its own logon user session
//RETURN:
// = 1 if yes
// = 0 if no
// = -1 if error determining -- check GetLastError() for info
int nOSError = NO_ERROR;
int res = -1;
DWORD dwProcSessID = -1;
if(::ProcessIdToSessionId(::GetCurrentProcessId(), &dwProcSessID))
{
TCHAR buffProcUsrName[UNLEN + 1];
buffProcUsrName[0] = 0;
DWORD dwcbSz = 0;
LPTSTR pStrUsrName = NULL;
if(WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwProcSessID, WTSUserName, &pStrUsrName, &dwcbSz) &&
pStrUsrName &&
pStrUsrName[0])
{
LPTSTR pStrDomName = NULL;
if(WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwProcSessID, WTSDomainName, &pStrDomName, &dwcbSz) &&
pStrDomName &&
pStrDomName[0])
{
dwcbSz = SIZEOF(buffProcUsrName);
::GetUserName(buffProcUsrName, &dwcbSz);
buffProcUsrName[SIZEOF(buffProcUsrName) - 1] = 0;
if(buffProcUsrName[0])
{
//The domain name is somewhat tricky as I don't know
//what format 'WTSDomainName' supplied it in...
//I'm assuming old, generic NetBIOS...
TCHAR buffProcDomName[MAX_PATH];
buffProcDomName[0] = 0;
GetEnvironmentVariable(_T("USERDOMAIN"), buffProcDomName, SIZEOF(buffProcDomName));
buffProcDomName[SIZEOF(buffProcDomName) - 1] = 0;
if(buffProcDomName[0])
{
//Compare the two
if(lstrcmpi(pStrUsrName, buffProcUsrName) == 0 &&
lstrcmpi(pStrDomName, buffProcDomName) == 0)
{
//Same
res = 1;
}
else
{
//Different
res = 0;
}
}
else
nOSError = ::GetLastError();
}
else
nOSError = ::GetLastError();
}
else
nOSError = ::GetLastError();
if(pStrDomName)
{
WTSFreeMemory(pStrDomName);
pStrDomName = NULL;
}
}
else
nOSError = ::GetLastError();
if(pStrUsrName)
{
WTSFreeMemory(pStrUsrName);
pStrUsrName = NULL;
}
}
else
nOSError = ::GetLastError();
::SetLastError(nOSError);
return res;
}
If someone finds a better solution, let me know...
Today i found this strange error in querying the time zone information .
The code to read the display name of time zones looks like this
typedef struct {
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
} TZI, * PTZI;
CRegKey RegKey;
CString regKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
int idx = 0;
bool bMoreKeys = true;
bool bSuccess = true;
while (bMoreKeys && bSuccess) {
CString sSubkeyName;
DWORD nLength = 200;
LPTSTR sBuffer = sSubkeyName.GetBufferSetLength(nLength);
FILETIME ftLastWriteTime;
bMoreKeys = (RegKey.EnumKey(idx, sBuffer, & nLength, & ftLastWriteTime) == ERROR_SUCCESS);
sSubkeyName.ReleaseBuffer();
if (!bMoreKeys) {
bMoreKeys = false;
break;
}
CString sSubKeyPath = regKey + _T("\\") + sSubkeyName;
CRegKey subKey;
if (subKey.Open(HKEY_LOCAL_MACHINE, sSubKeyPath, KEY_READ) != ERROR_SUCCESS) {
//LOG_ERROR
bSuccess = false;
break;
}
// Get the display name
CString sDispName;
DWORD nDispBufferLength = 1000;
LPTSTR sDispBuffer = sDispName.GetBufferSetLength(nDispBufferLength);
if (subKey.QueryStringValue(_T("Display"), sDispBuffer, & nDispBufferLength) != ERROR_SUCCESS) {
//LOG_ERROR
bSuccess = false;
break;
}
sDispName.ReleaseBuffer();
// Get the Bias (for later sorting);
TZI tzi;
nLength = sizeof(tzi);
if (subKey.QueryBinaryValue(_T("TZI"), & tzi, & nLength) != ERROR_SUCCESS && nLength != sizeof(tzi)) {
//LOG_ERROR
bSuccess = false;
}
(void) subKey.Close();
idx++;
}
But for some of the time zones for eg : argentina the return value is not error_success.
On further debugging into the QueryStringValue , i found this
if ((nBytes % sizeof(TCHAR) != 0) || (pszValue[nBytes / sizeof(TCHAR) - 1] != 0)) {
return ERROR_INVALID_DATA;
}
and when the nBytes size is 48 for any time zone display value then always error_invalid_data is returned .
To confirm this i have changed the regkey api call to
DWORD dwType = 0;
ULONG nBytes = 256 * sizeof(TCHAR);
TCHAR displayValue[256];
if (subKey.QueryValue(
_T("Display"), & dwType, (LPBYTE) displayValue, & nBytes) == 0 && dwType == REG_SZ) {}
and i dont get the error anymore and everything is working fine .
Couldnt find out any valid reason that why it occurs.Anyone has better explanation why for all the timezones with size 48 we get the invalid data error .
Thanks in advance.
EDIT:
PS: In some machines everything is working well with the code mentioned above (i.e the first one) and in some other machines its not which is what really strange and with the second code everywhere i could see the code is working fine.
The code
CRegKey Key;
LONG nA = Key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\Argentina Standard Time"), KEY_READ);
TCHAR pszValue[24];
ULONG nValueLength = _countof(pszValue);
LONG nB = Key.QueryStringValue(_T("Display"), pszValue, &nValueLength);
gets you a different error, 234, which is ERROR_MORE_DATA "More data is available."
This happens because the value itself does not fit fully into the provided buffer.
This is what you want instead:
CRegKey Key;
LONG nA = Key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\Argentina Standard Time"), KEY_READ);
ULONG nValueLength = 0;
LONG nB = Key.QueryStringValue(_T("Display"), NULL, &nValueLength);
CString sValue;
if(nValueLength > 0)
{
LONG nC = Key.QueryStringValue(_T("Display"), sValue.GetBufferSetLength(nValueLength - 1), &nValueLength);
}
Or, you need to provide large enough buffer, which you assume to be sufficient.
The string in the reg may not be null-terminated. Hence the following validation fails:
if ((nBytes % sizeof(TCHAR) != 0) || (pszValue[nBytes / sizeof(TCHAR) -1] != 0))
{
return ERROR_INVALID_DATA;
}
Note that it seems to be a bug in CRegKey::QueryStringValue, RegQueryValueEx allows to read not-null-terminated strings, and it is callers responsibility to handle this case. I had come across this exact issue in the code and had to replace the call to CRegKey::QueryStringValue with my own function.
I needed to grant access to a file on Windows in a c++ program. I browsed around and copy/pasted code from MSDN and came up with the following. It has been working as far as I can tell.
But then today I stumbled across a warning in MSDN for the use of AddAccessAllowedAceEx, which says: "The caller must ensure that ACEs are added to the DACL in the correct order.". It then refers the reader to this: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379298(v=vs.85).aspx
So, my request is for any seasoned Windows programmer to review my code below and tell me if I am going to have problems vis-a-vis ACE ordering within the DACL of the file I am modifying (which is passed in via szPath in my function). I will say that I simply added my new ACE to the end of the DACL. If this will be a problem, must I really read out all the ACE's from the DACL, inspect them and then add them back one at a time being sure to insert my new ACE in the correct position to respect the correct ordering?
char* whoOps::ACLAmigo::AddACEToDACL(char* szPath, char* szSecurityPrincipal, DWORD dwPermission)
{
ACL_SIZE_INFORMATION ACLInfo;
memset(&ACLInfo, 0, sizeof(ACL_SIZE_INFORMATION));
UCHAR BuffSid[256];
PSID pSID = (PSID)BuffSid;
int returnCode = ResolveSID(szSecurityPrincipal, pSID);
SE_OBJECT_TYPE SEObjType = SE_FILE_OBJECT;
PACL pOldDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
SECURITY_INFORMATION ACLSecInfo = DACL_SECURITY_INFORMATION;
returnCode = GetNamedSecurityInfoA(szPath, SEObjType, ACLSecInfo, NULL, NULL, &pOldDACL, NULL, &pSD);
char* szReturn = NULL;
if (returnCode != ERROR_SUCCESS) {
szReturn = "GetNamedSecurityInfoA() failed.";
} else {
BOOL getACLResult = GetAclInformation(pOldDACL, &ACLInfo, sizeof(ACLInfo), AclSizeInformation);
if (!getACLResult) {
szReturn = "GetAclInformation() failed.";
} else {
DWORD cb = 0;
DWORD cbExtra = sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(pSID);
cb = ACLInfo.AclBytesInUse + cbExtra;
PACL pNewDACL = static_cast<PACL>(HeapAlloc(GetProcessHeap(),0,cb));
BOOL initACLResult = InitializeAcl(pNewDACL, cb, ACL_REVISION);
if (!initACLResult) {
szReturn = "InitializeAcl() failed.";
} else {
for (DWORD i = 0; i < ACLInfo.AceCount; ++i) {
ACE_HEADER * pACE = 0;
GetAce(pOldDACL, i, reinterpret_cast<void**>(&pACE));
pACE->AceFlags = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
pACE->AceType = ACCESS_ALLOWED_ACE_TYPE;
AddAce(pNewDACL, ACL_REVISION, MAXDWORD, pACE, pACE->AceSize);
}
BOOL addACEResult = AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, dwPermission, pSID);
if (!addACEResult) {
szReturn = "AddAccessAllowedAceEx() failed.";
} else {
DWORD setSIResult = SetNamedSecurityInfoA(szPath, SEObjType, ACLSecInfo, NULL, NULL, pNewDACL, NULL);
if (!setSIResult) {
szReturn = "SetNamedSecurityInfoA() failed.";
} else {
szReturn = "AddACEToDACL() succesful.";
}
}
}
if (pNewDACL) HeapFree(GetProcessHeap(),0, pNewDACL);
}
if (pSD) LocalFree(pSD);
}
return szReturn;
}
ACE ordering is really important! Yes, best solution would be
read the content of the ACL before modifying
programmatically inspect each element (ACE) in the Security Descriptor
place your entry appropriatelly (denied before allow)
This serie has a good amount of samples.