Solved

DLL Callback

Posted on 2003-10-24
16
1,129 Views
Last Modified: 2013-11-20
Hi

I am using a DLL which has a callback function. Now as the callback has to be static I can't access the other class members, so as in other callbacks I used I had got a pointer to my class in the callback function as follows
CfvWorkBench* CFunctions::GetWorkBenchWnd()
{
  CMainFrame* pMainFrm = (CMainFrame*)AfxGetMainWnd() ;
  CSplitterWnd* pSplitter = &pMainFrm->m_wndSplitter;
  ASSERT_POINTER(pSplitter, CSplitterWnd);
  CfvWorkBench *pWorkBenchView = (CfvWorkBench*)pSplitter->GetPane(1,1);  //FAILURE HERE
  ASSERT_POINTER(pWorkBenchView, CfvWorkBench);
  return pWorkBenchView;
}
The problem is when I try this with the DLL callback I get a debug assert failure on the line marked,  this traces  from the Wincore.cpp at the following code
  // should also be in the permanent or temporary handle map
void CWnd::AssertValid() const
  ........
  .......
  CHandleMap* pMap = afxMapHWND();
  ASSERT(pMap != NULL);

Now I think this is somehow due to the callback being in its own thread, is this right and if so how do I work arround it, if not anyone any ideas on this. Note the CFunctions::GetWorkBenchWnd() function is used in other places in my code and works ok.

Now the other way I looked at this was
The DLL has a function to set up the callback like this
HSYNC WINAPI BASS_ChannelSetSync(
    DWORD handle,
    DWORD type,
    QWORD param,
    SYNCPROC *proc,
    DWORD user
);

SYNCPROC
void CALLBACK YourSyncProc(
    HSYNC handle,
    DWORD channel,
    DWORD data
    DWORD user
);
The info on this DLL says
user         User instance data to pass to the callback function
So I thought I could pass "this" as the user, but user is a DWORD so it wouldn't compile

Steven

0
Comment
Question by:Dj_Fx8
  • 8
  • 7
16 Comments
 
LVL 49

Expert Comment

by:DanRollins
ID: 9618226
Your DLL needs to have
      AFX_MANAGE_STATE(AfxGetStaticModuleState());
protect each call.  If you are exporting an object, then each exposed objeect method needs to have that as the first line of the function.  If you are exporting simple C-style fns, then it should be the first line of each of those, as well.

Part of what that does is to set up the processing the per-thread handle mapping that goes on behind the scenes.

This may not be the only problem (your definition of a callback function does not match what I consider to be a callback) but it is something to try.

-- Dan
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 9618881
Any pointer may be casted to DWORD (because it is really DWORD). You can pass pass "this" as the user casting it to DWORD.
However, passing pointers to MFC objects to Dll may not work because of heap or thread problems. If you have such problems, pass windows handles instead of class pointers. Having window handle you can talk with any window using PostMessage and SendMessage.
0
 

Author Comment

by:Dj_Fx8
ID: 9619147
Hi

Thanks for your replies only I'm still lost,
Dan I assume that  AFX_MANAGE_STATE(AfxGetStaticModuleState()); needs to be in the DLL, if so I did not write the DLL and have no access to add this, if not where and how do I use  AFX_MANAGE_STATE(AfxGetStaticModuleState()); also
>>>>your definition of a callback function does not match what I consider to be a callback
why?
Alex I had tried (DWORD)this as the user param but as you said it didn't work either
>>>>pass windows handles instead of class pointers. Having window handle you can talk with any window using PostMessage and SendMessage, could you perhaps explain this a little more.

I'm very new to this and greatly appreciate all the help.

Steven
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9620336
Try putting it at the top of that function.
0
 

Author Comment

by:Dj_Fx8
ID: 9629784
Hi Dan
I tried that but still the same problem, also the auther of the DLL says I can pass (DWORD)this as the user, so I'm really lost, however I have managed to pass this to the callback and use it to postmessage, I suppose along the lines Alex said, but still curious as to what the cause / solution is.

Steven
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9630240
In most callback scenarios, it is permitted for the user to pass a "cookie" (typically a DWORD value) into the DLL.  THen when it makes the callback, the DLL passes that value back to you.  Is that the case in this situation (perhaps that is what the
    DWORD user
value is in your call to BASS_ChannelSetSync().

If so, then when your callback is called, the DLL will pass that back to you.  Perhaps that is what the
      DWORD user
is in the YourSyncProc() fn.   If that is all correct, then do it this way:

SYNCPROC void CALLBACK MySyncProc( HSYNC handle, DWORD channel, DWORD data, DWORD user )
{
          CMyObject* pThis= (CMyObject*)user;
          pMyThis->DoAnyMemberFnInTheCMyObjectClassDef( nAnyParam );
}

CMyObject::SetupTheCallback()
{
        BASS_ChannelSetSync( myHandle, myType, param, MySyncProc, (DWORD)this);
);

The callback goes to a global function, but that global fn uses the passed in 'user' parameter as  a pointer to an object.  The call:

          pMyThis->AnyFn( parm1, parm2,...);

then 'gets into' the object and so can access all members.  It is possible to do this in another way -- using a static member -- but that accompishes exactly the same thing and just makes it a little more complicated to explain.

-- Dan
0
 

Author Comment

by:Dj_Fx8
ID: 9630339
Hi Dan,
The first parts are just as I thought and had tried. Now when you say
>>>It is possible to do this in another way -- using a static member  
I think thats maybe my problem I was declaring
SYNCPROC void CALLBACK MySyncProc( HSYNC handle, DWORD channel, DWORD data, DWORD user )
as static, could this be the problem as you say
>>>The callback goes to a global function
If so I'm not sure how to make it golbal, I always thought callbacks had to be static.

I've still a lot to learn :-))

Steven
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9630397
What I meant by 'global' was just it is not a member function of any class object.  Declare it just as shown, (without a leading 'MyObject::' in the declaration).

You can also declare it 'static' to make it module-local, but again, that is not relevant to making it work and that meaning of 'static' is quoite different from what it means when you declare a 'static member function'  You are probably confusing the two concepts.

-- Dan
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:Dj_Fx8
ID: 9630503
Ok I think I'm with you so far, so looking at my org prob, in my callback I got a pointer to CMyObject (pMyThis) and used it call a function(MySomething) in CMyObject just as you said, now this seemed to work fine until the following line in my function MySomething
 UpdatData(FALSE);
if fails the assert in wincore.cpp at the following

CWnd::AssertValid()
..
..
// should also be in the permanent or temporary handle map
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL); >>>>Here

