|
>Uh, no... The pointer becomes invalid under certain situations,
>like reallocation of the internal buffer (assign a much longer
>string into it), copying another CString into it, etc.
>(Friendly Tip: test such things before publishing them.) Period.
No, the pointer cannot become invalid if you never use any of the CStrings member functions (including operators) while you have a reference to the internal data. As I've already said, none of those operations are valid after you've called GetBuffer() until you've called ReleaseBuffer(). Yes, you could call them, but you could also go and delete a pointer after you've allocated it and then try to use it. Just because the compiler will allow you to do something, doesn't mean it's a valid operation.
My personal policy is, once you call GetBuffer(), forget you have a CString object. Don't use it until you've called ReleaseBuffer(). This policy is no different from a policy of never calling delete on a pointer you have a reference to. Further, my policy is to never hold on to a pointer retrieved from GetBuffer for any longer than needed and to always release it in the same scope. Typically this is about 2-3 lines of code between GetBuffer and ReleaseBuffer. Even further, I only do this with local non-static variables, to prevent any possibility of multithreaded corruption.
This policy is no more dangerous than using new/delete, and is in fact less dangerous due to the stringent conditions of it's use.
>A pointer allocated via "new" does not become invalid the next
>time "new" is called. Nor does it become invalid after scope
>where "new" was called ends (unlike a CString object going out
>of scope). A pointer obtained via GetBuffer() is a different
>story, even by your own words. This is exactly the reason why
>the argument about not using GetBuffer got started. Because
>the pointer returned by that function requires special
>handling (of the object).
Consider the following code:
{
char *p = new char;
m_p = p;
auto_ptr<char> pptr(p);
}
dosomethingwith(m_p);
In other words, you can do stupid things with new/delete just like you can do stupid things with GetBuffer()/ReleaseBuffer().
Here's a better one:
char *p = new char[100];
dosomethingswith(p);
dosomethingelsewith(p};
void dosomethingwith(char*p)
{
delete [] p;
}
Of course it's contrived, but unless you know exactly what a function is doing with a pointer you pass it, it could very well be deleting your pointer and reallocating it.
But you made an argument about the safety of local arrays, Your arguments about the CString going out of scope and deleting the pointer apply equally as well to local arrays. consider this:
{
char x[100];
m_x = x;
}
dosomethingwith(m_x);
The point here is that you have to take care either way. Neither way is inherantly more or less dangerous, and if you take precautions to prevent problems, it's quite safe.
>Make up yer mind. Which is it? "until you call ReleaseBuffer()",
>or "can't call any functions of the CString object"? Talking
>about not making sense...
Huh? This statement makes no sense. My original statement makes perfect sense. What's so difficult to understand about saying "Don't call any functions of the CString object until calling ReleaseBuffer()?" Unless you are being anal retentive and pointing out that ReleaseBuffer is a function of CString, which rates a big ol' *DUH*.
>If you have no idea, then you also have no idea if the data you
>are going to be reading is binary or not. Hence, using a
>CString-based buffer, to hold data that could contain NUL
>characters is stupid. (And, yes, I do know that you can get
>both bits of info at runtime.)
There is absolutely nothing wrong with using CStrings to store strings with embedded 0's. In fact, MFC does this quite often (for instance, in common file dialogs). That's why CString::GetLength() doesn't use strlen, and that's why ReleaseBuffer() offers you an option of specifying the length.
Further, if you're reading string data from the registry, it should be text. If you're reading DWORD data, then it could be anything.
>No, I have experienced notions that you should know
>(or at least have an idea about) what you are getting
>yourself into before starting a project. Serious
>developers know how important research is (or as our
>local Guru puts it, "Think first, code later").
I fail to see what this has to do with knowing how much memory your program is going to need when you write it. There is such a thing as generic applications which work generic data. Do you really think the author of RegEdit knew exactly how much data there would be in the registry when wrote it? Sure, you might know maximums, but allocating maximum memory everytime you need buffer space is stupid when you don't need it.
>However, NUL can be char-sized, or wchar_t-sized.
>NULL is (usually) int-sized. A literal "0" is
>(usually) int-sized. Note the difference?
>Good. I knew you could.
This difference is irrelevant in any circumstance except when the size of the data is important. 0 can and is implicitly converted to any type, since it can fit in even the smallest data type. Is sizeof(NULL) and sizeof(NUL) different? yes. Is NULL == NUL? yes. In any event, NUL isn't even a definition in C++, or C for that matter. It's a data communications thing.
>So why bother confusing newer developers by giving them
>the temptation to NOT manually put one in by using the
>CString-based buffers?
This just makes no sense whatsoever. C++ is not a "newbie" language and has may ways to shoot yourself in the foot. C++ is not Pascal, and should not and does not go about limiting developers "for their own good".
>BTW: There is such a thing as quitting while you
>(think you) are ahead...
Your arguments are hypocritical. The same issues are valid with either case. One is not more inherantly dangerous than the other.
|
|
|
|
|
> No, the pointer cannot become invalid if you never use any of the CStrings member
> functions (including operators) while you have a reference to the internal data.
I know that, that is what I said. My first response paragraph countered your statement that the pointer is valid until you call ReleaseBuffer(), which, as you just confimed, was incorrect.
> My personal policy is, once you call GetBuffer(), forget you have a CString object.
> Don't use it until you've called ReleaseBuffer(). [...Other good tips deleted...]
That statement, and the others that we both have made show why the use of CStrings in that manner should be avoided.
> Consider the following code: [...Deleted...]
A valid point, but I was talking direct use, without using a class to wrap the pointer. Besides, if you need auto_ptr to handle cleanup for you, you have no business messing with pointers in the first place!
>> Make up yer mind. Which is it? "until you call ReleaseBuffer()",
>> or "can't call any functions of the CString object"?
>
> Huh? This statement makes no sense. My original statement makes perfect sense.
That was referring to the original "...until you call ReleaseBuffer()" statement. Read it again if you do not understand.
> There is absolutely nothing wrong with using CStrings to store strings with embedded 0's.
I never said there was.
> This difference is irrelevant in any circumstance except when the size of the
> data is important.
Which also depends on which platforms, compilers, etc. you are working with. As such, you should never assume that any difference is irrelevant. For example, is the size of an "int" the same as the size of a pointer?
>This just makes no sense whatsoever. C++ is not a "newbie" language [...]
Right! This entire discussion between us is actually about that fact: the original poster had a simple problem with CString. Problems like this one, IMHO, point to a less experienced developer. Also, given the apparent (mis)use of CString, more experience with it would have been helpful.
While it was nice to have someone that I can fence with on the other side of this thread, make sure we do not lose sight of the intent in these messages. CString is a useful component of MFC, but it can be *overused* as well as *misused*. We need to not give less experienced developers so much rope that they hang themselves. First teach 'em about *how* CString works internally, and then tell them about "shortcuts" like GetBuffer(). Agreed?
Peace!
-=- James.
|
|
|
|
|
Your argument doesn't make a lot of sense to me.
All of the things you argue about (having to remember how long your string is, or if it's null terminated) apply equally to a seperate buffer as well.
Many Windows functions require a standard LPTSTR, such as Registry and file functions (some file operations are not possible using CFile) and I see nothing wrong with using a CString allocated buffer for them, then using CString to manipulate the data.
Remembering to call ReleaseBuffer is no more a problem than having to remember to call delete or free on dynamicly allocated memory (which you'd have to do for the functions I mention, since you wouldn't know at runtime how much memory they take until you query them). In my opinion, once you've called GetBuffer(), you no longer have CString object available to you. Forget it exists until you've called ReleaseBuffer. The memory returned by GetBuffer is no different from memory returned by new or malloc and requires the same care you would use for new or malloced memory. There are no "land mines" that are different from newd memory, and there is no catastrophe waiting to happen that's different from managing memory allocated by new.
Is your argument that you should use CStrings own functions for string operations? If so, I agree, but I do not agree with using a seperate buffer when you need to write to a standard C character array.
|
|
|
|
|
I tend to agree with Jim on this, if only from a OOP purist standpoint. True, calling ReleaseBuffer() is no big deal, mayber comparable to calling delete[] for every new[]. But an advantage of OOP is that ownership and maintenance issues are more transparent...don't screw with my private members (no pun intended), and I won't screw with yours. Forgetting to call ReleaseBuffer(), or placing it in the wrong place, can be problematic, even disasterous for a program. (Not very likely? For any moderately complex function, it is just as likely to bomb on ReleaseBuffer() logic as it is to bomb on delete[] logic.)
So if casting to an LPCSTR doesn't work because of the need for a non-const char buffer, than I would suggest creating a local static buffer. (I tend to alleviate my anger at the need to do this by cursing the crappy programmer who wrote the function const-unaware;) .)
Walter Gildersleeve
Freiburg, Germany
walter.gildersleeve@pe-gmbh.de
|
|
|
|
|
Many Windows functions require a standard LPTSTR, such as Registry and file functions (some file operations are not possible using CFile) and I see nothing wrong with using a CString allocated buffer for them, then using CString to manipulate the data.
Of course we all agree (I think) that it's ok to pass character data into a function using CString's implied "const char*" cast. If a function wants to write into a buffer, I make a real buffer and pass it in to them. I think this is a much safer coding approach. I know you and I can keep our calls to release buffer straight, but what happens when that shifty looking guy standing over there modifies our code? I don't know about you, but he looks lazy to me and I bet he hasn't carefully studied the CString docs. Maybe he's used to using (insert name of any other string class in the world), and makes the childish asumption that like them, CString won't just hand him a pointer that has a ticking time bomb attached to it. I trust you to keep it straight, but I don't trust him.
Remembering to call ReleaseBuffer is no more a problem than having to remember to call delete or free on dynamicly allocated memory (which you'd have to do for the functions I mention, since you wouldn't know at runtime how much memory they take until you query them). In my opinion, once you've called GetBuffer(), you no longer have CString object available to you. Forget it exists until you've called ReleaseBuffer.
You have put your finger on the essential evil of GetBuffer much better than I. You're exactly correct! Once you call GetBuffer you no longer have an object! GetBuffer defeats the whole purpose of object oriented programming!
I have nothing more to say on this subject, excuse me while I get back to a "goto" argument on another forum.
Jim
|
|
|
|
|
GetBuffer doesn't defeat the purpose of OO programming. In fact, it does exactly what auto_ptr does when you assign one auto_ptr to another. It basically gives you ownership of the memory until you're done with it. I'll agree that it doesn't enforce this (and it probably should, but it's too late to change that).
That's why i always use GetBuffer() and ReleaseBuffer() as close together as possible. Typically with a single operation between them.
char *p = s.GetBuffer(filesize);
ReadFile(handle, p, filesize, &bytesread, NULL);
s.ReleaseBuffer(bytesread);
is much cleaner, easier to maintain, and in my opionion safer than:
char *p = new char[filesize]
ReadFile (handle, p, filesize, &bytesread, NULL);
s = p;
delete [] p;
For starters, if you're worried about lazy and incompetant maintainers, there's lots to go wrong with the second. They might not know the difference between delete and delete []. the copying of the data can cause s to reallocate, possibly causing an allocation failure. But you won't know this until *AFTER* you've read the data, rather than before. So now you have to figure out how to unread the data and reposition the file pointer to deal with that. Also, this *REQUIRES* at a minimum, 1 delete and one new and one copy. The first example may not require any newly allocated memory if the CString buffer is already long enough, and if that's the case a copy won't happen either.
|
|
|
|
|
OK, so what's with the holy war?
I haven't checked all messages in this thread,
so forgive me if this has been posted already.
Why not use something like:
class CStringBuffer
{
public:
CStringBuffer(CString &str, int len = 0)
: m_str(str) { m_buf = str.GetBuffer(len); }
~CStringBuffer() { m_str.ReleaseBuffer(); }
operator LPTSTR() { return m_buf; }
private:
CString &m_str;
LPTSTR m_buf;
};
to handle acquisition/release like
CString str;
CStringBuffer buf(str, 100);
sprintf(buf, "%s %d", "Whatever", 666);
I almost always use a wrapper class like that for API
pairs like GlobalLock/GlobalUnlock and such. Works great!
/sven axelsson, sweden
|
|
|
|
|
Actually, this is more dangerous in some ways than just using GetBuffer()/ReleaseBuffer() seperately. Consider this code:
CString s;
...
CStringBuffer buf(s, 100);
sprintf(buf, "%s %d", "Whatever", 666);
s = "something else";
sprintf(buf, "%s %d", "Whatever", 666);
Now, buf could well be pointing off into a dangling pointer.
I like to leave GetBuffer()/ReleaseBuffer() as close as possible together to avoid the possibility of this. Of course, this would be no different than reassigning a char * to new data, but because you hide it in a class, it's not quite so obvious.
|
|
|
|
|
I have seen that there is a way to remove the Application's button ( Windows )from teh task bar, but what about a Win32 Console Application?
Is there a way to have the application running but not have its button on taskbar?
vcprog
|
|
|
|
|
I derived a custom class from CDialog ( MFC ) and then I would like to associate the new class to my resources, instead of CDialog. Why is it not possible to add our classes to available list of MFC classes?
The way I get around this is to derive from CDialog and then later change them to CMyDialog. Later if I use classwizard it is smart enough to know that it is CMyDialog instead of CDialog, How?
Hope some one has worked on this issue. Do anyone of you know, how to associate a resource with a New Class that is derived from MFC?
vcprog
|
|
|
|
|
You can't because your class is not part of MFC per se.
The way to do it is exactly as you did. ClassWizard does not only work on MFC, but all classes. Of course it therefore knows what you changed the derived class to.
Christian
The content of this post is not necessarily the opinion of my yadda yadda yadda.
To understand recursion, we must first understand recursion.
|
|
|
|
|
The following line will force to not have the default opened initial view on app statrtup, and it is OK.
<br />
cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;<br />
But I need to size the view according to selection from the user from a dialog as follows:
BOOL CSizeView::PreCreateWindow(CREATESTRUCT& cs)
{
CSizeDlg sizedlg;
int result = sizedlg.DoModal();
if(result == IDOK){
cs.cx &= sizedlg.m_Height;
cs.cy &= sizedlg.m_Width;
MoveWindow(0, 0, cs.cy, cs.cx);
}
return CView::PreCreateWindow(cs);
}
But the above does not work!
How can I fix it! Helps are apprecaietd!
|
|
|
|
|
IT CAN'T work, because you're sizing the window in PRECreateWindow, BEFORE it is created. Go to OnInitialUpdate and size it there. For a dialog it would be OnInitDialog.
Christian
The content of this post is not necessarily the opinion of my yadda yadda yadda.
To understand recursion, we must first understand recursion.
|
|
|
|
|
Dear Christian,
Thanks alot for reply!
The following is still a problem:
void CSizeView::OnInitialUpdate()
{
CView::OnInitialUpdate();
CREATESTRUCT cs;
CSizeDlg sizedlg;
int result = sizedlg.DoModal();
if(result == IDOK){
cs.cx = sizedlg.m_Height;
cs.cy = sizedlg.m_Width;
MoveWindow(0, 0, cs.cy, cs.cx);
}
}
the desired sizes using the radio buttons are: 768x1024, 200x320, 480x640 & 600x800 or a custom option, which exist on the dialog and can be selected by the user when selected the new document from menu!
Another problem is when the view is resized, it again gets back to the default size as normal startup!
Thankx again!
|
|
|
|
|
Do you mean it resizes and then resizes again, or does not remember it's size ?
I admit I typed this response without trying it, I've just found you need also to do this:
GetParent()->MoveWindow(0,0,x,y);
at least in my program, which shows a bitmap to be edited in the view.
I'd suggest this:
CView::OnInitialUpdate();
CSizeDlg sizedlg;
if(sizedlg.DoModal() == IDOK)
{
MoveWindow(0, 0, sizedlg.m_Width, sizedlg.m_Height);
GetParent()->MoveWindow(0, 0, sizedlg.m_Width, sizedlg.m_Height);
}
I see no point in the extra variables you were creating, and you also had the width and height going in the wrong spots.
Christian
The content of this post is not necessarily the opinion of my yadda yadda yadda.
To understand recursion, we must first understand recursion.
|
|
|
|
|
EXCELLENT! Thank you so much!
One more thing:
at least in my program, which shows a bitmap to be edited in the view.
As long as we are in the same pot, how about restricting the drwaing area to the same size. I mean even if the user maximizes the view, it still must remain the same drawing area as the desired size, but now it just maximizes it fully (resisez the area as well as the view!)?
|
|
|
|
|
Do you mean resizing the bitmap as well ?
Christian
The content of this post is not necessarily the opinion of my yadda yadda yadda.
To understand recursion, we must first understand recursion.
|
|
|
|
|
no! just the view. To explain further, please visualize this scenario:
The user opens a new document from menu->new, the size dialog pops up, user selects a 320x200 pxls view and then PASTEs a captured image data of some window by either the print screen key or a CTRL+C keys from any other graphic app.
At this point, when the user PASTEs the image into my apps view as above mentioned with size 320x200, the image could be bigger than the rect area of my view! So I'd have to resize to fit the image! Right!
Suppose the image will be of size 380x380 pxls, then I would resize to fit it, but I do not want the remaining of the view (the white area) to show while the view is fully maximized, as well not to effect the size of the bitmap at all!
Something like whta the "MGI PhotoSuite SE" app does already!
Thankx again Christian!
|
|
|
|
|
I have a base class which I have derived from CPropertyPage, called CXXXPropertyPage. This class overides OnKillActive() in order to call a Validate() method in the derived class (pure virtual in CXXXPropertyPage().)
If I have multiple CXXXPropertyPages in a CPropertySheet, all is well and Validate() is called for all property pages in the sheet via the CXXXPropertyPages base class.
The problem occurs if I only have one CXXXPropertyPage in the CPropertySheet. In this case, we want to SetWizardMode() in the CPropertySheet in order to remove the tab from the top right of the property page dialog. If we do this, then OnKillActive() is no longer called in the CXXXPropertyPage base class. I have tried to hook in at other levels, but with no luck - WM_NOTIFY does not work, nor does anything else I have tried.
Surely a wizard has some hook to allow validation before changing page or calling OnOK. I have also tried OnWizardNext(), OnWizardBack() and OnWizardFinnish().
Can you help me, please!
Regards
Tom
|
|
|
|
|
The 'OnWizard...' functions should be what you are looking for. When Wizard mode is active, these are the functions called for validation. You return FALSE to tell MFC to ignore the request. That is, if the user clicks on the 'Finish' button then 'OnWizardFinish' is called, and you would return 'FALSE' if you did not want the dialog to close.
What exactly happens? Which button do you click ? Which of the the 'OnWizard...' functions is being called (or not called)?
Ian Kilmister
|
|
|
|
|
Thanks Ian,
I had tried all three OnWizard... functions. But I must have done something wrong. I tried OnWizardFinish again and it miraculously worked straight away!
Thanks again.
Tom
|
|
|
|
|
hi
is it possible to get the handle to a submenu using the id of the menu item in the menu bar
thanks
subir
|
|
|
|
|
Popup menus don't have IDs, so you have to access them by their position.
HMENU hmenu = GetMenu(hwnd);
HMENU hFileMenu = GetSubMenu(hmenu, 0); // file menu is position 0
HMENU hEditMenu = GetSubMenu(hmenu, 1); // edit is 1, etc...
--Mike--
http://home.inreach.com/mdunn/
Is history an illusion caused by the passage of time, or is time an illusion caused by the passage of history?
|
|
|
|
|
I have my own class derived from CToolbar, and I want it to look like it's docked ( no caption bar, gripper on the left ) when it is floating. Can anyone suggest how I might achieve this ?
Thanks
Christian
The content of this post is not necessarily the opinion of my yadda yadda yadda.
To understand recursion, we must first understand recursion.
|
|
|
|
|
I have my own class derived from CToolbar
Haven't tried it before! One idea would come to mind is what if you override the non client stuff, the WM_NC***** CWnd virtuals! and make your own codings!?
|
|
|
|
|