<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Windows CE Programming &#187; thread</title>
	<atom:link href="http://www.hjgode.de/wp/tag/thread/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.hjgode.de/wp</link>
	<description>Windows Mobile Development and usage</description>
	<lastBuildDate>Fri, 03 Feb 2012 08:53:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<!--CodeProjectFeeder channel--><category>CodeProject</category><generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Howto run an application periodically</title>
		<link>http://www.hjgode.de/wp/2009/07/14/howto-run-an-application-periodically/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=howto-run-an-application-periodically</link>
		<comments>http://www.hjgode.de/wp/2009/07/14/howto-run-an-application-periodically/#comments</comments>
		<pubDate>Tue, 14 Jul 2009 12:55:47 +0000</pubDate>
		<dc:creator>josef</dc:creator>
				<category><![CDATA[CodeProject]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[notification]]></category>
		<category><![CDATA[periodically]]></category>
		<category><![CDATA[power state]]></category>
		<category><![CDATA[resume]]></category>
		<category><![CDATA[schedule]]></category>
		<category><![CDATA[SetPowerRequirement]]></category>
		<category><![CDATA[SetSystemPowerState]]></category>
		<category><![CDATA[suspend]]></category>
		<category><![CDATA[SystemIdleTimerReset]]></category>
		<category><![CDATA[thread]]></category>
		<category><![CDATA[timer]]></category>

		<guid isPermaLink="false">http://www.hjgode.de/wp/?p=193</guid>
		<description><![CDATA[How to run an application periodically]]></description>
			<content:encoded><![CDATA[<p>Hello</p>
<p>for some reason you might want to rerun an application very day or every hour. In commercial applications, one may want to let the device sync data every 4 hours, So you need an application that runs periodically at specified times or intervals. As this first sounds easy, it is not as simple as you mean.You cannot simply use timers, <strong>timers will not continue to run within Suspend mode</strong>.</p>
<p>To have an application run at specific times, Windows Mobile supports a <strong>notification database</strong>. Your device will already wake up periodically to reschedule events and to clean up the notification database. Normally, Windows Mobile Phone devices will awake every night at 0:00.</p>
<p>There are also some other possible notifications that can launch an application, see my <a href="http://www.hjgode.de/dev/iLock/index.html#iRunAtEvent" target="_blank">old web site</a>.</p>
<p>How can we do a scheduled application ourself? At first run, the scheduler application has to delete all previous notification entries of the notification database. Then it has to create a new timed notification and in our sample, it will then launch another application. The scheduler application itself should be small and only care about the schedules. The worker application can be what you want and will be started by the scheduler application.</p>
<p><span id="more-193"></span>The following function shows how to remove all notifications containing the name of the scheduler application:</p>
<pre>static HRESULT ClearRunApp(LPCTSTR szExeName)
{
	HRESULT hr = S_OK;

	// hold a notification
	PBYTE pBuff = (PBYTE)LocalAlloc(LPTR, 8192);

	if (!pBuff) {
		return E_OUTOFMEMORY;
	}

	TCHAR mExeName[MAX_PATH];
	wsprintf(mExeName, L"%s", szExeName);

	// at most 256 notification handles
	HANDLE hNotifHandlers[256];
	DWORD nNumHandlers, nNumClearedHandlers = 0;
	DWORD i = 0;
	int rc = CeGetUserNotificationHandles(hNotifHandlers, 255, &amp;nNumHandlers);
	if (!rc) {
		ULONG uErr = GetLastError();
		hr = E_FAIL;
		nclog(L"no more handles? in CeGetUserNotificationHandles()? GetLastError()=%u\n", uErr);
		goto FuncExit;
	}

	// iterate all notifications
	// Notice: We do not care about the status of the notification.
	// Just clear it even if it is not filed??
	nclog(L"RunAtTimes, ClearRunApp(): %s", L"######################\n");
	for (i=0; ipcent;

		nclog(L"RunAtTimes, ClearRunApp(): %s\n", pNotifTrigger-&gt;lpszApplication);

		// Notice some events with NULL lpszApplication might be inserted!
		if ( pNotifTrigger &amp;&amp; pNotifTrigger-&gt;lpszApplication ){
			if(pNotifTrigger-&gt;lpszApplication != NULL){
				if (wcsicmp(pNotifTrigger-&gt;lpszApplication, mExeName)==0) {
					nclog(L"RunAtTimes, ClearRunApp()-CeClearUserNotification for handle: 0x%0x\n", pnih-&gt;hNotification);
					CeClearUserNotification(pnih-&gt;hNotification);
				}
			}
		}
	}

	FuncExit:
	nclog(L"##### RunAtTimes, ClearRunApp():FuncExit ############\n");
	if (pBuff) {
		LocalFree(pBuff);
	}

	return hr;
}</pre>
<p>Then the scheduler app has to create a new notification entry:</p>
<pre>static HRESULT ScheduleRunApp(
  LPCTSTR szExeName,
  LPCTSTR szArgs)
{
	//do not add a schedule if actual date is 21.3.2003
	SYSTEMTIME t;
	memset(&amp;t, 0, sizeof(SYSTEMTIME));
	GetLocalTime(&amp;t);
	//check if the system clock is at factory default, device specific!
	if ( (t.wYear == 2003) &amp;&amp; (t.wMonth == 3) &amp;&amp; (t.wDay == 21) )
	{
		nclog(L"RunAtTimes: # no next run schedule as date is 21.03.2003!\n");
		return NOERROR;
	}

	HRESULT hr = S_OK;
	HANDLE hNotify = NULL;

	// set a CE_NOTIFICATION_TRIGGER
	CE_NOTIFICATION_TRIGGER notifTrigger;
	memset(&amp;notifTrigger, 0, sizeof(CE_NOTIFICATION_TRIGGER));
	notifTrigger.dwSize = sizeof(CE_NOTIFICATION_TRIGGER);

	// calculate time
	SYSTEMTIME st = {0};
	GetLocalTime(&amp;st);

	st = AddDiff(&amp;st, 5);
	wsprintf(str, L"Next run at: %02i.%02i.%02i %02i:%02i:%02i\n",
										st.wDay, st.wMonth , st.wYear,
										st.wHour , st.wMinute , st.wSecond );
	nclog(L"RunAtTimes: %s\n", str);

	notifTrigger.dwType = CNT_TIME;
	notifTrigger.stStartTime = st;

	// timer: execute an exe at specified time
	notifTrigger.lpszApplication = (LPTSTR)szExeName;
	notifTrigger.lpszArguments = (LPTSTR)szArgs;

	hNotify = CeSetUserNotificationEx(0, &amp;notifTrigger, NULL);
	// NULL because we do not care the action
	if (!hNotify) {
		hr = E_FAIL;
		nclog(L"CeSetUserNotificationEx FAILED...\n");
	} else {
		// close the handle as we do not need to use it further
		CloseHandle(hNotify);
		nclog(L"RunAtTimes: CeSetUserNotificationEx succeeded...\n");
	}
	return hr;
}</pre>
<p>In this sample code, the next schedule is created for 5 minutes in advance.<br />
No we take a look at the main function. The scheduler app has no GUI and does not need a GUI. I have created a Windows CE console application:</p>
<pre>	//launched without args
	//if launched from scheduler, lpCmdLine will have the value reboot as specified below
	if (wcslen(lpCmdLine) == 0)
	{
		//create a new schedule
		if ( !FAILED(ScheduleRunApp(lpFileName, L"reboot")) )
		{
			MessageBox(NULL, str, lpFileName, MB_TOPMOST | MB_SETFOREGROUND);
		}
		else
			MessageBox(NULL, L"error in RunAppAtTime", lpFileName, MB_TOPMOST | MB_SETFOREGROUND);
	}

	//Quiet install
	if (_wcsicmp(L"quiet", lpCmdLine)==0)
	{
		nclog(L"RunAtTimes: processing CmdLine=quiet...\n");
		//create a new schedule
		if ( !FAILED(ScheduleRunApp(lpFileName, L"reboot")) ){
			iRet=0;
			goto MainExit; //OK
		}
		else{
			iRet=-1;
			goto MainExit; //OK
		}
	}

	//check if launched with 'clear'
	if (_wcsicmp(L"clear", lpCmdLine)==0)
	{
		nclog(L"RunAtTimes: processing CmdLine=clear...\n");
		ClearRunApp(lpFileName);
		MessageBox(NULL, L"Schedule for this exe cleared", lpFileName, MB_TOPMOST | MB_SETFOREGROUND);
		iRet=1;
		goto MainExit; //OK
	}

	//launched from scheduler with 'reboot'
	if (_wcsicmp(L"reboot", lpCmdLine)==0)
	{

		nclog(L"RunAtTimes: processing CmdLine=reboot...\n");
		nclog(L"RunAtTimes: ...will reboot now after reschedule...\n");
		//schedule next run
		if ( !FAILED(ScheduleRunApp(lpFileName, L"reboot")) )
		{
			//the worker application should be launched here!
			nclog(L"RunAtTimes: starting target app %s...\n", szExtApp);
			PROCESS_INFORMATION pi;
			CreateProcess(szExtApp,NULL,NULL,NULL,NULL, 0, NULL,NULL,NULL, π);
			CloseHandle(pi.hThread);
			CloseHandle(pi.hProcess);
			//nclog(L"......REBOOT in 60 seconds.......\n");
			//Sleep(60000);
			//ITCWarmBoot();
			iRet=0;
			goto MainExit; //OK
		}
		else{
			MessageBox(NULL, L"error in ScheduleRunApp", lpFileName, MB_TOPMOST | MB_SETFOREGROUND);
			nclog(L"RunAtTimes: error in ScheduleRunApp\n");
			iRet=-2;
			goto MainExit; //OK
		}
	}</pre>
<p>Here you can see, that a new schedule is created, if the app is started without argument. Further more, the scheduler can be launched with &#8216;clear&#8217; as argument and it will then clear all schedules and does not create a new schedule. If it is launched with &#8216;reboot&#8217; (sorry about this argument), it assumes it has been launched by the scheduler and will then delete all schedules for itself, create a new schedule and launch the worker application (SyncSim).</p>
<p>The worker application has also some special APIs to use. When the scheduler app is launched by the OS from the notification database, it does not switch the device to full power (unanttended power mode?). So the first thing for the worker app is to request full power mode. When the OS runs an application from a notification entry, there is a special timeout, after that the device will suspend again (see WakeupPowerOff). So the worker application has to ensure, that the device will not fall back to suspend before the &#8216;work&#8217; is done. You can force the device to remain powered by periodically calling SystemIdleTimerReset().</p>
<p>OK, here is a full power request:</p>
<pre>        //Switch Backlight ON
	hPower = SetPowerRequirement(_T("BKL1:"), D0, POWER_NAME, NULL, 0); //POWER_FORCE

	//set to full power
	if ( SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE) == ERROR_SUCCESS ){
		nclog(L"SetSystemPowerState ok\n");
	}
	else{
		nclog(L"SetSystemPowerState FAILED with error 0x%0x\n", GetLastError());
	}</pre>
<p>Then a background thread with SystemIdleTimerReset():</p>
<pre>DWORD WINAPI ThreadProc(LPVOID lpParameter){
	bThreadEnded=false;
	nclog(L"ThreadProc started...\n");
	while(bRunThread){
		SystemIdleTimerReset();
		Sleep(1000);
	}
	nclog(L"ThreadProc ended normally\n");
	bThreadEnded=true;
	return 0;
}</pre>
<p>For the backlight: dont forget to release the power request:</p>
<pre>	if(hPower != NULL){
		HRESULT result = ReleasePowerRequirement(hPower);
		nclog(L"SyncSim-OnDestroy: ReleasePowerRequirement() returned 0x%0x\n", result);
	}</pre>
<p><strong>UPDATE 03. feb 2012</strong>: Download Visual Studio 2005 CPP source code (with some more test code inside): <b>DOWNLOAD:</b><a href="http://www.hjgode.de/wp/wp-content/plugins/download-monitor/download.php?id=156" title="Downloaded 0 times">RunAtTimes VS2005 solution and projects</a> -  (Hits: 0, size: 183.72 kB) (also removed dependencies on itc50.h/lib)</p>
<p>Download EVC4 projects: <b>DOWNLOAD:</b><a href="http://www.hjgode.de/wp/wp-content/plugins/download-monitor/download.php?id=33" title="Downloaded 391 times">RunAtTimes and SyncSim</a> -  (Hits: 391, size: 207.59 KB)</p>
<pre></pre>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a><strong><em>Bookmark It</em></strong></a>
<br />
<div class="d">
<br />
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F07%2F14%2Fhowto-run-an-application-periodically%2F&amp;title=Howto+run+an+application+periodically" rel="nofollow" title="Add to&nbsp;Del.icio.us"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/delicious.png" title="Add to&nbsp;Del.icio.us" alt="Add to&nbsp;Del.icio.us" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F07%2F14%2Fhowto-run-an-application-periodically%2F&amp;title=Howto+run+an+application+periodically" rel="nofollow" title="Add to&nbsp;digg"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/digg.png" title="Add to&nbsp;digg" alt="Add to&nbsp;digg" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F07%2F14%2Fhowto-run-an-application-periodically%2F&amp;title=Howto+run+an+application+periodically" rel="nofollow" title="Add to&nbsp;Google Bookmarks"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/google.png" title="Add to&nbsp;Google Bookmarks" alt="Add to&nbsp;Google Bookmarks" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.netscape.com/submit/?U=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F07%2F14%2Fhowto-run-an-application-periodically%2F&amp;T=Howto+run+an+application+periodically" rel="nofollow" title="Add to&nbsp;Netscape"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/netscape.png" title="Add to&nbsp;Netscape" alt="Add to&nbsp;Netscape" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F07%2F14%2Fhowto-run-an-application-periodically%2F" rel="nofollow" title="Add to&nbsp;Technorati"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/technorati.png" title="Add to&nbsp;Technorati" alt="Add to&nbsp;Technorati" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://myweb2.search.yahoo.com/myresults/bookmarklet?u=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F07%2F14%2Fhowto-run-an-application-periodically%2F&amp;t=Howto+run+an+application+periodically" rel="nofollow" title="Add to&nbsp;Yahoo My Web"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/yahoo.png" title="Add to&nbsp;Yahoo My Web" alt="Add to&nbsp;Yahoo My Web" /></a>
<br />
</div>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.hjgode.de/wp/2009/07/14/howto-run-an-application-periodically/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Windows mobile worker thread to post form data using a queue</title>
		<link>http://www.hjgode.de/wp/2009/06/14/mdiwatch2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mdiwatch2</link>
		<comments>http://www.hjgode.de/wp/2009/06/14/mdiwatch2/#comments</comments>
		<pubDate>Sun, 14 Jun 2009 06:09:44 +0000</pubDate>
		<dc:creator>josef</dc:creator>
				<category><![CDATA[CodeProject]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[cf]]></category>
		<category><![CDATA[filesystemwatcher]]></category>
		<category><![CDATA[opennetcf]]></category>
		<category><![CDATA[post data]]></category>
		<category><![CDATA[post form]]></category>
		<category><![CDATA[queue]]></category>
		<category><![CDATA[thread]]></category>

		<guid isPermaLink="false">http://www.hjgode.de/wp/?p=99</guid>
		<description><![CDATA[A compact framework 2 (Visual Studio 2005) windows mobile application to upload data created in a directory in background using a thread, a queue and form post.]]></description>
			<content:encoded><![CDATA[<p><strong>A threaded form uploader app in C#</strong></p>
<p>Recently I had to do an app that watches a dir for new files and then uploads them to a DocuShare server using http form post.</p>
<p>I was told, that it is very easy to use form post from code and decided to do the app in C#. Unfortunately, it was not that easy to find code that does uploads via form post, especially for Compact Framework (CF). Maybe it is easier in JAVA, but for JAVA I had to install a JAVA VM onto the windows mobile 6.1 device and for CF all runtimes are already on the device. I searched the internet for valuable form post code in C# and found one titled &#8216;<a title="C# is the better JAVA" href="http://www.galaxy-news.de/groups/9_c_ist_das_bessere_java_net_c_gruppe/forums/5096_post_class.html" target="_blank">C# is the better JAVA</a>&#8216;.<br />
OK, this code looked good and i tried it within Compact Framework. After changing the functions to ones available in CF, the code runs fine. As you know, CF does not have the same full featured functions as the .Net Framework for PCs.</p>
<p><span id="more-99"></span>Here is my FormPost class:</p>
<pre>using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Drawing;
/*
string sForm = "&lt;html&gt;" +
"&lt;body&gt;" +
"&lt;form action=\"http://xxx.xxx.xxx.xxx:80/docushare/UploadServlet\"" +
"enctype=\"multipart/form-data\" method=\"post\"&gt;" +
"&lt;label for=\"summary\"&gt;Zusammenfassung&lt;/label&gt;&amp;nbsp;&lt;input type=\"text\" id=\"summary\" name=\"summary\" size=\"100\"&gt; &lt;br/&gt;" +
"&lt;label for=\"description\"&gt;Beschreibung&lt;/label&gt;&amp;nbsp;&lt;input type=\"text\" id=\"description\" name=\"description\" size=\"100\"&gt; &lt;br/&gt; " +
"&lt;br/&gt;" +
"&lt;label for=\"datafile\"&gt;Datei&lt;/label&gt;&amp;nbsp;&lt;input type=\"file\" id=\"datafile\" name=\"datafile\" size=\"100\"&gt;" +
"&lt;br/&gt;&lt;input type=\"submit\" value=\"Send\"&gt;" +
"&lt;/form&gt;" +
"&lt;/body&gt;" +
"&lt;/html&gt;";
*/

/*    //================== usage =================
List&lt;Post.IPostAble&gt; Fields = new List&lt;Post.IPostAble&gt;();
Fields.Add(new Post.PostText("Test", "Hi"));
Fields.Add(new Post.PostPicture("C:\\Dokumente und Einstellungen\\Kevin\\Desktop\\test.png", "file", System.Drawing.Imaging.ImageFormat.Png));
Fields.Add(new Post.PostText("Test2", "Hallo"));
HPText.Text = Post.ExecutePostCommand("http://localhost/Test/upload.php", Fields);
*/
namespace Peiler.ServerCommunication
{
public static class Post
{
private static string CrLf = "\r\n";
public static String UserAgent = "PostMan";
public static String ExecutePostCommand(String URL, List&lt;IPostAble&gt; Fields)
{   // uriStr is set by config - the address where the form posts to.
HttpWebRequest Request = (HttpWebRequest)HttpWebRequest.Create(URL);
Request.PreAuthenticate = true;
Request.AllowWriteStreamBuffering = true;

String Boundary = System.Guid.NewGuid().ToString();

Request.ContentType = String.Format("multipart/form-data;boundary={0}", Boundary);
Request.Method = "POST";
// not sure if this was necessary, but found forums where it was an issue not to set it.
Request.UserAgent = Post.UserAgent;

// Build Contents for Post
String Header = String.Format("--{0}", Boundary);
String Footer = Header + "--";

//Get Bytes
Byte[] FieldBytes = new Byte[0];
foreach(IPostAble Field in Fields)
{
FieldBytes = ConnectByteArrays(FieldBytes, Field.GetBytes(Header));
}

//Footer
FieldBytes = ConnectByteArrays(FieldBytes, Encoding.UTF8.GetBytes(Footer.ToString()));

// now we have all of the bytes we are going to send, so we can calculate the size of the stream
Request.ContentLength = FieldBytes.Length;

using (Stream requestStream = Request.GetRequestStream())
{
requestStream.Write(FieldBytes, 0, FieldBytes.Length);
requestStream.Flush();
requestStream.Close();
System.Diagnostics.Debug.WriteLine("### post finished ###");

using (WebResponse response = Request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
return reader.ReadToEnd();
}
}
}
}

public interface IPostAble
{
Byte[] GetBytes(String Header);
}

public class PostText : IPostAble
{
public String FieldName;
public String FieldValue;

public PostText(String FieldName, String FieldValue)
{
this.FieldName = FieldName;
this.FieldValue = FieldValue;
}

public Byte[] GetBytes(String Header)
{
StringBuilder Content = new StringBuilder();
Content.Append(Header+"\r\n");
Content.Append(String.Format("Content-Disposition:form-data; name=\"{0}\"\r\n", FieldName));
Content.Append("\r\n");
Content.Append(FieldValue+"\r\n");
return Encoding.UTF8.GetBytes(Content.ToString());

}
}

public class PostPicture : IPostAble
{
public Bitmap Bmp;
public String FieldName;
public String FileName;
public System.Drawing.Imaging.ImageFormat Format;
public string sFormat = "image/jpeg";

public PostPicture(String FileName, String FieldName, System.Drawing.Imaging.ImageFormat Format)
{
this.FileName = Path.GetFileName(FileName);
this.FieldName = FieldName;
this.Bmp = new Bitmap(FileName);
this.Format = Format;
}

public PostPicture(String FileName, String FieldName, string strMimeType)
{
this.FileName = Path.GetFileName(FileName);
this.FieldName = FieldName;
this.Bmp = new Bitmap(FileName);
this.Format = System.Drawing.Imaging.ImageFormat.Jpeg;
this.sFormat = strMimeType;
}

public PostPicture(String FileName, String FieldName, Bitmap Bmp)
{
this.FileName = Path.GetFileName(FileName);
this.FieldName = FieldName;
this.Bmp = Bmp;
}

public Byte[] GetBytes(String Header)
{
if (Format == null)
Format = System.Drawing.Imaging.ImageFormat.Jpeg;
StringBuilder Content = new StringBuilder();
Content.Append(Header+CrLf);
Content.Append(String.Format("Content-Disposition:form-data; name=\"{0}\"; filename=\"{1}\""+CrLf, FieldName, FileName));
Content.Append("Content-Type: " + GetContentType(Format) + CrLf);
//Content.AppendLine("Content-Transfer-Encoding: binary");
Content.Append(CrLf);
MemoryStream BitmapStream = new MemoryStream();
Bmp.Save(BitmapStream, Format);
Byte[] ContentBytes = Encoding.UTF8.GetBytes(Content.ToString());
Byte[] BitmapBytes = BitmapStream.ToArray();
BitmapBytes = ConnectByteArrays(BitmapBytes, Encoding.UTF8.GetBytes(CrLf));//New Line
return ConnectByteArrays(ContentBytes, BitmapBytes);

}

private String GetContentType(System.Drawing.Imaging.ImageFormat Format)
{
System.Diagnostics.Debug.WriteLine("Format='"+Format.ToString()+"', JPEG.format='"+System.Drawing.Imaging.ImageFormat.Jpeg.ToString()+"'");
if (sFormat.Length &gt; 0)
return sFormat;
if (Format.Equals(System.Drawing.Imaging.ImageFormat.Jpeg))
return "image/jpeg";
else if (Format == System.Drawing.Imaging.ImageFormat.Png)
return "image/png";
else if (Format == System.Drawing.Imaging.ImageFormat.Gif)
return "image/gif";
throw new Exception("Unknown ImageFormat!");
}

}

internal static Byte[] ConnectByteArrays(Byte[] B1, Byte[] B2)
{
Byte[] Bytes = new Byte[B1.Length + B2.Length];
B1.CopyTo(Bytes, 0);
B2.CopyTo(Bytes, B1.Length);
return Bytes;
}

}
}</pre>
<p>As you can see, I had to change some lines. For whatever reason, the Class function GetContentType was unable to recognize ImageFormat correctly. The compare condition did fail, although the Format was set correctly. As a workaround I used a string to define the format and then used this to identify the format of the attachement to post.<br />
At the top of the code you see the html form code, which is used inside a web browser to post the data. I included it here for reference.</p>
<p><strong>Using a queue for posting data<br />
</strong>Another nice implementation is the use of a queue for the data to post. Using this technique I can simply put the files and texts I need to post in a queue and let a background thread do the work. The background thread tries to post the data using the formPost class as long as the application is running.</p>
<p><strong>The background worker class</strong><br />
Here is the thread class I used to work in the background:</p>
<pre>using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Peiler.ServerCommunication;
using System.Windows.Forms;

namespace MDIwatch
{
class myThread
{
//===============================================================
private Queue m_Queue = new Queue();
private bool m_bRunThread = false;
private System.Threading.Thread t2;
private bool m_safeToCloseGUI = true;
#region TEST THREADS
public bool _safeToCloseGUI {
get { lock (this) { return m_safeToCloseGUI; } }
}

public bool bRunThread
{

get { lock (this) { return m_bRunThread; } }
set { lock (this) { m_bRunThread = value; } }
}

private MDIwatch2 m_Form;

public myThread(Queue queueArgs, MDIwatch2 mForm)
{
this.m_Queue = queueArgs;
t2 = new System.Threading.Thread(new System.Threading.ThreadStart(threadProc2));
t2.Priority = System.Threading.ThreadPriority.BelowNormal;
m_Form = mForm;
t2.Start();
}
public void AbortThread(string s)
{
if (s == string.Empty)
s = "Thread abort requested!";
t2.Abort(s);
}
private void updateGUI(string s){
if(bRunThread)
m_Form.Invoke(m_Form.myUpdateTextBox, new object[] { s });
}
private void updateGUIstatus()
{
//just toggle an indicator and show we are still running
if (bRunThread)
m_Form.Invoke(m_Form.myUpdateStatus);

}
private void threadProc2()
{
try
{
postData pData = new postData();
bRunThread = true;
m_safeToCloseGUI = false;
updateGUI("--- Send Thread started ---");
do
{
updateGUIstatus();
//System.Threading.Interlocked.Increment(ref isSending);
while ((m_Queue.Count &gt; 0) &amp;&amp; bRunThread)
{
//ICollection syncedCollection = m_Queue;
lock (m_Queue.SyncRoot)// syncedCollection.SyncRoot)
{
pData = (postData)m_Queue.Peek();// get object from queue
}
//System.Threading.Interlocked.Decrement(ref isSending);
//send file now
System.Diagnostics.Debug.WriteLine("### Sending file '" + pData.sFullFileName);
updateGUI("### Sending file '" + pData.sFullFileName + "'");

if (postFormData(pData) == 0) //post and check
{
updateGUI("... file '" + pData.sFullFileName + "' posted");
lock (m_Queue.SyncRoot)
{
pData = (postData)m_Queue.Dequeue(); //remove object from queue
m_Form.Invoke(m_Form.myUpdateTextBox, new object[] { "___ file '" + pData.sFullFileName + "' removed from queue" });
}
}
System.Threading.Thread.Sleep(2000);
updateGUIstatus();
}
System.Threading.Thread.Sleep(2000);

} while (bRunThread);
}
catch (System.Threading.ThreadAbortException tax)
{
System.Diagnostics.Debug.WriteLine("ThreadAbortException: " + tax.Message);
updateGUI("ThreadAbortException: " + tax.Message);
}
finally {
updateGUI("--- Send Thread stopped ---");
System.Diagnostics.Debug.WriteLine("--- Send Thread stopped ---");
lock (this)
{
m_safeToCloseGUI = true;
bRunThread = false;
}
}
}

public void addFile2Queue2(postData pData)
{
//System.Net.HttpWebRequest wr;
//ICollection syncedCollection = m_fiQueue;
lock (m_Queue.SyncRoot)// syncedCollection.SyncRoot
{
m_Queue.Enqueue(pData);
m_Form.Invoke(m_Form.myUpdateTextBox, new object[] { "+++ Added file '" + pData.sFullFileName + "' to Queue" });
}
}

private int postFormData(postData pData)
{
int iRet = 0;
try
{
string theURL = "http://xxx.xxx.xxx.xxx:80/docushare/UploadServlet";

List&lt;Post.IPostAble&gt; Fields = new List&lt;Post.IPostAble&gt;();
Fields.Add(new Post.PostText("summary", pData.sSummary));
Fields.Add(new Post.PostText("description", pData.sDescription));
Fields.Add(new Post.PostPicture(pData.sFullFileName, "datafile", pData.sMimeString)); //System.Drawing.Imaging.ImageFormat.Jpeg));
string HPText /*.Text*/ = Post.ExecutePostCommand(theURL, Fields);
System.Diagnostics.Debug.WriteLine(HPText);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception " + ex.Message + "' in postFormData");
m_Form.Invoke(m_Form.myUpdateTextBox, new object[] { "==== Exception " + ex.Message + "' in postFormData ====" });
iRet = -1;
}
return iRet;
}
#endregion
public class postData
{
private string m_sDescription;
private string m_sSummary;
private string m_sFullFileName;
private string m_sMimeString;
public postData()
{
m_sDescription = "Description";
m_sSummary = "Summary";
m_sFullFileName = "FullFileName";
m_sMimeString = "mime/jpeg";
}
/// &lt;summary&gt;
/// provide an object for post data
/// &lt;/summary&gt;
/// &lt;param name="sDesc"&gt;a description of the post&lt;/param&gt;
/// &lt;param name="sSum"&gt;a summary about the post&lt;/param&gt;
/// &lt;param name="sFullName"&gt;an image file to post&lt;/param&gt;
public postData(string sDesc, string sSum, string sFullName)
{
m_sDescription = sDesc;
m_sSummary = sSum;
m_sFullFileName = sFullName;
string sExt = System.IO.Path.GetExtension(sFullName);
if (sFullName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase))
m_sMimeString = "image/jpeg";
else if (sFullName.EndsWith("bmp", StringComparison.OrdinalIgnoreCase))
m_sMimeString = "image/bmp";
else if (sFullName.EndsWith("tif", StringComparison.OrdinalIgnoreCase))
m_sMimeString = "image/tiff";
else if (sFullName.EndsWith("gif", StringComparison.OrdinalIgnoreCase))
m_sMimeString = "image/gif";
else
m_sMimeString = "image/unknown";

}
public string sDescription
{
get { return m_sDescription; }
set { m_sDescription = value; }
}
public string sSummary
{
get { return m_sSummary; }
set { m_sSummary = value; }
}
public string sFullFileName
{
get { return m_sFullFileName; }
set { m_sFullFileName = value; }
}
public string sMimeString
{
get { return m_sMimeString; }
set { m_sMimeString = value; }
}
}
}
}</pre>
<p>As you can see, I always try different ways to do things. Dont worry about this, I am not a software designer, I try to write working code.</p>
<p><strong>Using Invoke and Delegate</strong></p>
<p>As a worker thread can not update the GUI directly, as the GUI is running on a different thread, one has to use invoke to update the GUI. I included two UpdateGUI functions here: updateGUI and updateGUIstatus.</p>
<pre>...
public delegate void UpdateTextBox(string str);
public UpdateTextBox myUpdateTextBox;
public delegate void UpdateStatus();
public UpdateStatus myUpdateStatus;
...
myQueue = new Queue();
mThread = new myThread(myQueue, this);

//new delegate for thread update via Invoke
myUpdateTextBox = new UpdateTextBox(UpdateTextBoxMethod);
//new delegate for thread status invoke
myUpdateStatus = new UpdateStatus(UpdateStatusMethod);
...
public void UpdateTextBoxMethod(string str)
{
this.textBox1.Text = str + "\r\n" + this.textBox1.Text;
showNotification(str);
}
public void UpdateStatusMethod()
{
radioStatus.Checked = !radioStatus.Checked;

}
...</pre>
<p><strong>Shutting down the app</strong><br />
At the beginning of the implementation, I was not able to close the app. The worker thread invoked the GUI although the GUI thread already removed the TextFields I used to update the GUI. So I had to implement _safeToCloseGUI. Using this var I could safely close the GUI without the side effect of a worker thread trying to update the already removed GUI elements.</p>
<pre>private void MDIwatch_Closing(object sender, CancelEventArgs e)
{
// Signal the BackgroundWorkerThread to end
mThread.bRunThread=false;
System.Threading.Thread.Sleep(2000);

// The _safeToCloseGUI flag will  be true when the
// background worker thread has finished
if (mThread._safeToCloseGUI == false)
{
// The background worker thread has not closed yet,
// so don't close the GUI yet
e.Cancel = true;
}
}</pre>
<p><strong>The worker thread and the queue</strong></p>
<p>The worker thread threadProc2 always checks the queue for available data. If there is data, it peeks the next one from the queue and tries to post it. If the data has been posted, the data is removed from the queue. Doing so ensures, that you will not lost any data to send.</p>
<p><strong>The watch dir function</strong></p>
<p>The app has to watch two dirs. Unfortunately, the OpenNetCF FileSystemWatcher class does not support watching for different extensions. So I had to declare 4 FileSystemWatcher objects: for dir one with tif, jpg and bmp and a fourth for dir2. The FileSystemWatcher OnCreated events will then add new post data to the queue:</p>
<pre>private void postFile(myThread.postData pData){

mThread.addFile2Queue2(pData);

}</pre>
<p>Again, dont blame me for the code design. I hope this code will help you solve similar problems.</p>
<b>DOWNLOAD:</b><a href="http://www.hjgode.de/wp/wp-content/plugins/download-monitor/download.php?id=11" title="Downloaded 133 times">MDIwatch2</a> -  (Hits: 133, size: 37.15 KB)
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a><strong><em>Bookmark It</em></strong></a>
<br />
<div class="d">
<br />
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F06%2F14%2Fmdiwatch2%2F&amp;title=Windows+mobile+worker+thread+to+post+form+data+using+a+queue" rel="nofollow" title="Add to&nbsp;Del.icio.us"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/delicious.png" title="Add to&nbsp;Del.icio.us" alt="Add to&nbsp;Del.icio.us" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F06%2F14%2Fmdiwatch2%2F&amp;title=Windows+mobile+worker+thread+to+post+form+data+using+a+queue" rel="nofollow" title="Add to&nbsp;digg"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/digg.png" title="Add to&nbsp;digg" alt="Add to&nbsp;digg" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F06%2F14%2Fmdiwatch2%2F&amp;title=Windows+mobile+worker+thread+to+post+form+data+using+a+queue" rel="nofollow" title="Add to&nbsp;Google Bookmarks"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/google.png" title="Add to&nbsp;Google Bookmarks" alt="Add to&nbsp;Google Bookmarks" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.netscape.com/submit/?U=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F06%2F14%2Fmdiwatch2%2F&amp;T=Windows+mobile+worker+thread+to+post+form+data+using+a+queue" rel="nofollow" title="Add to&nbsp;Netscape"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/netscape.png" title="Add to&nbsp;Netscape" alt="Add to&nbsp;Netscape" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F06%2F14%2Fmdiwatch2%2F" rel="nofollow" title="Add to&nbsp;Technorati"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/technorati.png" title="Add to&nbsp;Technorati" alt="Add to&nbsp;Technorati" /></a>
<a onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://myweb2.search.yahoo.com/myresults/bookmarklet?u=http%3A%2F%2Fwww.hjgode.de%2Fwp%2F2009%2F06%2F14%2Fmdiwatch2%2F&amp;t=Windows+mobile+worker+thread+to+post+form+data+using+a+queue" rel="nofollow" title="Add to&nbsp;Yahoo My Web"><img class="social_img" src="http://www.hjgode.de/wp/wp-content/plugins/social-bookmarks0/images/yahoo.png" title="Add to&nbsp;Yahoo My Web" alt="Add to&nbsp;Yahoo My Web" /></a>
<br />
</div>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.hjgode.de/wp/2009/06/14/mdiwatch2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

