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. }

Tuesday, July 8, 2008

How to set window topmost in .net 2003 for CE?

this.BringToFront and this.Focus() do not work for CE. Have to use C++ way of TOPMOST and SetWindowPos(..) as following:

[DllImport("coredll.dll", EntryPoint="GetCapture", SetLastError=true)]
private static extern IntPtr GetCapture();
[DllImport("coredll.dll", EntryPoint="SetWindowPos", SetLastError=true)]
private static extern bool SetWindowPos(
IntPtr hWnd,
IntPtr hWndInsertAfter,
int x,
int y,
int cx,
int cy,
uint uFlags);

public IntPtr GetControlHandle(Control ctl)
{
ctl.Capture = true;
IntPtr hWnd = GetCapture();
ctl.Capture = false;
return hWnd;
}



///
/// Set a form to the top most position.
///

/// The form to set topmost.
/// Returns true if successful, false otherwise.
public bool SetWindowTopMost(Form frm)
{
return SetWindowTopMost(frm, true);
}

///
/// Set a form to the top most position.
///

/// The form to set topmost.
/// True to set the form topmost, false to set the form to no topmost.
/// Returns true if successful, false otherwise.
public bool SetWindowTopMost(Form frm, bool bTopMost)
{
// Get the handle to the form entered.
IntPtr hWnd = GetControlHandle(frm);
IntPtr hWndInsertAfter = IntPtr.Zero;

if (bTopMost)
{
// Set the topmost flag.
hWndInsertAfter = new IntPtr(HWND_TOPMOST);
}
else
{
// Set the no topmost flag.
hWndInsertAfter = new IntPtr(HWND_NOTOPMOST);
}

return SetWindowPos(hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
}

Friday, June 13, 2008

Uninstall application from code - C#

For Window CE:

RegistryKey rk = Registry.LocalMachine;
rk = rk.OpenSubKey(@"\SOFTWARE\Apps\Socket Bluetooth OBEX Software",true);
rk.DeleteValue("Instl", false);
rk.DeleteValue("InstlDirCnt", false);
rk.DeleteValue("InstlDir", false);
rk.Close();

rk = Registry.LocalMachine;
rk = rk.OpenSubKey(@"\SOFTWARE\Apps\Socket Bluetooth Software", true);
rk.DeleteValue("Instl", false);
rk.DeleteValue("InstlDirCnt", false);
rk.DeleteValue("InstlDir", false);

rk.Close();

Then check COntrol panel->Remove program, you won't see these 2 socket program.



For PC: ( Not Tested!)


Microsoft.Win32.RegistryKey Fregistry =
Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE")
.OpenSubKey("Microsoft").OpenSubKey("Windows").OpenSubKey("CurrentVersion")
.OpenSubKey("Installer").OpenSubKey("UserData")
.OpenSubKey("S-1-5-18").OpenSubKey("Products");
string []Names = Fregistry.GetSubKeyNames() ;
string uninstall = "";
string ApplicationName = "Adobe Reader 7.0.8";
for (int i = 0; i < Names.Length; i++)
{
Microsoft.Win32.RegistryKey FTemp = Fregistry.OpenSubKey(Names[i]).OpenSubKey("InstallProperties");
if (FTemp.GetValue("DisplayName").ToString() == ApplicationName)
{
object obj = FTemp.GetValue("UninstallString");
if (obj == null)
uninstall = "";
else
uninstall = obj.ToString();
i = Names.Length;
}
}

System.Console.WriteLine(uninstall);
System.Diagnostics.Process FProcess = new System.Diagnostics.Process();
string temp = "/x{" + uninstall.Split("/".ToCharArray())[1].Split("I{".ToCharArray())[2];
//replacing with /x with /i would cause another popup of the application uninstall
FProcess.StartInfo.FileName = uninstall.Split("/".ToCharArray())[0];
FProcess.StartInfo.Arguments = temp;
FProcess.StartInfo.UseShellExecute = false;
FProcess.Start();
System.Console.Read();

Tuesday, June 3, 2008

Treenodes and Context Meny

Make a tree structure with "Copy/paste" context menu....

private System.Windows.Forms.TreeView treeView1;
private ContextMenu cMenu = new ContextMenu();
TreeNode tmpNode, tNode;
Point pPt;

...

this.treeView1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.treeView1_MouseUp);

