Using SendMessage to get a CString value? - visual-c++

Using SendMessage I can pass a CString up to the parent easily enough (simplified):
CString strText = L"The text";
GetParent()->SendMessage(theApp.UWM_LOADED_SRR_FILE_MSG,
reinterpret_cast<WPARAM>(strText.GetString()));
Parent handler:
afx_msg LRESULT CMyDialog::OnSetSRRFilename(WPARAM wParam, LPARAM lParam)
{
LPCTSTR szFilename = reinterpret_cast<LPCTSTR>(wParam);
// Do something.
return 0;
}
That works, but what about the other way? How can I use messaging to get the current CString value from the parent? I know I can just cast to the parent object type and call the public method. But I want to use messaging. The way I understand it is that SendMessage is passing to a location to receive text.
Am I supposed to post a message to parent that says "i want the string" passing the handle of my window. And then in that handler it sends a message to that handle with the string value?
Am I overcomplicating this?
The string value is not a control, just a private variable.

You can simply define a message asking for the text. The caller (child) must have a buffer to receive the text. wParam can be the buffer length and lParam the buffer (pointer).
Alternatively, the message return value can be a memory object (handle or pointer) eg allocated by the parent and freed by the child. For an example of how the message could be defined check the WM_GETTEXT message; for the alternative case the GetClipboardData() function.
Third alternative, the message return-value can be a CString*, containing the requested text. It must be allocated by the parent by calling new, copy-constructed from the original. On return the child "owns" the object and must free it by calling delete. Or the message could just return the address of the CString object, or even its buffer (call GetString()) - in these cases it is considered "read-only" for the child.

