Okay, I've been dealing with this for two days now, and I can't find a solution.
Problem: I'm trying to set a filter to a File Selection Dialog using Winapi. I'm using GetOpenFileName function to do this. This function uses a structure to set options such as file extension filters. This structure's member called lpstrFilter needs a certain string format. I'm setting that string exactly as Winapi indicates, but for some reason, this string's value changes.
I've got this static const char *:
//This contains string "JPG"
static const char * extensionFilter = v->trabajo10.C_JMV_SelectFile_FileExtension7.GetString();
//This forms a filter string which applies to OPENFILENAME structure.
string sFilter;
sFilter.append("Format: ");
sFilter.append(extensionFilter);
sFilter.push_back('\0');
sFilter.append("*.");
sFilter.append(extensionFilter);
sFilter.push_back('\0');
const char * filter = sFilter.c_str();
ofn.lpstrFilter = filter; //This sets: --> Format: JPG\0*.JPG\0
//This opens the file selection dialog
if (GetOpenFileName(&ofn)==TRUE){
...
The File Selection Dialog looks CORRECTLY, like this:
The joke comes now, I modify the code like this:
//This contains string "JPG"
static const char * extensionFilter = v->trabajo10.C_JMV_SelectFile_FileExtension7.GetString();
if(1){
//This forms a filter string which applies to OPENFILENAME structure.
string sFilter;
sFilter.append("Format: ");
sFilter.append(extensionFilter);
sFilter.push_back('\0');
sFilter.append("*.");
sFilter.append(extensionFilter);
sFilter.push_back('\0');
const char * filter = sFilter.c_str();
ofn.lpstrFilter = filter; //This sets: --> Format: JPG\0*.JPG\0
}
//This opens the file selection dialog
if (GetOpenFileName(&ofn)==TRUE){
...
And this is the result, THE PROBLEM:
Filter string was modified???
if(1){
//This forms a filter string which applies to OPENFILENAME structure.
string sFilter;
sFilter.append("Format: ");
sFilter.append(extensionFilter);
sFilter.push_back('\0');
sFilter.append("*.");
sFilter.append(extensionFilter);
sFilter.push_back('\0');
const char * filter = sFilter.c_str();
ofn.lpstrFilter = filter; //This sets: --> Format: JPG\0*.JPG\0
}
The sFilter variable has a lifetime that ends when the block in which it declared ends. The pointer returned by sFilter.c_str() is valid until sFilter is modified or destroyed.
You are using this pointer after it has become invalidated. This is the same problem as you had yesterday, which I guessed at in comments to the question. This is why you need to show a full MCVE. This question also looks to be a duplicate of the one that you asked a week ago: Winapi GetOpenFileName Extension Filter not working. I suggest that you take some time to make sure that you fully appreciate the validity of the value returned by c_str().
You must ensure that sFilter lives until after you have finished with the pointer. Declare sFilter in an outer block to ensure that.
The problem is that you have a variable that fell out of scope
if(1){
string sFilter;
// ... code
// Right here
const char * filter = sFilter.c_str();
ofn.lpstrFilter = filter;
}
After that block ends filter falls out of scope, so ofn.lpstrFilter has a dangling pointer.
Answering ProtectedVoid's concern about declaring an unused object: Imagine a std::string was an expensive object and the condition was unlikely. You don't want the object constructed unless the condition is true. But then it must last beyond the scope of the condition. So we use the fact that a default constructed unique_ptr is much cheaper than a default constructed string:
std::unique_ptr<std::string> scope_extender;
if( something unlikely ){
//This forms a filter string which applies to OPENFILENAME structure.
std::string* sFilter = new std::string;
scope_extender.reset( sFilter );
sFilter->append("Format: ");
sFilter->append(extensionFilter);
sFilter->push_back('\0');
sFilter->append("*.");
sFilter->append(extensionFilter);
sFilter->push_back('\0');
const char * filter = sFilter->c_str();
ofn.lpstrFilter = filter; //This sets: --> Format: JPG\0*.JPG\0
}
Obviously I don't want to imply std::string is expensive enough to construct to be worth all that trouble. But some object in a similar situation might be. Also, there was a comment about what if it was many minor objects in one conditional. In that case, you would want a utility struct to hold them all together and the unique_ptr conditionally owns that struct.
Related
I'm working on making some Spotify API calls on an ESP32. I'm fairly new to C++ and while I seem to got it working how I wanted it to, I would like to know if it is the right way/best practice or if I was just lucky. The whole thing with chars and pointers is still quite confusing for me, no matter how much I read into it.
I'm calling the Spotify API, get a json response and parse that with the ArduinoJson library. The library returns all keys and values as const char*
The library I use to display it on a screen takes const char* as well. I got it working before with converting it to String, returning the String with the getTitle() function and converting it back to display it on screen. After I read that Strings are inefficient and best to avoid, I try to cut out the converting steps.
void getTitle()
{
// I cut out the HTTP request and stuff
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, http.getStream(), );
JsonObject item = doc["item"];
title = item["name"]; //This is a const char*
}
const char* title = nullptr;
void loop(void) {
getTitle();
u8g2.clearBuffer();
u8g2.setDrawColor(1);
u8g2.setFont(u8g2_font_6x12_tf);
u8g2.drawStr(1, 10, title);
u8g2.sendBuffer();
}
Is it okay to do it like that?
This is not fine.
When seeing something like this, you should immediately become suspicious.
This is because in getTitle, you are asking a local object (item) for a pointer-- but you use the pointer later, when the item object no longer exists.
That means your pointer might be meaningless once you need it-- it might no longer reference your data, but some arbitrary other bytes instead (or even lead to crashes).
This problem is independent of what exact library you use, and you can often find relevant, more specific information by searching your library documentation for "lifetime" or "object ownership".
FIX
Make sure that item (and also DynamicJsonDocument, because the documentation tells you so!) both still exist when you use the data, e.g. like this:
void setTitle(const char *title)
{
u8g2.clearBuffer();
u8g2.setDrawColor(1);
u8g2.setFont(u8g2_font_6x12_tf);
u8g2.drawStr(1, 10, title);
u8g2.sendBuffer();
}
void updateTitle()
{
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, http.getStream(), );
JsonObject item = doc["item"];
setTitle(item["name"]);
}
See also: https://arduinojson.org/v6/how-to/reuse-a-json-document/#the-best-way-to-use-arduinojson
Edit: If you want to keep parsing/display update decoupled
You could keep the JSON document "alive" for when the parsed data is needed:
/* "static" visibility, so that other c/cpp files ("translation units") can't
* mess mess with our JSON doc directly
*/
static DynamicJsonDocument doc(1024);
static const char *title;
void parseJson()
{
[...]
// super important to avoid leaking memory!!
doc.clear();
DeserializationError error = deserializeJson(doc, http.getStream(), );
// TODO: robustness/error handling (e.g. inbound JSON is missing "item")
title = doc["item"]["name"];
}
// may be nullptr when called before valid JSON was parsed
const char* getTitle()
{
return title;
}
I have a capture card from Black Magic Design company. In related document it is described that the GetBytes method, from IDeckLinkVideoInputFrame interface, allows direct access to the data buffer of a video frame. Here is my work:
HRESULT DeckLinkDevice::VideoInputFrameArrived (/* in */ IDeckLinkVideoInputFrame* videoFrame, /* in */ IDeckLinkAudioInputPacket* audioPacket)
{
char* str1;
voidPtrToFrame = NULL;
videoFrame->GetBytes(&voidPtrToFrame);
sprintf(str1, "%p", voidPtrToFrame);
// the below line does not work.
SetDlgItemText(m_uiDelegate->GetSafeHwnd(), IDC_handytxtBox, str1);
}
I also defined voidPtrToFrame in class of DeckLinkDevice:
class DeckLinkDevice::IDeckLinkInputCallback
{
...
void* voidPtrToFrame;
...
}
In the last line an error appears related to str1:
argument of type "char*" is incompatible with parameter of type
LPCWSTR
I want to know:
How can I display the value of voidPtrToFrame in an Edit control? i.e. I want to present the address of buffer containing the video frame. In the following image I provided the necessary information about GetBytes method.
I googled a lot and tested several ways. But I could not implement them in MFC.
You have two problems:
1. You get a crash or at least undefined behaviour
The variable str1 is never initialized. It's a classic beginner's error.
The problem is here:
char* str1;
voidPtrToFrame = NULL;
videoFrame->GetBytes(&voidPtrToFrame);
// here str1 points to an interterminate location, it has never been
// initialized !! Therefore your program most likely will crash
sprintf(str1, "%p", voidPtrToFrame)
You need this:
char str1[20]; //<<< changement here
voidPtrToFrame = NULL;
videoFrame->GetBytes(&voidPtrToFrame);
// now str1 points to a 20 byte buffer
sprintf(str1, "%p", voidPtrToFrame);
2. You must use wide characters
You are compiling for unicode, therefore you need this (previous other corrections are included here):
wchar_t str1[20];
voidPtrToFrame = NULL;
videoFrame->GetBytes(&voidPtrToFrame);
wsprintf(str1, L"%p", voidPtrToFrame);
SetDlgItemText(m_uiDelegate->GetSafeHwnd(), IDC_handytxtBox, str1);
So I wrote myself a little wrapper function to make a prepared statement for me:
sqlite3_stmt* Gladiateur::run_query_unfinalized(string query, vector<string> inputs){
sqlite3_stmt *statement;
// Prepare SQL
sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
// Bind parameter
sqlite3_bind_text(statement, 1, inputs[0].c_str(), -1, NULL);
return statement;
}
Normally it would go through the inputs vector and bind all of the strings, but I simplified the code to find out where the error lies.
I have another function set_list (also simplified):
int Gladiateur::set_list (string list_name) {
vector<string> bind = {list_name};
sqlite3_stmt *statement = this->run_query_unfinalized("SELECT id FROM lists WHERE name = ? LIMIT 1", bind);
printf("code %d\n", sqlite3_step(statement));
sqlite3_finalize(statement);
return 1;
}
I call set_list, and I get "code 101", which simply implies that there were no SQL-errors but no row was retrieved. I know that a matching row exists, though.
And here comes the weird part: I move the line
sqlite3_bind_text(statement, 1, inputs[0].c_str(), -1, NULL);
into the set_list function, just above the printf, and write bind[0] instead of inputs[0]. And it works! I get "code 100", the row is found, it also gave me the correct id when I checked.
But I want to do all the binding in run_query_unfinalized... and I am really confused why it doesn't work. Maybe I can't pass the statement variable like that?
The last parameter of the sqlite3_bind_text function cannot be NULL:
The fifth argument to the BLOB and string binding interfaces is a destructor used to dispose of the BLOB or string after SQLite has finished with it. The destructor is called to dispose of the BLOB or string even if the call to bind API fails. If the fifth argument is the special value SQLITE_STATIC, then SQLite assumes that the information is in static, unmanaged space and does not need to be freed. If the fifth argument has the value SQLITE_TRANSIENT, then SQLite makes its own private copy of the data immediately, before the sqlite3_bind_*() routine returns.
In this case, you need SQLITE_TRANSIENT.
Gladiateur::run_query_unfinalized(string query, vector<string> inputs)
The vector<string> inputs is a copy, and it ceases to exist when the function returns.
Perhaps you should change it to:
Gladiateur::run_query_unfinalized(const string& query, const vector<string>& inputs)
With a reference, the c_str should survive to do what you want to get done.
This is confusing. I understand pointers but what is with tracking references?
The instructions are not very clear on what to do even on the wiki, not to mention I was never taught this in adv C++. So, How do I get the items in a listbox into one of my classes?
Also is can i get a brief rundown of tracking references for future reference?
My form has a Listbox called listbox2 with some data in it.
My class called "ManifistOBJ" has a method called "setFilename(char*)"
Now in other programs i can easily add objects to the "AddFilename" method but how do i do it for a tracking reference?
Sofar iv tried:
DMManifest newmanifest = DMManifest();
for(int i =1;i< listBox2->Items->Count;i++)
{
ManifistOBJ newobj = ManifistOBJ();
System::String^ temp = listBox2->Items[i]->ToString();
String temp1 = temp;//?
char* temp2 = temp1.c_str();
newobj.setFilename(temp2);
newmanifest.push_back(newobj);
}
With that ^ next to string I cant DE-reference it. and I have no idea how to.
I could make the method take a string^ but that would mess up my other programs that use that library.
#include <msclr/marshal_cppstd.h>
System::String^ temp = listBox2->Items[i]->ToString();
std::string temp1 = msclr::interop::marshal_as< std::string >( temp );
C++/CLI Converting from System::String^ to std::string
Alright so I have an edit control and a static text control. When the user clicks a button, I want the program to take the text from the edit control and add it to the text in the static text control and then set that new value as the text in the static control. So I have a function that appends the buffers to one another but my program doesn't seem to be working. This is what I have:
//when button message is recieved:
SendMessage(hwndEditControl, WM_GETTEXT,255,(LPARAM)editbuffer);
GetWindowText(hwndTextControl, (LPWSTR)allText, GetWindowTextLength(hwndTextControl));
allText = appendStrings((char*)editbuffer, (char*)allText);
SetWindowText(hwndTextControl, (LPCWSTR)allText);}
// where appendStrings is defined as:
char* appendStrings (char* buffer1, char* buffer2)
{
std::string string1(buffer1), string2(buffer2);
std::string string3 = string1 + string2;
return (char*)string3.c_str();
}
//and
static char* editbuffer = new char;
static char* allText = new char; //these are defined as so
So anyway, when I push the button, I'm pretty sure that the appendStrings function is working because I think it takes what is in the editbox and adds it to the Text box. The reason I say "i think" though is because the string in the text box is always just jibberish. Its sometimes random symbols or just these "l's" (or what look like "L"s). I think it's a problem with my pointers but I'm not sure. I'm new to this so if there is an easier way, please tell me.
You're returning a pointer to a temporary that's being destroyed before the function returns.
Instead of returning a char *, return a std::string.