...

private void Form1_Load(object sender, System.EventArgs e)
{
cMenu.MenuItems.Add("Copy",new EventHandler(eCopy));
cMenu.MenuItems.Add("Paste", new EventHandler(ePaste));

TreeNode aNode;
aNode = treeView1.Nodes.Add("Dong");
aNode.Nodes.Add("Ning");
aNode.Nodes.Add("Yamei");

aNode = treeView1.Nodes.Add("Jing");
aNode.Nodes.Add("Yahong");
aNode.Nodes.Add("Ying");

treeView1.AllowDrop = true;
}
private void eCopy(object sender, System.EventArgs e)
{
// TreeNode tNode = treeView1.SelectedNode; //does not work.
tmpNode = treeView1.GetNodeAt(pPt);
tNode = tmpNode;
MessageBox.Show("Path is " + tNode.FullPath.ToString());
}
private void ePaste(object sender, System.EventArgs e)
{
//insert tNode to the pPt postion
TreeNode nodeTarget = treeView1.GetNodeAt(pPt);
TreeNode nodeSource = tNode;
MessageBox.Show("Path is " + nodeTarget.FullPath.ToString());
nodeTarget.Nodes.Add((TreeNode)tNode.Clone()); //needed
nodeTarget.Expand();
}

private void treeView1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
switch(e.Button)
{
case MouseButtons.Right:
cMenu.Show(treeView1, new Point(e.X,e.Y));
pPt = new Point(e.X,e.Y);
break;
default:
break;
}
}

Monday, June 2, 2008

Directory Control: browse/delete

private void button1_Click(object sender, EventArgs e)
{
folderBrowserDialog1.SelectedPath = @"C:\My Projects\temp\DirectoryControl\Test";
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
textBox1.Text = folderBrowserDialog1.SelectedPath;
}

private void button2_Click(object sender, EventArgs e)
{
if (Directory.Exists(textBox1.Text))
{
DirectoryInfo di = new DirectoryInfo(textBox1.Text);
DeleteAllFilesInDirectories(di);
Directory.Delete(textBox1.Text, true);
}
else
{
MessageBox.Show("Invalid path!");
}
}

private void DeleteAllFilesInDirectories(DirectoryInfo di)
{
FileInfo[] fi = null;
DirectoryInfo[] subDir = null;

fi = di.GetFiles();

if (fi != null)
{
foreach (FileInfo aFi in fi)
{
//delete all
aFi.Attributes = FileAttributes.Normal;
aFi.Delete();
}
subDir = di.GetDirectories();
foreach (DirectoryInfo adi in subDir )
{
DeleteAllFilesInDirectories(adi);
}
}
}

Thursday, April 24, 2008

IPC with CF on CE

With no remoting in the Compact Framework or named pipes on Windows CE there are still other ways to achieve Inter-process communication. Here is a list (feel free to tell me if I have missed any):

1. Sockets
2. Memory Mapped Files
3. Windows Messages
4. Point to Point Message Queues
5. MSMQ
6 Out-of-proc COM
7. Named Events + registry
8. Directly Share memory

1. Sockets are accessible to .NET apps via the System.Net.Sockets namespace. You could use a TcpListener (on 127.0.0.1 and some port you fancy) and a TcpClient or even get down to the Socket class which the other two aggregate. The msdn links given are good and as always search the web and newsgroup.

2. CF code for memory mapped files can be found here and an example of their use here

