ESP erreur lors de l'envoi des messages de fenêtre entre les filets
-
29-09-2019 - |
Question
J'ai une classe d'observation et une classe d'abonné.
Aux fins de test, l'observateur crée un thread qui génère des messages faux et appels CServerCommandObserver::NotifySubscribers()
, qui ressemble à ceci:
void CServerCommandObserver::NotifySubscribers(const Command cmd, void const * const pData)
{
// Executed in worker thread //
for (Subscribers::const_iterator it = m_subscribers.begin(); it != m_subscribers.end(); ++it)
{
const CServerCommandSubscriber * pSubscriber = *it;
const HWND hWnd = pSubscriber->GetWindowHandle();
if (!IsWindow(hWnd)) { ASSERT(FALSE); continue; }
SendMessage(hWnd, WM_SERVERCOMMAND, cmd, reinterpret_cast<LPARAM>(pData));
}
}
L'abonné est une classe dérivée de CDialog
, qui sont aussi hérite de CServerCommandSubscriber
.
Dans la classe dérivée, j'ajouté une entrée de carte de message, que les commandes de serveur de routes vers le gestionnaire de la classe d'abonné.
// Derived dialog class .cpp
ON_REGISTERED_MESSAGE(CServerCommandObserver::WM_SERVERCOMMAND, HandleServerCommand)
// Subscriber base class .cpp
void CServerCommandSubscriber::HandleServerCommand(const WPARAM wParam, const LPARAM lParam)
{
const Command cmd = static_cast<Command>(wParam);
switch (cmd)
{
case something:
OnSomething(SomethingData(lParam)); // Virtual method call
break;
case // ...
};
}
Le problème est que je vois des accidents étranges dans la méthode HandleServerCommand ():
Il ressemble à ceci:
Erreur de débogage!
Programme: c: \ myprogram.exe
Module:
Fichier: i386 \ chkesp.c
Ligne: 42La valeur de l'ESP n'a pas été correctement enregistrée à travers un appel de fonction. C'est généralement le résultat d'appeler un fonction déclarée avec un appel convention avec un pointeur de fonction a déclaré avec une vocation différente convention.
J'ai vérifié le pointeur de la fonction AfxBeginThread () veut avoir:
typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID); // AFXWIN.H
static UINT AFX_CDECL MessageGeneratorThread(LPVOID pParam); // My thread function
Pour moi, cela semble compatible, non?
Je ne sais pas, ce que je dois chercher. Toutes les idées?
J'ai fait une autre observation étrange, qui pourrait être lié:
Dans le NotifySubscribers
method, je l'appelle IsWindow()
pour vérifier si la fenêtre à laquelle les points de poignée, existe. Apparemment, il le fait. Mais appeler CWnd::FromHandlePermanent()
renvoie un pointeur NULL.
La solution 3
J'ai finalement décidé de le faire sans des messages de fenêtre et je suis affichons ma solution ici. Peut-être qu'il va aider quelqu'un d'autre.
Au lieu de laisser la fenêtre poste d'observateur des messages à ses abonnés, je laisse les données de vente d'observateur dans les tampons d'abonnés synchronisés. L'abonné de classe de dialogue utilise une minuterie pour vérifier périodiquement ses tampons et appeler les gestionnaires de winrar si ce ne sont pas vides.
Il y a quelques inconvénients:
- Il est plus d'effort de codage parce que pour chaque type de données, les besoins d'un élément tampon à ajouter à l'abonné.
- Il est également plus d'espace consommer, que les données existent pour chaque abonné et pas seulement une fois pendant l'appel
SendMessage()
. - On doit également faire la synchronisation manuellement au lieu de compter sur le fil d'observateur étant suspendu alors que les messages sont traités.
A - OMI - énorme avantage est qu'il a mieux la sécurité de type. Il ne faut pas jeter quelques valeurs lParam
dans des pointeurs en fonction de la valeur de wParam
. À cause de cela, je pense que cette solution de contournement est très acceptable, sinon même supérieure à mon approche originale.
Autres conseils
De afxmsg_.h
:
// for Registered Windows messages
#define ON_REGISTERED_MESSAGE(nMessageVariable, memberFxn) \
{ 0xC000, 0, 0, 0, (UINT_PTR)(UINT*)(&nMessageVariable), \
/*implied 'AfxSig_lwl'*/ \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
(memberFxn)) },
Ainsi, la signature est LRESULT ClassName::FunctionName(WPARAM, LPARAM)
, alors que le vôtre est void ClassName::FunctionName(const WPARAM, const LPARAM)
. Cela ne devrait pas compiler, au moins sous VS2008 il ne fonctionne pas.
Quelle est votre déclaration de HandleServerCommand dans la classe CServerCommandSubscriber (dans le fichier d'en-tête)?
Pour moi, cela semble compatible, n'est pas il?
Syntaxiquement il semble de cette façon.
Je ne sais pas, ce que je dois regarder pour. Toutes les idées?
Oui. J'ai eu le même problème lors de la compilation d'une bibliothèque de plug-in avec les paramètres de débogage et utilisé dans une application compilée de sortie-
En fait, les regards de problème comme une corruption de pile.
Puisque vous utilisez NotifySubscribers
dans un thread séparé, utilisez PostMessage
(ou PostThreadMessage
) au lieu de SendMessage
.
Cela peut ne pas être la cause réelle de l'accident, mais le changement devrait être fait de toute façon (que vous êtes un contexte de filetage de commutation en utilisant SendMessage
sans GARDIENNAGE des données que ce soit.