Thursday, November 6, 2008

Services for Windows Mobile in managed code

Services for Windows Mobile in managed code

Friday, April 18, 2008 @ 23:57

Services in Windows Mobile and Windows CE are DLL libraries loaded by service.exe and then run as ..... well as a services. Unfortunately these DLLs has to be written in native code. But why to use native code when man just needs little "invisible" application, that will survive Windows Mobile memory management?

ServiceApplication class

I wrote ServiceApplication class that is used in similar way as Application class in Windows Forms. In fact it is the console application with main loop that processes system messages. ServiceApplication can be controlled from outside world by interprocess messages. Simple WM_USER windows messages. The easiest service can look like this.

const int WM_USER = 0x400;
const int WM_QUIT_SERVICE = WM_USER + 5001;
const int WM_CLEAR_LOG = WM_USER + 5002;

static void Main(string[] args)
{
// Set register name
ServiceApplication.Name = "MyFirstService";

// Register messages
ServiceApplication.RegisterMessage(Interprocess.WM_QUIT_SERVICE);
ServiceApplication.RegisterMessage(Interprocess.WM_CLEAR_LOG);

// Set event handler for registered messages
ServiceApplication.OnRegisteredMessage +=
new ServiceApplication.RegisteredMessageEventHandler(ServiceApplication_OnRegisteredMessage);

// Start main application loop
ServiceApplication.Run();
}

There is a service name set, which is actually name of MessageWindows that represents the service. All messages to control the service is send to this MessageWindow. All messages that should be processed by service has to be registered. Whenever the registered message arrived, event is fired. Event handler may look like this.

static void ServiceApplication_OnRegisteredMessage(ref Microsoft.WindowsCE.Forms.Message message)
{
switch (message.Msg)
{
case Interprocess.WM_QUIT_SERVICE:
ServiceApplication.Exit();
break;

case Interprocess.WM_CLEAR_LOG:
ClearLogFile();
break;
}
}

Talking to service

As I mentioned earlier, service is controlled through windows messages. Following code sends message to the service.
const int WM_USER = 0x400;
const int WM_QUIT_SERVICE = WM_USER + 5001;
const int WM_CLEAR_LOG = WM_USER + 5002;

public void StopService()
{
IntPtr hwnd = FindWindow(IntPtr.Zero, "MyFirstService");
Message msg = Message.Create(hwnd, WM_QUIT_SERVICE, (IntPtr)0, (IntPtr)0);
MessageWindow.SendMessage(ref msg);
}

[DllImport("coredll.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(IntPtr lpClassName, string lpWindowName);

Example application

ServiceApplication class is bundled in little demo application. Whole solution consists of three projects. Fisrt project CallLoggerLib is library containing ServiceApplication class and Interprocess class for communication with service. Second project is service implementation itself. Very simple CallLoggerService thats logs information about last phone call into the file. Third project CallLogger is a simple UI application which is used to start and stop the service.

When deploying to the device all files has to be in same directory!!

Download file: CallLogger.zip [60 Kb]

CODEPLEX library

Updated at 9th June 2008.Peter Nowak did some improvements on my sample code and publish it as a complete library on Codeplex: Managed Services for Windows Mobile.

Firing thread safe events in Compact Framework

Firing thread safe events in Compact Framework

Friday, December 7, 2007 @ 09:54
Threading is one of the most important thing in application development on Windows platform. Almost none of the serious applications can run in single thread. You will always need to perform some operations on background. The only problem with threads is, when they try to share some critical resources. One of the typical case are UI elements. Whenever you try to change some UI control from different thread than UI thread you will end up with the exception.

Changing UI from thread

Before we look at the events, lets have a look at possibilities for changing UI from thread. All UI controls has methods Invoke and/or BeginInvoke. These methods accepts delegates as parameters. Invoking means, that the delegate given as the parameter will be exceuted in thread that is owner of the UI control. This will not cause parallel access to the UI resource, so no exception will be thrown.

The first option for invoking uses BeginInvoke method and WaitCallback delegate.

public partial class Form1 : Form
{
Thread thr;

public Form1()
{
InitializeComponent();
thr = new Thread(new ThreadStart(ThreadWorker2));
thr.Start();
}

private void ThreadWorker()
{
int i = 0;

while(true)
{
string message = string.Format("Value of i={0}", i++);
UpdateStatus(message);

Thread.Sleep(1000);
}
}

private void UpdateStatus(object status)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new WaitCallback(UpdateStatus), status);
return;
}

label1.Text = (string)status;
}
}
This solution is suitable for most situations where simple status reporting is required. Disadvantage of this solution is the only one argument of WaitCallback delegate. In case of multiple informations to be given, you will need to pass structure or array as the argument. Anyway, I'am still using this solution a lot.

Second option offers more scalability and more arguments to be passed, because of custom delegate instead of the WaitCallback. Following code is also part of the previous Form1 class.

public delegate void StatusEventHandler(DateTime date, string message);

private void ThreadWorker2()
{
int i = 0;

StatusEventHandler statusHandler = UpdateStatus2;

while (true)
{
string message = string.Format("Value of i={0}",i++);
this.Invoke(statusHandler, new object[] { DateTime.Now, message});
Thread.Sleep(1000);
}
}

private void UpdateStatus2(DateTime date, string message)
{
label1.Text = date.ToShortTimeString();
label1.Text += " " + message;
}

Firing thread safe events

Applications are usually splited into two or three layers, where UI and business logic are different layers. When event comes from business layer to UI layer we need to update some status or information. Problem is, if logic in business layer is running in thread. In that case, we need to catch the event and pass the status into thread safe Invokation described above. Such a design is really lousy, because UI logic must in fact rethrow the event to itself and layers are no more strictly separated.

It is necessary to fire event in business layer thread safe way, so that UI layer can immidiately show the status. Since delegates and events in Compact Framework has no Target property as it's in full .NET Framework, we need some workaround. Let's have a look at following class. It is simple worker class representing business layer. Class has UIControl property which represent the UI control that will be Invoked. So FireStatusUpdate method Invokes the control and fires the event. This means, the event will be fired in thread which owns the UIControl.

using System;
using System.Windows.Forms;
using System.Threading;

namespace ThreadTest
{
public class WorkerClass
{
private Thread thr;

// UI control for update
public Control UIControl { get; set; }

public delegate void StatusUpdate(DateTime dateTime, string message);
public event StatusUpdate OnStatusUpdate;

// Starts thread
public void Start()
{
thr = new Thread(new ThreadStart(MainWorker));
thr.Start();
}

// Main thread worker
public void MainWorker()
{
int i = 0;

while (true)
{
string message = string.Format("Value of i={0}", i++);
FireStatusUpdate(DateTime.Now, message);

Thread.Sleep(1000);
}
}

// Fire thread safe event
private void FireStatusUpdate(DateTime dateTime, string message)
{
// UIControl is set and OnStatusUpdate has subscriber
if (UIControl != null && OnStatusUpdate != null)
{
if (UIControl.InvokeRequired)
{
UIControl.Invoke(new StatusUpdate(FireStatusUpdate),
new object[] { dateTime, message });
return;
}

OnStatusUpdate(dateTime, message);
}
}
}
}

Following code is simple UI layer for WorkingClass.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace ThreadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

WorkerClass worker = new WorkerClass();
// add event handler
worker.OnStatusUpdate += new WorkerClass.StatusUpdate(worker_OnStatusUpdate);
// add UI control to invoke
worker.UIControl = this;
worker.Start();
}

void worker_OnStatusUpdate(DateTime dateTime, string message)
{
label1.Text = dateTime.ToLongTimeString();
label1.Text += " " + message;
}
}

Thread safe events in full framework

Following snippet shows the modification of the FireStatusUpdate method, to be used in full .NET Framework. There is no need for UIControl property, because delegates contains automaticaly filled property Target.
// Fire thread safe event
private void FireStatusUpdate(DateTime dateTime, string message)
{
// if OnStatusUpdate has subscriber
if (OnStatusUpdate != null)
{
// List all handlers
foreach (StatusUpdate handler in OnStatusUpdate.GetInvocationList())
{
// if handler target is control then Invoke it
if (handler.Target is Control)
(handler.Target as Control).Invoke(handler, new object[] { dateTime, message });
else
handler(dateTime, message);
}
}
}

Input mode in smart phone application

Input mode in smart phone application

Friday, October 5, 2007 @ 14:03
If you ever used TextBox control in smart phone application, you may found a bit confusing that input mode is selected to numbers. Entering alphabetic strings is then quite uncomfortable. Solution for this behavior is already included in .NET Compact Framework, but not so many coders knows about it.

