This is working for me...
std::string GetProgramDataPath() {
CHAR path[MAX_PATH];
HRESULT hr = SHGetFolderPathA(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path); // path accepted as LPSTR parameter?
if (SUCCEEDED(hr)) {
return std::string(path); // then automatically cast to const char*?
}
else {
return std::string();
}
}
...but I don't know why. I try to pass LPSTR, but I get:
Error C4700 "uninitialized local variable 'path' used"
I look up how to initialize LPSTR and come up with this:
std::string GetProgramDataPath() {
LPSTR path = new CHAR[MAX_PATH];
HRESULT hr = SHGetFolderPathA(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path);
if (SUCCEEDED(hr)) {
std::string strPath(path);
delete[] path;
return std::string(strPath);
}
else {
delete[] path;
return std::string();
}
}
Is this the 'correct' code? With new and delete it seems wrong. Am I doing something unsafe by just using CHAR[]? How come it works instead of LPSTR? I believe it has something to do with the "equivalence of pointers and arrays" in C, but it seems there are some automatic conversions from CHAR[] to LPSTR to const char * in this code I don't understand.
Instead of managing the memory your self with new and delete I'd use a std::string instead and let it manage the memory.
static std::string GetProgramDataPath()
{
std::string buffer(MAX_PATH, '\0');
const HRESULT result = SHGetFolderPathA
(
nullptr,
CSIDL_COMMON_APPDATA,
nullptr,
0,
buffer.data()
);
if (SUCCEEDED(result))
{
// Cut off the trailing null terminating characters.
// Doing this will allow you to append to the string
// in the position that you'd expect.
if (const auto pos{ buffer.find_first_of('\0') }; pos != std::string::npos)
buffer.resize(pos);
// Here is how you can append to the string further.
buffer.append(R"(\Some\Other\Directory)");
return buffer;
}
buffer.clear();
return buffer;
}
Here is one way you could do it using std::filesystem::path and SHGetKnownFolderPath.
namespace fs = std::filesystem;
static fs::path GetProgramDataPath()
{
struct buffer {
wchar_t* data{ nullptr };
~buffer() { CoTaskMemFree(data); }
} buf{};
const HRESULT result = SHGetKnownFolderPath
(
FOLDERID_ProgramData,
0,
nullptr,
&buf.data
);
return SUCCEEDED(result)
? fs::path{ buf.data }
: fs::path{};
}
int main()
{
fs::path path{ GetProgramDataPath() };
if (!path.empty())
{
// Here is one way you can append to a path.
// You can also use the append member function as well.
path /= R"(Some\Other\Directory)";
// When you're ready you can call either the generic_string or
// string member function on the path.
const std::string s1{ path.string() };
const std::string s2{ path.generic_string() };
// Prints: 'C:\ProgramData\Some\Other\Directory'.
std::cout << s1 << '\n';
// Prints: 'C:/ProgramData/Some/Other/Directory'.
std::cout << s2 << '\n';
}
}
This is working for me...but I don't know why.
LPSTR is just an alias for CHAR* (aka char*):
typedef CHAR *LPSTR;
In certain contexts, a fixed-sized CHAR[] (aka char[]) array will decay into a CHAR* (aka char*) pointer to its 1st element, such as when passing the array by value in a function parameter, as you are doing.
I try to pass LPSTR, but I get Error C4700 "uninitialized local variable 'path' used".
Because LPSTR is just a pointer, and you likely did not point it at anything meaningful.
Is this the 'correct' code?
Technically yes, that will work (though return std::string(strPath) should be return strPath instead). However, you should consider using std::string or std::vector<char> instead to manage memory for you, don't use new[]/delete[] directly, eg:
std::string GetProgramDataPath() {
std::vector<char> path(MAX_PATH);
HRESULT hr = SHGetFolderPathA(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path.data());
if (SUCCEEDED(hr)) {
return std::string(path.data());
}
return std::string();
}
Am I doing something unsafe by just using CHAR[]?
No.
How come it works instead of LPSTR?
Because CHAR[] decays into the same type that LPSTR is an alias of.
it seems there are some automatic conversions from CHAR[] to LPSTR to const char * in this code.
Correct.
Related
in uPyCraft IDE or Putty, just sending km.press('a') then it works fine,
but in my C++, i tried to writefile with km.press('a'), it doesn't work.
i can't find what is wrong
uPyCraft Successfull
`bool CSerialPort::OpenPort(CString portname)
{
m_hComm = CreateFile(L"//./" + portname,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if (m_hComm == INVALID_HANDLE_VALUE)
{
std::cout << "INVALID HANDLE" << std::endl;
return false;
}
else
return true;
}
bool CSerialPort::WriteByte(const char * bybyte)
{
byte iBytesWritten = 0;
if (WriteFile(m_hComm, &bybyte, 1, &m_iBytesWritten, NULL) == 0)
return false;
else
return true;
}
int main()
{
CSerialPort _serial;
_serial.OpenPort(L"COM4");
_serial.WriteByte("km.press('a')");
}`
i tried this,
but it doesn't work, i also check _serial Isn't INVALID HANDLE.
someone help me for sending "km.press('a')" to serial
and sending km.move(0,1) with using Putty and uPyCraft,
it works fine but
string test = "km.move(0,1)";
DWORD dwBytesWritten;
WriteFile(m_hComm,&test,sizeof(test),dwBytesWritten,NULL);
it doesn't work. just changing km.move(0,1) to km.move(0,10), then i don't know why but it works fine.
what is different with uPyCraft(Putty) and C++?
By the looks of it, I'm assuming your class definition looks something like this:
class CSerialPort {
public:
bool OpenPort(CString portname);
bool WriteByte(const char* bybyte);
private:
HANDLE m_hComm;
byte m_iBytesWritten;
};
byte is not the proper type. DWORD is.
CString may be used, but you are using wide string literals anyway so you could just use CreateFileW, std::wstrings and std::wstring_views.
WriteByte implies that you only want to write one byte - and indeed, your implementation does only write one byte - but it's the wrong byte. It writes one byte out of the memory of the bybyte variable, not the memory it points at.
A minor redefinition of the class:
#include <string_view> // added header
class CSerialPort {
public:
// take a `std::wstring` instead
bool OpenPort(const std::wstring& portname);
// WriteBytes instead of WriteByte:
bool WriteBytes(const void* bytesPtr, DWORD bytesToWrite);
// write both wide and non-wide string_views
bool WriteString(std::string_view str);
bool WriteString(std::wstring_view str);
private:
HANDLE m_hComm;
DWORD m_iBytesWritten; // the proper type
};
The implementation in the .cpp file then becomes:
bool CSerialPort::OpenPort(const std::wstring& portname) {
// Use CreateFileW since you've hardcoded wide string literals anyway:
m_hComm = CreateFileW((L"//./" + portname).c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
return m_hComm != INVALID_HANDLE_VALUE;
}
bool CSerialPort::WriteBytes(const void* bytesPtr, DWORD bytesToWrite)
{
return
WriteFile(m_hComm, bytesPtr, bytesToWrite, &m_iBytesWritten, nullptr) != 0;
}
// the WriteString overloads taking string_views pass on the pointer
// and length to `WriteBytes`:
bool CSerialPort::WriteString(std::string_view str) {
return WriteBytes(str.data(), str.size());
}
bool CSerialPort::WriteString(std::wstring_view str) {
return WriteBytes(str.data(), str.size() * // wchar_t's are more than 1 byte:
sizeof(std::wstring_view::value_type));
}
And your main would then use the WriteString overload taking a std::string_view (by passing a const char* to WriteString):
int main()
{
CSerialPort _serial;
if(_serial.OpenPort(L"COM4")) {
_serial.WriteString("km.press('a')");
} else {
std::cerr << "failed opening COM4\n";
}
}
Note: The section you added at the end has several errors:
string test = "km.move(0,1)";
DWORD dwBytesWritten;
WriteFile(m_hComm,&test,sizeof(test),dwBytesWritten,NULL);
&test takes the address of the std::string object. You should use test.c_str() to get a const char* to the first character in the string.
sizeof(test) gets the size of the std::string object, not the length of the actual string. You should use test.size() instead.
dwBytesWritten is passed by value but the function expects a pointer to a DWORD that it can write to. You should use &dwBytesWritten instead.
WriteFile(m_hComm, test.c_str(), test.size(), &dwBytesWritten, NULL);
The call to ImGui::InputText() takes a char array which I need to initialise from a std::string and then transfer the contents back to the std::string. In it's simplest form:
char buf[255]{};
std::string s{"foo"};
void fn() {
strncpy( buf, s.c_str(), sizeof(buf)-1 );
ImGui::InputText( "Text", buf, sizeof(buf) );
s=buf;
}
However, it appears wasteful to have two buffers (buf and the buffer allocated within std::string) both doing much the same thing. Can I avoid the buf buffer and the copying to and from it by using just the std::string and a simple wrapper "X".
I don't care about efficiency, I just want the simplest code at the call site.
This code does work but is it safe and is there a better way?
class X {
public:
X(std::string& s) : s_{s} { s.resize(len_); }
~X() { s_.resize(strlen(s_.c_str())); }
operator char*(){ return s_.data(); }
static constexpr auto len() { return len_-1; }
private:
std::string& s_;
static constexpr auto len_=255;
};
std::string s{"foo"};
void fn() {
ImGui::InputText( "Text", X(s), X::len() );
}
If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp.
misc/cpp/imgui_stdlib.h
namespace ImGui
{
// ImGui::InputText() with std::string
// Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
}
Your first code
std::string s{"foo"};
void fn() {
ImGui::InputText( "Text", &s );
}
Reading manuals works wonders.
I do have a simple test-case here (C++) which does LoadLibrary, GetProcAddress & calls the function (ProcAdd).
The signature is "char* ProcAdd(char*)".
Now I do get the string correctly in the executable, but once I do "FreeLibrary", it's gone... (obviously because I just did a return "hello").
The thing is, that I have another dll (.NET, C#) where the signature is "[return: MarshalAs LPSTR]string ProcAdd([MarshalAs LPSTR] string)".
Now this function ALSO returns a string, but when I do "FreeLibrary", the string is still accessible within my executable?!
How does that come, and how could I mimic the same behaviour?
(and yes, I know I can store it in another variable, but I would like to understand what is happening and how I can reproduce this .NET behaviour).
Thanks a lot!
As requested the code:
C++ exe:
int main( void )
{
HINSTANCE hinstLib;
MYPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
void * val = NULL;
// Get a handle to the DLL module.
// hinstLib = LoadLibrary(TEXT("C:\\Users\\steven\\temp\\myMyMy.orig.dll"));
hinstLib = LoadLibrary(TEXT("C:\\Users\\steven\\temp\\myMyMy.proxy.dll"));
// If the handle is valid, try to get the function address.
if (hinstLib != NULL)
{
ProcAdd = (MYPROC) GetProcAddress(hinstLib, "ProcAdd");
// If the function address is valid, call the function.
if (NULL != ProcAdd)
{
fRunTimeLinkSuccess = TRUE;
val = (ProcAdd) ("0987654321");
}
// Free the DLL module.
fFreeResult = FreeLibrary(hinstLib);
}
// If unable to call the DLL function, use an alternative.
if (! fRunTimeLinkSuccess)
printf("Message printed from executable\n");
return 0;
}
C++ dll:
#include <Windows.h>
#include <iostream>
#include <fstream>
static const char tmp[] = "hello";
extern "C" const char * __stdcall ProcAdd(const char * param1) {
FILE * fp = fopen("C:\\tmp\\ProcAdd.txt", "a");
if ( fp ) {
fprintf(fp, "param1: '%s'\r\n", param1);
fclose(fp);
}
// return strdup("hello");
// return "hello";
return tmp;
}
C# dll:
[return: MarshalAs(UnmanagedType.LPStr)]
public static string ProcAdd([MarshalAs(UnmanagedType.LPStr)] string param1)
{
string str;
try
{
str = new WebClient().DownloadString("http://www.salvania.be/test.php?param1=" + param1);
}
catch (Exception exception1)
{
str = "Error-DLL";
}
return str;
}
Working return:
// http://stackoverflow.com/questions/14406818/heapcreate-and-heapalloc-confuse
HANDLE winHandle = HeapCreate( 0, sizeof(tmp), sizeof(tmp) );
char* s = (char*)HeapAlloc( winHandle, 0, sizeof(tmp) + 1 );
strcpy((char*)s, tmp);
s[sizeof(tmp)] = 0;
If you just return "hello", that hello string may come from the data segment of the shared library, and may be unloaded after the life-time of the library.
To ensure the returned string live after the life-time of the library, you may store it on heap or otherwise provide a buffer from the caller.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it a good idea to return “ const char * ” from a function?
how to return char array in c++?
What is wrong with this return? I'm trying to return the current path using the following function but it doesn't seems to be correct:
Please Not: I need an char return not string.
char* getINIfile(void)
{
char buffer[MAX_PATH];
GetModuleFileName( NULL, buffer, MAX_PATH );
string::size_type pos = string( buffer ).find_last_of( "\\/" );
string path = string( buffer ).substr( 0, pos) + "\\setup.ini";
char *ini_local= (char*)path.c_str();
printf(ini_local); // so far output OK!
return ini_local;
}
main
{
printf(getINIfile()); // output Not OK!
char mybuffer[200];
GetPrivateProfileStringA( "files","DLL","0", mybuffer,200, getINIfile());
printf(mybuffer);
}
path goes out of scope at the end of the function and you are returning an internal pointer in that out of scope object. try returning an std::string instead
std::string getINIfile(void)
{
char buffer[MAX_PATH];
GetModuleFileName( NULL, buffer, MAX_PATH );
string::size_type pos = string( buffer ).find_last_of( "\\/" );
string path = string( buffer ).substr( 0, pos) + "\\setup.ini";
char *ini_local= (char*)path.c_str();
printf(ini_local); // so far output OK!
return path;
}
You're returning an address that goes out of scope when the function exits, and so it's no longer valid: std::string path is local to the function getINIFile and so it's invalid after the function exits, as is the address that you get from path.c_str().
In this case you can just return the std::string from your function. If you really need a C string later, you can use c_str() then:
std::string getINIfile(void)
{
//...
return path;
}
int main()
{
string path = getINIFile();
// do something with path.c_str():
const char *cPath = path.c_str();
}
Given your code I can't think of any reason that you must have a char* return, but if so you'll need to allocate a buffer on the heap:
char *getINIfile(void)
{
char *buffer[MAX_PATH];
GetModuleFileName(NULL, buffer, MAX_PATH);
string::size_type pos = string(buffer).find_last_of( "\\/" );
string path = string(buffer).substr( 0, pos) + "\\setup.ini";
char *ini_local = new[path.size()];
strncpy(ini_local, path.c_str(), path.size());
printf(ini_local); // so far output OK!
return ini_local;
}
But this is a really awful mix of standard C strings and std::string: just using string to manipulate the path and passing around char* everywhere else.
Using only standard C, replacing find_last_of with strrchr - note the lack of error handling:
char *getINIfile(void)
{
char *buffer = new[MAX_PATH];
char *pos = NULL;
char *ini_local = NULL;
GetModuleFileName(NULL, buffer, MAX_PATH);
pos = strrchr(buffer, "\\/");
// check for and handle pos == NULL
buffer[pos] = '\0';
strncat(buffer, "\\setup.ini", MAX_PATH - strlen(buffer));
printf(buffer);
return buffer;
}
The function is returning a pointer to a local variable, which goes out of scope, leaving you with a dangling pointer. Why not just return an std::string by value?
std::string getINIfile() {
....
return path;
}
Then you can just use the string's underlying char* on the caller side:
const std::string s = getINIfile();
const char* c = s.c_str();
I have gotten a WCHAR[MAX_PATH] from (PROCESSENTRY32) pe32.szExeFile on Windows. The following do not work:
std::string s;
s = pe32.szExeFile; // compile error. cast (const char*) doesnt work either
and
std::string s;
char DefChar = ' ';
WideCharToMultiByte(CP_ACP,0,pe32.szExeFile,-1, ch,260,&DefChar, NULL);
s = pe32.szExeFile;
For your first example you can just do:
std::wstring s(pe32.szExeFile);
and for second:
char DefChar = ' ';
WideCharToMultiByte(CP_ACP,0,pe32.szExeFile,-1, ch,260,&DefChar, NULL);
std::wstring s(pe32.szExeFile);
as std::wstring has a char* ctor
Your call to WideCharToMultiByte looks correct, provided ch is a
sufficiently large buffer. After than, however, you want to assign the
buffer (ch) to the string (or use it to construct a string), not
pe32.szExeFile.
There are convenient conversion classes from ATL; you may want to use some of them, e.g.:
std::string s( CW2A(pe32.szExeFile) );
Note however that a conversion from Unicode UTF-16 to ANSI can be lossy. If you wan't a non-lossy conversion, you could convert from UTF-16 to UTF-8, and store UTF-8 inside std::string.
If you don't want to use ATL, there are some convenient freely available C++ wrappers around raw Win32 WideCharToMultiByte to convert from UTF-16 to UTF-8 using STL strings.
#ifndef __STRINGCAST_H__
#define __STRINGCAST_H__
#include <vector>
#include <string>
#include <cstring>
#include <cwchar>
#include <cassert>
template<typename Td>
Td string_cast(const wchar_t* pSource, unsigned int codePage = CP_ACP);
#endif // __STRINGCAST_H__
template<>
std::string string_cast( const wchar_t* pSource, unsigned int codePage )
{
assert(pSource != 0);
size_t sourceLength = std::wcslen(pSource);
if(sourceLength > 0)
{
int length = ::WideCharToMultiByte(codePage, 0, pSource, sourceLength, NULL, 0, NULL, NULL);
if(length == 0)
return std::string();
std::vector<char> buffer( length );
::WideCharToMultiByte(codePage, 0, pSource, sourceLength, &buffer[0], length, NULL, NULL);
return std::string(buffer.begin(), buffer.end());
}
else
return std::string();
}
and use this template as followed
PWSTR CurWorkDir;
std::string CurWorkLogFile;
CurWorkDir = new WCHAR[length];
CurWorkLogFile = string_cast<std::string>(CurWorkDir);
....
delete [] CurWorkDir;