Archive for the ‘programming’ Category

C# Win32 messaging with SendMessage and WM_COPYDATA

July 29, 2008

I had a real pain recently where I wanted to control one windows app from another. I found some useful stuff on the net, but nothing that gave an end to end solution. So here’s what I came up with.

Firstly I’ll explain why this is useful. SendMessage is part of the Win32 API, and is used to send messages from one application to another. There are a set of predefined properties that the message can relate to, and these can be used to send messages to existing applications to perform all sorts of useful functions such as changing the font in notepad, or bringing a window to the fore. For more information of the wider use of the SendMessage function, have a look at:

http://www.autohotkey.com/docs/commands/PostMessage.htm

http://msdn.microsoft.com/en-us/library/ms644950(VS.85).aspx

The main use that I’m interested in is passing a specific instruction (via a string) from one app that I’ve written, to another one that I’ve written. This way I can effectively remote control one app from another (particularly useful if you want your main application to open a pop-up, and you don’t want to worry about the pop-up’s performance affecting the main application). Let’s now have a quick look at the SendMessage function:

SendMessage(int hWnd, int Msg, int wParam, int lParam)

hWnd - This is the window instance id of the application you want to send a message to. This id is retrieved using the FindWindow function

Msg - This is the type of message you want to send

wParam - Message specific data you pass in

wParam - Message specific data you pass in

Also used is the FindWindow function. This is to get the relevant window id:

FindWindow(String lpClassName, String lpWindowName)

lpClassName -The name of the class you want

lpWindowName - The name of the window that you want

To send a message that is a string, you need to use the WM_DATACOPY message property. The hard part is that you cannot just send the string as a parameter across. You need to send a pointer to the memory address of the string. If you just want to send an integer as a message you can use the WM_USER message property and send it as a value without a problem.

Below now is a brief listing of my MessageHelper.cs class, for the whole class file see:

http://craigcook.co.uk/samples/MessageHelper.cs.txt

//////////////////// Code Begins ////////////////////

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.InteropServices;
using System.Diagnostics;

public class MessageHelper
{
[DllImport("User32.dll")]
private static extern int RegisterWindowMessage(string lpString);

[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);

//For use with WM_COPYDATA and COPYDATASTRUCT
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);

//For use with WM_COPYDATA and COPYDATASTRUCT
[DllImport("User32.dll", EntryPoint = "PostMessage")]
public static extern int PostMessage(int hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);

[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);

[DllImport("User32.dll", EntryPoint = "PostMessage")]
public static extern int PostMessage(int hWnd, int Msg, int wParam, int lParam);

[DllImport("User32.dll", EntryPoint = "SetForegroundWindow")]
public static extern bool SetForegroundWindow(int hWnd);

public const int WM_USER = 0×400;
public const int WM_COPYDATA = 0×4A;

//Used for WM_COPYDATA for string messages
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}

public bool bringAppToFront(int hWnd)
{
return SetForegroundWindow(hWnd);
}

public int sendWindowsStringMessage(int hWnd, int wParam, string msg)
{
int result = 0;

if (hWnd > 0)
{
byte[] sarr = System.Text.Encoding.Default.GetBytes(msg);
int len = sarr.Length;
COPYDATASTRUCT cds;
cds.dwData = (IntPtr)100;
cds.lpData = msg;
cds.cbData = len + 1;
result = SendMessage(hWnd, WM_COPYDATA, wParam, ref cds);
}

return result;
}

public int sendWindowsMessage(int hWnd, int Msg, int wParam, int lParam)
{
int result = 0;

if (hWnd > 0)
{
result = SendMessage(hWnd, Msg, wParam, lParam);
}

return result;
}

public int getWindowId(string className, string windowName)
{

return FindWindow(className, windowName);

}
}

//////////////////// Code Ends ////////////////////

So now you can call the code to send a message like so:

MessageHelper msg = new MessageHelper();
int result = 0;
//First param can be null
int hWnd = msg.getWindowId(null, “My App Name”);
result = msg.sendWindowsStringMessage(hWnd, 0, “Some_String_Message”);
//Or for an integer message
result = msg.sendWindowsMessage(hWnd, MessageHelper.WM_USER, 123, 456);

Now all you need to do on the app that you want to receive the message is override the following function in the form class (obviously you can change what the responses are, and you’ll need to create constants for the parameters):

protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_USER:
MessageBox.Show(”Message recieved: ” + m.WParam + ” - ” + m.LParam);
break;
case WM_COPYDATA:
COPYDATASTRUCT mystr = new COPYDATASTRUCT();
Type mytype = mystr.GetType();
mystr = (COPYDATASTRUCT)m.GetLParam(mytype);
this.doSomethingWithMessage(mystr.lpData);
break;
}

base.WndProc(ref m);
}

