|
Thanks for the help. I have on more question. Here are two samples of what I believe you are telling me. I believe the first one is correcte, but would find it wonderful if the second one would also work. Thas tould let me simply override the Update method of my CRecordset objects.
In this first case I am protecting the entire DB operation. There are literally hundreds of updates in the project. All are unprotected.
class CTester(prsSomeTable)
{
UpdateDB()
{
CMutexAccessor *pMut;
pMut = new CMutexAccessor();
prs->Edit();
prs->x = 2;
prs->Update();
delete pMut;
}
};
In the second case I am only protecting the Update. Is this sufficient? If I can get away with simply protecting the Update operation itself, I can reduce the problem to about a dozen changes by overriding the Update method of my recordset classes.
class CTester(prsSomeTable)
{
UpdateDB()
{
CMutexAccessor *pMut;
prs->Edit();
prs->x = 2;
pMut = new CMutexAccessor();
prs->Update();
delete pMut;
}
};
Thanks for the help,
Bill
|
|
|
|
|
I couldn't tell from the little information you provide. What you have to determine is wether the operation is trying to change a shared resource. So: is that prs private to each thread? Is Edit() an read-only operation? If both answers are yes, I guess it is safe to leave those ops out of the mutex-protected zone. If in doubt, wrap it all up within the mutex and measure performance --maybe it is OK for your purposes.
Another subject is that CMutexAccessor should not be used like you do, but this other way:
UpdateDB()
{
CMutexAccessor mut("MYONEANDONLYNAMENOONECAMETOFIGUREOUTBEFORE");
prs->Edit();
prs->x = 2;
pMut = new CMutexAccessor();
prs->Update();
} I.e, construct it on the stack, not with new . This way you stay confident that the destructor will be called even if your function exits in unexpected fashions (for instance, if some of the operations throws an exception). To protect more or less code inside the function just move the CMutexAccessor mut... line up or down.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Thanks again. Sorry I forgot to type in the mutex name.
Here's what I actually came up with... (sample usage is at the bottom.
in stdafx.h:
extern CRequests * g_prsRequests;
In main program module
CRequests * g_prsRequests;g_prsRequests
In program entry point:
g_prsRequests = new CRequests();
Here is the modified CRequests class. What do you think?
class CRequests : public CRecordset
{
public:
CRequests(CDatabase* pDatabase = NULL);
DECLARE_DYNAMIC(CRequests)
long m_ID;
CString m_SERVICE_TYPE;
clip for brevity
CMutexAccesor *pMutex;
#define DBMUTEX "AFX_REQUESTS_3B2738B5_A172_11D5_9FCB_00B0D081C96F"
public:
virtual CString GetDefaultConnect();
virtual CString GetDefaultSQL();
virtual void DoFieldExchange(CFieldExchange* pFX);
virtual void AddNew()
{
try
{
pMutex = new CMutexAccesor(DBMUTEX);
CRecordset::AddNew();
}
catch (CDBException *e)
{
if (pMutex)
{
if (pMutex) delete pMutex;
pMutex = NULL;
}
throw e;
}
if (pMutex) delete pMutex;
}
virtual void Edit()
{
try
{
pMutex = new CMutexAccesor(DBMUTEX);
CRecordset::Edit();
}
catch (CDBException *e)
{
if (pMutex)
{
if (pMutex) delete pMutex;
pMutex = NULL;
}
throw e;
}
if (pMutex) delete pMutex;
}
virtual BOOL Update()
{
BOOL bRet;
try
{
bRet = CRecordset::Update();
}
catch (CDBException *e)
{
delete pMutex;
throw e;
}
delete pMutex;
return bRet;
}
void AbandonUpdate()
{
if (pMutex) delete pMutex;
}
};
in the cpp file I set pMutex = NULL in the constructor and delete it in the desctructor.
I'm using a pointer variable so I can create and destroy the mutex for each update operation.
e.g.
try {
g_prsRequests->Edit(); // mutex created
g_prsRequests->m_ID = 1;
g_prsRequests->Update(); // mutext destroyed.
}
catch (CException *e)
{
...blah blah
in the case of an actual DB exception, the mutex is already destroyed...
g_prsRequests->AbaondonUpdate(); // mutex destroyed
}
Thanks for the help,
Bill
|
|
|
|
|
No no no no no. You got it wrong. Do not have a CMutexAccessor pointer as a member of CRequests . Do not. This try catch labyrinth is something you would regret having to maintain in the long term.
Instead, have a local CMutexAccessor variable at the beginning of each protected zone, like this:
virtual void AddNew()
{
CMutexAccesor(DBMUTEX) mut;
CRecordset::AddNew();
}
virtual void Edit(){
CMutexAccesor(DBMUTEX) mut;
CRecordset::Edit();
} and so forth. That simple. This schema ensures exclusive access among sections of code adorned this way, no matter the object, the method, the thread or the process that piece of code belongs in. What identifies the section (and I guess this is where your misunderstanding stems from) is not the particular CMutexAccesor object used to perform the locking, but only the name of the mutex (DBMUTEX in your example). Do not worry also about mutex-protected methods calling other mutex-protected methods: it'll all work like a breeze (your approach is a maintenance nightmare with respect to this).
Hope I made myself clear. Do't hesitate to ask for more help, if needed.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Got it. Thanks. You are right, I was thinking that the object held the lock, not the name. I really appreciate your time on this. You have likely saved me many hours of frustration.
Thanks for the help,
Bill
|
|
|
|
|
In order to set my CEditView to read-only, i modified the createstruct passed to PreCreateWindow as so;
BOOL CLogView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.dwExStyle = ES_READONLY;
return CEditView::PreCreateWindow(cs);
}
But, this doesn't result in the view having a read-only status when the application is loaded. Neither does calling ModifyStyleEx during OnInititalUpdate with the relevant style.
I could just override input events to stop the user forcing the view in focus, but that isn't the logical or correct way to go about it.
Any ideas?
Simon
Hey, it looks like you're writing a letter!
|
|
|
|
|
try
GetEditCtrl().SetReadOnly(TRUE);
---
It may be that your sole purpose in life is simply to serve as a warning to others.
|
|
|
|
|
Hi!
A dynamically loaded DLL I wrote receives a pointer to a class that I
pass to it from the client app. A certain method in that class, (let's
call it) cClass::AllocMem()is called inside the DLL to allocate some memory.
The "problem" is that the allocated memory is stored on the heap
of the DLL, as opposed to the heap of the client app, which is
where I'd like it to reside. Is there any way for me to still be
able to call this AllocMem() method inside the DLL and have the
memory allocated inside the client app's heap?
This is just a plain vanilla Win32 application and DLL. I know in
MFC, there the AFX_MANAGE_STATE() macro to help out with these things,
but it is unavailable to me.
Does anybody have any ideas?
Thanks a bunch!
Steve The Plant
|
|
|
|
|
Have you tried declaring cClass::AllocMem() as virtual?
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Well, I tried and it worked! But I don't understand why.
Does it have anything to do the class' virtual table
when you pass it to the DLL?
Steve The Plant
|
|
|
|
|
Well, actually it's magic
Now seriously, what's happening here is this:
Both your client app and your DLL are having access to cClass.h and cClass.cpp , and both modules are subsequently compiling and linking their own copies of cClass::AllocMem() . Now, when you pass a pointer to a cClass object created by the app to the DLL, and the DLL code executes pcClass->AllocMem() , what it is actually doing is invoking its own version of cClass::AllocMem() against pcClass : crash promptly ensues when the clients tries to free the memory allocated, for the reasons you already diagnosed.
Now, if you make cClass::AllocMem() virtual, then the DLL does not execute any local version of the method, but instead uses pcClass to locate its associated virtual table and AllocMem() implementation, both of which reside at the app's home (which originally created the object). Having things arranged like this, you can even remove cClass.cpp from the DLL build and things will still work as long as the DLL limits itself to handle cClass pointers passed from the app, and all relevant methods are virtualized. This is also a good thing to have in the light of OO, as it defines an interface contract between the app and the DLL and allows you to change the implementation code without the DLL knowing nor having to be recompiled.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Waitasecond! According to Richter there is no such thing as a local DLL heap in Win32. The DLL uses the heap of the client process. So unless the DLL in question here creates it's own heap using HeapCreate then the OP's problem cannot be caused by allocation of different heaps.
Cheers
Steen.
"To claim that computer games influence children is rediculous. If Pacman had influenced children born in the 80'ies we would see a lot of youngsters running around in dark rooms eating pills while listening to monotonous music"
|
|
|
|
|
All right all right. The picture is larger than I showed, but, taken literally, Richter is wrong.
What is causing these problems with allocating memory on one side and deallocating at the other is the C runtime library (CRT) used by each of the parts. As you know, the CRT comes in six flavors:- (Release) Single-Threaded
- (Release) Multithreaded
- (Release) Multithreaded DLL
- Debug Single-Threaded
- Debug Multithreaded
- Debug Multithreaded DLL
Let's begin with the debug/release aspect. In debug mode, standard C/C++ functions like malloc() and new allocate, when requested a memory block, additional info about the block than can be later used to identify out of bounds errors and stuff like that. This info is stored contiguously to the block delivered to the app, so that when free() or delete are invoked, these functions know where the info should be with respect to the pointer to deallocate. Now, if your app uses a debug CRT and the DLL a release one, and the DLL malloc() s some memory and the app tries to free() it, a crash will follow as the debug CT cannot find that additional info I talked about. Other similar scenarios involving debug/release mixing follow analogous patterns.
Now for the DLL/not DLL option. If you choose not DLL, then the CRT is linked as a static library to your app (or DLL). The CRT maintains a private heap (yes, it does) called _crtheap which is created upon initiation of the app (or the DLL). So, even if you use the exact same version of CRT in static mode for your app and your DLL, your final executable will end up with two _crtheap s accessed respectively from the app CRT and the DLL CRT code. As before, allocating on one side and deallocating on the other leads to catastrophe.
Enough is enough. It is simple to check that all combinations of CRTs exhibit this problem except if both the app and the DLL use the same CRT version and this CRT is linked as a DLL. Then, only one _crtheap exists, shared by all folks in the program, and all other aspects being equal (debug/release, multithreaded/single threaded) things run smooth.
The moral of the story: Expect problems when passing around the responsibility of freeing a CRT-allocated chunk of memory except if you are 100% sure all parts involved (app, DLLs) use the same CRT version and this is linked dynamically.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Wow! You really do know what you're talking about, don't you? I stand corrected, I bow my head before you
I did a search on MSDN on the subject (yeah, I know, should have done it before), and article Q190799 does a pretty good job explaining things (at least after I had read your post first
I will immidiately check if my current multi-DLL application project is linked to the MT DLL version!
Cheers
Steen.
"To claim that computer games influence children is rediculous. If Pacman had influenced children born in the 80'ies we would see a lot of youngsters running around in dark rooms eating pills while listening to monotonous music"
|
|
|
|
|
Hey Guys
I have just made an installer for an application i have finished writing. This installer needs to right a value in the registry for a default file type. What would be the quickest way.
the value has to go into HKEY_LOCAL_MACHINE\SYSTEM\CLASSES\Signedfile\DefaultIcon\
the location of the icon goes into the default value of the DefaultIcon key.
Cheers
Peter
|
|
|
|
|
I believe WriteProfileString does it, but I always use a class from CP called CRegistry.
Christian
After all, there's nothing wrong with an elite as long as I'm allowed to be part of it!! - Mike Burston Oct 23, 2001
|
|
|
|
|
You need to use the function RegSetValueEx or the class that said Christian...
Best Regards....
Carlos Antollini.
|
|
|
|
|
maybe helpful......
BOOL SetRegKeyValue( LPTSTR pszKey, LPTSTR pszSubkey, LPTSTR pszValue )
{
BOOL bOk = FALSE;
LONG ec;
HKEY hKey;
TCHAR szKey[256];
lstrcpy( szKey, TEXT("SOFTWARE\\") );
lstrcat( szKey, pszKey );
ec = RegCreateKeyEx(
HKEY_CURRENT_USER, szKey, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,NULL, &hKey, NULL );
if( ERROR_SUCCESS == ec ){
if( NULL != pszValue ){
ec = RegSetValueEx(
hKey, pszSubkey, 0, REG_SZ, (BYTE *)pszValue,
(lstrlen(pszValue) + 1) * sizeof(TCHAR) );
}
if( ERROR_SUCCESS == ec ) bOk = TRUE;
RegCloseKey(hKey);
}
return bOk;
}
|
|
|
|
|
Could someone tell be how or where to find how to make a dinamic multidimensional array or some easier alternatives?
<marquee>Raffi
|
|
|
|
|
OK, I'll tell you how to find what you're looking for.
- Fire up a browser and visit www.google.com.
- In the search box, type "multidimensional arrays". (Omit the quotes.)
- You'll see a page of links. Click on the second link (www.eskimo.com/~scs/cclass/notes/sx4ba.html).
- Read the page content.
/ravi
"There is always one more bug..."
http://www.ravib.com
ravib@ravib.com
|
|
|
|
|
i'd use a vector of vectors.
-c
#define O 0.05
#define I(c) putchar(c)
main(){int I(int);double l[6];char lO[5];for(*(lO+1)=0;*(lO+1)<'2';I(0x0A),(*(l+5))=-25*O+((*(lO+1)
)++)*O)for((*(lO+2))=0;(*(lO+2))<'2';(*(l+4))=-40.*O+((*(lO+2))++)*O){for((*(l))=(*(l+1))=0,
*(lO)=1;++*(lO)&&(((*(l+2))=((*(l))*(*(l))))+((*(l+3))=((*(l+1))*(*(l+1))))<4.);(*(l+1))=(*(
l))*(*(l+1))+(*(l+5))+(*(l+1))*(*(l)),(*(l))=((*(l+2))-(*(l+3)))+(*(l+4)));I((*(lO)?42:0x20));}}
|
|
|
|
|
oups
Dynamic Multidimensional Array are very easy to create... but harder to use.
look at the creation of a 2d Array
int x = 100;
int y = 200;
int *MyArray;
MyArray = new int[x][y];
and to acces (here initialisation) you must do:
int i,j;
for(i=0;i<x;i++)
for(j=0;j<y;j++)
*(MyArray+i*y+j) =0;
and now ww wan't it 4d
int *MyArray;
MyArray = new int[x*y*z*a];
//and to acces
int i,j,k,l;
for(i=0;i<x;i++)
for(j=0;j<y;j++)
for(k=0;k<z;k++)
for(l=0;l<z;l++)
*(MyArray+(i*y*z*a)+(j*z*a)+(k*a)+l) =0;
delete MyArray;
hope there is no error
Remi Morin
Rmorin@Operamail.com
Remi.Morin@Lyrtech.com
|
|
|
|
|
What kind of data do you want to store?
Todd Smith
|
|
|
|
|
C++ doesn't directly support dynamic multidimensional arrays, however there are ways although a bit more complicated than necessary. Here is another example on how to create one. Unlike Remi Morin's "version" this one is a little harder to create but alot easier to use. And it's alot faster too I guess, since there is no multiplication necessary to access the data. This version does take up a bit more memory though...:
#include "memory.h"
#include "new.h"
void main()
{
int x = 4, y = 7, z = 9;
int i, j, k;
int nSize = x * y * z * sizeof(int);
int* p = (int*)::operator new(x * y * z * sizeof(int));
int* q = p;
::memset(p, 0, nSize);
int*** a = new int**[x];
for (i = 0; i < x; i++)
{
a[i] = new int*[y];
for (j = 0; j < y; j++)
{
a[i][j] = new (q) int[z];
q += z;
}
}
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
for (k = 0; k < z; k++)
{
a[i][j][k] = i + (j * x) + (k * x * y);
}
}
}
for (i = 0; i < x; i++)
{
delete[] a[i];
}
delete[] a;
::operator delete(p);
} Remember that the array objects will not be destructed using the destructor (if any) when deleted. It's possible to make that happen too of course by implicitly calling each and every destructor before deleting the array data. This must be done if the data stored in the array is not of a primitive type, or if for some reason the objects do not need to be destructed.
The included "new.h" file is necessary for using placement new used in the last for-loop in the process of creating the array. This places the allocated object(s) (int's in this example) at the specified pointer (q).
Sprudling
|
|
|
|
|
I've try it but I have a problem with this line in debug mode
int* p = (int*)::operator new(x * y * z * sizeof(int));
I'm unable to compile.
this error raised
E:\woop\woopDoc.cpp(827) : error C2665: 'new' : none of the 4 overloads can convert parameter 1 from type 'char [20]'
In release mode it's ok it work very fine
Why it dos'nt work in Debug?
Remi Morin
Rmorin@Operamail.com
Remi.Morin@Lyrtech.com
|
|
|
|
|