3. Passing windows messages between apps requires a windows handle of course. CF has the Microsoft.WindowsCE.Forms namespace not available on the desktop that includes the MessageWindow class. [This class was necessary to overcome the CF's limitation of not being able to pass delegates to pinvoke and hence achieve windows callbacks - this limitation is no longer in CF 2.0]. Learn about the MessageWindow here, here, here and here.

4. Point-to-Point Message Queues with the .NET Compact Framework

5. Pinvoking MSMQ is not easy (apparently, I haven't tried it myself) and I am aware of no sample code for that. CF 2.0 will support it via the System.Messaging namespace. For further details and a bunch of links on this subject check out these blog entries here.

6. COM interop is not supported in CF. A commercial offer is available. CF 2.0 will have some support for COM Interop but I don't know if out-of-proc servers will be supported. If you know drop me a note.

7. For simple data sharing, it is easy for an app to write data to predefined regisrty entries and signal a named event; at that point the other side can read from the registry. It looks something like this:

// SERVER PROCESS

//write data

private void cmdWrite_Click(object sender, System.EventArgs e) {

IntPtr hWnd = Win32Api.CreateEvent(IntPtr.Zero, true,

false, "YOUR_NAME_HERE");



// TODO write to reg

//e.g. with opennetcf Registry class



Win32Api.SetEvent(hWnd);

System.Threading.Thread.Sleep(500);

Win32Api.CloseHandle(hWnd);

}



// CLIENT PROCESS

private System.Threading.Thread mMonitorThread;

private bool mStayAlive;

private IntPtr mMonitorHwnd;



//read data

private void cmdRead_Click(object sender, System.EventArgs e) {

mStayAlive = true;



mMonitorHwnd = Win32Api.CreateEvent(IntPtr.Zero, true,

false, "YOUR_NAME_HERE");



mMonitorThread = new System.Threading.Thread(

new System.Threading.ThreadStart(

this.MonitorOtherProc));



mMonitorThread.Start();

}



// on background thread so make sure we don't

// touch GUI controls from here

private void MonitorOtherProc(){

while (mStayAlive){

Win32Api.WaitForSingleObject(mMonitorHwnd, -1);

if (mStayAlive == false) return;



MessageBox.Show("Got data "+

DateTime.Now.ToString(), "TODO read from reg");

// TODO read data from reg



Win32Api.ResetEvent(mMonitorHwnd);

}

}



// must call this before closing app - e.g. from Form_Closing

public void Shutdown(){

if (mMonitorThread == null) return;

mStayAlive = false;

Win32Api.SetEvent( mMonitorHwnd);

System.Threading.Thread.Sleep(500);

Win32Api.CloseHandle( mMonitorHwnd);

mMonitorThread = null;

}


8. Directly sharing memory is not advisable but we can do it. The logic is identical to case 7 with named events but instead of writing/reading from registry, we access memory directly. It looks something like this:


// BOTH CLIENT & SERVER PROCESS NEED THESE



// Returns pointer to shared memory

private IntPtr ObtainHandleToSharedMemory(){

const uint PHYS_ADDR =0x80230000;//Make sure this is not used

on your platform

const int MEM_SIZE = 10;



IntPtr hwnd =

Win32Api.VirtualAlloc(0, MEM_SIZE, Win32Api.MEM_RESERVE,

Win32Api.PAGE_READWRITE|Win32Api.PAGE_NOCACHE);

if (hwnd.ToInt32() != 0){

if (Win32Api.VirtualCopy(hwnd, PHYS_ADDR, MEM_SIZE,

(Win32Api.PAGE_READWRITE|Win32Api.PAGE_NOCACHE))

== true){

return hwnd;

}

}

MessageBox.Show(

Marshal.GetLastWin32Error().ToString(),"Failed");

return IntPtr.Zero;

}



// Define common structure/class in both client and server e.g.

private class SharedMemory{

public byte b1;

public byte b2;

public char c;

public bool flag;

public int i;



public SharedMemory(bool aFlag){

flag=aFlag;

if (aFlag){

b1=1;b2=2;c='!';i=3;

}else{

b1=0;b2=0;c=' ';i=0;

}

}



public override string ToString() {

return "b1=" + b1.ToString() + ", b2="+b2.ToString()

+ ", c=" + c + ", i=" + i.ToString();

}

}



// CLIENT

// As in previous example but instead of reading the registry

// read the following in MonitorOtherProc

IntPtr memHwnd=ObtainHandleToSharedMemory();

if (memHwnd.ToInt32() !=0 ){

SharedMemory sm=new SharedMemory(false);

Marshal.PtrToStructure(memHwnd,sm);

MessageBox.Show(sm.ToString(),sm.flag.ToString());

}



// SERVER

// As in previous example but instead of writing to registry

// do the following in cmdWrite_Click

IntPtr memHwnd=ObtainHandleToSharedMemory();

if (memHwnd.ToInt32() !=0 ){

SharedMemory sm=new SharedMemory(true);

Marshal.StructureToPtr(sm,memHwnd,false);

}