InputModeEditor

Microsoft.WindowsCE.Forms namespace contains class InputModeEditor with single static method SetInputMode that takes two arguments. First parametr is the object that takes the input (usually TextBox), second one is the enumeration InputMode which contains values like AlphaABC, AlphaT9, Numeric etc.. Usage is obvious:
using Microsoft.WindowsCE.Forms;

......

InputModeEditor.SetInputMode(textBox1, InputMode.AlphaABC);

Compact Web Server in Compact Framework

Compact Web Server in Compact Framework

Monday, October 22, 2007 @ 15:42
Today I want to write about small class library I wrote some times ago. It is the compact web server written in C#. Class library implements functionality of the single threaded web server, which can run in Windows CE powered system as well as desktop version of Microsoft Windows. Currently only the GET method is supported, but it's possible to pass query strings and process 'em in custom code. Let's see some examples.

Configuring web server

Configuration for web server is stored in WebServerConfiguration class. Here it's possible to setup server root, listening port, IP address etc.. Important thing is the possibility to configure default files like default.html, index.html or other. Do not forget to register MIME types for files that will be handled by the web server. Setting right mime types is necessary for correct response header of the server.

Special files are those files, that need to be treat in custom method and not just send to the client. CompactWeb server supports virtual directories so you can request directory placed outside the server root.

WebServerConfiguration webConf = new WebServerConfiguration();

webConf.IPAddress = IPAddress.Any;
webConf.Port = 80;

// Folder where the web is stored
webConf.ServerRoot = @"\Inetpub";

webConf.AddDefaultFile("default.html");
webConf.AddMimeType(".htm", "text/html");
webConf.AddMimeType(".html", "text/html");
webConf.AddMimeType(".png", "image/png");
webConf.AddMimeType(".jpg", "image/jpg");
webConf.AddMimeType(".gif", "image/gif");
webConf.AddMimeType(".bmp", "image/bmp");
webConf.AddMimeType(".cgi", "text/html");

// .cgi files will be special handled
webConf.AddSpecialFileType(".cgi");

// Register virtual directory
webConf.AddVirtualDirectory("photos", @"\My Documents\Photos");
Necessary step is to setup server root physically in the file system. Simply create the folder and put the website in. file system server root

Start me up

Starting the web server is the easiest thing in whole process. Registering OnLogEvent is fine to track client connections, response sending and exceptions. OnSpecialFileType is raised whenever the file registered as special is requested by client. Delegate for this event can handle output to the client.
webServer = new WebServer(webConf);
webServer.OnLogEvent += new WebServer.LogEvent(webServer_OnLogEvent);
webServer.OnSpecialFileType += new WebServer.SpecialFileType(webServer_OnSpecialFileType);
webServer.Start();
From here, your server is running and ready to serve all files placed in the server root. sample server home page

ASP, PHP, CGI whatever you want

If it's necessary to process some files in special way, you can use delegate to OnSpecialFileType and take the control over processing. Next piece of code handles file with .cgi extension. It takes the content of the file and replace <%=RESULT%> pattern with parameter userName passed in the query string. In case that you will call following URL http://127.0.0.1/form.cgi?userNamer=Joshua the <%=RESULT%> pattern will be replaced with Joshua.
void webServer_OnSpecialFileType(CompactWeb.WebRequest webRequest, out IO.Stream outputStream)
{
outputStream = new MemoryStream();

if (webRequest.FileName == "form.cgi")
{
// Read the requested file template
FileStream sourceFile = new FileStream(webRequest.FullPath, FileMode.Open);
StreamReader streamReader = new StreamReader(sourceFile);
string response = streamReader.ReadToEnd();
streamReader.Close();
sourceFile.Close();

// Parse query string into NameValueCollection
NameValueCollection query = WebServer.ParseQueryString(webRequest.QueryString);

// Replace <%=RESULT%> with the username
string userName = query["userName"];
response = response.Replace("<%=RESULT%>", userName);

// Creat response
byte[] buffer = Encoding.ASCII.GetBytes(response);
outputStream.Write(buffer, 0, buffer.Length);
}
}
file system server root

Download and Conclusion

Complete CompactWeb class library with this sample code is possible to download here CompactWebServer.zip . Even if the code is written in compact framework it can be compiled and used with full version of .NET framework. Usage is NOT limited to windows mobile. Feel free to suggest any ideas and improvements, or change the code for your needs. I believe that you will find this library useful.

Simulate keystrokes in Windows Mobile

Simulate keystrokes in Windows Mobile

Thursday, August 2, 2007 @ 00:39
I must say that I'am always amazed how many useful things you can perform just by simulating the keystrokes in Windows Mobile. In Windows CE SDK is the function keybd_event which generates WM_KEYUP or WM_KEYDOWN message. In fact it means, that you can "broadcast" the key press through whole system. It's unbelievable how many functions is binded to the keys even if it's not physically present on the device.

On this page in MSDN you can find list of many keys supported in Windows CE systems. There are interesting keys like VK_OFF, VK_NONAME, VK_ESC, VK_KEYLOCK. To be honest much better list is in book Programming Microsoft WindowsCe .Net. So let's look on some of the keys and how to play with them.

Sending application into background

Dreaming of minimizing application in Smartphone? If you want to send your application into background, just like the the back-button do - why not press it from application. Just send the VK_ESC key (0x1B).

Showing the Today screen

How to show today screen? Simulate the "home" button which is represented by F4 key. This VK_F4 has value of 0x73.

Lock the device keys

If you want to lock the device, just like it happens after idle time - you can perform it also by key press. On the Smartphone you need VK_APP6 key (0xC6). On the Pocket PC you will use VK_F22 or VK_KEYLOCK key (0x85). Windows Mobile 6.0 has the API function SHDeviceLockAndPrompt which puts device into a lock state as well.

Preventing the device from locking or sleeping

If you are using for instance TomTom navigation and PIN lock on your device, than you are probably fed up by the screen locking after idle time. You can prevent this locking by simulating key press, so the device will think you are still working on your device. But what key to press? You may think about shift or caps-lock, BUT thEn yOyR uSeR INpuTs WiLL loOk liKE tHiS. It must be the key without affects on the input. The VK_NONAME key (0xFC). Key actually does nothing and is reserved for the future use. In my opinion, the time is here.

Turn off the device

I was really surprised that you can simulate press of the key like power button is. The VK_OFF key (0xDF) performs soft reset on Smartphone and screen off on the Pocket PC.

Activate the Speakerphone

If you will read this post from Peter Foot, you will find how VK_F16 key (0x7F) can be useful, when you want to toggle Speakerphone while talking.

Putting it into C# class

Following class encapsulates SendKey function which simulates press of specified key. Function do two P/Invoke calls to keyb_event with KEYUP and KEYDOWN parameters set. You can use this class in Compact Framework 1.0.

using System.Runtime.InteropServices;

...

public class SystemCalls
{
//See more at http://msdn2.microsoft.com/en-us/library/ms927178.aspx
public const byte VK_NONAME = 0xFC; // Do nothing
public const byte VK_ESC = 0x1B; // Smartphone back-button
public const byte VK_F4 = 0x73; // Home Screen
public const byte VK_APP6 = 0xC6; // Lock the keys on Smartphone
public const byte VK_F22 = 0x85; // Lock the keys on PocketPC (VK_KEYLOCK)
public const byte VK_F16 = 0x7F; // Toggle Speakerphone
public const byte VK_OFF = 0x7F; // Power button

///
/// Puts `key` into to global keyboard buffer
///

///
public static void SendKey(byte key)
{
const int KEYEVENTF_KEYUP = 0x02;
const int KEYEVENTF_KEYDOWN = 0x00;
keybd_event(key, 0, KEYEVENTF_KEYDOWN, 0);
keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
}

[DllImport("coredll", SetLastError = true)]
private static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
}
The class is static so the usage is obvious.
SystemCalls.SendKey(SystemCalls.VK_F22);

Creating shortcuts in Windows Mobile Smartphone

http://bansky.net/blog/2007/07/creating-shortcuts-in-windows-mobile-smartphone/

Creating shortcuts in Windows Mobile Smartphone

Tuesday, July 24, 2007 @ 10:44
Sometimes people ask me about differencies in Windows Mobile for Smartphone and PocketPC. Hmm, I should say Windows Mobile Standard and Professional. Well, the most significant differences are in UI, and now I know the next one. In Smartphone you are not able to create shortcuts to file on your file system. To be precise you have no option how to create it.

