I'm using getenv("TEMP"), but I'm getting a warning telling me to use _dupenv_s.
I can't find an example of _dupenv_s on the net.
The docs read:
errno_t _dupenv_s(
char **buffer,
size_t *numberOfElements,
const char *varname
);
But what buffer are they referring to? I only have varname. Wouldn't it be better to avoid using a buffer?
_dupenv_s is a Microsoft function, designed as a more secure form of getenv.
_dupenv_s allocates the buffer itself; you have to pass it a pointer to a pointer and it sets this to the address of the newly allocated buffer.
For example,
char* buf = nullptr;
size_t sz = 0;
if (_dupenv_s(&buf, &sz, "EnvVarName") == 0 && buf != nullptr)
{
printf("EnvVarName = %s\n", buf);
free(buf);
}
Note that you're responsible for freeing the returned buffer.
Related
Someday. I wrote Socket wrapper class like Python built-in socket library for C++ (Visual Studio 2015)
All functions are working very well. However, I found some functions I wrotten has a problem.
After using std::string's method like .size(), .clear(), WSAGetLastError() always return zero.
std::string str = "Hello, world";
size_t recvsz = ::send(socket, str.c_str(), str.size(), 0);
// Why always return zero after using std::string's methods?
int err = ::WSAGetLastError();
Therfore, I can't know socket states, socket is dead, or socket is still waiting for receiving data.
So, I used a method storing WSAGetLastError() returning value before std::string's methods and then restoring it using WSASetLastError().
std::string str = "Hello, world";
// Before using std::string's methods
int err = ::WSAGetLastError();
size_t recvsz = ::send(socket, str.c_str(), str.size(), 0);
// Restore error value after std::string's methods
::WSASetLastError(err);
Is it correct way I used? or why std::string makes last error returing zero?
edited: wow... I mistake.. :(. I use ::send not ::recv. sorry for confusing you
SOCKET socket;
char buf[100] = {0x00,};
std::string data, temp_buf;
ssize_t recvsz = ::recv(socket, buf, 99, 0);
// When err has proper error code before std::string methods
// It just for a test
// int err = ::WSAGetLastError();
temp_buf = buf;
data += buf;
int err = WSAGetLastError(); // Always return zero!
Because you committed a gross indiscretion by writing into read-only memory that you don't own. In short, you blatted your computer's memory and now your program won't work predictably.
With a compliant C++11 compiler, you could do:
ssize_t recvsz = ::recv(socket, &str[0], str.size(), 0);
That's because &str[0] is a pointer to the actual string buffer (in a non-const context), and because from C++11 that data is guaranteed to be stored
contiguously. Technically, before then, not so much (though in practice it basically always is).
Otherwise you're going to have to allocate a little char[] buffer of your own, into which to receive data. You can just give it automatic storage duration with practically no cost, unless you're on a embedded system with tight resource constraints (but then you wouldn't be using string anyway).
By the way, be careful of your return types: size_t is unsigned. recv returns a ssize_t which is signed and may be negative to indicate an error condition.
const size_t BUF_SIZE = 256;
char buf[BUF_SIZE];
ssize_t recvsz = ::recv(socket, &buf[0], BUF_SIZE, 0);
if (recvsz < 0) {
// ... handling
}
// Now, optionally:
std::string str(buf, recvsz);
Now, with all that fixed, the answer is that the "last error" is permitted to be changed even by a successful operation. So, yes, you'll have to save then re-set a previous "last error" value if you want it to be retained. This does seem to be of dubious value — why do you not handle the error where it is generated? Carrying on after causing an error seems like a bad way to do business.
You should not write to the internal buffer of std::string this way. It is managed by the object and you must use functions like append() or assign() to update it, so the string can track its current content and length.
Moreover, c_str() returns const char* pointer and you cannot write to such memory.
If you are sure, that you will always write the correct amount of bytes (so the size data written and the actual length of data stored internally will match), you can use operator[] to get the reference to a particular character inside the string (presumably, the first one in your case) and use its address as the target buffer.
As #AndyG noticed in his comment, C++ 17 introduced std::basic_string::data() which also returns pointer to the actual buffer.
Your code should take one of the following forms:
size_t recvsz = ::recv(socket, &str[0], str.size(), 0);
size_t recvsz = ::recv(socket, str.data(), str.size(), 0); // C++ 17, str must not be const
It is well defined C++ to pass std::vector to C APIs that expect an output array like so because std::vector is contiguous:
std::vector<char> myArray(arraySize);
cStyleAPI(&myArray[0], arraySize);
Is it safe to pass std::string in the same manner to C APIs? Is there any guarantee in standard C++03 that std::string is contiguous and works in the same way as std::vector does in this situation?
If the C API function requires read-only access to the contents of the std::string then use the std::string::c_str() member function to pass the string. This is guaranteed to be a null terminated string.
If you intend to use the std::string as an out parameter, C++03 doesn't guarantee that the stored string is contiguous in memory, but C++11 does. With the latter it is OK to modify the string via operator[] as long as you don't modify the terminating NUL character.
So, I know this has been answered already, but I saw your comment in Praetorian's answer:
It's an OpenGL driver bug that results in the return value for the
maximum length string being broken. See
https://forums.geforce.com/default/topic/531732/glgetactiveattrib-invalid/.
glGetActiveAttrib won't try to write to the pointer returned by the
new[] call with the 0 size allocation, but the string isn't null
terminated. Then, later in the code, the non-null terminated string is
copied into a std::string for storage, which results in a read buffer
overflow. Very confusing to me too, and just checking here to see if
std::string would make things easier to follow.
Uhm... forgive me but if this is your problem then all these solutions seem to be overly complicated. If the problem boils down to the fact that you get a 0 as the buffer size that you need (which means that you will end up with a string that's not NULL-terminated, since there's no space for the NULL terminator) then simply make sure a NULL terminator is always present:
int arraySize;
/* assume arraySize is set to the length we need */
...
/* overallocate by 1 to add an explicit NULL terminator. Just in case. */
char *ptr = malloc(arraySize + 1);
if(ptr != NULL)
{
/* zero out the chunk of memory we got */
memset(ptr, 0, arraySize + 1);
/* call OpenGL function */
cStyleAPI(ptr, arraySize);
/* behold, even if arraySize is 0 because of OpenGL, ptr is still NULL-terminated */
assert(ptr[arraySize] == 0);
/* use it */
...
/* lose it */
free(ptr);
}
This seems, to me, to be the simplest, sanest solution.
Yes, but you'll need to pass them using the c_str method to guarantee null-termination.
No, it isn't, but generally because C strings are assumed to be zero-terminated, which your pointer-to-char isn't. (If you use string, you can use string::c_str() instead, that's 0-terminated.)
Anyways, C++11 does require the elements of vector to be contiguous in memory.
cStyleAPI(&myArray[0], arraySize);
If your cStyleAPI receives a char* for input, that is what std::string::c_str() is for.
If it receives a pre-allocated char* for output, then no. In that case, you should use a std::vector<char> or std::array<char>.
use resize_and_overwrite
https://en.cppreference.com/w/cpp/string/basic_string/resize_and_overwrite
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1072r10.html
[[nodiscard]] static inline string formaterr (DWORD errcode) {
string strerr;
strerr.resize_and_overwrite(2048, [errcode](char* buf, size_t buflen) {
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage
return FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
errcode,
0,
buf,
static_cast<DWORD>(buflen),
nullptr
);
});
return strerr;
}
Consider the following function:
unique_ptr<char> f(const wstring key, const unsigned int length)
{
assert(length <= key.length());
const wstring suffix = key.substr(length, key.length() - length);
const size_t outputSize = suffix.length() + 1; // +1 for null terminator
char * output = new char[outputSize];
size_t charsConverted = 0;
const wchar_t * outputWide = suffix.c_str();
wcstombs_s(&charsConverted, output, outputSize, outputWide, suffix.length());
return unique_ptr<char>(output);
}
The intent here is to accept a wstring, select length characters from the end, and return them as a C-style string that's wrapped in a unique_ptr (as required by another library - I certainly didn't chose that type :)).
One of my peers said in passing that he thinks this leaks memory, but he didn't have time to elaborate, and I don't see it. Can anybody spot it, and if so explain how I ought to fix it? I probably have my blinders on.
It's not necessarily a leak, but it is undefined behavior. You created the char array using new[] but the unique_ptr<char> will call delete, and not delete[] to free the memory. Use unique_ptr<char[]> instead.
Also, your conversion may not always behave the way you want it to. You should make 2 calls to wcstombs_s, in the first one pass nullptr as the second argument. This will return the number of characters required in the output string.
wcstombs_s(&charsConverted, nullptr, 0, outputWide, suffix.length());
Check the return value, and then use the result stored in charsConverted to allocate the output buffer.
auto output = std::unique_ptr<char[]>(new char[charsConverted]);
// now use output.get() to get access to the raw pointer
Can't figure out what's wrong, I don't seem to be getting anything from fread.
port.h
#pragma once
#ifndef _PORT_
#define _PORT_
#include <string>
#ifndef UNICODE
typedef char chr;
typedef string str;
#else
typedef wchar_t chr;
typedef std::wstring str;
inline void fopen(FILE ** ptrFile, const wchar_t * _Filename,const wchar_t * _Mode)
{
_wfopen_s(ptrFile,_Filename,_Mode);
}
#endif
#endif
inside main()
File * f = new File(fname,FileOpenMode::Read);
chr *buffer;
buffer = (wchar_t*)malloc(f->_length*2);
for(int i=0;i<f->_length;i++)
{
buffer[i] = 0;
}
f->Read_Whole_File(buffer);
f->Close();
for(int i=0;i<f->_length;i++)
{
printf("%S",buffer[i]);
}
free(buffer);
inside file class
void Read_Whole_File(chr *&buffer)
{
//buffer = (char*)malloc(_length);
if(buffer == NULL)
{
_IsError = true;
return;
}
fseek(_file_pointer, 0, SEEK_SET);
int a = sizeof(chr);
fread(&buffer,_length ,sizeof(chr) , _file_pointer);
}
You're mixing pointers and references all over the place.
Your function only needs to take a pointer to the buffer:
void Read_Whole_File(char *buffer) { ... }
And you should pass that pointer as-is to fread(), don't take the address of the pointer:
size_t amount_read = fread(buffer, _length, sizeof *buffer, _file_pointer);
Also remember:
If you have a pointer ptr to some type, you can use sizeof *ptr and remove the need to repeat the type name.
If you know the length of the file already, pass it to the function so you don't need to figure it out twice.
In C, don't cast the return value of malloc().
Check for errors when doing memory allocation and I/O, things can fail.
buffer is a reference to a chr *. Yet you're reading into &buffer which is a chr ** (whatever that is). Wrong.
You don't even need to pass a reference to buffer in Read_Whole_File, just use a regular pointer.
aside from your original problem...
from your code:
typedef char chr;
chr *buffer;
buffer = (wchar_t*)malloc(f->_length*2);
for(int i=0;i<f->_length;i++)
{
buffer[i] = 0;
}
don't you think there is something wrong here ? in case you cannot spot the errors, here is the list:
chr is a char, so buffer is a char *
you are using malloc. are you coding in C or in C++ ? if it is C++, consider using new
the buffer you allocate is explicitly casted to a wchar_t * but buffer is a char *
in the malloc you are allocating a block of size length*2 when you should be using length * sizeof(w_char_t). don't make any assumption on the size of a type (and even writing sizeof(char) is no problem, it renders the intentions explicit)
the for loop goes from 0 to length, but since buffer is defined as a buffer of char, only length bytes are initialized, whereas you alocated length*2 bytes, so half your buffer is still uninitialized.
memset() has been defined to avoid this kind of for loop...
please be a little bit careful when coding !
First a couple of nits:
What's with the malloc and free? What's wrong with new and delete? You are obviously writing C++ here, so write C++.
Overloading fopen, fseek, fread in the case of wchar_t bothers me, immensely. Much better: Use templates, or define your own functions. Don't overload those names that belong to C.
This is not not a nit. The following is almost certainly not doing what you want:
fread(&buffer,_length ,sizeof(chr) , _file_pointer);
chr here is a pointer to a reference, so sizeof is almost certainly going to be either 32 or 64.
I am trying to allocate a char array of size 1000. This array
is passed to a function where it should be filled with the data
that has been received from the TCP Socket. The problem occurs
then when I try to delete[] buffer: Here I get as a result a User
Panic 42. Unfortunately, I do not really see what is going wrong
in this simple code fragement...
int main
{
unsigned char *buffer = new unsigned char[1000];
Recv(&buffer);
delete[] buffer;
return (0);
}
void Recv(unsigned char **buffer)
{
TRequestStatus iStatus;
TSockXfrLength len;
TBuf8<1000> buff;
iSocket.RecvOneOrMore( buff, 0, iStatus, len );
User::WaitForRequest(iStatus);
*buffer = ( unsigned char* )buff.Ptr();
}
Thanks for any useful hints!
What Konrad says is true, but I don't think he knows Symbian. If you do need a function to read bytes into a char buffer, then a better fix would be:
void Recv(unsigned char *aBuffer, int aSize)
{
TRequestStatus iStatus;
TSockXfrLength len;
TPtr8 buff(aBuffer, aSize);
iSocket.RecvOneOrMore( buff, 0, iStatus, len );
User::WaitForRequest(iStatus);
}
TBuf8 is a descriptor which contains an array to hold the data. TPtr8 is a descriptor which refers to an external buffer that you specify. Either can be passed into RecvOneOrMore, since they both inherit from the parameter type, TDes8&. So the socket can be made to write its data directly into your buffer, instead of writing into a buffer on the stack and then copying as Konrad's code does.
You probably also need to check the status to detect errors, and report success/failure and the length written back to the caller.
Your array allocation is without effect because inside the function, you assign a new pointer to the array:
*buffer = ( unsigned char* )buff.Ptr();
Now the buffer points to another memory location, presumably one that you may not free using delete (e.g. one on the stack, or allocated using something other than new).
To fix the problem, it's probably best to copy the data to your array:
void Recv(unsigned char *buffer)
{
TRequestStatus iStatus;
TSockXfrLength len;
TBuf8<1000> buff;
iSocket.RecvOneOrMore( buff, 0, iStatus, len );
User::WaitForRequest(iStatus);
unsigned char* const tmpbuf = static_cast<char*>(buff.Ptr());
std::copy(tmpbuf, tmpbuf + len, buffer);
}
Notice that the buffer pointer is now passed directly to the function Recv, no further indirection needed since we don't manipulate the pointer directly.
Um, you're trying to delete[] something that's not allocated by you. You're delete[]ing buff.Ptr() while leaking the array allocated in main().