|
i assume you mean 'how do i put a combo box on a toolbar' and that is easy ... just make it a rebar (CReBarCtrl) and plonk it on one of the bands ... s'easy really ... app wizard can generate it for you and all you have to do is edit the dialog template to add the combo box
"every year we invent better idiot proof systems and every year they invent better idiots"
|
|
|
|
|
Actually, I wanted to put it onto a CToolBar. I finally found an example in the MSDN samples. Subclassed CToolBar and added a CComboBox to it. Created a new toolbar resource with just one button, and then when creating the CToolBar I replaced that button with the combo box.
|
|
|
|
|
I have derived a new static control where one can the font of the text contained in the static control. However if the font size is set very big then half the text is obscured. Is there a way I can find out the new dimensions of the text and as such enlarge the static control dynamically.
|
|
|
|
|
use GetTextMetrics() and adjust the size of your static control using MoveWindow()
"every year we invent better idiot proof systems and every year they invent better idiots"
|
|
|
|
|
Does anybody know how to implement a ruler control such as found when one is editing or creating a dialog in MSDEV, or know of a sample application with source code.
|
|
|
|
|
i would create a toolbar and plonk a static control on it that you resize as the toolbar resizes ... in the static you can set the mapping mode to whatever you want and draw the markings for the measurements
could be other ways but hey...
"every year we invent better idiot proof systems and every year they invent better idiots"
|
|
|
|
|
static control on it that you resize as the toolbar resizes
And any help you can offer on the toolbar resizes? Post your code lines here please.
|
|
|
|
|
hmmmm
check out CDialogBar ... it becomes part of the main window and doesnt float around which may be better for a ruler ... plonk the static control in and catch size change messages in the frame window and change the static control size from there
it might fit ur needs more
"every year we invent better idiot proof systems and every year they invent better idiots"
|
|
|
|
|
check out CDialogBar ... it becomes part of the main window and doesnt float around
Are you sure the dialog bars don't float around, or even dock around?
PS. Learn a little more before you post answers, pleeease.
|
|
|
|
|
Wordpad!
Wordpad offers just such a ruler, and it's source comes with VC++.
|
|
|
|
|
|
The MSDN tells me that the thread process for CreateThread must be a function of the following form: DWORD WINAPI ThreadProc( LPVOID lpParameter ).
When I implement this as a simple global procedure I have no problems but if I make the function a member of a class (SerialComms for example) C++ refuses to accept it as a parameter and complains about an illegal cast. Nothing I have tried could coax it into recasting to an acceptable value. The pointer it wants is,
DWORD (WINAPI *ThreadFunction )(void *)
but when I point to a member function it actually sees,
DWORD (WINAPI SerialComms::*ThreadFunction )(void *)
As far as I am concerned these are identical except for the namespace. The linkage is identical also so C++ should only warn me and not kill the compile. I have solved this problem as follows:
union
{
DWORD (WINAPI SerialComms::*x )(void *);
DWORD (WINAPI *y )(void *);
} z;
// Get the read thread procedure address. This is a weird "cast" method.
z.x = ThreadFunction;
m_readThread = CreateThread( NULL, 0, z.y, 0, 0, &m_threadId );
It works but I am not real happy with a "smartass" solution like this.
Question? How can I recast a member function so I don't have to trick C++ into a funny union "cast"? (For C++ purists, my apologies. I'm an old, unreformed assembler and C hacker!)
|
|
|
|
|
Generally, you would declare a member threadproc as static . This placates the C interface, which has no knowledge of member fns. It sort of promotes the fn from out of the classes vtable space into the global arena.
The only sticky point you'll have is that now that the fn is static it can't access the non-static members of the class (static fns have no this pointer). To get around this (no pun) limitation, pass the this pointer as the lpParameter.
later... Actually, your fix is kind of neat - - does it get around the member access problem?
|
|
|
|
|
Thanks for your thoughts Tim. The problem with static member functions is that you have the same function for each instance. This means that there can only be a single thread running for all of my read processes. This is not suitable for my application. I need a unique thread function in each class instance for a number of reasons.
Now comes the interesting part. My little "cast" definitely works for compiling a member function to be the thread procedure. However, class members are ONLY visible if a pointer to the class is passed as the thread parameter. For example (called from my object initialisation routine),
m_thread = CreateThread( NULL, 0, z.y, this, 0, &m_threadId );
NOTE that I do not use the lpParameter at all, I just pass it. I acces members without a pointer i.e. m_count not ((int *)lpParameter)->m_count. If lpParameter is set to null, the function is still started by the CreateThread but "this" is seen as NULL and the member functions are not visible. I read the doco to mean that the lpParameter is passed for my use but it seems that Windows is using it to overwrite "this" somehow. Go figure....
|
|
|
|
|
I think I know why this behaviour can occur:
When calling a instance method, std. C++ uses one "hidden" param "this". Since you have done your nice union and CreateThread is calling "straight" into your instance method using expecting __stdcall and your C++ method is expecting "this-call" unless other specified.
It it is so:
Why does you get this ptr. since as far as I know, "this-call" is expecting "this" in the EXC register, otherwise __stdcall and "this-call" passes params, and cleaning up, in the same manner.
/mario
|
|
|
|
|
You are 100% correct. I have traced this through and I now understand what is happening. THIS METHOD IS EVIL. DO NOT USE! It works by accident. My "smartass" technique is just that - smartass.
When a method with a single parameter like my thread function is called in C++, the parameter is pushed onto the stack and then the address of the instance object is pushed. This is the hidden "this" parameter. My member thread function understands these two stack entries as "this" and lpParameter.
The CreateThread only pushes ONE parameter when it calls the thread function. This is meant to be lpParameter but my function mistakes it to be "this". The second stack entry is rubbish but my function takes it to be lpParameter. So if I call CreateThread from a member function and pass "this" as the lpParameter it all seems to work as long as I don't try to use lpParameter. But in fact my stack is already corrupt. Because the thread has its own stack and when it returns this is destroyed, I don't actually blow anything up. But would you bungee jump 300 metres on a 10 kilo fishing line? Back to the drawing board.
|
|
|
|
|
Well, looks like you beat me to it! Funny - I could have sworn I was replying to your previous message, but your posting seemed to intervene and mess up the ... er ... thread...
Anyway, I do recommend beginthreadex if you are using anything in the standard library. And consider using static?
|
|
|
|
|
Sorry for jumping in on top of you. No excuses!
|
|
|
|
|
_beginthreadex is recommended, check the article in MSDN.
|
|
|
|
|
Well, actually you only have one function anyway - its the instance data that is duplicated. So, if one class spins many threads, you need locks on the data. If several instances spin one thread each (sort of begs the question) you're still time slicing the same code. Static local vars would be a problem.
But to the interesting stuff - here's some code I've been playing with.
I first tried to reproduce your code using CreateThread - but since I am using some std stuff here, _beginthread turned out to be a good deal safer, as it links with a 'thread safe' version of the runtime.
The code below should compile for both Borland CB4 and VC6. The only syntactical change I had to make was in the assignment to z.x. Borland complained that ThreadProc alone was ambiguous.
The call to _beginthread shows the same behavior for the this pointer - if NULL, an access violation occurs at runtime. If this , members are accessed transparently.
Don't know if this is a serindipitous side effect, as Mario suggests, or something we can rely on, but certainly is interesting.
Note that there are no locks here, and the calls to cout can get a bit mixed (moreso in debug).
#include <windows.h>
#include <process.h>
#include <iostream>
using namespace std;
class Fred {
private:
int m_nThreadCount;
union {
void (__cdecl Fred::*x )(void *);
void (__cdecl *y )(void *);
} z;
public:
void __cdecl ThreadProc(void*);
void StartThread();
Fred():m_nThreadCount(0){};
};
void Fred::ThreadProc(void* lpParam) {
++m_nThreadCount;
cout << "Hey its me Fred!" << " Count is " << m_nThreadCount << endl;
}
void Fred::StartThread() {
z.x = &Fred::ThreadProc;
_beginthread(z.y, 0, this);
cout << "Thread started..." << endl << flush;
}
int main(int argc, char* argv[])
{
cout << "Thread test!" << endl;
Fred fred;
fred.StartThread();
int x;
cin >> x;
return 0;
}
I guess the next step is to look at some registers and see whats going on. BTW you might want to use _beginthreadex . I wanted to keep things simple.
Lastly, it seems to me that if the union trick works, there must be a way to cast the pointer passed to _beginthread . Hmmm...
this certainly makes for an interesting thread though...
|
|
|
|
|
I'm surprised that what you've done works. _beginthread takes three parms:
address of the thread functions
stack size
arg list pointer
What you're doing is completly non portable: you're using the 'this' pointer as the arg list and praying that Borland compiler will put it into the correct register and fake it for you. If you look at what your lpParm value is and compare it to what was passed as 'this' in your beginthread, it will be probably the same, but what happens when you compile in release mode? Plus you're also taking into account the memory layout of the union on your compiler to be the same on all compilers; it may not, since you have two different data types - one the address to a non-class function, and one an address to a scoped class function. Bet this will puke in IA64 and maybe other compilers. Bet the Intel c++ compiler will go completly nuts on you with this code.
Simplest, portable solution:
class Fred
public:
static void _Thread(void*pThis) { Fred*pFred=(Fred*)pThis; pFred->ThreadFunc(); }
void ThreadFunc() { ;/*whatever*/ }
void StartThread() { _beginthread(&_Thread,0,this); }
};
Note that this is the same as a lot of the callback stuff you'll get from the SDK. Lot cleaner, simpler to understand & teach newbies, and a hell of a lot easier to maintain!
Just my two cents as always...
|
|
|
|
|
Yup - I agree.
Now try doing it without using a static member fn.
But it looks like we all agree at this point that this is not good code - I just found the union trick interesting.
BTW I ran the code in both release and debug under Borland and VC. It seems to be a prevalent disease, this bad this.
Cheers
T
|
|
|
|
|
Actually, using a static is not portable either. It will probably work with most compilers, but technically it's undefined functionality. The compiler vendor is allowed to implement static members any way it chooses, so long as they follow certain rules. The static trick works primarily because a static member has the same function signature as non-static member in most implementations.
To be portable, you should use a non-member function and use the parameter passed to the function as a pointer to the object, however this also means you won't be able to access private or protected members through that pointer.
|
|
|
|
|
Ah, but it is portable - see Inside the C++ Object Model (S. Lippman), pages 121-124. Static functions are managled internally into non-class functions. "Taking the address of a static memmber function always yields the value of its location in memory, that is, its address. Because the static function is without a this pointer, the type of its address is not a pointer to a class member function but the type of a nonmember pointer to function." Sadly, the ARM doesn't give more than a trivial explanation of static data and functions, other than they have "external linkage". What reference were you using for the undefiend functionality?
In any case, you can work around the private/protected problem like this:
void bogus(void *ptr) { ((Fred*)ptr)->publicBogus(); }
class Fred
{
public:
void publicBogus() { ; /* do the real work here */ }
};
|
|
|
|
|
You're confusing portable (as in conforming to the ANSI/ISO standard) with "it works" based on a common implementation. Inside the C++ Object model was written long before the standard (1996). IIRC he even states that what he writes is based on common implementations.
|
|
|
|