On pocket PC it's quite easy, just select the file in explorer, tap and click Copy. Then go to the folder where you want to place the shortcut, tap and click Paste Shortcut. Unfortunately Smartphones has no Paste Shortcut option. So, it was only matter of time when someone will ask me how to fix this glitch.

How the shortcut works?

In Windows CE (so Windows Mobile) the shortcut file is very simple plain text file. If you open some *.lnk file in notepad, you will see something like this:

42#"\Program Files\Total Commander\cecmd.exe"
The number 42 is of course Answer to Life, the Universe, and Everything. But in this case it is the length of the command placed in shortcut. So that Program Files\Total Commander\cecmd.exe without the quotes. The quotes are neccesary when the path contains spaces. Don't think that you can put qoutes everywhere. If the path has no spaces than put no quotes. Like in this example:
18#\Windows\notes.exe
The # sign is just the delimiter. In cases you want to pass some arguments to the exacutable, you can add it right after the path. Don't forget to add length of the parameters to begining number. Following examples shows executing with parameters. First one executes Total Commander and pass \Windows as the parametr. Second one executes autoprof and pass two parameters. Quotes are NOT count to the length.
51#"\Program Files\Total Commander\cecmd.exe" \Windows


27#\Windows\autoprof -s Silent

Do it from C#

The principles mentioned above is obviously very easy to rewrite into the C# code. So here is the method and example how to place short cut to Total Commander into the start menu.
/// 
/// Creates the link file
///

/// Destination of the shortcut
/// Shortcut file name
/// Parameters

/// true if link was created
private bool CreateLink(string destination, string shortCut, string parameters)
{
try
{
bool isQuoted = false;

// when path contains spaces the set quote
destination = destination.Trim();
if (destination.IndexOf(' ') > -1) isQuoted = true;


// count length. +1 for space between path and arguments
string link = destination;
int len = destination.Length + parameters.Length + 1;

// add quotes when neccesary
if (isQuoted)
link = """ + link + """;

// add parametrs if any
if (parameters != null && parameters.Length > 0)
link += " " + parameters;

// write everything into the file
FileStream fs = new FileStream(shortCut, FileMode.Create,
FileAccess.Write, FileShare.ReadWrite);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine(len.ToString()+"#"+link);
sw.Close();
fs.Close();
}
catch
{
// Your exception handling here
return false;
}

return true;
}
Usage:
CreateLink(@"\Program Files\Total Commander\cecmd.exe",
@"\Windows\Start Menu\go windows.lnk", "\Windows");

Complete application

I wrote application called ShortcutMaker as the complete solution for creating shortcuts in smartphones. It is written in C# and uses Compact Framework 1.0, so that it will run without other requirements on WM 2003 and above. Since CF 1.0 has no support for standard file-open and folder-browsing dialogs, I utilize the one from Peter Foot. You can download it from here with examples. Those are now implementeted in OpenNETCF.

Download the instalation cabinet file: ShortcutMakerInstall.cab

Monday, October 20, 2008

How to control bluetooth OBEX default service on/off?

To control the Infrared state (on/off) or bluetooth OBEX default service in a similar way as in the control pannel, and to get the current state of the infrared module use the code below:

Code:

//set irda service status
HRESULT OBEXIOCTL(DWORD dwIOCTL)
{
HANDLE hService;
BOOL fRet;
hService = CreateFile(TEXT("OBX0:"),GENERIC_READ|GENERIC_WRITE,0,
NULL,OPEN_EXISTING,0,NULL);
if (INVALID_HANDLE_VALUE == hService)
{
CloseHandle(hService);
return FALSE;
}
fRet = DeviceIoControl(hService,dwIOCTL,0,0,0,0,NULL,0);
CloseHandle(hService);
return (0 == fRet)?E_FAIL:S_OK;
}

//query irda service status
DWORD OBEXQuery()
{
HANDLE hService;
BOOL fRet;
DWORD dwStatus;
hService = CreateFile(TEXT("OBX0:"),GENERIC_READ|GENERIC_WRITE,0,
NULL,OPEN_EXISTING,0,NULL);
if (INVALID_HANDLE_VALUE == hService)
{
CloseHandle(hService);
return FALSE;
}
fRet = DeviceIoControl(hService,IOCTL_SERVICE_STATUS,0,0,&dwStatus,sizeof(DWORD),0, 0);
CloseHandle(hService);
if(0 == fRet)
return 0xFFFFFFFF;
else
return dwStatus;
}

bool GetIRDAState()
{
switch(OBEXQuery())
{
case SERVICE_STATE_SHUTTING_DOWN:
case SERVICE_STATE_UNLOADING:
case SERVICE_STATE_UNINITIALIZED:
case SERVICE_STATE_OFF:
return 0;
break;

case SERVICE_STATE_STARTING_UP:
case SERVICE_STATE_ON:
return 1;
break;
default:
break;
};
return 0;
}

Friday, October 10, 2008

C# TrayIcon Class

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;
using System.Windows.Forms;

namespace XXXX
{
public class TrayIcon
{
//Declare click event
public event System.EventHandler Click;

private WindowSink windowSink;
private int uID = 11000;

//Constructor
public TrayIcon()
{
//Create instance of the MessageWindow subclass
windowSink = new WindowSink(this);
windowSink.uID = uID;
}

//Destructor
~TrayIcon()
{
Remove();
}


public void Add(IntPtr hIcon)
{
TrayMessage(windowSink.Hwnd, NIM_ADD, (uint)uID, hIcon);
}

public void Remove()
{

TrayMessage(windowSink.Hwnd, NIM_DELETE, (uint)uID, IntPtr.Zero);
}

public void Modify(IntPtr hIcon)
{

TrayMessage(windowSink.Hwnd, NIM_MODIFY, (uint)uID, hIcon);

}

private void TrayMessage(IntPtr hwnd, int dwMessage, uint uID, IntPtr hIcon)
{
NOTIFYICONDATA notdata = new NOTIFYICONDATA();

notdata.cbSize = 152;
notdata.hIcon = hIcon;
notdata.hWnd = hwnd;
notdata.uCallbackMessage = WM_NOTIFY_TRAY;
notdata.uFlags = NIF_MESSAGE | NIF_ICON;
notdata.uID = uID;

int ret = Shell_NotifyIcon(dwMessage, ref notdata);
}

#region API Declarations

internal const int WM_LBUTTONDOWN = 0x0201;
internal const int WM_RBUTTONUP = 0x0205;
//User defined message
internal const int WM_NOTIFY_TRAY = 0x0400 + 2001;

internal const int NIM_ADD = 0x00000000;
internal const int NIM_MODIFY = 0x00000001;
internal const int NIM_DELETE = 0x00000002;

const int NIF_MESSAGE = 0x00000001;
const int NIF_ICON = 0x00000002;


internal struct NOTIFYICONDATA
{
internal int cbSize;
internal IntPtr hWnd;
internal uint uID;
internal uint uFlags;
internal uint uCallbackMessage;
internal IntPtr hIcon;
//internal char[] szTip = new char[64];
//internal IntPtr szTip;
}

[DllImport("coredll.dll")]
internal static extern int Shell_NotifyIcon(
int dwMessage, ref NOTIFYICONDATA pnid);

[DllImport("coredll.dll")]
internal static extern int SetForegroundWindow(IntPtr hWnd);

[DllImport("coredll.dll")]
internal static extern int ShowWindow(
IntPtr hWnd,
int nCmdShow);

[DllImport("coredll.dll")]
internal static extern IntPtr GetFocus();

#endregion


#region WindowSink

internal class WindowSink : Microsoft.WindowsCE.Forms.MessageWindow
{
//Private members
private int m_uID = 0;
private TrayIcon trayIcon;

//Constructor
public WindowSink(TrayIcon tIcon)
{
trayIcon = tIcon;
}

public int uID
{
set
{
m_uID = value;

}
}

protected override void WndProc(ref Microsoft.WindowsCE.Forms.Message msg)
{

if (msg.Msg == WM_NOTIFY_TRAY)
{
switch ((int)msg.LParam)
{
case WM_LBUTTONDOWN:
if ((int)msg.WParam == m_uID)
{
//If somebody hooked, raise the event
if (trayIcon.Click != null)
trayIcon.Click(trayIcon, null);

}
break;

case WM_RBUTTONUP:
{
MessageBox.Show("R Button Up");
}
break;
}
}

}
}
#endregion

}
}

Tuesday, October 7, 2008

How to set device clock (set system time)?

http://msdn.microsoft.com/en-us/library/ms172517.aspx

To get or set the system time of the device, use platform invoke to call the native GetSystemTime or SetSystemTime functions.