This is a long comment:
If this were inter-process communication, then you would consider SendMessage, WM_COPYDATA, clipboard, etc.
But it looks like you are working on a single-threaded application. You don't need the added complexity of windows message handling. Just use standard c++ to call the functions in other classes directly. For example,
auto ptr = dynamic_cast<CMyParent*>(GetParent());
if (ptr)
{
CString str;
ptr->GetSRRFilename(str);
ptr->SetSRRFilename(str);
...
}
else
{
MessageBox("cast failed, \
parent is not CMyParent class. We are not interested.");
}
Or use static_cast if you are sure that GetParent() is a CMyParent class.
I don't know about the design of your classes. Maybe you have CMyParent1 and CMyParent2, they share some functions and it's difficult to tie them together (but it shouldn't be impossible), in that case you can use SendMessage as a cheat.

Based on the comments I now have this solution:
afx_msg LRESULT CMyDialog::OnGetSRRFilename(WPARAM wParam, LPARAM lParam)
{
return reinterpret_cast<LRESULT>(GetLastEditedSRRFile().GetString());
}
And in my dialog that needs to get the data:
const auto szFileName = reinterpret_cast<LPCTSTR>(GetParent()->SendMessage(theApp.UWM_GET_SRR_FILENAME_MSG));
Works fine! Thanks guys.

Related

Managing CStringArray entries

This is in the context of a Visual Studio MFC application.
The CStringArray::Add() member takes an LPCTSTR argument. If I use a CString argument, I presume the argument is implicitly converted to LPCTSTR for Add() by the LPCTSTR operator of CString, so if I have a CStringArray arr and a CString s and call arr.Add(s), this is exactly the same as calling arr.Add((LPCTSTR)s), right?
So when Add() is called, does CStringArray simply store the passed pointer, or does it make a separate copy of the the string and store that? I'm asking because I wonder if I have to make a persistent string when I add it, or if my string can be deleted after it is added. For example, which if any of foo1(), foo2(), or foo3() is correct in the following?
class MyClass :
{
...
CStringArray arr;
CString mystr;
void foo1() { arr.Add(_T("Bippety")); }
void foo2() { CString s(_T("Boppety"); arr.Add(s); }
void foo3() { mystr = _T("Boop"); arr.Add(mystr); }
...
};
The thing that worries me is that after foo1() and foo2() return, the passed argument goes out of scope and is deleted. Does the array then hold its own copy, or does it hold an invalid pointer?
Is the data stored in the CStringArray as just a pointer, or does it actually even go so far as to create a CString when Add() is called?
Finally, if I'm passing LPCTSTR to add, then I assume that if I created the string by a call to new to add it, then I'm surely responsible for deleteing it before CStringArray is destroyed, right?
I just can't quite see how the LPCTSTR can survive in the CStringArray when it's only a local string.
CStringArray is an MFC type array that stores CString's. If you compile with Unicode, the CString is a CStringW and if you compile for MBCS, the CString is a CStringA.
The point of the class is that you do not have to worry so much about memory management. It does it for you. When you pass an LPCTSTR type string to the array, it constructs a CString object and adds it to the array. When the CStringArray object goes out of scope and/or its destructor gets called, all the CStrings it is holding onto will be cleaned up.
However, there is a caveat...
The CString class is reference counted more or less. It has an internal class such that when you make a copy of a class, it doesn't copy all of the data. Instead, it adds a reference to some of the internal structures and uses that. If you do some operation that changes the data, then it detaches itself from the reference and sets up its own new data structures. You have to step through the code to see this.
Now, the CStringArray has two Add() methods. One takes an LPCTSTR the other a const CString&. The second one makes a copy of CString doing the quasi reference counting mentioned in the previous paragraph.
That is just added information. You really don't need to worry about memory management too hard with the CStringArray. It does the heavy lifting for you.
The only part where you have to worry about CString reference counting is if you do stupid stuff and ignore const.
Consider the following code:
#include <afx.h>
#include <stdio.h>
void main()
{
CString s("HERE is my string\n");
CStringArray sArray;
sArray.Add(s);
sArray.Add(s);
_tprintf((LPCTSTR) s); // "HERE is my string"
_tprintf((LPCTSTR) sArray[0]); // "HERE is my string"
_tprintf((LPCTSTR) sArray[1]); // "HERE is my string"
sArray[0].MakeLower();
_tprintf((LPCTSTR) s); // "HERE is my string"
_tprintf((LPCTSTR) sArray[0]); // "here is my string"
_tprintf((LPCTSTR) sArray[1]); // "HERE is my string"
LPCTSTR lpsz = sArray[1];
_tcsupr((LPTSTR) lpsz); // don't do it!!!, also changes s!!!
_tprintf((LPCTSTR) s); // "HERE IS MY STRING"
_tprintf((LPCTSTR) sArray[0]); // "here is my string"
_tprintf((LPCTSTR) sArray[1]); // "HERE IS MY STRING"
}
If you look at the printout from the third grouping, you see that we violated const-ness when we cast lpsz to a non-const string for the call to _tcsupr (strupr). Because of the way MFC reference counts strings, the "s" string contents were also effected. In the case of the second grouping calling the MakeLower() member function of CString, only that particular string was effected.
This is more than you asked. Maybe it will be helpful to people.

Can't get MFC worker thread to pass hWnd or MDI window pointer to thread

For some reason passing an hWnd or a pointer of a newly created MDI window cannot be recasted into its original value in the worker thread. I've tried creating the thread from the App class, Document class and View class. All have the same effect. I'm using a global function for the worker thread. Funny thing is I'm using the same code I used to do the same thing in an MFC MDI back in 1998 and it worked great back then but doesn't seem to work now.
Perhaps I'm just not seeing the problem. What is going on here? What I want to do is simple, create a new View window, capture it's hWnd and pass that hWnd to the worker thread so the worker thread can send messages to the window to print strings and so forth. I'd like to launch the thread from the Document class. The compiler is VS2010 and it's being run in Debug.
After reading this: http://msdn.microsoft.com/en-us/library/h14y172e%28v=VS.100%29.aspx, I realize that you probably can't pass a pointer to the view class around to worker threads. So I'm focusing on the hWnds. In the OnTestConnect block, a valid hWnd is returned as is a valid pointer to the new View window.
Here's the code (from the App class):
struct THREADPARMS
{
HWND hWndView;
int test;
};
(note, I've tried defining the struct as typedef and with a variable name, all have same result)
UINT Starter( LPVOID pParms )
{
THREADPARMS* pThreadParms = (THREADPARMS* )pParms;
//This step shouldn't be necesarry but I tried it anyway. Should
//be able to use pThreadParms->hWndView without casting.
//The hWnd value does not come across as valid. It is valid before sending.
HWND hWnd = (HWND)pThreadParms->hWndView;
//The int comes across fine
int iNum = pThreadParms->test;
CHFTAppBView* pView = (CHFTAppBView*) CHFTAppBView::FromHandle(hWnd);
//This bombs with a debug error becuase pView ptr is invalid (though it was
//valid before sending over
pView->SendMessage( ID_FILE_PRINT, 0, 0 );
return 0;
}
void CHFTAppBApp::OnTestConnect()
{
THREADPARMS* pThreadParms = new THREADPARMS;
//Create the window
AfxGetApp()->OnCmdMsg( ID_FILE_NEW, 0, NULL, NULL );
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
CMDIChildWnd* pChild = (CMDIChildWnd*)pFrame->GetActiveFrame();
CHFTAppBView* pView = (CHFTAppBView*)pChild->GetActiveView();
pThreadParms->hWndView = pView->m_hWnd;
pThreadParms->test = 10;
AfxBeginThread( Starter, pThreadParms );
}
Part 2
//Create the window
AfxGetApp()->OnCmdMsg( ID_FILE_NEW, 0, NULL, NULL );
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
CMDIChildWnd* pChild = (CMDIChildWnd*)pFrame->GetActiveFrame();
CHFTAppBView* pView = (CHFTAppBView*)pChild->GetActiveView();
HWND h = pView->m_hWnd;
SendMessage( h, ID_FILE_PRINT, 0, 0 );
I'm trying to determine if the hWnd is valid. The pView is valid. Stepping through the code and looking at the pointer stats in the Debugger Watch shows a helathy pointer that references the CView class. But it does not return a healthy hWnd. Then Debugger Watch says 'can't evaluate' hWnd memory address and says 'unused=0'. Running it past IsWindow returns true, however. Go figure. Trying to send a CView message to the CView window with that handle just gets ignored. Why would the GetAvtiveView return a valid pointer but the hWnd in that class return garbage?
Part 3
After some more digging, turns out the HWND is valid though the hwnd variable displays 'unused=???' in the Watch window. I assume it's valid because the hWnd received in the thread code matches the hWnd attached to the pView pointer in the main code. The pView pointer the hWnd is taken from is valid as well because Watch recognizes it as a valid CView class pointer by returning the name of the CView class it represents. There are still two problems however. One is that even though the system sends a valid hWnd back to a CView window (pView->m_hWnd), a SendMessage(pView->m_hWnd, ID_FILE_PRINT_PREVIEW, 0, 0,) refuses to work. The system ignores it. I expected the FilePrintPreview command in the newly created view window to run. Second is that FromHandle returns a CWnd object and casting it to a CView fails using all of these methods:
UINT ThreadTest1( LPVOID pParms )
{
THREADPARMS* pThreadParms = (THREADPARMS* )pParms;
//Returns a CWnd even though the handle is to a valid CView
CHFTAppBView* pView2 = (CHFTAppBView*) CHFTAppBView::FromHandle(hWnd);
//Doesn't seem to do anything. Still returns a hWnd that the system recognizes as
//a CWnd. That's what reports in the Watch window.
CHFTAppBView* pView = reinterpret_cast<CHFTAppBView*>(CHFTAppBView::FromHandle(hWnd));
//Doesn't appear to do anything
DYNAMIC_DOWNCAST( CHFTAppBView, pView2 );
//Confirms what watch window says -- it's a CWnd not a CView.
if ( !pView->IsKindOf( RUNTIME_CLASS(CHFTAppBView) ) )
AfxMessageBox( "Not CView" );
::SendMessage( hWnd, ID_FILE_PRINT, 0, 0 );
return 0;
}
void CHFTAppBDoc::Main()
{
AfxGetApp()->OnCmdMsg( ID_FILE_NEW, 0, NULL, NULL );
THREADPARMS* pThreadParms = new THREADPARMS;
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
CMDIChildWnd* pChild = (CMDIChildWnd*)pFrame->GetActiveFrame();
CHFTAppBView* pView = (CHFTAppBView*)pChild->GetActiveView();
pThreadParms->hWndView = pView->m_hWnd;
AfxBeginThread( ThreadTest1, pThreadParms );
SendMessage( pView->m_hWnd, ID_FILE_PRINT, 0, 0 );
//Or
::SendMessage( pView->m_hWnd, ID_FILE_PRINT, 0, 0 );
}
So the questions is how can the hWnd be converted to a the correct window pointer in the thread code? Why can't the system cast it to a CView?
Part 4
Probem solved (for now). Lessons:
Cannot pass a CView window pointer to a worker thread. See link above about this.
Use SendMessage to communicate between theads and windows
//This works from the worker thread now
THREADPARMS* pThreadParms = (THREADPARMS* )pParms;
HWND hWnd = static_cast(pThreadParms->hWndView);
LRESULT lRst = ::SendMessage( pThreadParms->hWnd, WM_GGG, 0, 0 );
//Or just
LRESULT lRst = ::SendMessage( pThreadParms->hWnd, WM_GGG, 0, 0 );
//As well as this from the CDoc method that creates the thread:
CHFTAppBView* pView = (CHFTAppBView*)pChild->GetActiveView();
LRESULT lRst = ::SendMessage( pView->m_hWnd, WM_GGG, 0, 0 );
CView's message map...
ON_MESSAGE( WM_GGG, kk )
Pass HWNDs to worker threads to identify CView windows
Cannot cast from CWnd to a lower derrived class (and excpet it to work right).
Use ON_MESSAGE in view's message map to capture commands sent with SendMessage (be sure to include the method declaraion as afx_msg in the View's .h file)
All pretty straight forward but for those looking for answers (when faced with a myriad of possible causes) this summary may help...
I still don't completely undertstand why casting FromHandle to a CView worked in my old MFC app and not now. Perhaps it has something to do with where the code is located. In the old MFC app it was located in the CView window and in this code in the CDoc class. CDocument is not derrived from CWnd but CView is.
Anyway, that wraps this problem up. A BIG thank you to all who offered advice -- Nik, Scott, Ulrich and Mark. Your sage advice was very helpful.
You can call CHFTAppBView::FromHandle(hWnd) but what you get back isn't a pointer to a CHFTAppBView; it's a pointer to a CWnd. By casting it, you're telling the compiler "trust me, this is really a pointer to a CHFTAppBView. Except it really isn't and you shouldn't treat it as such or pretend that it is.
After all, if a recipe calls for orange juice, then you don't take a lemon, paint it orange, juice it and call it orange juice. Do you?
So what to do? Well, if all you want to do is send the ID_FILE_PRINT message, you don't even need a CWnd as such. You could just just do:
::SendMessage(hWnd, ID_FILE_PRINT, 0, 0);
Of course, that's also likely to be wrong. What you probably meant to do was this:
::SendMessage(hWnd, WM_COMMAND, ID_FILE_PRINT, 0);
Update:
Again, you CANNOT do what you are trying to do. Let's go step by step, shall we?
//Returns a CWnd even though the handle is to a valid CView
CHFTAppBView* pView2 = (CHFTAppBView*) CHFTAppBView::FromHandle(hWnd);
Right. CWnd::FromHandle returns a pointer to a CWnd, not to anything else. It is NOT giving you back a pointer to your original CHFTAppBView. It's giving you a NEW CWnd which is attached to that same HWND. It is NOT VALID to cast that to a CHFTAppBView because this new CWnd is not a CHFTAppBView.
//Doesn't seem to do anything. Still returns a hWnd that the system recognizes as
//a CWnd. That's what reports in the Watch window.
CHFTAppBView* pView = reinterpret_cast<CHFTAppBView*>(CHFTAppBView::FromHandle(hWnd));
Again, CWnd::FromHandle returns a pointer to a new CWnd which knows nothing about CHFTAppBView. You are telling the compiler "trust me, this pointer is to a CHFTAppBView object!" Except it is NOT. It is a pointer to a CWnd attached to a HWND that is a CHFTAppBView.
//Doesn't appear to do anything
DYNAMIC_DOWNCAST( CHFTAppBView, pView );
This STILL wouldn't do anything. First of all, DYNAMIC_DOWNCAST returns a pointer to CHFTAppBView so right there, you're calling the MFC RTTI functions but not doing anything with the result. But even if you did save the result, it wouldn't help. You would be trying to convert a generic CWnd into something it is not.
I'll try to explain this one more time: When you create your view, MFC creates a CHFTAppBView object which is associated with the HWND of the view. When you call CWnd::FromHandle passing in the HWND of the view, MFC creates a NEW and distinct CWnd instance which points to that same HWND - that second CWnd is NOT CHFTAppBView and the HWND knows nothing about your MFC classes, views, documents or anything else.
You are trying to take the CWnd * that CWnd::FromHandle returns and hammer it into a CHFTAppBView *. No matter how hard you try, this will not work. All you can ever get will be a CWnd * and nothing else.
As a sidenote, you also cannot pass MFC objects from one thread to another, so passing the original CHFTAppBView * will cause weird issues to crop up, and may lead to hard to track errors.
Update 2:
You ask "Why can't a CWnd object be cast to a window class that derrives from CWnd?"
Let's start from CWnd::FromHandle shall we?
CWnd* PASCAL CWnd::FromHandle(HWND hWnd)
{
CHandleMap* pMap = afxMapHWND(TRUE); //create map if not exist
ASSERT(pMap != NULL);
CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd);
#ifndef _AFX_NO_OCC_SUPPORT
pWnd->AttachControlSite(pMap);
#endif
ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);
return pWnd;
}
This leads us to CHandleMap::FromHandle. The code for that is a bit complicated and posting it here won't help, so let's not clutter things up. But here is what the function does, conceptually:
If a handle is found in the map, it returns it; otherwise, it creates a new CWnd and makes that CWnd point to the HWND you passed in. It then returns a pointer to that CWnd to you. But notice, this is ONLY a CWnd. Nothiong else. So you see, you cannot just convert that returned CWnd to something else - even if the something else is derived from a CWnd - because what you have is just a CWnd and nothing more.
Let's say you have a wife. She is very beautiful and you love her very much. You carry a picture of her in your wallet. Now, when you meet someone and they ask if you are married, you take out your wallet and proudly show them her picture. The person to who you're talking do doesn't think that you are married to the picture. And you don't think that you can magically "convert" the picture into your wife.
The situation here is somewhat similar. CWnd::FromHandle gives you a "picture" of sorts. It's good for showing around, but not good for much else. You are asking "why can't I transform the picture to my wife?" The answer is because that's not how pictures work.