TiddlyBlogger updated

November 5, 2007

I’ve recently updated my original TiddlyBlogger code to include JayFresh’s additions mentioned here (cheers for your work here Jon).

The added features are:

  • You can publish tags
  • You get a response to the XML-RPC Ajax call (i.e. you know if your post was successful
  • The ‘publish as blog’ option now only appears when you add the tag ‘blog’ to your tiddler.

Changes planned for the future are:

  • Being able to pull down blogs from WordPress (and editing them).
  • Support for more complex formatting (hyper links, bullet points etc)

How do I see users presence on a website? (or How do I detect when a user closes the browser?)

September 24, 2007

This applies to a website with the client/server relationship. The short answer to this is that you can’t (that I’m aware of anyway). Not properly using any web browser/server components anyway. You can however make a good attempt at it. The problem is this situation is that the client and the server are not in permanent communication with each other, they are disconnected. I would also like to point out that there may be better ways of doing this, this is just a way that has worked for me in the past.

Let’s first set the scene. You have a website where users come and go. They log in and out. Now you want to be able to tell at any one time how many, and specifically which, users are logged in. There are many reasons for wanting to do this, the reason I first did was to create a messaging plug-in for one of my websites.

The easiest way of doing this is every time a user logs in you create an audit record (you could use application variables or write to a DB, it doesn’t really matter), and then when they log out you create another audit record. Then you can compare the records to tell if a user is still logged in.

This is fine if all of your users are nice people that actually bother to click ‘log out’ 99% of users will just close the browser or tab when they are finished, resulting in the users session remaining until it expires (usually about 20 minutes or so later). This is where the problem lies, and what we need to solve.

The solution lies in three areas:

  • An audit log (DB, application variables) to store who is currently logged in. Managed by having records for log in and out.
  • Capturing session start and end.
  • Calling the server to log the session end from the client.

The easiest way to work through this is backwards:

  • The last thing that needs to happen is the function that writes the ‘log out’ audit record is called.
  • This will be called by a function when the user session expired. This can go somewhere like the global.asax file (assuming .Net).

The next problem is that the session will still be alive until it reaches its expiry time. We need to force the session to expire early. This can be done by using the following code at the server level:

HttpContext.Current.Session.Abandon();

We now need to call this from the client at the appropriate time i.e. when the user closes the browser. Unfortunately there is no client-side (JavaScript) event that is fired when the browser closes. There is a:

window.close;

event, but don’t let this fool you. This only works for pop-ups, it doesn’t work for the main browser window. The closest events that do work are:

window.unload;

window.beforeunload;

So you could do something like:

Client JavaScript:

window.unload = funManageUnload;

var closeMe = true;

function funManageUnload()
{
if (closeMe)
{
//Make Ajax call to server

}
}

function navigateWebSite(url)
{
closeMe = false;
window.location.href = url;
}

Server C#

private void killSession()
{
HttpContext.Current.Session.Abandon();
}

void Session_End(object sender, EventArgs e)
{
//Logout code
}

private void logIn(string uName, string pWord)
{
//Login code
}

private void logOut(string uName)
{
//Logout code
}

public bool isUserLoggedIn(string uName)
{
//Compare audit records
}

Both those JavaScript events fire when a form unloads, which does happen when the browser closes. So you can set your code to call specific JS functions when these events fire. You now have to tackle working out when the form is unloading because the browser has been closed, or because of just normal website navigation. There are a couple of ways of doing this:

  • You can use frames in your website. By running the main site through a single frame within a parent frame and then change the child frame URL to different pages as you navigate your website. That way the parent frame will always be loaded and will only unload (or fire the window.unload event) when the browser is closed. The problem with this solution is that it makes searching your website hard. It also means that you cannot have direct URL’s to specific pages of your website (unless you pass in the page name via a querystring), which will mean that search engines (i.e. Google) will not be able to trawl your site. This isn’t really a problem if your site is for personal or company use.
  • Run ALL form submitting through one JavaScript function. You can use a variable to determine if the page is just navigating through the website normally, if set to true (as in the example above) you can make an Ajax call to the server to end the session. If it is set to false then do nothing because this is just normal website navigation. The problem with this way is that if you are using any additional form elements other than plain HTML (e.g. ASP.Net form controls) then there may be form submissions that you cannot trap with JavaScript (e.g. if you allow sorting on an asp:DataGrid)
  • You can use the ASP.Net pagemethods functionality. This allows you to call a function on the server from your JavaScript code. This can be used in the funManageUnload() function, and then the server side function called can kill the session etc. For more information on this see the website http://www.asp.net/ajax/

I’ve not aimed to give a definite solution to this problem, but I’ve hoped to give some pointers in directions that you can take. If anyone finds any better solutions I’d love to hear them.