Note that the GetSystemTime function returns Coordinated Universal Time (UTC, also known as Greenwich Mean Time). To get your local time, you must add or subtract the number of hours between your time zone and UTC. For example, 24:00 (midnight) in UTC is 19:00 in New York--an offset of minus 5 hours (UTC–5).

To determine the UTC offset for your time zone, see the Time Zone tab of Date and Time Properties.

Some device emulators do not initially set daylight-saving time correctly, which could affect your result.

Visual Basic

Public Structure SYSTEMTIME
Public wYear As UInt16
Public wMonth As UInt16
Public wDayOfWeek As UInt16
Public wDay As UInt16
Public wHour As UInt16
Public wMinute As UInt16
Public wSecond As UInt16
Public wMilliseconds As UInt16
End Structure

Declare Function GetSystemTime Lib "CoreDll.dll" _
(ByRef lpSystemTime As SYSTEMTIME) As UInt32

Declare Function SetSystemTime Lib "CoreDll.dll" _
(ByRef lpSystemTime As SYSTEMTIME) As UInt32

Public Sub GetTime
' Call the native GetSystemTime method
' with the defined structure.
Dim st As New SYSTEMTIME
GetSystemTime(st)

' Show the current time.
MessageBox.Show("Current Time: " & st.wHour.ToString() _
& ":" & st.wMinute.ToString())
End Sub

Public Sub SetTime
' Call the native GetSystemTime method
' with the defined structure.
Dim st As New SYSTEMTIME
GetSystemTime(st)

' Set the system clock ahead one hour.
st.wHour = Convert.ToUInt16(((CInt(st.wHour) + 1)) Mod 24)
SetSystemTime(st)

End Sub


C#

[DllImport("coredll.dll")]
private extern static void GetSystemTime(ref SYSTEMTIME lpSystemTime);

[DllImport("coredll.dll")]
private extern static uint SetSystemTime(ref SYSTEMTIME lpSystemTime);


private struct SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}

private void GetTime()
{
// Call the native GetSystemTime method
// with the defined structure.
SYSTEMTIME stime = new SYSTEMTIME();
GetSystemTime(ref stime);

// Show the current time.
MessageBox.Show("Current Time: " +
stime.wHour.ToString() + ":"
+ stime.wMinute.ToString());
}
private void SetTime()
{
// Call the native GetSystemTime method
// with the defined structure.
SYSTEMTIME systime = new SYSTEMTIME();
GetSystemTime(ref systime);

// Set the system clock ahead one hour.
systime.wHour = (ushort)(systime.wHour + 1 % 24);
SetSystemTime(ref systime);
MessageBox.Show("New time: " + systime.wHour.ToString() + ":"
+ systime.wMinute.ToString());
}







Thursday, September 25, 2008

Copying files from Desktop to a CE device

http://blogs.msdn.com/harsh/archive/2008/05/08/copying-files-from-desktop-to-a-ce-device.aspx

So I have this problem, where I need to create a tool that creates a folder with a bunch of files on the PC and then copies all these files over to a CE device. The method of choice here was via ActiveSync over USB. One of the design goals was to be able to copy these files to a number of devices simultaneously. However, research revealed that this is not really possible since, activesync only supports one active connection at a time. So much for the great idea.

So now we are faced with this problem of copying files over to the device over ActiveSync. Since the app had a lot of GUI, I created that in C#. The .NET framework exposes excellent API for file manipulation based on paths. The problem with ActiveSync connections is when the device is cradled, there is no drive letter associated with the connection. So all device paths are then \My Documents\ or \Program Files\ etc.

This creates a problem as far as the File manipulation API in System.IO. Since these methods accept relative as well as fully qualified paths, any path like the above, would be treated as a relative path and not a fully qualified path, thereby causing a lot of grief.

The only way to get past this so far has been to use RAPI.

RAPI stands for Remote Application Programming Interface, and was created ages ago with the first wave of activesync and windows mobile products.

RAPI has some documentation on MSDN at: http://msdn.microsoft.com/en-us/library/aa458022.aspx

However since RAPI is a native API, almost all the documentation is geared towards native implementation.

After searching the intertubes for a suitable solution, I couldn’t find one that was recent, and that was guaranteed to work.

Below is the solution:




using System;
using System.Threading;
using System.Runtime.InteropServices;
using COMTYPES = System.Runtime.InteropServices.ComTypes;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Logger = Microsoft.MobileDevices.Tools.Utilities.Logger;


namespace Microsoft.MobileDevices.Tools.RAPILibrary
{
public class RAPILibrary
{
# region RAPI EXTERN
// -------------------------------------------------------
// RAPI.DLL Definitions
// -------------------------------------------------------

public const int MAX_PATH = 260;
public const uint MAXDWORD = 0xFFFFFFFF;
public const uint S_OK = 0;

public struct RAPIINIT
{
public int cbSize;
public IntPtr heRapiInit;
public int hrRapiInit;
};

// From WINBASE.H -- for CeCreateFile
public enum ACCESS : uint
{
READ = 0x80000000,
WRITE = 0x40000000,
}
// From WINBASE.H -- for CeCreateFile
public enum SHARE
{
READ = 0x00000001,
WRITE = 0x00000002,
}
// From WINBASE.H -- for CeCreateFile
public enum ACTION
{
CREATE_NEW = 1,
CREATE_ALWAYS = 2,
OPEN_EXISTING = 3,
OPEN_ALWAYS = 4,
TRUNCATE_EXISTING = 5
}

// From WINBASE.H -- for CeCreateFile
public enum FILEFLAGATT : uint
{
ATTRIBUTE_READONLY = 0x00000001,
ATTRIBUTE_HIDDEN = 0x00000002,
ATTRIBUTE_SYSTEM = 0x00000004,
ATTRIBUTE_DIRECTORY = 0x00000010,
ATTRIBUTE_ARCHIVE = 0x00000020,
ATTRIBUTE_INROM = 0x00000040,
ATTRIBUTE_ENCRYPTED = 0x00000040,
ATTRIBUTE_NORMAL = 0x00000080,
ATTRIBUTE_TEMPORARY = 0x00000100,
ATTRIBUTE_SPARSE_FILE = 0x00000200,
ATTRIBUTE_REPARSE_POINT = 0x00000400,
ATTRIBUTE_COMPRESSED = 0x00000800,
ATTRIBUTE_OFFLINE = 0x00001000,
ATTRIBUTE_ROMSTATICREF = 0x00001000,
ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
ATTRIBUTE_ROMMODULE = 0x00002000,
FLAG_WRITE_THROUGH = 0x80000000,
FLAG_OVERLAPPED = 0x40000000,
FLAG_NO_BUFFERING = 0x20000000,
FLAG_RANDOM_ACCESS = 0x10000000,
FLAG_SEQUENTIAL_SCAN = 0x08000000,
FLAG_DELETE_ON_CLOSE = 0x04000000,
FLAG_BACKUP_SEMANTICS = 0x02000000,
FLAG_POSIX_SEMANTICS = 0x01000000,
}


///
/// Closes handle passed in
///

/// handle to be closed
///
[DllImport("rapi.DLL", CharSet = CharSet.Unicode)]
public static extern int CeCloseHandle(IntPtr hObject);

///
/// Creates file on device
///

/// path of file
/// Read/Write
/// Share mode
///
/// File creation options
/// Flags and attributes
///
/// pointer to the created file
[DllImport("rapi.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr CeCreateFile(string lpFileName, ACCESS dwDesiredAccess, SHARE dwShareMode, int Res1, ACTION dwCreationDisposition, FILEFLAGATT dwFlagsAndAttributes, int Res2);

///
/// Reads the file
///

/// handle to the file
/// buffer to copy data to
/// file size to read
/// file size read
///
///
[DllImport("rapi.DLL", CharSet = CharSet.Unicode)]
public static extern int CeReadFile(IntPtr hFile, IntPtr lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, int Reserved);

///
/// Writes file to device
///

/// handle of file to write to
/// data to write
/// length of buffer
/// length of file on device
///
///
[DllImport("rapi.DLL", CharSet = CharSet.Unicode)]
public static extern int CeWriteFile(IntPtr hFile, IntPtr lpBuffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, int Reserved);

///
/// Copies file from desktop to device
///

/// Full path of file on the desktop including filename
/// Path of file name on the device. '\' denotes root
public static void CopyFiletoDevice(string deskFile, string devFilePath)
{
try
{
int numTries=0;
// Verify file exists on desktop
FileInfo fInfo = new FileInfo(deskFile);
if (fInfo.Exists)
{
IntPtr devFileHandle;
// create a file on the device
do // force valid handle in the loop
{
devFileHandle = CeCreateFile(devFilePath, ACCESS.READ | ACCESS.WRITE, SHARE.READ | SHARE.WRITE, 0, ACTION.CREATE_ALWAYS, FILEFLAGATT.ATTRIBUTE_NORMAL, 0);
if (devFileHandle.ToInt32() < 0)
{
CeCloseHandle(devFileHandle);
}
} while (devFileHandle.ToInt32() < 0);


// read file from desktop into a byte buffer
Byte[] buffer;
ReadFileAsBinary(deskFile, out buffer);

int numBytesWritten = 0;
unsafe
{
fixed(byte* bufferPtr = buffer)
{
int retVal = CeWriteFile(devFileHandle, (IntPtr)(bufferPtr), (int)fInfo.Length, ref numBytesWritten, 0);
if (retVal == 0)
{
throw new IOException("RAPILibrary:CopyFileToDevice() Error Writing file on device. " + CeGetLastError().ToString());
}
}
}

// close handles
CeCloseHandle(devFileHandle);
}
else
{
Logger.WriteLog("RAPILibrary:CopyFileToDevice() File does not exist on desktop: " + deskFile);
}

}
catch (IOException ioEx)
{
Logger.WriteLog(ioEx.Message);
}
catch (Exception ex)
{
Logger.WriteLog(ex.ToString());
}
}

///
/// Reads file and returns byte[]
///

/// Full path of file
/// byte array for file data
private static void ReadFileAsBinary(string path, out Byte[] buffer)
{
Logger.WriteLog("RAPILibrary:ReadFileAsBinary() Reading the file " + path);
FileInfo fInfo = new FileInfo(path);
long fileLength = fInfo.Length;
buffer = new Byte[fileLength];
FileStream fs = fInfo.OpenRead();
fs.Read(buffer, 0, (int)fileLength);
fs.Close();
Logger.WriteLog("RAPILibrary:ReadFileAsBinary() Exit");
}
}
}

Wednesday, September 10, 2008

How to create Customized Control in C#?

http://www.codeproject.com/KB/miscctrl/cutebutton.aspx

Introduction

This is a short and simple demonstration of .NET framework's capability of creating custom controls.

Here I'm going to make a custom control and then, test my control in a Windows application. I have implemented some custom properties for my control, so you can learn how it is done in C#.

Building the Control