JNA - Use structure array as byref argument

I know parts of this issue is covered by some posts here and I have looked at them and tested some but with no luck.
I have this native method signature which should populate the provided CBadgeData structure array with results:
int elc_GetBadges(int nHandle, char* cErr, int* nRecCount, CBadgeData** arr)
The CBadgeData structure is implemented as follows:
package test.elcprog;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
public class CBadgeData extends Structure{
public static class ByReference extends CBadgeData implements Structure.ByReference { }
public int nBadgeID, nTrigger, nExtraData;
public String cName;
public CBadgeData(Pointer pointer){
super(pointer);
}
public CBadgeData(){ }
public String ToString() {
return nBadgeID + "," + nTrigger + "," + nExtraData + "," + cName;
}
#Override
protected List getFieldOrder() {
String[] s = new String[]{"nBadgeID","nTrigger","nExtraData","cName"};
return Arrays.asList(s);
}
}
My last try to craft this argument and call the method looked like this:
CBadgeData.ByReference[] badges = new CBadgeData.ByReference[max_items];
new CBadgeData.ByReference().toArray(badges);
int ret = inst.elc_GetBadges(handle, err, recCount, badges);
It fails with segmentation error.
My Question is what Java type should be provided here as an argument for the native CBadgeData** in the call to elc_GetBadges?
EDIT -1-
Populating the array myself (with or without terminating null pointer) didn't work and caused further Seg crashes. I then used Pointer[] arg as technomage suggested:
Pointer[] pointers = new Pointer[max_items];
for(int i=0; i<max_items; i++){
pointers[i] = new CBadgeData.ByReference().getPointer();
}
int ret = inst.elc_GetBadges(handle, err, recCount, pointers);
This caused no error but seems to not make any changes to the returning struct which should have contain 4 items in this case:
int bid = new CBadgeData(pointers[i]).nBadgeID; // this returns null for all items
Using explicit read() / write() on the struct led to Seg crashes again (on the read):
Any idea what am I still missing here?
EDIT -2-
Interestingly enough - using the Memory.get directly, after calling the native method, gets the correct results:
Memory m= (Memory)pointers[0];
System.out.println("1st int: "+m.getInt(0)); // this gets 24289 which is 5ee1
System.out.println("2nd int: "+m.getInt(4)); // this gets 3
System.out.println("3rd int: "+m.getInt(8)); // this gets 255
System.out.println("String: "+m.getString(12)); // this gets "Badge[5EE1]" as supposed
But the read() still crashes. Any thoughts?
I'm inferring that CBadgeData** input is intended to be an array of pointer to CBadgeData.
As such, the Structure.ByReference tagging is correct.
Structure.toArray() is probably not appropriate here, or at least not necessary (it allocates a contiguous block of structs in memory). You can just populate your array with CBadgeData.ByReference instances.
Perhaps your callee is expecting a NULL pointer at the end of the array? I don't see another indicator of the array length to the callee.
CBadgeData.ByReference[] badges = new CBadgeData.ByReference[max_items+1];
for (int i=0;i < badges.length-1;i++) {
badges[i] = new CBadgeData.ByReference();
}
badges[badges.length-1] = null;
Pretty sure that works. If for whatever reason there's a bug handling Structure.ByReference[], I know that Pointer[] is reliable and will do the same thing.
EDIT
If you use Pointer[] instead of Structure.ByReference[] (please post a bug to the project site if Structure.ByReference[] does not work), you will have to manually call Structure.write/read before/after your native function call, since JNA will not know that the pointers reference structures that need to be synched with native memory. I'd bet, however, that the cause of your crashes when using Structure.ByReference[] was simply that JNA was automatically calling Structure.read() after the call and triggered the same error that you see when calling it explicitly.
If you get a segfault on read, it likely means that your structure fields aren't properly aligned or defined, or (less likely) that you have corrupt data that can't be read properly. To diagnose this, set jna.dump_memory=true and print out your struct after calling Structure.write() to see if the contents of the structure appear as you'd expect. It'd also help to post the native and JNA forms of your structure here, if possible.

