IPC: Using Window Message
There are numerous ways to communicate between two applications, in this post will tell you the way to communicate between two different app using raw window message. This is most simpler and primitive way of IPC communication.
Step of achieving IPC using Window Message:-
- Create Dialog based application, implement way to set caption of dialog box window (will let you know in next step, requirement behind it). Use WIN32 Api SetWindowText to change window caption.
void CMFCTwoWayCommunicationDlg::OnBnClickedButtonSetcaption() { CString tmpString; GetDlgItemText(IDC_EDIT_CAPTION,tmpString); if (!tmpString.IsEmpty()) { SetWindowText(tmpString); } } - Use FindWindow Api to find window handle of remote process, use #32770 as classname (its dialog window class name) and caption set in step 1 to find remote window.
void CMFCTwoWayCommunicationDlg::OnBnClickedButtonFindwindow() { CString tmpString; GetDlgItemText(IDC_EDIT_WINDOWCAPTION,tmpString); m_pRemoteWnd = NULL; // this CWnd* pointer declared in class m_pRemoteWnd=FindWindow(L"#32770",tmpString); if (m_pRemoteWnd != NULL) { MessageBox(L"Remote Window Found"); } } - Now add Message Handller for WM_APP+1 for handlling external message.
ON_MESSAGE(WM_APP+1,&CMFCTwoWayCommunicationDlg::OnApplicationMessage) LRESULT CMFCTwoWayCommunicationDlg::OnApplicationMessage(WPARAM wParam, LPARAM lParam) { CString strMsg; strMsg.Format(L"Message Recv: %d",lParam); MessageBox(strMsg,L"MSG RECV from Remote Client"); return 0; } - Use PostMessage Api to post message to remote window using Window Handle retrieved in step 2.
void CMFCTwoWayCommunicationDlg::OnBnClickedButtonSendmsg() { int iMsgToSend = GetDlgItemInt(IDC_EDIT_SENDMSG); if(m_pRemoteWnd != NULL) { m_pRemoteWnd->PostMessageW(WM_APP+1,0,iMsgToSend); } }
Output:-
Now you must be wondering why PostMessage not SendMessage, because PostMessage lineup message in remote window message queue and return. Even if application doesn’t exist it will work without exception.
Download code :- MFCTwoWayCommunication
Using Cryptography on Window Socket
During one of my project, I am working on transferring encrypted data over plain socket and decrypting incoming encrypting data. In plain language my task is to enable SSL over plain socket. There many examples available on Internet to do so in VC++ (will mention link at bottom).
Algorithm for enabling SSL connection would be something like this:-
- Use your own certificate or create your own self signing certificate using (CertStrToName, CryptAcquireContext, CryptGenKey, CertCreateSelfSignCertificate and CryptFindCertificateKeyProvInfo Cryptographic api’s).
- Then call AcquireCredentialsHandle api to retrieve handle to context to be used encrypting outgoing message and decrypting incoming message.
- Then handshake with remote party using data supplied by InitializeSecurityContext. Once handshaking is completed.
- Use QueryContextAttributes and EncryptMessage to encrypt message while sending to remote host.
- Use DecryptMessage to decrypt incoming data from remote host.
- Once done, don’t forget to delete your self signing certificate and context.
Looking for Links: -
- http://www.codeproject.com/KB/IP/ssl_sockets.aspx (Non MFC based)
- http://www.codeproject.com/KB/IP/sslsocket.aspx (MFC Based)
Some problem I encountered while using above:-
I am using self signing certificate and storing it at Machine store (using CRYPT_MACHINE_KEYSET option in CryptAcquireContext). So if I logged with my profile working on machine, every thing is working fine. However my colleague when logged-in to my machine, he is not able to create certificate or able to get reference on previously created certificate.
Reason behind this, according to MSDN is:-
By default, keys and key containers are stored as user keys. For Base Providers, this means that user key containers are stored in the user's profile. A key container created without this flag by an administrator can be accessed only by the user creating the key container and a user with administration privileges.
Windows XP: A key container created without this flag by an administrator can be accessed only by the user creating the key container and the local system account.
A key container created without this flag by a user that is not an administrator can be accessed only by the user creating the key container and the local system account.
The CRYPT_MACHINE_KEYSET flag can be combined with all of the other flags to indicate that the key container of interest is a computer key container and the CSP treats it as such. For Base Providers, this means that the keys are stored locally on the computer that created the key container. If a key container is to be a computer container, the CRYPT_MACHINE_KEYSET flag must be used with all calls to CryptAcquireContext that reference the computer container. The key container created with CRYPT_MACHINE_KEYSET by an administrator can be accessed only by its creator and by a user with administrator privileges
Since my application going to run as System account, so I have no option, I have to use CRYPT_MACHINE_KEYSET and store my key in system store. So I have done following to solve my problem :- create my keyset at the time of creating self signing certificate and delete it at time closing SSL connection using CRYPT_DELETEKEYSET in CryptAcquireContext
Default and Customized Constructors
- A default (no-parameter) constructor is for creating an object with default state.
1.1. That is, with member data set to default values.
1.2. The principle behind this is that (by design) such objects can be used immediately after creation.
1.3. Note that it must make sense to construct an object with default state.
1.4. There are 2 points to note about code like the following :
- some_class instance;
2.1. This indicates that we want to create an object "instance" of type "some_class" and that "instance" should have default state.
2.2. If it is deemed acceptable that a "some_class" object, in default state, has member data that can be set to default values, then it does make sense to have "instance" in default state.
2.3. The compiler requires the definition of a default constructor in order to successfully compile code like the above.
- If there are no constructor functions at all for "some_class", it means that we want the compiler to create the default constructor for us. This is not always a good idea because an unsatisfactory constructor may be created for our code.
- If we have at least one non-default constructor for "some_class", but no default constructor for the class, e.g. :
some_class(int i);
This indicates the following :
4.1. That by design a "some_class" object has no default state.
4.2. That in order to be constructed properly, parameters must be given in order to sensibly initialize the state of the object.
4.3. In the case of "some_class", it means that parameter "i" is used to somehow initialize an instance to a meaningful state.
4.4. In this case, the compiler will not generate any default constructor for "some_class" because by design the class must not have one.
Failure of javascript usage of MFC ActiveX Control – Part 4
The following is a continuation of Bio's reply :
Hello Anand,
As promised, the following is my long answer :
Long Answer
1. Unless the value of the CLSID attribute of the OBJECT tag in your HTML code contains the symbol "CLSID:" as in :
<OBJECT classid="CLSID:2D0B18FB-673F-4B90-865F-3EC067C9DEA9" id="a"></OBJECT>
The CLASSID attribute remains invalid and so your ActiveX will not get created.
2. Next, comes the problem with "myobject" :
myobject = new ActiveXObject("SAMPLEACTIVEX.sampleActiveXCtrl.1");
The issue has to do with the calling of the IPersistPropertyBag::InitNew() method of your MFC ActiveX Control. This interface method is called by the MSHTML.DLL code within IExplorer.exe.
3. The IPersistPropertyBag::InitNew() method is implemented for you by MFC via the COleControl::XPersistPropertyBag::InitNew() method. Your MFC class would have been derived from COleControl and so this function is already embedded within your MFC class.
3. Inside COleControl::XPersistPropertyBag::InitNew(), you will notice a call to set COleControl::m_bInitialized to TRUE. This is important as we will see later.
4. Now, when any of your control's methods is called, the JScript engine will first get MSHTML to obtain the ID of your method. This is the essence of late binding. Hence, if MyAdd() is to be invoked, JScript will need to know the ID of the MyAdd() function which in your ActiveX control would be DISPID_MYADD.
5. MSHTML will inquire of your ActiveX the ID of MyAdd() by calling your ActiveX control's IDispatch::GetIDsOfNames() method. This is implemented for you by MFC with the function : COleDispatchImpl::GetIDsOfNames().
6. After obtaining this ID, the IDispatch::Invoke() method will be called. The IDispatch::Invoke() method is implemented for your ActiveX by the COleDispatchImpl::Invoke() method. It is here that your MyAdd() function should get called. However, there is a line within COleDispatchImpl::Invoke() that goes like this :
// allow subclass to disable Invoke
if (!pThis->IsInvokeAllowed(dispid))
return E_UNEXPECTED;
This is the function COleControl::IsInvokeAllowed() and it is the "archilles tendon" of your problem.
7. Examining COleControl::IsInvokeAllowed() :
BOOL COleControl::IsInvokeAllowed(DISPID)
{
return m_bInitialized;
}
We will note that the state of the COleControl::m_bInitialized, if FALSE, will result in the premature conclusion of the COleDispatchImpl::Invoke() method, resulting in the return of the E_UNEXPECTED HRESULT. This is the cause of the exception in your Javascript.
8. In summary, adding "CLSID:" to the CLASSID attribute of the OBJECT tag is not sufficient. You must use the ActiveX control as declared by the OBJECT tag whose id is "a". The use of the ActiveXObject() function is meant to create and return a COM interface. There are 2 problems with the use of the ActiveXObject() function in relation to your Javascript :
8.1 By declaring "myobject" and then setting it to a new ActiveXObject(), a brand new instance of your ActiveX control will be created. The "a" object will remain but it will not be used. The calling of IPersists* interface methods of your ActiveX control will not be called.
8.2 "myobject" will end up without its COleControl::XPersistPropertyBag::InitNew() ever being called which will result in its COleControl::m_bInitialized remaining FALSE. This results in the COleDispatchImpl::Invoke() function returning E_UNEXPECTED.
Hence my recommendation that you use the "a" object and remove the declaration and use of "myobject".
I hope the above rather long explanation helps.
- Bio.
Failure of javascript usage of MFC ActiveX Control – Part 3
The following was my reply to Anand :
Hello Anand,
Quick answer :
1. In your OBJECT tag :
<OBJECT classid="2D0B18FB-673F-4B90-865F-3EC067C9DEA9" id="a"></OBJECT>
add a preceding "CLSID:" before the GUID of your ActiveX control :
<OBJECT classid="CLSID:2D0B18FB-673F-4B90-865F-3EC067C9DEA9" id="a"></OBJECT>
2. Do not create your ActiveX control as an ActiveX object :
// var myobject; // remove
// myobject = new ActiveXObject("SAMPLEACTIVEX.sampleActiveXCtrl.1"); // remove
if(a != null) alert("Object Found");
if((a.MyAdd(2,3))!=null)
alert("got result");
else
alert("Result is Null");
Rather, use the ActiveX control (i.e. "a") referred to in your OBJECT tag.
Pls try and let me know if you succeed. I'll return with a longer answer on another post.
- Bio.
Failure of javascript usage of MFC ActiveX Control – Part 2
The following is Anand's IDL file snippet :
#include <olectl.h>
#include <idispids.h>#define DISPID_ADD (1025314)
#define DISPID_MYADD (1025315)[ uuid(4B16D152-7D25-48A4-8DFE-EABF2AFB7FA3), version(1.0),
helpfile("sampleActiveX.hlp"),
helpstring("sampleActiveX ActiveX Control module"),
control ]library sampleActiveXLib
{importlib(STDOLE_TLB);
// Primary dispatch interface for CsampleActiveXCtrl
[ uuid(D99114F8-641E-471E-AA70-5644C0A91E9A),
helpstring("Dispatch interface for sampleActiveX Control")]
dispinterface _DsampleActiveX
{
properties:
methods:
[id(DISPID_ABOUTBOX)] void AboutBox();
[id(DISPID_ADD)] long Add(short x, short y);
[id(DISPID_MYADD)] long MyAdd(short param1, short param2);};
// Event dispatch interface for CsampleActiveXCtrl
[ uuid(AD8B5691-3F3D-413E-8468-77F7440ADD33),
helpstring("Event interface for sampleActiveX Control") ]
dispinterface _DsampleActiveXEvents
{
properties:
// Event interface has no properties
methods:
};// Class information for CsampleActiveXCtrl
[ uuid(2D0B18FB-673F-4B90-865F-3EC067C9DEA9),
helpstring("sampleActiveX Control"), control ]
coclass sampleActiveX
{
[default] dispinterface _DsampleActiveX;
[default, source] dispinterface _DsampleActiveXEvents;
};
}
Failure of javascript usage of MFC ActiveX Control – Part 1
The following case is extracted from the MSDN Visual C++ Forum :
Posted by Anand K Reddy :
I have created an MFC ActiveX dll and I have exposed the methods in Dispatch map. I am able to call the method and get the result using C#.NET client but unable to call from Javascript. When the method is called from Javascript and exception is thrown without any message. I am not able to debug where exactly the problem is.
The Javascript code is pasted below and the sample activeX control is attached.
<HTML>
<HEAD>
<OBJECT classid="2D0B18FB-673F-4B90-865F-3EC067C9DEA9" id="a"></OBJECT>
<TITLE></TITLE><script type='text/javascript' language='javascript'>
function callTest()
{
try
{
var myobject;
myobject = new ActiveXObject("SAMPLEACTIVEX.sampleActiveXCtrl.1");if(myobject != null) alert("Object Found");
if((myobject.MyAdd(2,3))!=null)
alert("got result");
else
alert("Result is Null");
}
catch(e)
{
alert("exception:"+e.message);
}
}</SCRIPT>
</HEAD>
<BODY>
<INPUT TYPE="button" value="TEST" onClick="callTest()">
</BODY>
</HTML>
</code>