  1. Open the Visual Studio and start a new project. Your project must be based on the Windows Control Library template. Call your project ctlCuteButton and click OK.

  2. Once you have your project open, delete the UserControl from the project. Just remove it because the 'User Control' is not exactly what we need here.

  3. Now go to the 'Project' menu: Project->Add User Control... and select the Custom Control template there. 'Custom Control' is what we need in this case. You may call it cuteButton. Now click OK. A new Custom control has been added to your project.

  4. The first thing we must do here is to change the base class of the cuteButton:

    Override the following line:

    public class cuteButton : System.Windows.Forms.Control

    by this one:

    public class cuteButton : System.Windows.Forms.Button

    Now your control is based on the System.Windows.Forms.Button class.

  5. Now let's create some custom properties for our control. This is done by inserting the following code inside the cuteButton class:

    Collapse
    private Color m_color1 = Color.LightGreen;  //first color
    

    private Color m_color2 = Color.DarkBlue; // second color

    private int m_color1Transparent = 64; // transparency degree

    // (applies to the 1st color)

    private int m_color2Transparent = 64; // transparency degree

    // (applies to the 2nd color)


    public Color cuteColor1
    {
    get { return m_color1; }
    set { m_color1 = value; Invalidate(); }
    }

    public Color cuteColor2
    {
    get { return m_color2; }
    set { m_color2 = value; Invalidate(); }
    }

    public int cuteTransparent1
    {
    get { return m_color1Transparent; }
    set { m_color1Transparent = value; Invalidate(); }
    }

    public int cuteTransparent2
    {
    get { return m_color2Transparent; }
    set { m_color2Transparent = value; Invalidate(); }
    }

    The Invalidate() methoid is used to refresh the design view and all controls inside (the tip from Tom Welch).

  6. And the last thing to do before compiling our control is to override the Paint event. So let's do it:

    // Calling the base class OnPaint
    

    base.OnPaint(pe);
    // Create two semi-transparent colors

    Color c1 = Color.FromArgb(m_color1Transparent , m_color1);
    Color c2 = Color.FromArgb(m_color2Transparent , m_color2);
    Brush b = new System.Drawing.Drawing2D.LinearGradientBrush(ClientRectangle,
    c1, c2, 10);
    pe.Graphics.FillRectangle (b, ClientRectangle);
    b.Dispose();
  7. Now you may compile the control by pressing ++.

Here is the complete contents of cuteButton.cs file (just in case…)

Testing the Control

  1. Open a new instance of the VS .NET. Create a new project choosing the Windows Application template.

  2. From a new Windows Forms project, we can add the compiled custom control to the toolbox. I do this by right-clicking the toolbox, selecting Customize Toolbox, and from the .NET Framework Components tab, clicking Browse and locating the Control Library DLL # (in our case, ctlCuteButton\bin\debug\cuteButton.dll). The component cuteButton will then appear in the Toolbox.