Postmessage LParam truncation

My app has a worker thread, and I use PostMessage to send a string to the main thread. For 1 message, the string is truncated when it gets to the message handler in the main thread.
The string is constructed in the worker thread from a string of raw data that is like this. It ends at the last '20'.
'01010000000030000102000008850008855343414E204544474520000000000000000000'
Decoded into the string I want to send it looks like this, which is correct:
'0100 0.50000 LSB0288.588.5SCAN EDGE '
The code that creates the 'SCAN EDGE ' portion and posts it is:
tmp and s_out are strings
x := 35;
for i := 1 to 10 do
begin
tmp := '$' + copy(s,x,2);
TryStrToInt(tmp,dec);
s_out := s_out + chr(dec);
x := x + 2;
end;
PostMessage(MainHandle,UM_CLONE, UM_756, Integer(PChar(s_out)));
The message handler in the main thread is:
i is a string
i := pChar(msg.LParam);
when it gets to the main thread i looks like this in the debugger:
'0100 0.50000 LSB0288.588.5SCAN EDG'#0
How can I correct this?
You are posting the contents of a String variable that is local to the thread procedure that is calling PostMessage(). If the String goes out of scope and gets freed before the main thread processes the posted message, the memory will be garbage.
You need to either:
1) use SendMessage() instead of PostMessage() so the String stays in scope until the message handler exits:
SendMessage(MainHandle, UM_CLONE, UM_756, LPARAM(PChar(s_out)));
2) dynamically allocate a String in memory, fill it as needed, post it, and then let the main message handler free it when it is done copying it:
var
s_out: PString;
New(s_out);
...
s_out^ := s_out^ + chr(dec);
...
if not PostMessage(MainHandle, UM_CLONE, UM_756, LPARAM(s_out)) then
Dispose(s_out);
.
var
ps: PString;
i: String;
ps := PString(msg.LParam);
i := ps^;
Dispose(ps);
PS: notice I also changed your Integer() cast to LPARAM(). This is very important if you ever upgrade to Delphi XE2 or later. PostMessage() and SendMessage() use LPARAM, not Integer. You can get away with it in Delphi 7 because LPARAM is an alias for Integer in that version, but that is not the case anymore in modern Delphi versions. LPARAM is an alias for NativeUInt now. LPARAM has always been an unsinged type in the Win32 API, Delphi just got it wrong in the early versions, but Embarcadero has really been pushing for type-correctness in the RTL/VCL since adding support for 64-bit and cross-platform development. If you don't match the right data types correctly, you can cause range checks errors and such at runtime.

