I have the following code
NCore.h
#ifndef _NCORE_H_
#define _NCORE_H_
#include <Windows.h>
#include <cstdio>
namespace Neat
{
class NCore
{
private:
// Structure Definitions
struct NApplicationVersion
{
int major = 0;
int minor = 0;
int build = 0;
LPCSTR toString();
};
// Application Variables
LPCSTR applicationName;
NApplicationVersion applicationVersion;
protected:
public:
NCore();
LPCSTR ApplicationName(LPCSTR _applicationName = NULL);
NApplicationVersion ApplicationVersion(LPCSTR _applicationVersion = NULL);
};
}
#endif
NCore.cpp
#include "NCore.h"
Neat::NCore::NCore()
{
this->applicationName = NULL;
}
LPCSTR Neat::NCore::NApplicationVersion::toString()
{
char str[16];
memset(&str, 0, sizeof(str));
sprintf_s(str, sizeof(str), "%i.%i.%i", this->major, this->minor, this->build);
return str;
}
LPCSTR Neat::NCore::ApplicationName(LPCSTR _applicationName)
{
if (_applicationName)
this->applicationName = _applicationName;
return this->applicationName;
}
Neat::NCore::NApplicationVersion Neat::NCore::ApplicationVersion(LPCSTR _applicationVersion)
{
if (_applicationVersion)
{
//I know this isn't needed. I was just testing something.
Neat::NCore::NApplicationVersion *nav = (Neat::NCore::NApplicationVersion *)malloc(sizeof(Neat::NCore::NApplicationVersion));
sscanf_s(_applicationVersion, "%i.%i.%i", &nav->major, &nav->minor, &nav->build);
this->applicationVersion.major = nav->major;
this->applicationVersion.minor = nav->minor;
this->applicationVersion.build = nav->build;
free(nav);
}
return this->applicationVersion;
}
main.cpp
#include <Windows.h>
#include "NCore.h"
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT iCmdShow)
{
Neat::NCore n;
n.ApplicationName("test");
LPCSTR test = n.ApplicationName();
LPCSTR test2 = n.ApplicationVersion().toString();
if (strcmp(test2, "0.0.0") == 0)
{
MessageBox(NULL, "", "", MB_OK);
}
n.ApplicationVersion("10.50.136");
if (strcmp(test2, "0.0.0") == 0)
{
MessageBox(NULL, "", "", MB_OK);
}
LPCSTR test3 = n.ApplicationVersion().toString();
if (test3 == "10.50.136")
{
MessageBox(NULL, "", "", MB_OK);
}
while (true);
return 0;
}
My issue is that test2 initializes as "0.0.0" and the first MessageBox is shown. But after I call ApplicationVersion("10.50.136") it changes test2 to "10.50.136" and the second MessageBox is not shown.
Can someone please explain why this happens/how to fix it?
EDIT: I was testing a function that could operate as both a get/set function. I'm fairly new to this and I learn by failing. I just can't figure out what exactly went wrong here.
EDIT 2: I changed the code as follows...
NCore.h
struct NApplicationVersion
{
int major = 0;
int minor = 0;
int build = 0;
char asString[16];
LPCSTR toString();
};
NCore.cpp
LPCSTR Neat::NCore::NApplicationVersion::toString()
{
memset(this->asString, 0, 15);
sprintf_s(this->asString, 16, "%i.%i.%i", this->major, this->minor, this->build);
return this->asString;
}
Is this viable?
From my understanding, I was putting the variable "str" on the stack. This causes it to not have a set place in memory(?) and when other calls altered the stack they also altered the data the pointer "test2" was trying to read from?
It's pretty easy, actually. toString invokes undefined behavior by returning an array (str) that's allocated locally to the function and goes out of scope with the return:
LPCSTR Neat::NCore::NApplicationVersion::toString()
{
char str[16];
memset(&str, 0, sizeof(str));
sprintf_s(str, sizeof(str), "%i.%i.%i", this->major, this->minor, this->build);
return str;
}
In most common C++ implementations, str will be on the stack. (The C++ standard does not require a unified concept of "stack" where all automatic variables live, but most common implementations work that way.)
So, subsequent functions that modify the stack will also modify the C-style string pointed to by the call to toString(). The subsequent call to n.ApplicationVersion(), for example, would likely trash str. Your second message box won't show as long as the string changed to anything other than "0.0.0", and it won't take much to corrupt the stack in that way.
Based on your subsequent edit: Making the string a member of your class will somewhat work. Any call to toString will rewrite this string, affecting all callers that saved a pointer to this buffer.
Still, this is certainly much safer than saving the string on the stack. Furthermore, as long as only toString writes to this buffer, you have well defined rules for when that string is valid.
LPCSTR is not the string, it is just the pointer to the char array. When you set new version, the char array itself is changing. That's why you observer the change in test2 variable immediately. If you want this string not to be changed, copy and save it in your internal buffer.
Related
I have this function which simply encrypts a string (this function works fine, and tested).
DWORD SomeObj::Encrypt(string * To_Enc) {
DWORD text_len = (To_Enc->length());
if (!CryptEncrypt(this->hKey,
NULL, // hHash = no hash
1, // Final
0, // dwFlags
(PBYTE)(*To_Enc).c_str(), //*pbData
&text_len, //*pdwDataLen
128)) { //dwBufLen
return SERVER_ERROR;
}
return SERVER_SUCCESS;
}
And I have this piece of code:
string s= "stringTest";
Encrypt(&s);
which simply call the function passing the pointer of a string.
The program is causing an access violation exception right when it calls the function Encrypt(&s), I guess that it's something about the parameter &s being passed but I can't figure this out. Any idea from your experience ?
This answer will reiterate important points already made in the comments, with example code.
Your current code:
DWORD SomeObj::Encrypt(string * To_Enc) {
DWORD text_len = (To_Enc->length());
if (!CryptEncrypt(this->hKey,
NULL, // hHash = no hash
1, // Final
0, // dwFlags
(PBYTE)(*To_Enc).c_str(), //*pbData
&text_len, //*pdwDataLen
128)) { //dwBufLen
return SERVER_ERROR;
}
return SERVER_SUCCESS;
}
On the line:
(PBYTE)(*To_Enc).c_str(), //*pbData
Note that you are casting away const-ness from the pointer value returned from the c_str() method call.
This should immediately be a red flag; there may be times when casting away const-ness is a valid use case, but it is more the exception than the rule.
Untested, but using a temporary, mutable buffer should solve your problem, such as:
#include <cstddef>
#include <vector>
...
DWORD SomeObj::Encrypt(string * To_Enc) {
std::vector<std::string::value_type> vecBuffer(To_Enc->length() * 3, 0); // use the maximum value that could be output, possibly some multiple of the length of 'To_Enc'
std::size_t nIndex = 0;
for (auto it = To_Enc->cbegin(); it != To_End->cend(); ++it)
{
vecBuffer[nIndex++] = *it;
}
DWORD text_len = (To_Enc->length());
if (!CryptEncrypt(this->hKey,
NULL, // hHash = no hash
1, // Final
0, // dwFlags
reinterpret_cast<PBYTE>(&vecBuffer[0]), //*pbData
&text_len, //*pdwDataLen
vecBuffer.size())) { //dwBufLen
return SERVER_ERROR;
}
To_Enc->assign(&vecBuffer[0], text_len); // assumes 'text_len' is returned with the new length of the buffer
return SERVER_SUCCESS;
}
I have a function called ReversedIPAddressString which takes IPAddress as a TCHAR* and then returns reveresed IPAddress as TCHAR*. I am able to get reversed IP fine but when I passed this TCHAR* pointer(reversedIP) to some other function(let's say dns.query(TCHAR*)),IP value always junk. I am wondering what I am missing?
I am pasting my code here for your reference...
Caller Method:
bool DNSService::DoesPtrRecordExixts(System::String^ ipAddress)
{
IntPtr ipAddressPtr = Marshal::StringToHGlobalAuto(ipAddress);
TCHAR* ipAddressString = (TCHAR*)ipAddressPtr.ToPointer();
bool bRecordExists = 0;
WSAInitializer initializer;
Locale locale;
// Initialize the identity object with the provided credentials
SecurityAuthIdentity identity(userString,passwordString,domainString);
// Initialize the context
DnsContext context;
// Setup the identity object
context.acquire(identity);
DnsRecordQueryT<DNS_PTR_DATA> dns(DNS_TYPE_PTR, serverString);
try
{
bRecordExists = dns.query(ReversedIPAddressString(ipAddressString)) > 0;
}
catch(SOL::Exception& ex)
{
// Free up the pointers to the resources given to this method
Marshal::FreeHGlobal(ipAddressPtr);
if(ex.getErrorCode() == DNS_ERROR_RCODE_NAME_ERROR)
return bRecordExists;
else
throw SOL::Exception(ex.getErrorMessage());
}
// Free up the pointers to the resources given to this method
Marshal::FreeHGlobal(ipAddressPtr);
return bRecordExists;
}
Called Method:
TCHAR* DNSService::ReversedIPAddressString(TCHAR* ipAddressString)
{
TCHAR* sep = _T(".");
TCHAR ipArray[4][4];
TCHAR reversedIP[30];
int i = 0;
TCHAR* token = strtok(ipAddressString, sep);
while(token != NULL)
{
_stprintf(ipArray[i], _T("%s"), token);
token = strtok((TCHAR*)NULL, sep);
i++;
}
_stprintf(reversedIP, _T("%s.%s.%s.%s.%s"), ipArray[3], ipArray[2], ipArray[1], ipArray[0],_T("IN-ADDR.ARPA"));
return reversedIP;
}
DNS.Query method declaration:
int query(__in const TCHAR* hostDomain, __in DWORD options=DNS_QUERY_STANDARD)
Hope to get help from you.
Thanks in advance!
Ramani
You're returning a pointer to the local array TCHAR reversedIP[30]; which is allocated withing your function ReversedIPAddressString. When this function exits, your array becomes out of scope - it does not exist anymore. This is undefined behaviour.
You should return a string object instead, for example std::basic_string<TCHAR>
See this question: pointer-to-local-variable
Have an annoying problem with my code, probably I am doing something wrong because my Python
implementation is much faster!
C++ implementation problems:
Iterating over "HKEY_CLASSES_ROOT" takes a lot of ram, I assume it's because c++ implementation uses lots of variables. Fixed
It's also slow, much slower than python implantation of the code Fixed
The code even slower when trying to iterate over HKEY_CLASSES_ROOT Fixed
New Questions:
Thanks to Nam Nguyen i understood what causing the leaks in my code, and directly effecting execution time, the code below is the fixed one. how come c++ implementation runs as fast as my python implementation?
C++ implementation:
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <string>
using namespace std;
#define MAX_KEY_LENGTH 255
int RecurseOpenRegEx(HKEY hkey, string subKey = "",string search = "nothing", DWORD sum = 0)
{
TCHAR csubKey[MAX_KEY_LENGTH];
DWORD nSubKeys = 0;
DWORD pathLength = MAX_PATH;
TCHAR storeKeyName[MAX_KEY_LENGTH];
DWORD keyLength;
HKEY hKey = hkey; //somehow i need to reassign HKEY, otherwise it won't pass it with the function, this is bigger than me tough...
const char * ccsearch = search.c_str();
const char * ccsubKey;
if (subKey != "")
{
ccsubKey = subKey.c_str();
copy(subKey.begin(), subKey.end(),csubKey); //convert string to TCHAR
}
if (RegOpenKeyEx(hkey, ccsubKey, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
{
if (RegQueryInfoKey(hkey, csubKey, &pathLength, NULL,&nSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
{
sum += nSubKeys;
for (DWORD subKeyIndex = 0; subKeyIndex < nSubKeys; subKeyIndex++)
{
keyLength = MAX_KEY_LENGTH;
if (RegEnumKeyEx(hkey, subKeyIndex, storeKeyName, &keyLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
{
string sKeyName = storeKeyName; //Convert TCHAR to string explicitly
if (subKey != "")
{
sKeyName = subKey + "\\" + sKeyName;
}
sum += RecurseOpenRegEx(hKey, sKeyName);
}
}
}
}
RegCloseKey(hkey); //Now closing the right key
return sum;
}
int main()
{
cout << "sum of all keys: " << RecurseOpenRegEx(HKEY_LOCAL_MACHINE);
return 0;
}
Python implementation:
import winreg
def recurseRegistrySearch(key, keySearch = "",subkey = "", subkeypath = "", x = 0):
key = winreg.OpenKey(key, subkey, 0)
y = winreg.QueryInfoKey(key)[0]
x += y
for items in range(x):
try:
subkey = winreg.EnumKey(key, items)
if ((keySearch.lower() in subkey.lower()) and (keySearch != "")):
print(subkeypath + "\\" + subkey)
x += recurseRegistrySearch(key, keySearch, subkey, subkeypath = subkeypath + "\\" + subkey)
except WindowsError:
pass
return x
print("sum of all keys: {0}".format(recurseRegistrySearch(winreg.HKEY_CLASSES_ROOT)))
There is leak of resource in your code. You open hkey but you close hKey (note the difference in case of k and K).
On a side note, you store the opened registry key into hkey itself. And it happens that hkey is the passed in parameter, shared among all calls to RecurseOpenRegEx. That is why "somehow i need to reassign HKEY".
Basically, what I can advise you now is immediately clean up your code. Bugs like these are hard to spot when your code is a too difficult to read. Once done, I believe you will find it easier to debug/trace.
Probably it's your heavy use of string variables ,which involves a lot of dynamic memory allocations.
Try accepting parameters as LPCTSTR .assign string variables inside the function with those LPCTSTRs and pass parameters with str.c_str().
I am doing a threading, but i don't know how to print out the pointer value.
UINT __cdecl CThreadDlg::func(LPVOID pParam)
{
data *ptr = (data*)pParam;
AfxMessageBox(L"You have entered a thread function.");
ptr->a = "abc12345"; // char * a
AfxMessageBox(???); // what should i put here to print out the value of a?
return 0;
}
Maybe something like this:
CString a;
a.Format(_T("%p"), ptr->a);
AfxMessageBox(a);
Edit:
to get value 'ptr->a' points to:
a.Format(_T("%s), ptr->a);
I believe
ptr->a
But that completely depends on the AfxMessageBox API.
Edit: Looks like AfxMessageBox expects a LPCTSTR which is nothing but a const char *.
LPCTSTR lpStr = (LPCTSTR)ptr->a;
This should help if your compiler isn't casting automatically
Use the swprintf function to build a C string with the text, then print that. Consider this example:
UINT __cdecl CThreadDlg::func(LPVOID pParam)
{
data *ptr = (data*)pParam;
AfxMessageBox(L"You have entered a thread function.");
ptr->a = "abc12345"; // char * a
wchar_t buf[ 1024 ];
swprintf( buf, "ptr->a points to %p", ptr->a );
AfxMessageBox(buf);
return 0;
}
I have the following code , which gets a return value from a function as char*
cDestDrive = ReadFromRegistry(HKEY_CURRENT_USER,NDSPATH,szDestPathRoot);
I am able to read the value inside cDestDrive till the time I am assigning it. The moment I am assigning it:
CString strServerAddress = cDestDrive;
the value of cDestDrive gets changed (corrupted) and I am not able to get the value in CString strServerAddres any Idea why this is happening.
Edit:
Code to Read from Registry
char* CNDSShellExtender::ReadFromRegistry(HKEY hKey,LPCTSTR lpNDS,LPSTR lpRegKey)
{
HKEY hRegKey=NULL;
if(hKey==NULL || lpNDS==""||lpNDS==NULL||lpRegKey==""||lpRegKey==NULL)
MessageBox(NULL,"Reading from Registry Failed!Invalid Path",
_T("Network Drive Solution"),
MB_ICONERROR);
LONG lOpenRes=RegOpenKey(hKey,lpNDS,&hRegKey);
if (lOpenRes!=ERROR_SUCCESS ||lpNDS==NULL)
MessageBox ( NULL, "Can not Find Any Server to Connect",
_T("NDSShellExtension"),
MB_ICONERROR );
if(lOpenRes==ERROR_SUCCESS && lpNDS!=NULL)
{
TCHAR tSZValue[MAX_PATH] = {0};
DWORD dwBufSize=MAX_PATH;
LONG lCloseOut;
LPBYTE lpStorage = reinterpret_cast<LPBYTE>(tSZValue);
char* cpRegKeyVal=tSZValue;
if (ERROR_SUCCESS == RegQueryValueEx(hRegKey,lpRegKey , 0, 0, (BYTE*)tSZValue, &dwBufSize))
{
lCloseOut= RegCloseKey(hRegKey);
if (lCloseOut != ERROR_SUCCESS)
MessageBox (NULL, "Registry Not Closed",
_T("NDSShellExtension"),
MB_ICONERROR );
return cpRegKeyVal;
}
else
{
lCloseOut= RegCloseKey(hRegKey);
if (lCloseOut != ERROR_SUCCESS)
MessageBox (NULL, "Registry Not Closed",
_T("NDSShellExtension"),
MB_ICONERROR );
return "";
}
}
return "";
}
I believe you are returning a char* pointing to a an array that is allocated on the stack, i.e. this line:
TCHAR tSZValue[MAX_PATH] = {0};
followed by:
char* cpRegKeyVal=tSZValue;
This is dangerous, and you are experiencing first hand the end result!
EDIT: why don't you directly assign to a CString in the function and return that?
The function returns a pointer to tSZValue which is a local variable, so ceases to exist when it goes out of scope.
You are returning a pointer to tSZValue, which is a temp variable and will be overwritten sometime after the function exits.
The simplest solution: have ReadFromRegistry() return a CString instead of a char *.
It looks like ReadFromRegistry doesn't allocate memory to return the value (or it does, but it's on the stack and is destroyed before the function returns).
Instead of returning a char *, maybe you could pass in a reference to a char * as a parameter, and allocate your memory outside of ReadFromRegistry.