    You can play a bit changing it’s properties (cuteColor1, cuteColor2, cuteTransparent1, cuteTransparent2).

That’s all so far about building and using custom controls.

Good Luck.

(Or, do not need to add control from toolbox, add the control project to your project, then the control will appear automatically.)


Thursday, September 4, 2008

Window CE, how to get File size, version information in C#?

For file size, length, last write etc, you can use FileInfo as following:

FileInfo aa = new FileInfo("\\xxx");
aa.LastWriteTime;
aa.Length;
aa.CreationTime;

for file version, you have to use some C++ help here:

public class FileVersionInfo
{
#region Variables

private string m_sFileName;
private byte[] m_bytVersionInfo;

#endregion

#region Constants

private const int GMEM_FIXED = 0x0000;
private const int LMEM_ZEROINIT = 0x0040;
private const int LPTR = (GMEM_FIXED | LMEM_ZEROINIT);

#endregion

#region Constructors

///
/// Constructor.
///

/// File name and path.
private FileVersionInfo(string sFileName)
{
if (File.Exists(sFileName))
{
int iHandle = 0;
int iLength = 0;
int iFixedLength= 0;
IntPtr ipFixedBuffer = IntPtr.Zero;

// Get the file information.
m_sFileName = Path.GetFileName(sFileName);
iLength = GetFileVersionInfoSize(sFileName, ref iHandle);

if(iLength > 0)
{
// Allocate memory.
IntPtr ipBuffer = AllocHGlobal(iLength);

// Get the version information.
if(GetFileVersionInfo(sFileName, iHandle, iLength, ipBuffer))
{
// Get language independant version info.
if(VerQueryValue(ipBuffer, "\\", ref ipFixedBuffer, ref iFixedLength))
{
// Copy information to array.
m_bytVersionInfo = new byte[iFixedLength];
Marshal.Copy(ipFixedBuffer, m_bytVersionInfo, 0, iFixedLength);
}
}

// Free memory.
FreeHGlobal(ipBuffer);
}
}
else
{
m_bytVersionInfo = new byte[200];
}
}

#endregion

#region Properties

///
/// Get the file build part.
///

public int FileBuildPart
{
get
{
return Convert.ToInt32(BitConverter.ToInt16(m_bytVersionInfo, 14));
}
}

///
/// Get the file major part.
///

public int FileMajorPart
{
get
{
return Convert.ToInt32(BitConverter.ToInt16(m_bytVersionInfo, 10));
}
}

///
/// Get the file minor part.
///

public int FileMinorPart
{
get
{
return Convert.ToInt32(BitConverter.ToInt16(m_bytVersionInfo, 8));
}
}

///
/// Get the name of the file.
///

public string FileName
{
get
{
return m_sFileName;
}
}

///
/// Get the file private part.
///

public int FilePrivatePart
{
get
{
return Convert.ToInt32(BitConverter.ToInt16(m_bytVersionInfo, 12));
}
}

///
/// Get the product build part.
///

public int ProductBuildPart
{
get
{
return Convert.ToInt32(BitConverter.ToInt16(m_bytVersionInfo, 22));
}
}

///
/// Get the product major part.
///

public int ProductMajorPart
{
get
{
return Convert.ToInt32(BitConverter.ToInt16(m_bytVersionInfo, 18));
}
}

///
/// Get the product minor part.
///

public int ProductMinorPart
{
get
{
return Convert.ToInt32(BitConverter.ToInt16(m_bytVersionInfo, 16));
}
}

///
/// Get the product private part.
///

public int ProductPrivatePart
{
get
{
return Convert.ToInt32(BitConverter.ToInt16(m_bytVersionInfo, 20));
}
}

#endregion

#region Functions

///
/// Allocate unmanged memory.
///

/// Length to allocate.
/// IntPtr object.
private static IntPtr AllocHGlobal(int iLength)
{
return LocalAlloc(LPTR, (uint)iLength);
}

///
/// Free allocated memory.
///

/// IntPtr object to free.
private static void FreeHGlobal(IntPtr hGlobal)
{
LocalFree(hGlobal);
}

///
/// Get the file version information.
///

/// File name and path.
/// FileVersionInfo object.
public static FileVersionInfo GetVersionInfo(string sFileName)
{
return new FileVersionInfo(sFileName);
}

#endregion

#region Win32API

[DllImport("coredll", EntryPoint="GetFileVersionInfo", SetLastError=true)]
private static extern bool GetFileVersionInfo(
string filename,
int handle,
int len,
IntPtr buffer);

[DllImport("coredll", EntryPoint="GetFileVersionInfoSize", SetLastError=true)]
private static extern int GetFileVersionInfoSize(
string filename,
ref int handle);

[DllImport("coredll.dll", EntryPoint="LocalAlloc", SetLastError=true)]
private static extern IntPtr LocalAlloc(
uint uFlags,
uint Bytes);

[DllImport("coredll.dll", EntryPoint="LocalFree", SetLastError=true)]
private static extern IntPtr LocalFree(
IntPtr hMem);

[DllImport("coredll", EntryPoint="VerQueryValue", SetLastError=true)]
private static extern bool VerQueryValue(
IntPtr buffer,
string subblock,
ref IntPtr blockbuffer,
ref int len);

#endregion
}
}

Then:

FileVersionInfo fiWindows = FileVersionInfo.GetVersionInfo(@"\Windows\sdcgina.exe");
//MessageBox.Show("1 Window:" + fiWindows.FileMajorPart + "_" + fiWindows.FileMinorPart + "_" + fiWindows.FileBuildPart + "_"
// + fiWindows.ProductMajorPart + "_" + fiWindows.ProductMinorPart + "_" + fiWindows.ProductPrivatePart + "_" + fiWindows.ProductBuildPart
// + "SystemCF:" + fiSystemCF.FileMajorPart + "_" + fiSystemCF.FileMinorPart + "_" + fiSystemCF.FileBuildPart + "_"
// + fiSystemCF.ProductMajorPart + "_" + fiSystemCF.ProductMinorPart + "_" + fiSystemCF.ProductPrivatePart + "_" + fiSystemCF.ProductBuildPart);

Monday, August 25, 2008

Correct Use of the SetFont() Function in MFC

Correct Use of the SetFont() Function in MFC


http://support.microsoft.com/kb/85518

SUMMARY

The SetFont() member function of the CWnd class changes the font in a specified control. For this function to work correctly in a Windows- based application, you must ensure that the CFont object specified in the SetFont() call is not destroyed until after the specified control has been destroyed.

Back to the top

MORE INFORMATION

The SetFont() accepts a CFont object as a parameter; the control uses the specified font to display its text. The SetFont() function is implemented to send a WM_SETFONT message to the control with the font handle that corresponds to the CFont object.

An application can delete the font specified in a WM_SETFONT message only under certain circumstances; these same restrictions apply to the CFont object specified in a SetFont() call.

Specifically, do not destroy the specified CFont object until after the CWnd control has been destroyed. Windows does not copy the font specified in a SetFont() call. If the font is destroyed before the control is destroyed, unpredictable results can occur.

For example, when an application uses SetFont() to change the font a dialog box uses, the application should not destroy the CFont object until after it has destroyed the dialog box. Make the CFont object a member variable of the derived dialog box class instead of making the font a local variable in one of the functions in the class. This is the best method to ensure that the CFont object exists for the lifetime of the dialog box. When the application destroys the dialog box, the dialog box class destructor automatically calls the CFont destructor to delete the font handle.

The best time to specify the font for any controls in the dialog box is in the OnInitDialog() member function.

The code below demonstrates deriving a dialog box class from CModalDialog and using the SetFont() member function:

Back to the top

Sample Code

/*
* Compiler options needed: None
*/

class CMyAboutBox : public CDialog
{
CFont m_font;

public:
// Constructor -- This code assumes a dialog box
// template named "ABOUTDLG" in the application's .RC file.

CMyAboutBox(CWnd* pParentWnd = NULL) :
CModalDialog("ABOUTDLG", pParentWnd) {};

BOOL OnInitDialog();
};

// OnInitDialog() function -- Called after Windows creates
// the dialog box but before it is painted on the screen.

BOOL CMyAboutBox::OnInitDialog()
{
LOGFONT lf; // Used to create the CFont.

CDialog::OnInitDialog(); // Call default ::OnInitDialog

memset(&lf, 0, sizeof(LOGFONT)); // Clear out structure.
lf.lfHeight = 20; // Request a 20-pixel-high font
strcpy(lf.lfFaceName, "Arial"); // with face name "Arial".
m_font.CreateFontIndirect(&lf); // Create the font.

// Use the font to paint a control. This code assumes
// a control named IDC_TEXT1 in the dialog box.
GetDlgItem(IDC_TEXT1)->SetFont(&m_font);

return TRUE;
}

Back to the top


APPLIES TO
Microsoft Foundation Class Library 4.2, when used with:
Microsoft C/C++ Professional Development System 7.0
Microsoft Visual C++ 1.0 Professional Edition
Microsoft Visual C++ 1.5 Professional Edition
Microsoft Visual C++ 1.51
Microsoft Visual C++ 1.52 Professional Edition
Microsoft Visual C++ 1.0 Professional Edition
Microsoft Visual C++ 2.0 Professional Edition
Microsoft Visual C++ 2.1
Microsoft Visual C++ 4.0 Standard Edition

Back to the top

Keywords:
kbinfo kbuidesign KB85518

Thursday, August 21, 2008

How to detect key press like:Ctrl + Enter

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
//MessageBox.Show("e.KeyData:" + e.KeyData.ToString() + "-e.KeyCode:" + e.KeyCode.ToString() + "-e.Modifiers:" + e.Modifiers.ToString() + "-e.KeyValue:" + e.KeyValue.ToString());

if (e.KeyData == (Keys.Control | Keys.Enter))
MessageBox.Show("Ctrl+Enter!");
}


Note:

You can check for Ctrl inside the KeyPress event by using the static
properties Control.ModifierKeys
In theory you should be able to do

if(e.KeyChar == (char)13 && Control.ModifierKeys == Keys.Ctrl)

Except this doesn't work. Modifierkeys are translated to characters
inside the KeyPress event. When you hold ctrl while clicking Enter
(char)10 is sent instead of (char)13 and the Control click is suppressed,
so all you have to do to detect Ctrl+Enter is

if(e.KeyChar == (char)10)

The same goes for other combinations like

if(e.KeyChar == (char)97) // [A]
if(e.KeyChar == (char)1 ) // [CTRL]+[A]

To detect key combinations put something like this inside the KeyPress
event

MessageBox.Show(((int)e.KeyChar).ToString());

But to detect, you have to use

if (e.KeyData == (Keys.Control | Keys.Enter))

Tuesday, August 19, 2008

How to set command line argument in dialog based program?

Use GetCommandLine function as following:

BOOL CColdBootApp::InitInstance()
{
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
LPTSTR lCmd = ::GetCommandLine();

// MessageBox(NULL, lCmd, L"", MB_OK);

if(_tcscmp(lCmd, L"/nobar")==0)
{
//cold boot
// MessageBox(NULL, L"Cold boot now!", L"", MB_OK);
ColdReboot();
return FALSE;
}

CColdBootDlg dlg;
m_pMainWnd = &dlg;

int nResponse = dlg.DoModal();
...


}

