|
Hi all, I'm confused and liked to know your opinion.
In short:
I think there is a general WTL::CString design problem if a CString is
passed by reference between DLLs to retrieve data.
Example, calling the following DLL-provided method to this is not safe:
int GetName(CString& o_strName);
In long:
Referring to ATLMISC.H, an empty CString will point to a global
variable named rgInitData. rgInitData acts as a global placeholder to be pointed to for any initially empty strings.
<br />
_declspec(selectany) int rgInitData[] = { -1, 0, 0, 0 };<br />
<br />
_declspec(selectany) CStringData* _atltmpDataNil = (CStringData*) &rgInitData;<br />
Since any file that uses WTL::CString #inlcudes the code above, it will get an rgInitData in for each obj file, to avoid linker problems the declspec is used. Thats ok so far.
But problems arise in case of DLL projects, as each DLL will get its own rgInitData instance with the corresping atltmpDataNil pointer.
The problem is: create an empty CString in DLL-A, the CString will point to its rgInitData-A object to indicate it is empty.
Now handing this CString object over to DLL-B, then the CString internal implementation uses the address of rgInitData-B to determine if its an empty string or not.
As the match will fail, the DLL-B side implementation assumes there is data to be freed, resulting in attempting to free rgInitData-A (which is a global variable on the stack).
Do you agree? Do you know a workaround?
Regards
Xavi
Please note, that here no Debug/Realease or MFC, non-MFC issues apply.
|
|
|
|
|
You're seeing the general problem of trying to pass objects between modules that are using the CRT allocator. This may work, if you're using the DLL version of the CRT, but is bad practice since it may not work in future versions of the CRT. You need to use a process-wide allocator such as CoTaskMemAlloc() /CoTaskMemFree() when passing data between modules.
|
|
|
|
|
Hi Mike
Thanks a lot for your input, I'm glad for any opinion, though (hopefully not beeing a nitpicker) this here is actually a different problem (or isn't it?)
I totally agree that the "pass object between DLLs" is serious topic on itselfs and has to be managed carefully when it comes to allocation/deallocation.
Actually I think we manage it correctly here (linking all to the CRT-DLLs).
Let me put it this way: I'm quite sure we use the same allocs/deallocs, but because the CString internal logic uses a pointer to something that exists per DLL, passing a CString between the DLLs will cause the logic to fail and to deallocate where a deallocation is not appropriate.
To make things worser, this only applies for empty strings, as soon as the string points to heap allocated memory, the logic works fine.
So I came to the conclusion, seeing a CString& in a DLL interface is a problem.
It seems MFC has the same problem, using MFC-extension DLLs to manage it (http://www.sources.ru/cpp/mfc/t4650.htm[^])
Xaver Birrer
Visual C++ MCSD
|
|
|
|
|
Ah yes, good point about the special "empty" string object. Fortunately (although it's not very convenient) you can work around it by assigning "" to CString s, which doesn't break other code like IsEmpty() .
|
|
|
|
|
Yo coders,
I am experimenting with the following code, using VC++ 6:
.............
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <string>
#include <list>#include <functional>
using namespace std;
class test
{
public:
int m_i;
test(int i = 0)
{
m_i = i;
cout << "test object with m_i=" << m_i << " being constructed" << endl;
};
~test()
{
cout << "test object with m_i=" << m_i << " being deleted" << endl;
};
};
typedef list<test> TESTLIST;
int main(int argc, char* argv[])
{
TESTLIST myTestList;
myTestList.push_front(1);
myTestList.push_front(2);
return 0;
}
.........
The output of the program is:
test object with m_i=1 being constructed
test object with m_i=1 being deleted
test object with m_i=2 being constructed
test object with m_i=2 being deleted
test object with m_i=2 being deleted
test object with m_i=1 being deleted
........
How is it possible that objects can be constructed once but deleted twice?
thanks,
Neil
cheers,
Neil
|
|
|
|
|
If you add monitoring of the copy constructor, you'll find the missing constructions...
#include <iostream>
#include <list>
using namespace std;
class test
{
public:
int m_i;
test(int i = 0)
{
m_i = i;
cout << "test object with m_i=" << m_i << " being constructed" << endl;
};
test(test const& t) : m_i(t.m_i)
{
cout << "test object with m_i=" << m_i << " being copy constructed" << endl;
};
test& operator=(test const& t)
{
m_i = t.m_i;return *this;
cout << "test object with m_i=" << m_i << " being copy constructed" << endl;
};
~test()
{
cout << "test object with m_i=" << m_i << " being deleted" << endl;
};
};
typedef list<test> TESTLIST;
int main(int argc, char* argv[])
{
TESTLIST myTestList;
myTestList.push_front(1);
myTestList.push_front(2);
return 0;
}
gives
test object with m_i=1 being constructed
test object with m_i=1 being copy constructed
test object with m_i=1 being deleted
test object with m_i=2 being constructed
test object with m_i=2 being copy constructed
test object with m_i=2 being deleted
test object with m_i=2 being deleted
test object with m_i=1 being deleted
|
|
|
|
|
When writing classes that you want to place into STL containers, make sure you declare a copy constructor and copy-assignment operator. The containers to a lot of copying, and make use of those two functions. Since you didn't declare one, the compiler generates a default one for you (which basically does a memcpy).
If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
|
|
|
|
|
Thanks, I realised my mistake soon after... this is only my 2nd day working with STL!
So it is best to only put simple types, strings or pointers in a list then, and not "large" objects directly .. as there doesn't seem to be a member function to remove an object from a list without deleting it!
cheers,
Neil
|
|
|
|
|
You can place large objects in a list if you like, just realize that it will be expensive to do things like sort it (especially if the list has a substantial number of elements).
list::erase will remove an element from the list. If the element was controlled by the list (that is, you created a list like list<MyObjects> myList; ), then yes, it will be deleted. If you want a copy of it, you should grab it prior to calling erase. If the list was a list of pointers, erase will just delete the pointer. The object the pointer was pointing to will still be there (so make sure you have a pointer that still points to it somewhere or you will have a memory leak).
If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
|
|
|
|
|
Zac Howland wrote: basically does a memcpy
Not quite - the Standard says:
The implicitly defined copy constructor for class X performs a memberwise copy of its subobjects. The
order of copying is the same as the order of initialization of bases and members in a userdefined
constructor (see 12.6.2). Each subobject is copied in the manner appropriate to its type:
- if the subobject is of class type, the copy constructor for the class is used;
- if the subobject is an array, each element is copied, in the manner appropriate to the element type;
- if the subobject is of scalar type, the builtin assignment operator is used.
Virtual base class subobjects shall be copied only once by the implicitlydefined
copy constructor (see
12.6.2).
Personally, I only define copy constructor/assignment operator when I've got a member that's not RAII compliant...and then I think 'Why doesn't that sub-object manage itself' and change it so I can use the implicit copy-constructor and assignment operator!
|
|
|
|
|
I said "basically" for a reason. Try fitting what the standard declares into a 1 sentence note about why you should declare (or not declare) a copy-constructor.
Stuart Dootson wrote: Personally, I only define copy constructor/assignment operator when I've got a member that's not RAII compliant...and then I think 'Why doesn't that sub-object manage itself' and change it so I can use the implicit copy-constructor and assignment operator!
I'm just the opposite. I almost always define them explicitly so that if I have problems, I already have the methods there for me to debug and see what is going on. You can't step through implicit functions.
If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
|
|
|
|
|
Sorry - what I should have said was that I didn't like the comparison of the copy constructor to memcpy - it copies each member individually, it doesn't just copy the raw bytes of the object. Which is why I'm quite happy to rely on implicit copy constructors - because 99% of the time, they're implementing what I'd be implementing anyway.
I take your point about having copy constructors to step through @ debug time - it's just not something I find myself doing.
|
|
|
|
|
Stuart Dootson wrote: Sorry - what I should have said was that I didn't like the comparison of the copy constructor to memcpy - it copies each member individually, it doesn't just copy the raw bytes of the object. Which is why I'm quite happy to rely on implicit copy constructors - because 99% of the time, they're implementing what I'd be implementing anyway.
The main reason I compare it to memcpy is simply to emphasize that if you have a pointer member variable, only the pointer value will be copied (that is, a new object that the pointer is pointing to will not be created).
As a side note, they will only implement what you would do if your data members are simple types. If they are complex types that don't have a copy-constructor/assignment operator defined, chances are it isn't going to do what you would expect (e.g. if you have a custom vector-type class that doesn't have a copy-constructor).
Stuart Dootson wrote: I take your point about having copy constructors to step through @ debug time - it's just not something I find myself doing.
After finding myself trying to debug implicit functions several times, I got in the habit of making sure they were explicit. It only takes one time of spending a week trying to figure out why your code doesn't work (when it should) before you want to make sure you don't run into the problem again.
If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
|
|
|
|
|
i'm currently experiencing a strange problem under vc++ .NET 7.0. i hope
someone has a hint for me, i'm kind of lost atm.
i'm using a vector of object instances in my code.
using the release build,I get a runtime error intermittently while adding to the vector using push_back.
When i use the debug build. i can add
as much elements as i want without any problems .
Is there any special setting to be done for reease build?
It is is simple as
EMEventContainer instance("key","value");
this_vector.push_back(instance);
But it does not work properly in release build.
|
|
|
|
|
rana74 wrote: Is there any special setting to be done for reease build?
No. You have a bug.
"Alot of the people on this forum are incredibly stupid, thinking that the internet is real" Score: 1.0 in the Soap Box
led mike
|
|
|
|
|
post copy constructor of EMEventContainer
dynamic allocations inside EMEventContainer?
Kuphryn
|
|
|
|
|
This is usually a sign that you have some memory issues elsewhere in your code. Debug builds allocate extra space before and after sections of memory to help indicate if you have any buffer overrun issues. Release builds don't have that, so if you do have such a problem, you will corrupt memory rather quickly. Things to look for are the constructor of the EMEventContainer object, its copy constructor, its copy assignment operator, any heap allocations within that class, and if you are using multiple threads, any unprotected accesses to an STL container.
If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
|
|
|
|
|
Hello,
I have Visual C++ program that implements Graphical User Interface (GUI). This program uses CWinApp, CFormView, CFrameWnd, and CDocument.
Now I need to package this C++ application to work as COM DLL (or ActiveX).
This program must be used as popup window from another program written in Java.
Is it possible to convert Visual C++ program with GUI into COM DLL or ActiveX ?
Please advice.
|
|
|
|
|
I expect you'll have to create a new MFC ActiveX project and copy across and/or modify the existing code.
Kevin
|
|
|
|
|
My former employer wanted a similar thing done. After months of research, we found out that yes, we could reuse SOME of the existing code, but that we would still have to rewrite a lot of code to get it to behave as an ActiveX control. Basically, if your code is layered (e.g. a 3-layer setup where GUI is separate from business logic is seperate from data access) then you will likely only have to rewrite the GUI layer. If not, you will be doing it piece-meal.
If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
|
|
|
|
|
Hello,
Thank you for information!
I found an article called "Adding automation to MFC applications":
http://www.codeproject.com/com/mfc_autom.asp
This article outlines steps that should be taken to enable MFC application as COM object.
I am trying to follow these steps. But, I am running into number of problems. I am new to Visual C++ environment, and it takes me some time to resolve these issues.
The problem that I am trying to resolve now is with IDL file. I am able to compile IDL with MIDL. I included generated header file into my Visual C++ project. However, when I include generated C file, I am getting compile errors.
If at some point I will succeed with this project, I will share details of my work on this web site.
Thank you!
|
|
|
|
|
I have the following code:
std::vector<emeventcontainer>::iterator it=pevent.begin();
try{
while(it != pevent.end()) {
RepDataManager::Log(RepDataManager::HIDEBUG, " Processing next");
EMEventContainer curr = *it;
......
.....
.....
...
it++;
RepDataManager::Log(RepDataManager::HIDEBUG , " About to process next element");
}
This function succeeds in debug builds but results in abnormal crashes in release build .I am using VC++ .NET 7.0
How can i avoid this crash?I have read abotu checked iterators but do not know how to work around this crash.I have tried #define _SECURE_SCL 0 but to no avail.
|
|
|
|
|
rana74 wrote: EMEventContainer curr = *it;
By any chance , Should there any problem, in how curr is used.
Can you see, by commenting that usage, iteration though all elements is problem free?
|
|
|
|
|
I have read that we can use
#define _HAS_ITERATOR_DEBUGGING 0
#define _SECURE_SCL 0
to disable the checked-iterator behaviour in VC++ 5.0
However,instead of defining this at the top of each c++ file,is there some place in project settings where i can define this ,so that it applies to all c++ files?
|
|
|
|
|
1. Include your macros in a common header file and include this file in all your required files.
OR
2. Include your macros in stdafx.h
OR
3. If I am not wrong there is a compiler option "/D". Check this in MSDN.
S o h a i l K a d i w a l a
modified 21-Apr-21 21:01pm.
|
|
|
|
|