How to call a function from another function in c++?

I have this function definition inside my cpp file;
LRESULT CRebarHandler::onSetRedraw(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled=false;
if (m_ieVer==6){
if (!m_hWndToolbar)
scanForToolbarSlow();
}
return S_OK;
}
My problem is I don't know how to call it from another function inside the same file. I want to call it from this function:
void CRebarHandler::setButtonMenu2(){
bool b=false;
onSetRedraw(0,0,0,false); <------ is this the correct way?
}
Must I provide all the 4 values? Can I just send no value?
Help me..
Yes, the way you have things defined you must provide values for all the parameters. Additionally, that last parameter must be a variable, because you defined it to be passed by reference.
If there are situations where that seems kind of silly and you'd just like it to fill in all 0's and false for you, you can supply the definition with those values as defaults, like so:
LRESULT CRebarHandler::onSetRedraw(UINT uMsg=0, WPARAM wParam=0, LPARAM lParam=0, BOOL& bHandled)
{
(note: You can't supply a default for bHandled, as it is passed by reference. That's an indication that your routine wants to modify it, and thus is must be a modifiable variable).
Then you can call it like this:
onSetRedraw(false)
Another option would be to just make another copy of the routine without any of those parameters. If you want you could make it private, so that only members of the class itself can call it.
It all depends on the function prototype, if there is an overload with default params, you dont need to provide values from within the function. If there isnt, you'll need all four parameters.
Since both methods seem to be in the same class, the method that you show * should * work.
No, the last parameter is a reference value so you need to provide a variable instead of a value (to be slightly more accurate, you need to provide an lvalue instead of an rvalue):
void CRebarHandler::setButtonMenu2(){
bool b=false;
onSetRedraw(0,0,0,b); // note the change here
}
As your example stands, you must provide all the arguments. But it's possible to provide default arguments so that you don't have to. That said, the rules of C++ require you to provide default arguments for the last parameters before the first values. Since a reference must be initialized with a variable/lvalue, it would be awkward to do this.
I think you should refactor your code. It becomes much more reusable.
There is almost nothing on your onSetRedraw function needing the parameters, so it turns out to be even easier to Extract a method from there.
void CRebarHandler::scan()
{
if (m_ieVer==6){
if (!m_hWndToolbar)
scanForToolbarSlow();ยด
}
}
LRESULT CRebarHandler::onSetRedraw(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled=false;
scan();
return S_OK;
}
void CRebarHandler::setButtonMenu2()
{
bool b=false;
scan();
}

Resources