(
CString aa = ::GetCommandLine();
aa.MakeLower();
BOOL bShowBar = FALSE;
BOOL bDoAsk = FALSE;
if(aa.Find(L"/nobar")==-1)
bShowBar = TRUE;
else
bShowBar = FALSE;

if(aa.Find(L"/noask")==-1)
bDoAsk = TRUE;
else
bDoAsk = FALSE;

if(bDoAsk)
{
if(MessageBox(NULL, L"Are you sure?", L"Cold Boot", MB_YESNOCANCEL|MB_TOPMOST|MB_ICONEXCLAMATION )!=IDYES)
return FALSE;
else
{
if(!bShowBar)
{
ColdReboot();
return FALSE;
}
}
}else
{
if(!bShowBar)
{
ColdReboot();
return FALSE;
}
}
)
//*************How to use command line exe file**********************//
/*
C# 5.0:
// Run the CAB
Process CabRunner = Process.Start( "wceload" , "/noui " + CABFile );
CabRunner.WaitForExit();

C# 4.0:
// Used to create a new process.
[DllImport("Coredll", EntryPoint="CreateProcess", SetLastError=true)]
private static extern Int32 CreateProcess(
string sImageName,
string sCmdLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
Int32 boolInheritHandles,
Int32 dwCreationFlag,
IntPtr lpEnvironment,
IntPtr lpCurrentDir,
byte[] si,
ProcessInfo pi);
public IntPtr OpenExternalProgram(string sExeName, string sCmdLine)
{
byte[] b = new byte[128];
ProcessInfo pi = new ProcessInfo();

try
{
// Create a new process that opens the external (exe) program.
CreateProcess(sExeName, sCmdLine, IntPtr.Zero, IntPtr.Zero, 0, 0, IntPtr.Zero, IntPtr.Zero, b, pi);
return pi.hProcess;
}
catch
{
return IntPtr.Zero;
}
}
C++:
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));

STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);

if(::CreateProcess(_T("\\ColdBoot.exe"), _T(""), NULL, NULL, NULL, FALSE, 0, NULL, &si, &pi) != 0)
{
::WaitForSingleObject(pi.hProcess, 300L); // wait to its finish
::CloseHandle(pi.hProcess);
}else
MessageBox(NULL,L"WarmReboot failed!",L"",MB_OK);

*/

Tuesday, August 5, 2008

阴历规则

[声明]: 以下资料摘自http://thunder.cwru.edu/ccal/rule.htm [孟卓(zhuo@thunder.cwru.edu)]

阴历规则

中国的阴历,又称农历,大家应该都很熟悉。很多的传统节日,像春节,中秋都是阴历的节日。不过,若是问起阴历的规则,怕是知道的人就不多了。多数人知道初 一是新月出现的那一天,月圆的那一天则在十五左右。不少人也知道二十四节气在阳历的每月中大概的日期。再细节的概念如闰月对大多数人则不甚了解了。本人经 过一番查找与阅读,终于对阴历的规则有了一定的认识。在此愿与大家分享。

首先阴历以月为基本单位,一个月以新月出现的那一天为始直至下一个新月出现的前一天。由于月亮公转的周期介于29到30天之间,阴历的一个月也就由新月出 现时刻的早晚或是29天或是30天。大月为30天,小月为29天。与阳历不同的是,大小月在不同的年中不固定。如春节的前一天常称为大年三十,但有不少年 如2000年的阴历十二月只有29天。由于十二个月的时间较阳历年即地球绕太阳公转一周的时间短11天左右,为了使阴历年与阳历年保持相对稳定,每隔两三 年就需要加入一个闰月。大约每十九年要加入七个闰月。而二十四节气则是由地球在绕太阳公转的轨道上的位置确定的。以每年的冬至为始,每15度为一个节气。 是故二十四节气在阳历的每月中有大概固定的日期。古时以二十四节气指导农耕,这就是阴历又称农历的原因。其中阳历下半月的十二个节气又称为中气。中气出现 的时刻和闰月的确定有直接的关系。

中国阴历的计算有下列几条规则:

  • 所有新月和节气出现时刻的计算以北京时间为准。现北京时间为东经120度即东八区标准时。但计算1929年以前的阴历时应以北京即东经116度25分的当地时为准。此条规则用以区分中国阴历和其他类似阴历如日本阴历。
  • 一天始自夜里零时。尽管旧时的子时相当于现今的23时至1时,但是子时的上四刻属于前一天,下四刻才属于新的一天,所以和现今一天始自零时的定义是一致的。
  • 新月出现的一天为一个月的第一天。如某个节气的出现时刻也在这一天,则不论该节气的出现时刻是否比新月晚,一律算落入新的一个月中。
  • 每年的冬至总是出现在这年的阴历十一月中。
  • 从一年的冬至后一天起到下一年冬至这一天止的这段时间中,下称其间,如有十三个新月出现,则其间要加入一个闰月。需要加入闰月时,其间第一个没有 中气的月为闰月。因为其间只有十二个中气,所以其间至少有一个月没有中气,也存在有两个月没有中气的可能性。但这种情况下只有第一个没有中气的月为闰月。 闰月的前一个月为几月则该闰月称为闰几月。

以上所列的阴历的计算规则是在清朝顺治年间,即公元1645年,开始采用的。以上规则的一个重要特点就是理论上完全以天文观测为依据,其中没有任何 数学关系。这和阳历,即格里历,完全是由数学关系确定的形成了鲜明的对比。当然,具体到未来阴历的计算,仍然需要月亮与地球运动的数学模型,而且精度高的 这种数学模型相当复杂,在没有电脑的情况下,一般人根本不可能推算出精确的阴历来。而阳历的数学关系则非常简单,是不是闰年很容易计算。这又形成了另外一 种鲜明的对比。

Friday, August 1, 2008

evc4工程移植vs.net2005所碰到的问题积累

在移植项目前,建议还是看看以下的文章:

循序渐进:将 eMbedded Visual C++ 应用程序迁移到 Visual Studio 2005
eMbedded Visual C++ 到 Visual Studio 2005 升级向导(注意其最后一句话:默认情况下,Embedded Visual C++ 4.0 版会将 MFC Pocket PC 应用程序的对话框样式(Border)设置为 DS_MODALFRAME。MFC 8.0 不支持此样式。 —— 应改为Thin,如果不改的话,窗口就无法弹出。)
从 eVC 移植所带来的已知问题
Migrating Microsoft eMbedded Visual C++ Projects to Visual Studio 2005
开发环境:Windows XP +SP2, Visual Studio 2005 professional, Windows Mobile 6.0 Professional SDK。
注:(1)对于Windows Mobile 5.0 SDK 开发的程序在Windows Mobile 6.0 下也能运行,而且不需要对程序进行任何的修改。
(2)由于有些错误,是环境配置问题,所以在Debug/Resease模式下,都需要进行修改,以下错误中,如果是这类错误,都在标号后面写着 “Resealse 模式也需要改”,当天切换到别的SDK下,如Windows Mobile 5.0 SDK,也需要修改。
以下是针对Debug模式下的:

1、StdAfx.cpp (Resealse 模式也需要改)
编译错误:D:Program FilesMicrosoft Visual Studio 8VCceatlmfcincludeafxver_.h(77) : fatal error C1189: #error : Please use the /MD switch for _AFXDLL builds
解决方法:右击工程名,打开Project properties对话框,切换到C/C++->Code generation页,将Runtime Libarary 设置成“Multi-threaded DLL(/MD)”,即可解决此问题。

2、编译错误:error C2065: 'i' : undeclared identifier
原因:是由于存在以下的代码段:
for (int i = 0; i < MAX_LEN; i ++)
{
//……
}
for (i = 0; i < MAX_NUM; i ++)
{
//……
}
对于evc离开循环后,循环变量仍然有效,并且仍可以使用,但是在VS2005下是不行的,由此可见VS2005对变量的定义与审查更为严格,还有就是对数组越界问题也比EVC来的强。
解决方法:(不能完全相信编译器,也不能把所有的语法检查都丢给编译器)
int i = 0;
for (i = 0; i < MAX_LEN; i ++)
{
//……
}
for (i = 0; i < MAX_NUM; i ++)
{
//……
}

3、error C2664: '_wcsnicmp' : cannot convert parameter 2 from 'LPWORD' to 'const wchar_t *'
需要强制类型转换。

4、error C2061: syntax error : identifier 'HELPINFO'
自己增加HELPINFO的类型,增加头文件HelpInfo.h。

5、error C2146: syntax error : missing ';' before identifier 'm_wndCommandBar'
原因:在Windows Mobile 5.0/6.0 下CCeCommandBar类被CCommandBar替换
解决方法:
CCeCommandBar m_wndCommandBar; ---- 〉CCommandBar m_wndCommandBar;

6、error C2065: 'NUM_TOOL_TIPS' : undeclared identifier
解决:
//#if defined(_WIN32_WCE_PSPC) && (_WIN32_WCE >= 212)
#define NUM_TOOL_TIPS 8
//#endif

7、error C3861: 'ON_WM_HELPINFO': identifier not found
同 4

