Solved

Change Text on PropertyPage (Dan?)

Posted on 2002-05-27
25
452 Views
Last Modified: 2012-08-13
I've been working on a project that uses DLLs and PropertyPages. Each DLL has a PropertyPage stored in it. The main application that loads these DLLs contains a PropertySheet. The main application goes through each of the DLLs, gets their PropertyPage, and adds it to the PropertySheet. It them creates the PropertySheet using the Create function. I have a text control (listed as "static text control" even though the value can be changed using the SetDlgItemText function) on each PropertyPage. I want to update the text of this control 1 time every second with a new value.

Here's where I am. I've added a function to each of the DLLs that returns the value i want to display on the PropertyPage. So i have extracted the value i want to display and stored it in a variable in the main application. Am i going in the right direction? What is the next step in updating the PropertyPage values?
0
Comment
Question by:xebra19
  • 14
  • 10
25 Comments
 

Author Comment

by:xebra19
ID: 7036778
Notes:
*Everything works with the PropertyPage except changing the text. I'm having no problem with adding the pages or anything else.
*I want this to work so that I can change the values for more than one ProperyPage (multiple DLLs)
0
 
LVL 49

Accepted Solution

by:
DanRollins earned 300 total points
ID: 7037948
>>I want to update the text of this control 1 time every second with a new value.

>>I've added a function to each of the DLLs that returns the value i want to display on the PropertyPage. So i have extracted the value i want to display and stored it in a variable in the main application

These two passages seem to be in conflict; I'm not certain what you are trying to do.

It seems most likely that you want each of your plug-in PropertyPages to be able to display some text generated by the Application.  If that is the case, then the simplest method would be to have the Sheet cycle through all of the pages, and do a SendMessage to update a particular control.  For instance:

    CPropertyPage* pPage;
    CString sNewStatus= "Waiting For input";
    for ( int j=0; j< nPgCnt; j++ ) {
        pPage= <a pointer to the page from one of the DLLs>;
        CWnd* pStatus= pPage->GetDlgItem(IDC_StatusLine);
        if ( pStatus) {
             pStatus->SetWindowText( sNewStatus );
        }
    }

I think you already have a list of CPropertyPage objects, so you could just loop through that list, setting pPage to each one.

You can set a Window timer in the CPropertySheet and have it do the update onece per second.

There are other ways to do this, that just seems the easiest (if I understadn the Q...)

-- Dan
0
 

Author Comment

by:xebra19
ID: 7037966
i haven't tried your code yet, but just by looking at it I'm 99.99% sure that's what i want to do.

You got it almost right (although it's my fault for giving a cryptic explanation). The values are in fact generated by the DLLs, not the Application. I wasn't sure if it would be better to do this from the DLLs or the main Application. I thought that the main application would be better because I would have access to everything in one location. To facilitate this I exported another function from each DLL. This function returns the value that I want to use as a new value ("Waiting For input" in your example). This gives me access to everything I need, right in the main application.

Is this the best way to go, or are their better ways?
Does your example still apply?

Thank you.
0
 

Author Comment

by:xebra19
ID: 7038259
I tried out the code and got a Debug Assertion in winocc.cpp on line 76.

Line 76: ASSERT(::IsWindow(m_hWnd));
0
 

Author Comment

by:xebra19
ID: 7038262
(that's in the GetDlgItem function)
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7038548
You get that ASSERT if the page has not been Created.  THe system does not actually create the dialog until the user clicks a tab to make it visible.  You need to 'protect' the call with something like:

if ( ::IsWindow(pPage->m_hWnd) ) {
   // its ok to use GetDlgItem() now
}
else {
   // the pge is not now visible and has has never been
   // displayed, so nothing to do anyway...
}

=--==-=-=-=-=-=-=-=-
But I am still in the dark about what you are trying to accomplish!  I showed you how to set the text of a static control in each of the pages... from the App.  But now you say that you want each or some of the DLLs to send messages to the app.  Which is it?

-- Dan
0
 

Author Comment

by:xebra19
ID: 7038619
it's the first one. i meant that the values i want to put on the pages are coming from the DLLs. i set it up so that i could access the values from the application, but i was wondering if it was better to it from the DLLs. if this works it doesn't matter. i will check it out.
0
 

Author Comment

by:xebra19
ID: 7038650
ok, i tried the code. it works (so forget about the other stuff).

For 100 more points: I have an array which holds pointers to each PropertyPage (1 from each DLL). I have another array that holds the new values I want to be sent to the text controls. Is there some loop i could go through that checks to see which page is active and keeps updating that text with the value from it's variable once every second. When the active page changes, it starts updating that one once a second?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7038665
Updating the text of an static control is a very 'inexpensive' operation.  In your situation, I would simply create a Window timer for the CPropertySheet and in the OnTimer function, I'd just do as I've described above; that is, loop through the list of pages and call SetWindowText to the static text control.  

 for ( int j=0; j< nPgCnt; j++ ) {
       pPage= m_ArrayOrPtrsToPgs[j];
       if ( ::IsWindow(pPage->m_hWnd) ) {
           CWnd* pStatus= pPage->GetDlgItem(IDC_StatusLine);
           if ( pStatus) {
               pStatus->SetWindowText( sNewStatus );
           }
       }
}

In other words, I wouldn't worry about if the page is visible or active or not... just update *all* of them once per second.

-- Dan
0
 

Author Comment

by:xebra19
ID: 7038676
i'm new to the Windows scene (on the development side), and i'm still learning much of the API and MFC. How would I go about creating a Window timer for the CPropertySheet?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7039780
1) In the IDE, edit the CPP file for your CPropertySheet-derived object.  
2) Press Ctrl+W to bring up the ClassWizard.
3) Scroll down the right-hand list until you see WM_TIMER double-click it.  The Wizard will create a function named OnTimer.
4) If you have not already done so, locate OnInitDialog in that same right-hand list.  Double-click it to create an OnInitDialog member function.
5) In OnInitDialog, add code like this:

