Solved

Passing a string using SendMessage

Posted on 2001-06-12
20
1,256 Views
Last Modified: 2013-11-20
I want to send a String from one Application to another. I'm using SendMessage to do this and am passing the string in the wParam. But for some reason, I'm not able to "get" that string in the receiving application. Any clues guys ?
I have registered a window message say APP_MY_MSG. Then I'm doing a

CString csUser = "User1";
lRet = ::SendMessage((HWND)-1, APP_MY_MSG, (WPARAM)csUserID, APP_ISUSERVALID);

In my other Application, I get the message fine and can even switch with the lParam (as APP_ISUSERVALID), but I'm not able to get the value that I pass from here. Cd u tell me whatzz wrong
0
Comment
Question by:sri_darr
  • 11
  • 6
  • 2
  • +1
20 Comments
 
LVL 32

Expert Comment

by:jhance
Comment Utility
Nope, can't do it unless the string you want to send fits in an LPARAM.  So that gives you 4 characters.

Use the WM_COPYDATA message instead to pass small amounts of data to your other application.
0
 
LVL 32

Expert Comment

by:jhance
Comment Utility
By the way, the problem with your original approach is that your two process DO NOT SHARE their memory spaces.  So a string pointer in one process is inaccessible to the other.  Remember that all WIN32 processes are protected from each other.
0
 
LVL 3

Expert Comment

by:qocarlos
Comment Utility
I'd suggest to use memory mapped files. Have a look at
CreateFileMapping and MapViewOfFile functions

Carlos
0
 
LVL 32

Expert Comment

by:jhance
Comment Utility
gocarlos,

A memory mapped file is a viable option but it's probably overkill for sending a simple string.  I'm not sure at what size the data needs to be for it to make more sense to do a mapped file but my guess is that the WM_COPYDATA is the best choice for this application.
0
 
LVL 3

Expert Comment

by:qocarlos
Comment Utility
jhance,

I totally agree with you. Actually I use memory mapped files to share complex data structures between applications, but for this simple case WM_COPYDATA should be just fine.

Carlos
0
 
LVL 32

Expert Comment

by:jhance
Comment Utility
By the way, here is a Microsoft article that outlines the various methods of copying data between processes:

http://support.microsoft.com/support/kb/articles/q95/9/00.ASP
0
 
LVL 2

Expert Comment

by:MadYugoslav
Comment Utility
Also You can replace:
CString csUser = "User1";
with this:
static char csUser[] = "User1";
0
 
LVL 32

Expert Comment

by:jhance
Comment Utility
>>Also You can replace:

Sure you can do that, but what does that have to do with this situation?
0
 
LVL 4

Author Comment

by:sri_darr
Comment Utility
Thx jhance (and Carlos) for pointing me in the right direction. I used WM_COPYDATA ... and it almost works OK .,.. except that I get some junk along with the actual string that I pass ... Here's the code .. Am Idoing something wrong ?

// The Applilcation that sends the String

     const char szUserID[] = ("Test");
     int nLen = strlen(szUserID);

     COPYDATASTRUCT     stData;
     stData.dwData     = 0;
     stData.cbData     = strlen(szUserID);
     stData.lpData     = (void *)szUserID;

     lRet = ::SendMessage( (HWND) -1, WM_COPYDATA, (WPARAM)m_hWnd, (LPARAM) &stData);

// The Application that receives the string
    LPCSTR pszUserID = (LPCSTR)pCopyDataStruct->lpData;
           
     if (lstrcmpi(pszUserID, "Test") == 0)
     {
              m_bValidUser = TRUE;
     }
     else
     {
             m_bValidUser = FALSE;
     }
---\
The Problem is that in the receiving application I get the string as "Test|'cw|hA" .. Basically some junk along with the actual data. Why is this ?
Or shd I use the pCopyDataStruct->cbData in the receiving Appln to extract only that many characters out of the string and then check it ? That seems weird ..
0
 
LVL 32

Expert Comment

by:jhance
Comment Utility
Do one of two things:

1) In your send, send the terminating NULL of the string:

stData.cbData     = strlen(szUserID) + 1;

2) In your receiver, be sure the data is NULL terminated before using it.  So take the length of the string passed and stuff a NULL into the buffer.  Remember that WM_COPYDATA ONLY passes the exact number of bytes you tell it to.  No more.

I'd suggest #1.
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
LVL 4

Author Comment

by:sri_darr
Comment Utility
I'd like to use #1 too ... rather than #2. But it doesnt seem to work. I set the cbData in the sending appln. to stData.cbData     = strlen(szUserID) + 1;
as youd told me ... But I still get some junk past the string's length in the receiving appln. So for now, I use the cbData member in the receiving appln. to extract only that many from the data (Left()) and it works OK. I'll just wait for some more time.... to see if someone has an idea !
0
 
LVL 32

Expert Comment

by:jhance
Comment Utility
I put together a quick sample and tested it out.  I found no problems with doing it as below...


BOOL CCopyDataTestDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
     // TODO: Add your message handler code here and/or call default
     ::Beep(500, 500);

     TCHAR *pRcvData = (TCHAR *)(pCopyDataStruct->lpData);
     SetDlgItemText(IDC_RCVDATA, pRcvData);

     return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}

