I'm trying to write a function that displays the PID of the owning process of all threads in a system in multiple message boxes.
void CheckProcess()
{
LPCSTR blahzix;
HANDLE tsnap = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0);
THREADENTRY32 tentry;
tentry.dwSize = sizeof (tentry);
BOOL CRec = Thread32First(tsnap, &tentry);
GetLastError();
while (CRec)
{
blahzix = tentry.th32OwnerProcessID;
MessageBox (NULL, NULL, blahzix, MB_OK);
CRec = Thread32Next(tsnap, &tentry);
}
CloseHandle(tsnap);
}
I was confused about why this wasn't working for a while so I opened it up in OllyDBG and found out that the code for MessageBoxwas having its third parameter receive the PID data directly instead of receiving an address which has the PID stored as a string, resulting in an access violation when the MessageBox function tries to access data stored at 00000004. If I remove the line blahzix = tentry.th32OwnerProcessID; and append the third line to look like this: LPCSTR blahzix = "anything"; then instead of crashing MessageBox properly displays a messagebox with the title anything.
Why does this happen? Am I correct in thinking that the compiler decides "variable blahzix isn't used anywhere except in this messagebox function and it always equals tentry.th32OwnerProcessID so they might as well be the same variable, I'll just get rid of it and make messagebox use that variable instead."
EDIT: Let me rephrase my question. If LPCSTR blahzix = "fdisaf"; uses the = operator and the = operator is supposed to change the value of the variable on the left to the value of the data or variable on the right, then how come it's use to create a string for blahzix is valid? Since blahzix is apparently actually a pointer I would assume the = operator should only change where the variable points to instead of changing the contents in it. How come using the = operator in this instance changes the data of the string that it points to and using it in this instance: blahzix = tentry.th32OwnerProcessID; changes the data of the pointer instead of the data of the string? Shouldn't the = operator change one or the other? How can I make the = operator specify which data I want to change?
THREADENTRY32::th32OwnerProcessID is a DWORD, not a string. Assigning it to a pointer and treating it as a string does not make sense. It's also invalid (a constraint violation without an explicit cast); I'm surprised how/why this even compiled.
Don't blame your own errors on the compiler. Compiler bugs are very, very rare.
blahzix = tentry.th32OwnerProcessID;
doesn't convert the integer process id to a string. It merely writes the process id as the address that blahzix points to. You don't own memory at this address so attempting to display a string from it results in undefined behaviour. A crash is likely.
To display the process id as a string via a MessageBox, you need to convert it to a char array. Given C++11 support you can do this by changing
blahzix = tentry.th32OwnerProcessID;
MessageBox (NULL, NULL, blahzix, MB_OK);
to
std::string s = std::to_string(tentry.th32OwnerProcessID);
char const *pchar = s.c_str();
MessageBox (NULL, NULL, pchar, MB_OK);
Alternatively, if you're limited to using C (the question is tagged as C++ but the code is all C), you could use
char str[12];
sprintf(str, "%d", tentry.th32OwnerProcessID);
MessageBox (NULL, NULL, str, MB_OK);
Related
I am currently implementing functions and classes from Borland C++ Builder 5 in Visual Studio 2022. One of the classes is used to handle Windows registry IO, and one of its methods is supposed to return a list of values which the current key contains.
I am using Windows' RegEnumValueA function which, after passing correct arguments, always returns 87 – which stands for "Invalid Parameter".
The method looks as follows:
void __fastcall TRegistry::GetValueNames(TStrings* Strings)
{
HKEY hKey = m_CurrentKey;
DWORD dwIndex = 0;
CHAR cValueName[TREG_MAX_VALUES_BUF_SIZE] = {};
LPSTR lpValueName = cValueName;
DWORD dwValueBufSize = TREG_MAX_VALUES_BUF_SIZE;
LPDWORD lpcchValueName = &dwValueBufSize;
LPDWORD lpType = NULL;
BYTE cData[TREG_MAX_VALUES_BUF_SIZE] = {};
LPBYTE lpData = cData;
LPDWORD lpcbData = NULL;
long res = RegEnumValueA(hKey, dwIndex, lpValueName, lpcchValueName, NULL, lpType, lpData, lpcbData);
while (res != ERROR_NO_MORE_ITEMS)
{
if (res != ERROR_SUCCESS)
{
throw(Exception(AnsiString(res)));
}
else
{
++dwIndex;
res = RegEnumValueA(hKey, dwIndex, lpValueName, lpcchValueName, NULL, lpType, lpData, lpcbData);
}
}
}
As you can see, all the parameters are set correctly. My suspicion is that the NULL passed after lpcchValueName is causing this problem, since I've seen some people having the same issue after looking it up. Unfortunately, these were problems from years ago and were related to system-specific issues on e.g. Windows NT. The call to this method looks as follows:
int main()
{
TRegistry* treg = new TRegistry; // Create a TRegistry object
if (treg->OpenKey(AnsiString("TRegistryTest"), false)) // Open the TRegistryTest key
{
if (treg->OpenKey(AnsiString("subkey1"), true)) // Open the subkey1 key
{
TStringList ts;
treg->GetValueNames(&ts); // Write the value names into a TStringList
}
}
delete treg;
}
TStringList is essentially a container which stores AnsiString values, which in turn are basically glorified std::strings.
I expected the RegEnumValueA function to exit with code 0 as long as there are registry values left to read - in this case, there are 4 values in total in TRegistryTest/subkey1.
Changing TREG_MAX_VALUES_BUF_SIZE does not influence the result at all - it's currently set to a value of 200.
Your lpcbData parameter, which you have set to NULL is invalid. This should be the address of a DWORD that specifies the size (in bytes) of the buffer pointed to by the lpData parameter (i.e. the size of the cData array).
From the documentation:
[in, out, optional] lpcbData
A pointer to a variable that specifies the size of the buffer pointed
to by the lpData parameter, in bytes. When the function returns, the
variable receives the number of bytes stored in the buffer.
This parameter can be NULL only if lpData is NULL.
Also, note that, on success, the values in the variables pointed to by that lpcbData argument and by lpcchValueName (i.e. dwValueBufSize) will be modified to contain the actual sizes of the data/value returned. So, you should reset those before each call. For lpcchValueName, you would use a line like dwValueBufSize = TREG_MAX_VALUES_BUF_SIZE;, with similar code for the lpcbData target, depending on what you call that variable. (And I'm not sure it's an especially good idea to use the same size variable for both, as your comment seems to suggest.)
If you look at C++Builder 5's source code for its TRegistry::GetValueNames() method, you would see that there are two big differences between its approach versus your approach:
BCB5 first calls RegQueryInfoKeyA() to retrieve the number of values in the key, and the length of the longest value name in the key. It then allocates a buffer of that length, and then runs a for loop calling RegEnumValueA() to retrieve the name for each index. But it ignores the return value, which means it could fail if the Registry key is modified mid-loop.
Whereas you are (rightly so) calling RegEnumValueA() in a while loop until it reaches the end of the list or fails. But, you are using a fixed-length buffer to receive the names. 200 should be OK in practice for most systems, but you should use a dynamically allocated buffer and pay attention to RegEnumValueA()'s return value so you can know when it tells you that it needs additional buffer space that you can then allocate.
BCB5 sets the lpType, lpData, and lpcbData parameters of RegEnumValueA() to NULL, since it does not need those data.
Whereas you are setting the lpData parameter to the address of a local buffer, which is wrong (as other comments/answers have already pointed out), and unnecessary anyway since all you really want is each value's name and not its content.
I have a Function which returns a LPSTR/const char * and I need to convert it to a std::string. This is how I am doing it.
std::string szStr(foo(1));
It works just fine in all the cases just when foo returns a 32 characters long string it fails. With this approach I get "". So I thought it had to do something with the length. So I changed it a bit.
std::string szStr(foo(1) , 32);
This gives me "0"
Then I tried another tedious method
const char * cstr_a = foo(1);
const char * cstr_b = foo(2);
size_t ln_a = strlen(cstr_a);
size_t ln_b = strlen(cstr_b);
std::string szStr_a( cstr_a , ln_a );
std::string szStr_b( cstr_b , ln_b );
But strangely enough in this method both the pointers are getting the same value, viz foo(1) should return abc and foo(2) should return xyz. But here cstr_a is first getting abc but the moment cstr_b gets xyz, the value of both cstr_a and cstr_b becomes xyz. I am dazed and confused with this.
And yes, I cannot use std::wstring.
What is foo?
foo is basically reading a value from the registry and returning it as a LPSTR. Now one the value in the registry which I need to read is a MD5 hashed string (32 charecters) That's where it fails.
The Actual Foo function:
LPCSTR CRegistryOperation::GetRegValue(HKEY hHeadKey, LPCSTR szPath, LPCSTR szValue)
{
HKEY hKey;
CHAR szBuff[255] = ("");
DWORD dwBufSize = 255;
::RegOpenKeyEx(hHeadKey, (LPCSTR)szPath, 0, KEY_READ, &hKey);
::RegQueryValueEx(hKey, (LPCSTR)szValue, NULL, 0, (LPBYTE)szBuff, &dwBufSize);
::RegCloseKey(hKey);
LPCSTR cstr(szBuff);
return cstr;
}
The Original cast code:
StrResultMap RegValues;
std::string lid(CRegistryOperation::GetRegValue(HKEY_CURRENT_USER, REG_KEY_HKCU_PATH, "LicenseID"));
std::string mid(CRegistryOperation::GetRegValue(HKEY_CURRENT_USER, REG_KEY_HKCU_PATH, "MachineID"), 32);
std::string vtill(CRegistryOperation::GetRegValue(HKEY_CURRENT_USER, REG_KEY_HKCU_PATH, "ValidTill"));
std::string adate(CRegistryOperation::GetRegValue(HKEY_CURRENT_USER, REG_KEY_HKCU_PATH, "ActivateDT"));
std::string lupdate(CRegistryOperation::GetRegValue(HKEY_CURRENT_USER, REG_KEY_HKCU_PATH, "LastUpdate"));
RegValues["license_id"] = lid;
RegValues["machine_id"] = mid;
RegValues["valid_till"] = vtill;
RegValues["activation_date"] = adate;
RegValues["last_updated"] = lupdate;
Kindly help me get over it.
Thanks.
As a complement to Nordic Mainframe's anwser, there are 3 common ways to return a buffer from a C or C++ function :
use a static buffer - simple and nice until you have re-entrancy problems (multiple threads or recursivity)
pass the buffer as an input parameter, and simply return the number of characters written to it - ok if the size of buffer is really a constant
malloc the buffer in the function (it is in the heap and not in the stack) and document in flashing red that it must be freed by caller
But as you tagged your question as C++, you could create the std::string in the function and return it. C++ functions are allowed to return std::string because the different operators (copy constructor, affectation, ...) take care automatically of the allocation problem.
You can avoid returning a pointer to a buffer which has gone out of scope by returning a std::string directly.
std::string CRegistryOperation::GetRegValue(HKEY hHeadKey, LPCSTR szPath, LPCSTR szValue)
{
HKEY hKey = 0;
CHAR szBuff[255] = { 0 };
DWORD dwBufSize = sizeof(szBuf);
if (::RegOpenKeyEx(hHeadKey, (LPCSTR)szPath, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
::RegQueryValueEx(hKey, (LPCSTR)szValue, NULL, 0, (LPBYTE)szBuff, &dwBufSize);
::RegCloseKey(hKey);
}
return std::string(szBuf);
}
The GetRegValue function returns a pointer to a buffer in GetRegValue's stack frame. This does not work: after GetRegValue terminates the pointer cstr points to undefined values somewhere in the Stack. Try to make szBuff static and see if that helps.
LPCSTR CRegistryOperation::GetRegValue(HKEY hHeadKey, LPCSTR szPath, LPCSTR szValue)
{
HKEY hKey;
//Here:
static CHAR szBuff[255] = ("");
szBuff[0]=0;
DWORD dwBufSize = 255;
::RegOpenKeyEx(hHeadKey, (LPCSTR)szPath, 0, KEY_READ, &hKey);
::RegQueryValueEx(hKey, (LPCSTR)szValue, NULL, 0, (LPBYTE)szBuff, &dwBufSize);
::RegCloseKey(hKey);
LPCSTR cstr(szBuff);
return cstr;
}
UPDATE: I did not mandate to return std::string, or pass a buffer in, because that would change the interface. Returning a pointer to a static buffer is a common idiom and mostly unproblematic if the lifetime of the returned pointer value is limited to a few scopes (Like for building a std::string from the buffer value).
Multithreading isn't really an issue aynmore, because almost every compiler now has some form of thread local storage support right in the language. __declspec(thread) static CHAR szBuff[255] = (""); for example, should work for Microsoft compilers, __thread for gcc. C++11 even has a new storage class specifier for this (thread_local). You shouldn't call GetRegValue from a signal handler though, but that's OK - you can't do too much there anyway (for example allocate memory from the heap!).
UPDATE: Commenters argue, that I should not suggest this, because the pointer to the static buffer will point to invalid data when GetRegValue is called again. While this is obviously true, I think it is wrong to make an argument from that. Why? Look at these examples:
A pointer returned from strdup() is valid until free()
A pointer to something created with new is valid until deleted.
A const char * returned from string::c_str() is valid as long as the string is not modified.
A std::vector iterator is invalid, if an element from the std::vector is erased.
A std::list iterator is still valid, if an element from the std::vector is erased, unless it points to the erased element.
A pointer returned from GetRegValue is valid until GetRegValue is called again.
a std::ifstream is valid when,..you know, good(), fail() and so on.
There is no point in saying, "look, the thing gets invalid when you are not careful enought", because programming is not about being careless. We are handling objects, which have conditions under which they are valid or not and if an object has well defined conditions under which it is valid or not, then we can write programs with well defined behaviour. Returning a pointer to a static buffer (that is thread-local) has a well defined meaning and a developer can use this to write a well defined program. Unless said developer is negligent or too lazy to read the documentation of the routine of course.
HRESULT UrlCanonicalize(
_In_ PCTSTR pszUrl,
_Out_ PTSTR pszCanonicalized,
_Inout_ DWORD *pcchCanonicalized,
DWORD dwFlags
);
Example:
LPCTSTR pszURL = URL.c_str();
LPSTR pszOutPut = new CHAR[ strUrl.length ];
DWORD* dwCount = new DWORD[ strUrl.length ];
hRes = UrlCanonicalize( pszURL, pszOutPut,dwCount, URL_ESCAPE_UNSAFE );
Output:
E_INVALIDARG
This API fails and returns E_INVALIDARG every time I try to call it. Please give me a working code snippet to call the UrlCanonicalize function.
If you know the C++ language, the SDK documentation for the function pretty much tells you everything that you need to know:
You pass it a C-style nul-terminated string that contains your URL.
You pass it pointer to a buffer to receive the output string.
You pass it one or more flags that customize the function's behavior.
And finally, it returns to you an HRESULT value, which is an error code. If it succeeds, that value will be S_OK. If it fails, it will be some other error code.
It works like this:
std::wstring originalURL(L"http://www.example.com/hello/cruel/../world/");
// Allocate a buffer of the appropriate length.
// It needs to be at least as long as the input string.
std::wstring canonicalURL(originalURL.length() + 1, L'\0');
DWORD length = originalURL.length() + 1;
// Call the function to modify the string.
HRESULT hr = UrlCanonicalize(originalURL.c_str(), // input string
&canonicalURL[0], // buffer
&length, // pointer to a DWORD that contains the length of the buffer
URL_UNESCAPE | URL_ESCAPE_UNSAFE);
if (SUCCEEDED(hr))
{
// The function succeeded.
// Your canonicalized URL is in the canonicalURL string.
MessageBox(nullptr, canonicalURL.c_str(), L"The URL is:", MB_OK);
}
else
{
// The function failed.
// The hr variable contains the error code.
throw std::runtime_error("The UrlCanonicalize function failed.");
}
If you want to make sure that the buffer is sufficiently long (and avoid having to handle that error), use the constant INTERNET_MAX_URL_LENGTH (declared in WinInet.h) when allocating it:
std::wstring canonicalURL(INTERNET_MAX_URL_LENGTH, L'\0');
DWORD length = INTERNET_MAX_URL_LENGTH;
The code you tried has a couple of problems:
You've incorrectly initialized the dwCount variable. The function wants a pointer, but that doesn't mean you should declare the variable as a pointer. Nor do you want an array; this is a single DWORD value. So you need to declare it as a regular DWORD, and then use the address-of operator (&) to pass the function a pointer to that variable. Right now, you're passing the function garbage, so it's failing.
You're using C-style strings, which you should avoid in C++ code. Use the C++ string class (std::wstring for Windows code), which is exception safe and manages memory for you. As you already know, the c_str() member function gives you easy access to a C-style nul-terminated string like all C APIs want. This works fine, you do not need to use raw character arrays yourself. Avoid new whenever possible.
Potentially, a third problem is that you're trying to use the C++ string type std::string instead of std::wstring. The former is an 8-bit string type and doesn't support Unicode in a Windows environment. You want std::wstring, which is a wide string with Unicode support. It's what all the Windows API functions expect if you have the UNICODE symbol defined for your project (which it is by default).
Here you go:
LPCTSTR pszURL = URL.c_str();
DWORD nOutputLength = strUrl.length * 2 + 32;
LPTSTR pszOutPut = new TCHAR[nOutputLength];
hRes = UrlCanonicalize( pszURL, pszOutPut, &nOutputLength, URL_ESCAPE_UNSAFE);
On the third parameter you provided garbage instead of pointer to initialized value, so you had API failure back. MSDN has it all for you:
A pointer to a value that, on entry, is set to the number of characters in the pszCanonicalized buffer.
I think I may be missing something really obvious here, but I have been struggling for way too long on this and my C++ is way rusty (10yrs+)
The code below works fine, but I need to be able to pass a variable into the query for lname. If I build the query in a string or char array I get an error that it is not compatible with parameter type of SQLWCHAR*
I know the code below is vulnerable to sql injection, but this is a one time hit on an isolated system, so I am really looking for simplicity more than anything else...
SQLHENV env;
SQLHDBC dbc;
SQLHSTMT sql_hStmt;
SQLRETURN ret;
SQLWCHAR outstr[1024];
SQLSMALLINT outstrlen;
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
ret = SQLDriverConnect(dbc, NULL, L"DSN=myDSN", SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &sql_hStmt);
SQLWCHAR* SQL = L"select * from DB.employees where lname='Smith'";
ret = SQLExecDirect(sql_hStmt, SQL, SQL_NTS);
SQLFetch(sql_hStmt);
There are two problems here, one is constructing a string containing the query you want, the other is passing that string as an argument to the function.
My recommendation is to stay as "C++" as possible until you reach these C boundaries. So we should use std::wstring for the string processing up until the point where it needs to be a C-style string:
std::wstring statementText = L"select * from DB.employees where lname='Smith'";
ret = SQLExecDirect(sql_hStmt, const_cast<SQLWCHAR*>(statementText.c_str()), SQL_NTS);
The c_str() member function returns a pointer to a null-terminated array (i.e., a C-style string), but this pointer has the type const wchar_t*; that is, the contents of this C-style string cannot be modified.
This is a problem because SQLWCHAR* is just wchar_t*; it doesn't make any promise to leave the data alone. That is why I included the const_cast, to remove the const from the c_str() value.
This is not something you generally want to do. const_cast is arguably the scariest cast because you directly open up the door to undefined behavior, as it's UB to modify a const object:
const int x = 0;
const int* p = &x; // anyone using this pointer can't modify x
int* bad = const_cast<int*>(p); // but this one is not so good
*bad = 5; // undefined behavior
The reason it's okay here, though, is that SQLExecDirect doesn't actually modify the string it's passed; it's simply an implementation error that const isn't used, so us taking that away is okay. (This lack of const mistake is very common in C.)
If you really need a buffer that can be modified, then starting in the current version of C++ (C++11) you can do this safely:
std::wstring statementText = L"select * from DB.employees where lname='Smith'";
ret = SQLExecDirect(sql_hStmt, &statementText[0], SQL_NTS);
We're taking the address of the first element, which itself is in a null-terminated array; another C-style string. This time, though, we have a modifiable array; the type already matches.
(The reason I make note this is okay in C++11 is that technically in the previous version, C++03, this behavior wasn't guaranteed. It was actually intended to be, but an error in wording in the standard made it not so. To be practical, you're fine either way.)
Whichever one you want to use is up to you. Some will argue to just use &str[0] all the time so we definitely have no UB, I would argue to document your intent and belief that the function doesn't modify the string and cast away const but ultimately operate in a const mindset. If something bad happens it's easy to relax away from const than it is to wish you had put it on.
One important thing to note is that all these returned pointers (either str.c_str() or &str[0]) are only good as long as the str object itself is alive and not modified. This is bad:
const wchar_t* get_query()
{
std::wstring result = /* build query */;
// oops, this pointer stops being meaningful when result stops existing!
return result.c_str();
}
With that all out of the way, building up these strings is easy. We have std::wstringstream:
std::wstringstream ss;
ss << "this is basically an expanding buffer that accepts anything std::wcout will";
ss << std::endl;
ss << "this includes integers " << 5 << " and other stream-insertable types";
So you probably want something like this:
std::wstring build_query(const std::wstring& name)
{
// you can provide a starting string
std::wstringstream result(L"select * from DB.employees where lname=");
result << "\'" << name << "\'";
return result.str(); // this captures the buffer as a C++ string
}
// Remember, this would be bad!
//
// SQLWCHAR* SQL = const_cast<SQLWCHAR*>(build_query(L"Smith").c_str());
//
// Because the C++ string returned by build_query is temporary;
// it stops existing at the end of this full expression,
// so SQL would be a bad pointer. This is right:
std::wstring SQL = build_query(L"Smith");
ret = SQLExecDirect(sql_hStmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS);
Hope that helps.
Also, I would avoid using all-upper identifiers except for macros, because such names are overwhelmingly expected to be macros by people reading C++ code. Additionally, I've used C++-style casts in my example code; you should do the same. The C-style cast ((type)value) is simply too powerful to be safe.
I suggest you PREPARE a paramererized query string. See here
If you simply concatenate strings to build a new query each time, you may be leaving your site open to an SQL injection attack
You can simply do it by :
You can do it by
int var = 10;
string str = to_string(var);
string requete="INSERT INTO stat(temps) VALUES (\"";
requete += str;
requete += "\")";
mysql_query(&mysql,requete.c_str());
just specify in mySql that the field has a type of int , double , float etc.
Thank you to The Beast. If you are recording two or more data, it can be used as follows.
int state;
string mesafe = to_string(getdata1);
string aci = to_string(getdata2);
string query = "INSERT INTO tarama (mesafe,aci) VALUES (\"";
query += mesafe;
query += "\",\"";
query += aci;
query += "\")";
qstate = mysql_query(conn, query.c_str());
I couldn't get over this problem for days. Now the problem is solved.
I am trying to learn a little c++ and I have a silly question. Consider this code:
TCHAR tempPath[255];
GetTempPath(255, tempPath);
Why does windows need the size of the var tempPath? I see that the GetTempPath is declared something like:
GetTempPath(dword size, buf LPTSTR);
How can windows change the buf value without the & operator? Should not the function be like that?
GetTempPath(buf &LPTSTR);
Can somebody provide a simple GetTempPath implementation sample so I can see how size is used?
EDIT:
Thanks for all your answers, they are all correct and I gave you all +1. But what I meant by "Can somebody provide a simple GetTempPath implementation) is that i have tried to code a function similar to the one windows uses, as follow:
void MyGetTempPath(int size, char* buf)
{
buf = "C:\\test\\";
}
int main(int argc, char *argv[])
{
char* tempPath = new TCHAR[255];
GetTempPathA(255, tempPath);
MessageBoxA(0, tempPath, "test", MB_OK);
return EXIT_SUCCESS;
}
But it does not work. MessageBox displays a "##$' string. How should MyGetTempPath be coded to work properly?
Windows needs the size as a safety precaution. It could crash the application if it copies characters past the end of the buffer. When you supply the length, it can prevent that.
Array variables work like pointers. They point to the data in the array. So there is no need for the & operator.
Not sure what kind of example you are looking for. Like I said, it just needs to verify it doesn't write more characters than there's room for.
An array cannot be passed into functions by-value. Instead, it's converted to a pointer to the first element, and that's passed to the function. Having a (non-const) pointer to data allows modification:
void foo(int* i)
{
if (i) (don't dereference null)
*i = 5; // dereference pointer, modify int
}
Likewise, the function now has a pointer to a TCHAR it can write to. It takes the size, then, so it knows exactly how many TCHAR's exist after that initial one. Otherwise it wouldn't know how large the array is.
GetTempPath() outputs into your "tempPath" character array. If you don't tell it how much space there is allocated in the array (255), it has no way of knowing whether or not it will have enough room to write the path string into tempPath.
Character arrays in C/C++ are pretty much just pointers to locations in memory. They don't contain other information about themselves, like instances of C++ or Java classes might. The meat and potatoes of the Windows API was designed before C++ really had much inertia, I think, so you'll often have to use older C style techniques and built-in data types to work with it.
Following wrapper can be tried, if you want to avoid the size:
template<typename CHAR_TYPE, unsigned int SIZE>
void MyGetTempPath (CHAR_TYPE (&array)[SIZE]) // 'return' value can be your choice
{
GetTempPath(SIZE, array);
}
Now you can use like below:
TCHAR tempPath[255];
MyGetTempPath(tempPath); // No need to pass size, it will count automatically
In your other question, why we do NOT use following:
GetTempPath(buf &LPTSTR);
is because, & is used when you want to pass a data type by reference (not address). I am not aware what buf is typecasted to but it should be some pointer type.
Can somebody provide a simple
GetTempPath implementation sample so I
can see how size is used?
First way (based on MAX_PATH constant):
TCHAR szPath[MAX_PATH];
GetTempPath(MAX_PATH, szPath);
Second way (based on GetTempPath description):
DWORD size;
LPTSTR lpszPath;
size = GetTempPath(0, NULL);
lpszPath = new TCHAR[size];
GetTempPath(size, lpszPath);
/* some code here */
delete[] lpszPath;
How can windows change the buf value without the & operator?
& operator is not needed because array name is the pointer to first array element (or to all array). Try next code to demonstrate this:
TCHAR sz[1];
if ((void*)sz == (void*)&sz) _tprintf(TEXT("sz equals to &sz \n"));
if ((void*)sz == (void*)&(sz[0])) _tprintf(TEXT("sz equals to &(sz[0]) \n"));
As requested, a very simple implementation.
bool MyGetTempPath(size_t size, char* buf)
{
const char* path = "C:\\test\\";
size_t len = strlen(path);
if(buf == NULL)
return false;
if(size < len + 1)
return false;
strncpy(buf, path, size);
return true;
}
An example call to the new function:
char buffer[256];
bool success = MyGetTempPath(256, buffer);
from http://msdn.microsoft.com/en-us/library/aa364992(v=vs.85).aspx
DWORD WINAPI GetTempPath(
__in DWORD nBufferLength,
__out LPTSTR lpBuffer
);
so GetTempPath is defined something like
GetTempPath(DWORD nBufferLength, LPTSTR& lpBuffer);
What mean, that compiler passes the value lpBuffer by referenece.