#define CNUM_MyUpdateTimerId 1234 // near the top of the file
... then in OnInitIDalog() ...
SetTimer( CNUM_MyUpdateTimerId, 1000, 0 ); //1000=1 second

Now your OnTimer member will be called once per second until you use code like this:

KillTimer( CNUM_MyUpdateTimerId );

-- Dan
0
 

Author Comment

by:xebra19
ID: 7040659
CWnd* pStatus= pPage->GetDlgItem(IDC_MYCONTROL);

I compiled the code and it worked at first. But then I changed the value in IDC_MYCONTROL to another value and my program doesn't compile because it says that my IDC_NAME is an undeclared identifier. I don't see how the application would even know what the proper name is because that is stored in the DLL. I changed the name of the text control in the DLL but it won't let me do it in the application.
0
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 15

Expert Comment

by:Tommy Hui
ID: 7040983
This is the general problem between EXEs and DLLs. They really are separate beasts and there should be very specific interfaces/contracts between them. One interface you have already defined is retrieving the property pages from the DLL. The application is in charge of initializing the property sheet, but the DLLs are in charge of its pages. To keep the EXE from knowing too much about the DLL, you should have another interface that is in charge of telling the property page that the value needs to be updated.

Another idea is to have an interface in the DLL that tells the EXE that it needs to update the text on a regular basis. Then the EXE would be in charge of telling the DLL to update its text.

Either way, the DLL is in charge of updating its page. The application can be the one in charge of the timer.
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7041042
>>But then I changed the value in IDC_MYCONTROL to another
value

The normal way to solve this is to have a common .h file that all plug-ins must include.  It would show programmers what calls are available and what parameters are needed.  It could also declared shared/common values such as the ID of that text control.

But that can get a little wonky.  It is probably better to have each DLL export another function that would allow the App to update status (or whatever it is you are trying to do).

I have read your question, and then read the explanation and apology for the ambiguous nature of the question.  But it is *still* not clear what you are trying to do.  It sounds like you are trying to do something backwards, but I really can't tell.  

Why don't you simply describe it?  Is it some big secret?  Or do you just enjoy making me chase around in circles?

-- Dan
0
 

Author Comment

by:xebra19
ID: 7041199
i'm not trying to make this difficult. i'm not trying to keep any secrets. Here's another overview:

The application loads DLLs and calls a function in each DLL that returns a pointer to a CPropertyPage object. Each DLL has it's own CPropertyPage. Each page has a text control that i want to update with a new value once every second. The application has a CPropertySheet. Once it gets the pointers to each of the PropertyPages it adds them one by one to the PropertySheet and then creates the sheet using the Create function. The point of this question is to find out the best way to change the value of the text controls 1 time every second. I exported another function from each of the DLL. This function is called 1 time every second. It returns a value generated by the DLL. This value is then stored in the CString (sNewStatus in your example). Then your code comes in to play.