8、error C2440: 'static_cast' : cannot convert from 'void (__cdecl CMyAppView::* )(void)' to 'LRESULT (__cdecl CWnd::* )(WPARAM,LPARAM)'None of the functions with this name in scope match the target type
解决方法:
afx_msg void OnHotLinkExplain(); --- 〉
afx_msg LRESULT OnHotLinkExplain(WPARAM wParam,LPARAM lParam);

9、error C2664: 'CSize CDC::GetTextExtent(LPCTSTR,int) const' : cannot convert parameter 1 from 'WORD *' to 'LPCTSTR'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast需要强制转换
pDC->GetTextExtent(&i, 1).cx); ——>
pDC->GetTextExtent((LPCTSTR)&i, 1).cx;

10、error C2039: 'OnHelpInfo' : is not a member of 'CView'
error C2039: 'OnHelpInfo' : is not a member of 'CFrameWnd'
error C2039: 'OnHelpInfo' : is not a member of 'CDialog'
解决方法:用TRUE替换相应的类成员函数OnHelpInfo'
return CView::OnHelpInfo(pHelpInfo); ——> return TRUE;

11、error C2039: 'm_bShowSharedNewButton' : is not a member of 'CCommandBar'
D:Program FilesMicrosoft Visual Studio 8VCceatlmfcincludeafxext.h(557) : see declaration of 'CCommandBar'
解决方法:
直接注释掉 m_wndCommandBar.m_bShowSharedNewButton = FALSE;

12、MyApp.rc(380) : fatal error RC1015: cannot open include file 'wceres.rc'.
解决方法:
直接注释掉:#include "wceres.rc" // WCE-specific components
但是,这个错误很讨厌,每次你修改资源文件后,都得修改该语句,不知道为什么。

13、Resease 模式下也要修改
error LNK2019: unresolved external symbol SHInitExtraControls referenced in function "protected: __cdecl CMyAppView::CMyAppView(void)" ( 0CMyAppView@@IAA@XZ)
问题:程序中调用了SHInitExtraControls();
error LNK2019: unresolved external symbol SHSipPreference referenced in function "protected: void __cdecl CMyAppView::OnKillfocusWord(void)" ( OnKillfocusWord@CMyAppView@@IAAXXZ)
问题:程序中调用了SHSipPreference
以上两个函数都在:Library: aygshell.lib里
解决方法:
工程-->属性-->Linker -->input -- > Additional Denpendencies :aygshell.lib

14、Resease 模式下也要修改
orelibc.lib(wwinmain.obj) : error LNK2019: unresolved external symbol wWinMain referenced in function wWinMainCRTStartup
属性—〉Linker—〉Anvanced—〉EntryPoint
将 wWinMainCRTStartup 更改为 WinMainCRTStartup
Entry Point是WinMainCRTStartup(ANSI)或wWinMainCRTStartup(UINCODE),即: ... WinMainCRTStartup 或wWinMainCRTStartup 会调用WinMain 或wWinMain。

15、 error C3861: 'LoadStdProfileSettings': identifier not found
注释掉函数 LoadStdProfileSettings;
该函数的具体功能,看MSDN。
BTW:编译的时候,有可能会出现一些由以上错误产生的连锁错误,俗称“蝴蝶效应”,如error C2143: syntax error : missing ';' before '}'
error C2143: syntax error : missing ';' before ','
error C2143: syntax error : missing ';' before '{'
少了了'{'、'}'、';'等等,把以上的错误—主要矛盾解决了,这些错误—错误矛盾也就迎刃而解了。何况,这个工程是以前在EVC IDE下编译通过,MS再怎么优化或改进编译器,也总不可能发生自相矛盾的事情吧,总要考虑兼容性吧,要对自己或公司的前辈有信心!
到此,已经能够编译通过,但是运行的时候,又出现如下的问题:

16、Resease 模式下也要修改
按F5,出现如下的对话框:

解决方法:
右击工程的属性—〉General—〉Project Defaults –〉Use MFC :
Use MFC in a shared DLL ——> Use MFC in a static DLL
也正是因为这个,VS2005产生的EXE程序比EVC产生的要大200多k。

这样,程序基本移植完成,但是还要把所有的功能过一遍,有可能还会碰到,诸如对话框出现乱码、菜单不对等问题。


[收集FROM 蓝天清水博客]
在将evc4的工程移植到.net2005时会碰到很多问题,以下只是其中我遇到的一些问题的摘录以及相关的处理结果:

1.首先在将EVC4的工程转化成.net2005时要将工程的属性中的c/c++->代码生成->运行时库改为:多线程DLL(/MD)

2.在工程的属性中的链接器->高级->入口点改为:WinMainCRTStartup

3.遇到stdafx.cpp
_CE_ACTIVEX was not defined because this Windows CE SDK does not have DCOM.
_CE_ACTIVEX could be caused to be defined by defining _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA, but it is recommended that this be done only for single-threaded apps.
_ATL_NO_HOSTING was defined because _CE_ACTIVEX was not defined.
的错误:在stdafx.h文件里加上#define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA可解决.

4.遇到StdAfx.cpp
WINVER not defined. Defaulting to 0x0501
在工程的属性c/c++->预处理器->预处理器定义中加上_WIN32_WINNT=0x500试试

Wednesday, July 23, 2008

Introduction into threading in .NET (this article)

http://www.codeproject.com/KB/threads/ThreadingDotNet.aspx

Introduction

Im afraid to say that I am just one of those people that unless I am doing something, I am bored. So now that I finally feel I have learnt tha basics of WPF, it is time to turn my attention to other matters.

I have a long list of things that demand my attention such as (WCF/WF/CLR Via C# version 2 book), but I recently went for (and got, but turned it down in the end) which required me to know a lot about threading. Whilst I consider myself to be pretty good with threading, I thought yeah I'm ok at threading but I could always be better. So as a result of that I have decided to dedicate myself to writing a series of articles on threading in .NET. This series will undoubtely owe much to an excellent Visual Basic .NET Threading Handbook that I bought that is nicely filling the MSDN gaps for me and now you.

I suspect this topic will range from simple to medium to advanced, and it will cover a lot of stuff that will be in MSDN, but I hope to give it my own spin also.

I dont know the exact schedule, but it may end up being something like


Thursday, July 10, 2008

Simple Window Template

  1. #include


  2. // Step 4: the Window Procedure
  3. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  4. {
  5. switch(msg)
  6. {
  7. case WM_CREATE:

  8. break;
  9. case WM_CLOSE:
  10. DestroyWindow(hwnd);
  11. break;
  12. case WM_DESTROY:
  13. PostQuitMessage(0);
  14. break;
  15. default:
  16. return DefWindowProc(hwnd, msg, wParam, lParam);
  17. }
  18. return 0;
  19. }

  20. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  21. LPSTR lpCmdLine, int nCmdShow)
  22. {
  23. WNDCLASSEX wc;
  24. HWND hwnd;
  25. MSG Msg;
  26. static char appName[] = "Your Application";

  27. //Step 1: Registering the Window Class
  28. wc.cbSize = sizeof(WNDCLASSEX);
  29. wc.style = CS_HREDRAW | CS_VREDRAW;
  30. wc.lpfnWndProc = WndProc;
  31. wc.cbClsExtra = 0;
  32. wc.cbWndExtra = 0;
  33. wc.hInstance = hInstance;
  34. wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  35. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  36. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  37. wc.lpszMenuName = NULL;
  38. wc.lpszClassName = appName;
  39. wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

  40. if(!RegisterClassEx(&wc))
  41. {
  42. MessageBox(NULL, "Window Registration Failed!", "Error!",
  43. MB_ICONERROR | MB_OK);
  44. return 0;
  45. }

  46. // Step 2: Creating the Window
  47. hwnd = CreateWindowEx(
  48. WS_EX_CLIENTEDGE,
  49. appName,
  50. "Your Window Title",
  51. WS_OVERLAPPEDWINDOW,
  52. CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
  53. NULL, NULL, hInstance, NULL);

  54. if(hwnd == NULL)
  55. {
  56. MessageBox(NULL, "Window Creation Failed!", "Error",
  57. MB_ICONERROR | MB_OK);
  58. return 0;
  59. }

  60. ShowWindow(hwnd, nCmdShow);
  61. UpdateWindow(hwnd);

  62. // Step 3: The Message Loop
  63. while(GetMessage(&Msg, NULL, 0, 0) > 0)
  64. {
  65. TranslateMessage(&Msg);
  66. DispatchMessage(&Msg);
  67. }
  68. return int(Msg.wParam);
  69. }