I currently have a class called TextureObject. In the creation function I create the texture, and assign a LPCSTR in the class to a parameter given in the function. When I return that LPCSTR later, it returns in an unexpected manner.
Some type names and functions are from DirectX 11, just ignore them.
Code:
The h File:
class TextureObject
{
public:
ID3D11ShaderResourceView *pTexture;
LPCSTR GetFilename() const { return *FFilename; }
bool IsNotNull;
void CreateTexture(ID3D11Device &dev,LPCSTR Filename);
void ReCreate(ID3D11Device &dev);
void Release();
int relativeId;
private:
LPCSTR *FFilename;
};
The cpp file:
void TextureObject::CreateTexture(ID3D11Device &dev,LPCSTR Filename)
{
D3DX11CreateShaderResourceViewFromFile(
&dev, // the Direct3D device
Filename, // load Wood.png in the local folder
NULL, // no additional information
NULL, // no multithreading
&pTexture, // address of the shader-resource-view
NULL); // no multithreading
FFilename = new LPCSTR(Filename);
IsNotNull = true;
}
void TextureObject::ReCreate(ID3D11Device &dev)
{
CreateTexture(dev, *FFilename);
}
When using vs 2012 debugger in the CreateTexture function, the Filename debugger values are:
0x0a06fed0 "C:\Users\Utilizador\Desktop\particle.png"
Which is perfect for me! When i assign the class's FFilename:
FFilename = new LPCSTR(Filename);
It's ok. When I check the value of FFilename within the scope of this function, it's the same value of the Filename. But when i use GetFilename, things start getting crazy:
= 0x0a06fed0 "îþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþü =I.C"
Mmm, I just met you, and this is crazy, but... Here's my value. mKay?
Well, please help me. Thank You
You are not copying the string. You are copying the pointer. I think you probably wanted to copy the string, because you cannot guarantee the caller's pointer will still reference valid data at a later time.
LPCSTR is just a const char*. There's probably a corresponding windows call, but I would just use strdup to copy the string.
Define FFilename as LPCSTR:
LPCSTR FFilename;
And then:
void TextureObject::CreateTexture(ID3D11Device &dev,LPCSTR Filename)
{
D3DX11CreateShaderResourceViewFromFile(
&dev, // the Direct3D device
Filename, // load Wood.png in the local folder
NULL, // no additional information
NULL, // no multithreading
&pTexture, // address of the shader-resource-view
NULL); // no multithreading
FFilename = strdup(Filename);
IsNotNull = true;
}
void TextureObject::ReCreate(ID3D11Device &dev)
{
CreateTexture(dev, FFilename);
}
Since you are using C++, you are free to use std::string instead, which will be cleaned up automatically when the object is destroyed.
When you create your pointer FFilename, you're initializing it with another pointer. That's not going to make a copy of the string, now you have two pointers pointing to the same thing. Presumably that thing is a temporary object, and when you go to look at it later it's no longer valid.
I'd suggest using std::string for this instead, it's much less error prone. The c_str method can get a LPCSTR at any time.
As marcin_j said, use std::[w]string. As for the line:
FFilename = new LPCSTR(Filename);
It just allocates 4 bytes for a pointer and initializes it to the filename string. It doesn't actually copy the string. So you can still use the string, but it is owned by whoever calls TextureObject::CreateTexture, and may be released while TextureObject is still referencing it.
Change the class to:
class TextureObject
{
public:
// ...all the same stuff as before...
private:
wstring FFilename; // it's better to store filenames as Unicode
};
And the methods to:
void TextureObject::CreateTexture(ID3D11Device* dev, const wstring& Filename)
{
D3DX11CreateShaderResourceViewFromFile(
dev, // the Direct3D device
Filename.c_str(), // load Wood.png in the local folder
NULL, // no additional information
NULL, // no multithreading
&pTexture, // address of the shader-resource-view
NULL); // no multithreading
FFilename = Filename;
IsNotNull = true;
}
void TextureObject::ReCreate(ID3D11Device* dev)
{
CreateTexture(dev, FFilename.c_str());
}
Related
I have a Win32 C++ dll (A) that calls another Win32 C++ dll (B). (B) is loaded using LoadLibrary and contains a method:
Draw(HDC hDC, LPRECT lpRect, LPBUFFER buffer, LPOPTIONS options)
Buffer structure is defined as:
struct Buffer
{
char* pData;
long Length;
TCHAR FileName[MAX_PATH];
Extension Extension;
};
typedef Buffer BUFFER, *LPBUFFER;
(A) fills BUFFER with filename, length etc and calls the Draw function. The Draw function then uses the values from BUFFER. It all works fine when DLLs are compiled as 64-bit but if I compile them as 32-bit then I start getting garbage values in BUFFER fields in (B). Logs shows that the values are good in (A) but turn into garbage when they reach (B).
I tried changing the Structure Alignment Option /ZpX and calling convention for Draw method (__cdecl, __stdcall) but none helped. I think it is related to calling convention because if I change Draw function syntax and put BUFFER as first param then (B) gets correct values. What's going on here?
Function pointer type:
typedef bool (__cdecl *DrawFunc)(HDC hDC, LPRECT lpRect, LPBUFFER buffer, LPOPTIONS options);
Then in InitInstance:
pDrawFunc = (DrawFunc)GetProcAddress(dllHandle, "Draw");
UPDATE
1. As mentioned above, if I put BUFFER as first param then it receives correct values.
2. HDC being a single numeric value always receives correct value
3. RECT gets incorrect values, very large ones
I believe the problem has something to do with structs. Only structs get incorrect values.
UPDATE 2
OK I found out my own silly mistake, the declaration for Draw method had LPRECT whereas the implementation had RECT. My bad, sorry about that.
But I am still not sure why:
1. Other parameters were showing garbage values?
2. Why it worked in 64-bit?
Ok, I create a solution with 3 projects: library B, that contains Draw(), library A, that has Test(), that loads library B and call Draw() with some Buffer* and application test, that links with library A and calls Test(). Everything works fine, both for 32 bit and 64. Small snippet of Test():
#include "stdafx.h"
#include "A.h"
#include "../B/B.h"
namespace {
LPBUFFER CreateBuffer(const char* const data, LPCTSTR const name)
{
if(!data || !name)
return NULL;
LPBUFFER buffer = new BUFFER();
buffer->Length = static_cast<long>(strlen(data) + 1);
buffer->pData = new char[buffer->Length];
strcpy_s(buffer->pData, buffer->Length * sizeof(char), data);
buffer->Extension = 0;
::ZeroMemory(buffer->FileName, _countof(buffer->FileName) * sizeof(TCHAR));
_tcscpy_s(buffer->FileName, name);
return buffer;
}
void DestroyBuffer(LPBUFFER buffer)
{
delete [] buffer->pData;
buffer->Length = 0;
buffer->pData = NULL;
buffer->Extension = 0;
::ZeroMemory(buffer->FileName, _countof(buffer->FileName) * sizeof(TCHAR));
delete buffer;
}
} // namespace
A_API void Test()
{
HMODULE b_lib = ::LoadLibrary(_T("B.dll"));
if(!b_lib)
{
::OutputDebugString(_T("Can't load library\n"));
return;
}
typedef bool (*DrawFunction)(HDC hDC, LPRECT lpRect, LPBUFFER buffer, LPOPTIONS options);
DrawFunction draw = reinterpret_cast<DrawFunction>(::GetProcAddress(b_lib, "Draw"));
if(!draw)
{
::OutputDebugString(_T("Can't get address of Draw()"));
goto FINISH_LABEL;
}
LPBUFFER buffer = CreateBuffer("test", _T("path"));
draw(NULL, NULL, buffer, NULL);
DestroyBuffer(buffer);
FINISH_LABEL:
::FreeLibrary(b_lib);
b_lib = NULL;
}
And a whole solution: https://www.dropbox.com/s/5ei6ros9e8s94e2/B.zip
I've been researching passing a struct as a parameter from a C++ client to a C++ server using COM. I've found many examples but none that really explained it to me like I'm five nor any that really provided a firm understanding of how to do what I want, which is simply pass a C++ struct through a COM interface where both sides are C++. Should be easy, right?
I have established my struct as follows in the IDL file on server-side:
[
uuid(7F0C9A48-3C41-425B-B4E6-8156B61D5355),
version(1.0)
]
typedef struct xxxData
{
int iWidth;
int iHeight;
SafeArray(short) pxxxData;
} xxxData;
// Fix for UUID DECLARATION FOR _uuidof() functionality
// From http://go4answers.webhost4life.com/Example/error-c2787-no-guid-been-associated-158947.aspx
cpp_quote("struct __declspec(uuid(\"{7F0C9A48-3C41-425B-B4E6-8156B61D5355}\")) xxxData;")
Which works, so far as I can tell.
Now my client calls GetImageData which is shown as follows:
[id(16)] HRESULT GetImageData([in,out] VARIANT* pData);
Now my client call is as follows with this function:
VARIANT* pData = new VARIANT;
VariantInit( pData );
xxxData* data = new xxxxData;
HRESULT hr = mpCOMEvents->GetImageData(pData);
data = (FBIS_ImageData*)(pData->pvRecord);
int length = data->iWidth * data->iHeight;
However, length is giving me an incorrect address location. This makes me wonder if my use of pvRecord is incorrect and if I can really typecast it?
Here is my COM server side:
xxxData data;
//SAFEARRAY *psa;
IRecordInfo *pRI;
HRESULT hr;
/* Pass in Structure Information */
data.iHeight = 100;
data.iWidth = 100;
// Used http://vcfaq.mvps.org/com/4.htm as reference
hr = GetRecordInfoFromGuids(LIBID_xxxLib, 1, 0, 0x409, _uuidof(xxxData), &pRI);
VariantInit(pData);
pData->vt = VT_RECORD;
pData->pvRecord = &data;
pData->pRecInfo = pRI;
pRI = NULL;
There's some confusion here.
If you're not aiming to be automation friendly, change your IDL to:
[size_is=iWidth*iHeight] unsigned short* pxxxData;
and don't use SAFEARRAY API on this. For marshalling, you'll have to compile a proxy/stub DLL and register it.
If you're aiming to be automation friendly, change your IDL to:
SAFEARRAY(short) pxxxData;
and do use the SAFEARRAY API on this. For marshalling, you'll have to compile a typelib (optionally, embed it) and register it. This also enables early-binding (e.g. VB6, tlbimp).
This will work for languages/environments that support user-defined types. For the ones that don't (e.g. scripting languages), you'll have to use an oleautomation/dual/IDispatch-based interface instead of a struct (and implement in in the server).
EDIT: Based on the changes you made to your question.
You should declare the pData parameter as out only, GetImageData will populate it, not use it and possibly replace it. It also only requires marshaling on return, not on the call. Here's a suggestion:
[id(16)] HRESULT GetImageData([out] VARIANT* pData);
Your client code has a memory leak, it always creates an xxxData. Here's a suggestion:
// If pData is in-out, this is not safe, use CoTaskMemAlloc(sizeof(VARIANT)) instead.
// The callee may override the buffer by assuming it was CoTaskMemAlloc'ed, thus
// assuming it can CoTaskMemFree the original location and set the pointer to a new
// CoTaskMemAlloc'ed location.
// The callee may be a proxy.
// Assuming it's out only, we can provide any location with enough space for a VARIANT.
VARIANT vData;
VariantInit( &vData );
xxxData* data; // remove memory leak
HRESULT hr = mpCOMEvents->GetImageData(&vData);
// error handling removed for clarity (I hope)
data = (xxxData*)(vData.pvRecord);
int length = data->iWidth * data->iHeight;
// ... use data ...
// Don't forget to clear the variant, or there'll be a memory leak
// It implies:
// vData.pRecInfo->RecordDestroy(vData.pvRecord);
// This should recursively release memory allocated in each field
// and finally release the memory allocated for the struct itself.
// vData.pRecInfo->Release();
VariantClear( &vData );
// don't use data past this point
Your server code is setting pData->pvRecord to point to the stack, which means that it will potentially be overwritten by a caller or some other invoked function. Here's a suggestion:
xxxData* data; // Changed to pointer
IRecordInfo *pRI;
HRESULT hr;
// data.iHeight = 100; // removed
// data.iWidth = 100; // removed
hr = GetRecordInfoFromGuids(LIBID_xxxLib, 1, 0, 0x409, _uuidof(xxxData), &pRI);
// error handling removed for clarity (I hope)
VariantInit(pData);
// This will allocate memory for the struct itself
// For fields that require memory allocation, follow "normal" COM rules,
// such as using CoTaskMemAlloc for buffers, SysAllocString or similar for BSTRs,
// etc.
// For each inner (pointed to) structure, you should call RecordCreate on the
// respective IRecordInfo instance for that type.
data = (xxxData*)pRI->RecordCreate();
data->iHeight = 100; // new
data->iWidth = 100; // new
// If pData is in-out, this will leak, use VariantClear instead.
// Assuming it's out only, use VariantInit as it points to (allocated) garbage.
VariantInit(pData);
pData->vt = VT_RECORD;
pData->pvRecord = data; // data is already a pointer
pData->pRecInfo = pRI;
pRI = NULL;
// This won't (normally) leak, the caller must call VariantClear on the out VARIANT.
// The caller may be a stub.
What is the correct way of doing this:
_bstr_t description;
errorInfo->GetDescription( &description.GetBSTR() );
or:
_bstr_t description;
errorInfo->GetDescription( description.GetAddress() );
Where IError:GetDescription is defined as:
HRESULT GetDescription (BSTR *pbstrDescription);
I know I could easily do this:
BSTR description= SysAllocString (L"Whateva"));
errorInfo->GetDescription (&description);
SysFreeString (description);
Thanks
The BSTR is reference counted, I seriously doubt that will work right if you use GetAddress(). Sadly the source code isn't available to double-check that. I've always done it like this:
BSTR temp = 0;
HRESULT hr = p->GetDescription(&temp);
if (SUCCEEDED(hr)) {
_bstr_t wrap(temp, FALSE);
// etc..
}
To follow up on #Hans's answer - the appropriate way to construct the _bstr_t depends on whether GetDescription returns you a BSTR that you own, or one that references memory you don't have to free.
The goal here is to minimize the number of copies, but also avoid any manual calls to SysFreeString on the returned data. I would modify the code as shown to clarify this:
BSTR temp = 0;
HRESULT hr = p->GetDescription(&temp);
if (SUCCEEDED(hr)) {
_bstr_t wrap(temp, false); // do not copy returned BSTR, which
// will be freed when wrap goes out of scope.
// Use true if you want a copy.
// etc..
}
A late answer that may not apply to earlier (or later) versions of Visual Studio; however,
VS 12.0 has the _bstr_t implementation inline, and evidently an internal Data_t instance is created with a m_RefCount of 1 when calling GetBSTR() on a virgin _bstr_t. So the _bstr_t lifecycle in your first example looks to be okay:
_bstr_t description;
errorInfo->GetDescription( &description.GetBSTR() );
But if _bstr_t is dirty, the existing internal m_wstr pointer will be overwritten, leaking the previous memory it referenced.
By using the following operator&, a dirty _bstr_t can be used given that it's first cleared via Assign(nullptr). The overload also provides the convenience of utilizing the address operator instead of GetBSTR();
BSTR *operator&(_bstr_t &b) {
b.Assign(nullptr);
return &b.GetBSTR();
}
So, your first example could instead look like the following:
_bstr_t description(L"naughty");
errorInfo->GetDescription(&description);
This evaluation was based on comutil.h from VS 12.0.
_bstr_t (and its ATL sibling CComBSTR) are resource owners of BSTR. Spying from the code it seems that 'GetAddress' is specifically designed for the use case of working with BSTR output parameters where it is expected that client frees the BSTR.
Using 'GetAddress()' is not equivalent to using '&GetBSTR()' in case the _bstr_t already owns a BSTR. MSDN states: 'Frees any existing string and returns the address of a newly allocated string.'.
_bstr_t bstrTemp;
HRESULT hr = p->GetDescription(bstrTemp.GetAddress());
Caveat: this specific use case of 'GetAddress' is not stated in the documentation; it is my deduction from looking at the source code and experience with its ATL counter part CComBSTR.
Since user 'caoanan' questioned this solution, I paste here the source code Microsoft:
inline BSTR* _bstr_t::GetAddress()
{
Attach(0);
return &m_Data->GetWString();
}
inline wchar_t*& _bstr_t::Data_t::GetWString() throw()
{
return m_wstr;
}
inline void _bstr_t::Attach(BSTR s)
{
_Free();
m_Data = new Data_t(s, FALSE);
if (m_Data == NULL) {
_com_issue_error(E_OUTOFMEMORY);
}
}
inline _bstr_t::Data_t::Data_t(BSTR bstr, bool fCopy)
: m_str(NULL), m_RefCount(1)
{
if (fCopy && bstr != NULL) {
m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(bstr),
::SysStringByteLen(bstr));
if (m_wstr == NULL) {
_com_issue_error(E_OUTOFMEMORY);
}
}
else {
m_wstr = bstr;
}
}
My answer is also late. Suppose you have the signature HRESULT PutDescription (BSTR NewDescription);. In that case do the following
_bstr_t NewAdvice = L"Great advice!";
HRESULT hr1 = PutDescription(NewAdvice.GetBSTR());
By the rules of COM, the function PutDescription is not allowed to change or even destroy the passed BSTR.
For the opposite HRESULT GetDescription (BSTR *pActualDescription); pass a virgin _bstr_t by means of the function GetAddress():
_bstr_t GetAdvice;
HRESULT hr2 = GetDescription(GetAdvice.GetAddress());
The function GetAddress() frees any existing string and returns the address of a newly allocated string. So, if you pass a _bstr_t that has some content, this content will be freed and is therefore lost. The same happens to all _bstr_ts that share the same BSTR. But I think this is a stupid thing to do. Why passing an argument with content to a function that is supposed to change that content?
_bstr_t GetAdvice = L"This content will not survive the next function call!";
HRESULT hr = GetDescription(GetAdvice.GetAddress());
A real moron might even pass a _bstr_t that is assigned to a raw BSTR:
BSTR Bst = ::SysAllocString(L"Who would do that?");
_bstr_t GetDescr;
GetDescr.Attach(Bst);//GetDescr wraps Bst, no copying!
HRESULT hr = GetDescription(GetDescr.GetAddress());
In that case GetDescr gets the expected value but the content of Bst is unpredictable.
int GetDataStr(_bstr_t & str) override {
BSTR data = str.Detach();
int res = m_connection->GetDataStr( &data );
str.Attach(data);
return res;
}
Trying to wrap this short example in C++. (and its been a while since I did this).
int main(int argc, char* argv[])
{
//Objects
CFtpConnection* pConnect = NULL; //A pointer to a CFtpConnection object
ftpClient UploadExe; //ftpClient object
pConnect = UploadExe.Connect();
UploadExe.GetFiles(pConnect);
system("PAUSE");
return 0;
}
.h -
class ftpClient
{
public:
ftpClient();
CFtpConnection* Connect();
void GetFiles(CFtpConnection* pConnect);
};
.cpp -
//constructor
ftpClient::ftpClient()
{
}
CFtpConnection* ftpClient::Connect()
{
// create a session object to initialize WININET library
// Default parameters mean the access method in the registry
// (that is, set by the "Internet" icon in the Control Panel)
// will be used.
CInternetSession sess(_T("FTP"));
CFtpConnection* pConnect = NULL;
try
{
// Request a connection to ftp.microsoft.com. Default
// parameters mean that we'll try with username = ANONYMOUS
// and password set to the machine name # domain name
pConnect = sess.GetFtpConnection("localhost", "sysadmin", "ftp", 21, FALSE );
}
catch (CInternetException* pEx)
{
TCHAR sz[1024];
pEx->GetErrorMessage(sz, 1024);
printf("ERROR! %s\n", sz);
pEx->Delete();
}
// if the connection is open, close it MOVE INTO CLOSE FUNCTION
// if (pConnect != NULL)
// {
// pConnect->Close();
// delete pConnect;
// }
return pConnect;
}
void ftpClient::GetFiles(CFtpConnection* pConnect)
{
// use a file find object to enumerate files
CFtpFileFind finder(pConnect);
if (pConnect != NULL)
{
printf("ftpClient::GetFiles - pConnect NOT NULL");
}
// start looping
BOOL bWorking = finder.FindFile("*"); //<---ASSERT ERROR
// while (bWorking)
// {
// bWorking = finder.FindNextFile();
// printf("%s\n", (LPCTSTR) finder.GetFileURL());
// }
}
So basically separated the connection and file manipulation into 2 functions. The findFile() function is throwing the assert. (Stepping into the findFile() and it is specifically at the first ASSERT_VALID(m_pConnection) in inet.cpp. )
How does the way I am passing the arround CFtpConnection* pConnect look?
EDIT - Looks like CObject vfptr is overwritten (0X00000000) in the GetFiles() function.
Any help is appreciated. Thanks.
ANSWER:
This session object must be allocated in the Connection function, with a pointer
declared as a member function of the class. When creating the object within the function,
"CInternetSession sess(_T("MyProgram/1.0"));" the object/session will be terminated when the function exits, being thrown off the stack. When that happens, we can't use the pConnect pointer in other functions.
There is a hierarchy to WinInet objects, with session being the top. If session is gone nothing else can be used. Thus, we must use new to allocate the object in memory so that it sustains after this function exits.
I don't think there is any real value in having the ftpClient class return the CFTPConnection object out of Connect (unless i'm missing something that you intend?) - it should just have that as a Member variable, and GetFiles could use that member directly (Likewise you'd add the CInternetSession as a member of the class and avoid the problem you describe above when it goes out of scope.)
In that manner the ftpClient manages the lifetime of the CFTPConnection and can destroy it in its destructor.
I've already found a workaround to this problem, but was just wondering if anyone knew what was actually happening to cause the problem I was seeing. My guess is that it has something to do with mutability of strings, but I thought the CString object accounted for that in the copy constructor.
The following code causes mFileName to be overwritten:
class File {
public:
...
CString GetFilename() {return mFileName;}
private:
CString mFileName;
};
class FileContainer {
private: File* mFile;
public:
FileContainer() {
mFile = new File("C:\temp.txt");
}
GetFilename(CString& fileName) {
fileName = mFile->GetFileName();
}
}
void UpdateText() {
FileContainer fileCnt;
CString filePath(L"");
this->fileCnt.GetFilename(filePath);
...
::DrawText(hDC, filePath, -1, &destRect, DT_PATH_ELLIPSIS | DT_MODIFYSTRING | DT_CALCRECT);
}
What happens is that the first time UpdateText is called, GetFilename returns C:\temp.txt. Assuming that the bounding rect caused the text to be truncated to "...\temp.txt" on the first call, "...\temp.txt" is what is returned from GetFilename on the second call to UpdateText.
Even more perplexing is that this didn't cause mFileName to be changed:
void UpdateText() {
FileContainer fileCnt;
CString filePath(L"");
this->fileCnt->GetFilename(filePath);
filePath = L"TEST";
}
GetFilename always returned C:\temp.txt. So it would seem that the DrawText function is somehow finding the original CString and modifying it. But how?
UPDATE: I figured I'd throw another odd chunk of code that also causes mFileName to be overwritten:
class File {
public:
...
CString GetFilename() {return CString(mFileName);}
private:
CString mFileName;
};
That seems like it should create a new object and return that new object. Yet, somehow, DrawText still overwrites mFileName.
If I change the code to the following, I don't have any issues:
class File {
public:
...
CString GetFilename() {return CString(mFileName.GetBuffer());}
private:
CString mFileName;
};
The only thing that seems to solve the problem is to construct a new CString the way I showed in the workaround. What is DrawText doing when I pass the DT_MODIFYSTRING option?
First, note that CString can be used as a raw string pointer in two ways:
operator LPCSTR - gives a pointer which should never be modified.
GetBuffer - gives a pointer to memory specifically for the purpose of modifying the string.
Now, DrawText is declared to accept a LPCSTR. So when you pass a CString object directly as in your code, it implicitly uses operator LPCSTR to give the function what it says it wants, a constant string pointer.
However, DT_MODIFYSTRING says that DrawText can modify the string it was given. So internally, DrawText must be throwing away the constness of the pointer and modifying the string anyway.
This combination is a bad thing. But the fault is mainly in the implmentation of DrawText which is violating its own declaration.
As for why this modifies other CString objects: Apparently when a CString object is copied, it delays copying the internal string memory until something tries to modify the string through a CString member function. But until that happens, the operator LPCSTR of each CString object would still point to the same shared internal memory. This is normally fine, as long as any code using it is obeying the rules of const-correctness. However, as we've already seen, DrawText with DT_MODIFYSTRING is not playing by the rules. Thus, it is overwriting memory shared by multiple CString objects.
So to fix this problem, you either need to stop using DT_MODIFYSTRING if you don't actually need the modified text. Or else you need to pass the string to DrawText using filePath.GetBuffer() and then call filePath.ReleaseBuffer() afterwards.
Well there are some discrepancies in the code that you posted:
In 'class File':
GetFileName() {return mFileName;}
There is no return type here? Also in the FileContainer class you define the stored 'File' object as a pointer, but in the GetFileName function you access it as if it was not a pointer?
File* mFile;
...
mFile.GetFileName();
As far as why this is happening well right now I can't really tell. Another work around however would be to change the GetFileName function to return a const ref, this should ensure that the returned value can never be changed.
const CString& GetFileName() { return mFileName; }