Your code worked when i had the name of the control set to IDC_FIRST. However i changed that name in the DLL to a more descriptive name (IDC_STAT) and i did the same in the code you posted. It now says that it's an undeclared identifier. I don't see why it would have known the name when it was IDC_FIRST or why there would be a difference when it was changed to IDC_STAT.
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7041328
OK, here's what sounds backward:
The DLL is providing a string to the App, which then sends that string back to be displayed in the DLL.  Why not just have the DLL display it itself?

Anyway, my example of GetDlgItem is not good because the item ID is giving you problems.  

Instead, export a function from the DLL that returns a CStatic* or even an HWND of the control that need to be updated.  

A better alterntive would be to have each DLL export a function like, say, SetStatus(s).  Then the DLL can decide what to do with the text (which control to display it in, etc.)

-- Dan
0
 

Author Comment

by:xebra19
ID: 7042849
yes i know that sounds backward. my original idea (the confusing part) was to do just that (have the DLL set the text). Have it export a function that takes the string as a parameter and then updates the control. I didn't know if this would work because I didn't know if the PropertySheet would automatically update the text if the PropertyPages were changed from inside the DLL. Could you please help me complete this code?:

APP:
//Sets up loading the library
TheOnTimerFunction()
{
     //calls SetStatus with the new text as the parameter
}

DLL:
__declspec(dllexport) void CDLLApp::SetStatus(CString status)
{
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     // What would go here?
     // I want it to update my control with the text in the CString.
}

Is that the way to do it?
0
 

Author Comment

by:xebra19
ID: 7043212
never mind about the last post. i think i figured it out. it works. could you tell me if the following code is correct?:
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7043294
Not unless you post it, lol.  -- Dan
0
 

Author Comment

by:xebra19
ID: 7043298
sorry my ISP was having problems the code is on it's way
0
 

Author Comment

by:xebra19
ID: 7043302
APP:
void CMyAppDlg::OnTimer(UINT nIDEvent)
{
     for(int j=0; j < dllnum; j++) //the number of dlls loaded is stored in dllnum
     {
          FARPROC Values;
          Values = GetProcAddress(hMod[j], "UpdateVals");

          Values();
     }

     CDialog::OnTimer(nIDEvent);
}

DLL:
__declspec(dllexport) void CDLLApp::UpdateVals()
{
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     
     CPropertyPage* pPage = UpPg; //UpPg is the PropertyPage
     CWnd* pStatus;
     CString sVal;

     if (IsWindow(pPage->m_hWnd))
     {

          sVal.Format("%d", vals); // vals is an int that holds the new value
          pStatus= pPage->GetDlgItem(IDC_VAL);
          if (pStatus) pStatus->SetWindowText( sVal );
     }
}
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7043340
In the EXE I'd add a check like:

if ( Values == NULL ) {
   // the DLL is missing the export!
}

So now the DLL is creating the value and displaying it itself.  And the EXE is just kicking the DLL once in a while to tell it to update iteslf.  I think you are on the right path.

-- Dan
0
 

Author Comment

by:xebra19
ID: 7043431
ok, so would you say this method is the best, or would putting all the code in the DLL be an even better solution?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7043570
I have far too little information about your actual application -- what you want this EXE and these DLLs to eventually accomplish -- to judge what would be best.  

It might be more reasonable to have each DLL property page set its own timer, but I don't even know what it is displaying or why 1-second is a reasonable interval or if the EXE needs absolute control over this function or even if a timer is a sensible means to accomplish your secret goal.

One good rule of thumb, its: if it works, use it.  Move on to the next step.  Most projects are just proof-of-concept experiments until the second rewrite anyway.

-- Dan
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 7049255
hi xebra19,
Do you have any additional questions?  Do any comments need clarification?
-- Dan
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

Unlike C#, C++ doesn't have native support for sealing classes (so they cannot be sub-classed). At the cost of a virtual base class pointer it is possible to implement a pseudo sealing mechanism The trick is to virtually inherit from a base class…
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

758 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

18 Experts available now in Live!

Get 1:1 Help Now