void CCopyDataTestDlg::OnSendbutton()
{
     // TODO: Add your control notification handler code here
     COPYDATASTRUCT cd;
     CString mSendData;
     GetDlgItemText(IDC_SENDDATA, mSendData);
     
     cd.cbData = mSendData.GetLength();
     cd.dwData = 0;
     cd.lpData = (LPVOID)((LPCTSTR)mSendData);

     ::SendMessage(HWND_BROADCAST, WM_COPYDATA, (WPARAM)this->m_hWnd, (LPARAM)&cd);
}
0
 
LVL 4

Author Comment

by:sri_darr
Comment Utility
Beats me jhance...
I put the exact same code as youd given here ... but it still doesnt get the exact string. It gets somethign like "User1c|1nW|aW" etc... ie. Some junk after the valid string.
0
 
LVL 32

Expert Comment

by:jhance
Comment Utility
Can you run it in the debugger and view the received block of data?  I inspected mine and see the string followed by a "00" (i.e. a NULL).

What Windows are you running this on?  It shouldn't matter but who knows??

I tested on Win98 SE.  I'll run it on Win2000 and see what happens...
0
 
LVL 32

Expert Comment

by:jhance
Comment Utility
Hey, I see what you're saying on Win2000.  Let me look a bit closer....
0
 
LVL 4

Author Comment

by:sri_darr
Comment Utility
I have Win NT 4.0 here.... And all the while I was only debugging it. At the stage where I'm passing it, the NULL termination is fine ... the string has '\0' at the end. It's only the receiving end where the problem occurs.
0
 
LVL 32

Accepted Solution

by:
jhance earned 50 total points
Comment Utility
There is some more information at:

http://msdn.microsoft.com/library/psdk/winbase/ipc_2mw1.htm

See where it says:

"The receiving application should consider the data read-only. The lParam parameter is valid only during the processing of the message. The receiving application should not free the memory referenced by lParam. If the receiving application must access the data after SendMessage returns, it must copy the data into a local buffer."

I think this is the heart of the matter.  I changed my code to that shown below.  It may be overly conservative but I'm not sure I know EXACTLY when Windows grabs the data out of mSendData.  If it's AFTER the function has returned the data may be gone or damaged.

The following seems to work reliably on Win98 and Win2000.  No NT4 system handy....

BOOL CCopyDataTestDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
     // TODO: Add your message handler code here and/or call default
     ::Beep(500, 500);

     TCHAR *pRcvData = _tcsdup((TCHAR *)(pCopyDataStruct->lpData));

     SetDlgItemText(IDC_RCVDATA, pRcvData);

     free (pRcvData);

     return TRUE;

     //return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}

void CCopyDataTestDlg::OnSendbutton()
{
     // TODO: Add your control notification handler code here
     static COPYDATASTRUCT cd;
     static CString mSendData;

     GetDlgItemText(IDC_SENDDATA, mSendData);
     
     cd.cbData = mSendData.GetLength() + 1;
     cd.dwData = 0;
     cd.lpData = (LPVOID)((LPCTSTR)mSendData);

     ::SendMessage(HWND_BROADCAST, WM_COPYDATA, (WPARAM)this->m_hWnd, (LPARAM)&cd);
}
0
 
LVL 4

Author Comment

by:sri_darr
Comment Utility
jhance, it still isnt' working the way you've said and so I'm using the LEFT() method to extract the string. But anyway since you pointed me in the right direction to get this done, I owe this to you.
Thanks
0
 
LVL 32

Expert Comment

by:jhance
Comment Utility
It would really help if you posted the exact code you are using.  I know that this works and so I really think you are doing something not quite right...
0
 
LVL 4

Author Comment

by:sri_darr
Comment Utility
Here's it :
In my first dialog app where I'm sending the information:
//FirstDlg.cpp
BOOL CFirstDlg::OnInitDialog()
{
     CDialog::OnInitDialog();
...
...
     CString csUserID = "Test";

     COPYDATASTRUCT     stData;
     stData.dwData     = 0;
     stData.cbData     = csUserID.GetLength();
     stData.lpData     = (LPVOID)(LPCTSTR)csUserID;

     lRet = ::SendMessage( (HWND) -1, WM_COPYDATA, (WPARAM)m_hWnd, (LPARAM) &stData);

...
...
}

and in the sedond dlg app where I'm receiving this string
//SecondDlg.cpp

BOOL CSecondDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
CString     csLauncherUserID;
LPCSTR     pszUserID = (LPCSTR)pCopyDataStruct->lpData;
int     nLength   =  pCopyDataStruct->cbData;
CString csUserID  = pszUserID;

GetDlgItemText(IDC_TXT_USERID, csLauncherUserID);

if (csUserID.Left(nLength) == csLauncherUserID)
{
        AfxMessageBox(USER_VALID);
}
else
{
     AfxMessageBox(USER_INVALID);
}

return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}


// I then changed it to TCHAR* as youd given in my SecondDlg. But it didnt change anything.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

If you use Adobe Reader X it is possible you can't open OLE PDF documents in the standard. The reason is the 'save box mode' in adobe reader X. Many people think the protected Mode of adobe reader x is only to stop the write access. But this fe…
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
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 video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

744 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

8 Experts available now in Live!

Get 1:1 Help Now