The UpdateDate is called to refresh the screen with newly received data from the callback

I can't understand why this would be cause when I look at the pointer pMyThis it does point the CMyObject, whats happened to my pMap

0
 
LVL 49

Accepted Solution

by:
DanRollins earned 500 total points
ID: 9630566
Maybe we're back to the original problem,  Try this:

SYNCPROC void CALLBACK MySyncProc( HSYNC handle, DWORD channel, DWORD data, DWORD user )
{
          AFX_MANAGE_STATE( AfxGetStaticModuleState() );  // <<<<< added
          CMyObject* pThis= (CMyObject*)user;
          pMyThis->DoAnyMemberFnInTheCMyObjectClassDef( nAnyParam );
}

If that does not work, you may need to forego using UpdateData() to do the DDX.  For instance, just use:

      ::SetDlgItemText( m_hDlg, IDD_SomeEditBox, sSomeStringFromTheDLL );

There might be another way... I'll see if I can find it.

-- Dan
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9630638
Also try this:
         AFX_MANAGE_STATE( AfxGetAppModuleState() );  << *App* ModuleState
(just guessing, never used it)
-- Dan
0
 

Author Comment

by:Dj_Fx8
ID: 9635738
Hi

I tried both
AFX_MANAGE_STATE( AfxGetStaticModuleState() );  
and
AFX_MANAGE_STATE( AfxGetAppModuleState() );
and neither of the two prevented the problem so I tried
::SetDlgItemText( m_hDlg, IDD_SomeEditBox, sSomeStringFromTheDLL );
and it worked ok, so would I be right in assuming this is some sort of bug in MFC.
Just one last brief question, I now can process by callback by either using SendMessage or by the method we have been working on, is there any advantage of using one or the other methods
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9636010
It is not really a bug in MFC, but if you want to call it that, then that's OK.

>>I now can process by callback by either using SendMessage or by the method we have been working on,

I don't know what you mean.  But here is the rule that will avoid those errors:

In your callback handler, access all CWnd-derived objects via the HWND, not the CWnd*  Thus, for instance, use

          ::SetDlgItemText( m_hWnd, nID, sText)
rather than
          pwnd->SetDlgItemText( nID, sText);  // will fail
or  
         SetDlgItemText( nID, sText);  // will fail... implying the CWnd* associated with 'this' object.

You can also use ::SendMessage( hWnd, ...) and ::PostMessage( hWnd,...) should the need arise.

-- Dan
0
 

Author Comment

by:Dj_Fx8
ID: 9636221
Sorry I didn't explaine that to well

>>I now can process by callback by either using SendMessage or by the method we have been working on,

In the callback function
Method 1

  CNetRadio* pNetRadio = (CNetRadio*)user;
  pNetRadio->SendMessage(UWM_METASYNC, data, 0);

Method 2

  CNetRadio* pNetRadio = (CNetRadio*)user;
  pNetRadio->DoMeta((char*)data); //call function

Is it beter to use method 1 and send/post a msg or method 2 by just  calling a function.

Anyway I can't thank you enough for all your help and here's the well earned pts

Thanks
Steven

PS I'm just writing a new question, for a new prob :-( which I be posting soon, you might like to keep an eye out for it, I'm sure you'd have no probs sorting it :-)
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9638376
Thanks for the points and the grade :)

As to Method #1 or #2, I can't say for certain which is better.  Both equate to a direct call into the CWnd-derived object -- which could cause problems.  As far as I know, the DLL could be calling from a different thread.  Also, certain resource-related issues could crop up (think of this: if you look up something in a StringTable, should you look in the DLL or the EXE?  -- as a callback handler it is physically 'in' the EXE, but the module that is calling it is the DLL).   If I had to chose, I'd say SendMessage, since there may be more context-insulation there.

I think that PostMessage would be perfectly safe, since the message would wait there until the real thread, in the context of the main app, could process it.

In general, I would recommend doing a bare minimum of work in the callback... just update some status variables in memory and let a timer in the main window handle U/I work.  Or post custom message like "UM_CheckForAnyStatusUpdatesNeeded"  to tell the main window to update the U/I when it gets a chance.

-- Dan
0
 

Author Comment

by:Dj_Fx8
ID: 9645463
I would have given you an A++ but, no suce thing here. Your last post has given me a few guidelines to work along.

Cheers

Steven
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

Suggested Solutions

Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now