| Patrick's profilePatricks Live-SpaceBlogLists | Help |
Patricks Live-Space... |
||||||
January 20 Case Study: A Facebook Application for Windows Mobile 6Sehr sympathische Firma:
"The need for a special list control affirmed the value of our general policy of using native code for our applications."
Durchaus lesenswerte Case Study zur Facebook Applikation:
- Patrick January 14 Ein Windows Mobile ABC: C wie Certs.cab NachtragLeider muss ich zu dem "Ein Windows Mobile ABC: C wie Certs.cab" einen Nachtrag einstellen.
Die Zertifikate im SDK sind am 31.12.2009 abgelaufen und können daher nicht mehr verwendet werden.
Auf dem Windows Mobile Developer Blog findet man aber ein neues Downloadpaket für aktuelle Zertifikate (Gültig bis zum 31.12.2015).
Da leider keine neue Certs.cab in dem Download dabei ist muss man die Zertifikate nun selbst auf das Gerät bringen bzw. halt seine eigene Certs.cab erstellen. Dazu gibt es auch eine Anleitung.
- Patrick January 01 2010Ich wünsche allen Lesern erst einmal ein frohes und erfolgreiches neues Jahr 2010! Es ist mir eine große Ehre diese Auszeichnung tragen zu dürfen und hoffe Ihr auch in 2010 gerecht werden zu können. In diesem Sinne auf ein erfolgreiches neues Jahr. December 24 Türchen Nummer 24Sollte man die Weihnachtsgeschenke für seine Lieben bereits gekauft haben, so kommt man meist zum eher unangenehmen Teil: Dem Verpacken.
Dies gilt jedoch auch für Anwendungen, die auf einem mobilen Gerät installiert werden sollen. Hierfür gibt es das CAB-Datei Format. Um eine entsprechende Installationsdatei zu erstellen, nehmen wir einmal an, dass wir die zu verpackenden Dateien unter c:\HelloWorld abgelegt haben. Die Dateien, welche dort vorliegen müssen, sind die HelloWorld.exe, welche installiert werden soll, sowie die HelloWorld.inf Datei, welche die Informationen für den CabWizard vorbehält, um eine CAB-Datei erzeugen zu können. Hier nun der Inhalt der INF-Datei dazu. Hierbei hat die Programmiersprache diesmal keine Relevanz: [SOURCE FILE]
Name=CHelloWorld.cab Path=C:\HelloWorld\HelloWorld.cab AllowUninstall=TRUE [Version]
Signature="$Chicago$" CESignature="$Windows CE$" Provider="Company" [CEStrings]
AppName="HelloWorld" InstallDir="%CE1%\CHelloWorld" [CEDevice]
ProcessorType=0 VersionMin=0.0 VersionMax=0.0 BuildMin=0 BuildMax=0 [SourceDisksNames]
1=,Source1,, [SourceDisksFiles]
"HelloWorld.exe"=1 [CopyFiles1]
"HelloWorld.exe",,,0x00000001 [DestinationDirs]
CopyFiles1=0,"%CE1%\HelloWorld" [DefaultInstall]
CopyFiles=CopyFiles1 Wird nun auf der Kommandozeile der Befehl "c:\Program Files\Microsoft Visual Studio 9.0\SmartDevices\SDK\SDKTools\cabwiz.exe" "Company HelloWorld2.inf" ausgeführt (Pfad ist natürlich hinsichtlich seinen Gegebenheiten anzupassen), so findet sich im entsprechenden Zielordner die CAB-Installationsdatei wieder.
Weitere Informationen zum CAB-Wizard erhalten Sie hier: http://msdn.microsoft.com/de-de/library/cc433670%28VS.71%29.aspx Patrick & Peter December 23 Türchen Nummer 23So kurz vor weihnachten sollte der Weihnachtsbaum hell leuchten. Warum auch nicht dann die LED des Bluetoothmoduls des Gerätes zum Blinken bringen? Nichts einfacher als das, in dem man das Bluetooth Modul aktiviert. Dabei gilt zu beachten, dass dieses Beispiel nur auf Geräten funktioniert, die den Microsoft Bluetooth Stack verwenden. C++ #include "bthutil.h" #pragma comment(lib, "Bthutil.lib") ... DWORD dwMode = 0; BthGetMode(&dwMode); if(dwMode==BTH_POWER_OFF || dwMode==BTH_CONNECTABLE) { BthSetMode(BTH_DISCOVERABLE); } C# In C# ist es auch an dieser Stelle nötig, mittels P/Invoke auf die nötige DLL zu zu greifen, um auf die entsprechenden Informationen zu erhalten. Somit hier das Snippet: ... public enum RadioMode { Off = 0, Connectable = 1, Discoverable = 2 } static class Program { [DllImport("BthUtil.dll")] private static extern int BthGetMode(out RadioMode dwMode); [DllImport("BthUtil.dll")] private static extern int BthSetMode(RadioMode dwMode); /// <summary> /// The main entry point for the application. /// </summary> [MTAThread] static void Main() { RadioMode rm = RadioMode.Off; BthGetMode(out rm); if (rm == RadioMode.Off || rm== RadioMode.Connectable) { BthSetMode(RadioMode.Discoverable); } } } ... Patrick & Peter December 22 Türchen Nummer 22In vielen Fenstern und Wohnungen findet man zur Weihnachtszeit eine ganze Menge an Lichterketten und sonstiger beleuchteter Weihnachtsdekoration. Das manuelle Einschalten dieser vielen Lichterketten und Lämpchen kann eine zeitraubende Beschäftigung werden. Gleiches gilt auch für Windows Mobile Applikationen die durchgehend laufen sollen. Wenn der Anwender diese Applikationen manuell starten muss dauert dies natürlich auch eine Weile. Wäre es nicht angenehmer wenn dies automatisch geschehen würde? Mit folgendem Codeschnipsel kann aus der eigenen Anwendung heraus, beispielsweise per Optionsdialog, eine Autostartverknüpfung angelegt werden. C++ //Dies ist eigentlich das gleiche Schnipsel wie Türchen Nummer 8 //jedoch wird diesmal die CSIDL_STARTUP verwendet. //Autostart Ordner auslesen WCHAR szShortcutPath[MAX_PATH*2] = L"\0"; SHGetSpecialFolderPath(NULL,szShortcutPath,CSIDL_STARTUP,0); wcscat(szShortcutPath,L"\\MeinShortcut.lnk"); //Modulnamen auslesen WCHAR szModulePath[MAX_PATH*2] = L"\0"; GetModuleFileName(GetModuleHandle(NULL), szModulePath, MAX_PATH); //Shortcut erstellen SHCreateShortcut(szShortcutPath,szModulePath); In C# ist es diesmal nicht notwendig P/Invoke zu nutzen, um an diese Informationen zu kommen. Die Klasse Environment hierfür reicht völlig. Hier also das Snippet: C# ... [DllImport("coredll.dll")] private static extern int SHCreateShortcut(StringBuilder szShortcut, StringBuilder szTarget); ... [MTAThread] static void Main() { StringBuilder shortcut = new StringBuilder(System.Environment.GetFolderPath(Environment.SpecialFolder.Startup)+@"\MeinShortcut.lnk"); StringBuilder target = new StringBuilder(@"\windows\calc.exe"); SHCreateShortcut(shortcut, target); } ... Patrick & Peter December 21 Türchen Nummer 21Der große Vorteil eines digitalen Adventskalenders ist, dass man volle Kontrolle über alle Türchen hat und niemand vorab schon mal die "24" öffnen kann. Eine vollständige Kontrolle bspw. über sämtliche Tasten kann auch mal in einer Windows Mobile Applikation benötigt werden. Das folgende Codeschnipsel zeigt wie man dies mit der AllKeys API bewerkstelligen kann. C++ //Alle Tasten abfangen(bspw. in WM_CREATE) AllKeys(TRUE); ... //In WM_KEYDOWN und WM_KEYUP den WPARAM prüfen. //Siehe http://msdn.microsoft.com/en-us/library/bb431750.aspx für Keycodes case WM_KEYDOWN: OutputDebugString(L"WM_KEYDOWN\n"); //Behandlung für den Tastencode ist hier nicht aufgeführt HandleKey(wParam); break; case WM_KEYUP: OutputDebugString(L"WM_KEYUP\n"); //Behandlung für den Tastencode ist hier nicht aufgeführt HandleKey(wParam); break; ... Im verwaltetem Code ist es mehr Code nötig, um das gleiche Ziel zu erreichen. Das liegt daran, dass eine Windows Form generell die Verarbeitung von derlei Nachrichten intern bereits durchführt. Um also auf die internen Window Messages zugreifen zu können, ist es nötig, eine Klasse zu erstellen, die von der Klasse MessageWindow ableitet. Zusätzlich ist auch das P/Invoke notwendig, um die AllKeys-Methode auf zu rufen. Somit ist es diesmal für das Snippet nötig, eine Anwendung für intelligene Geräte anzulegen, welche eine Geräteanwendung darstellt. Der Code is dann entsprechend ähnlich abzuändern: C# ... using Microsoft.WindowsCE.Forms; ... public partial class MessageForm : Form { [DllImport("coredll.dll", SetLastError = true)] static extern bool AllKeys(bool bAllKeys); MsgWindow msgWin; public MessageForm() { InitializeComponent(); AllKeys(true); msgWin = new MsgWindow(this); } ... } ... public class MsgWindow : MessageWindow { public const int WM_KEYDOWN = 0x0100; public const int WM_KEYUP = 0x0101; private MessageForm msgform; public MsgWindow(MessageForm msgform) { this.msgform = msgform; } protected override void WndProc(ref Message msg) { switch (msg.Msg) { case WM_KEYDOWN: MessageBox.Show("Key was down with WPARAM:" + ((int)msg.WParam).ToString() + "and LPARAM:" + ((int)msg.LParam).ToString()); break; case WM_KEYUP: MessageBox.Show("Key was up with WPARAM:" + ((int)msg.WParam).ToString() + "and LPARAM:" + ((int)msg.LParam).ToString()); break; } base.WndProc(ref msg); } } Mehr Informationen zur Klasse MessageWindow erhalten Sie hier: http://msdn.microsoft.com/de-de/library/microsoft.windowsce.forms.messagewindow%28VS.80%29.aspx Patrick & Peter December 20 Türchen Nummer 20Gerade in der Weihnachtszeit sind die Schaufenster derart mit Werbung oder Kunstschnee zugekleistert, das der Blick auf die schönen Dinge dahinter verwehrt ist. Auch dies gilt für manche Windows Mobile Anwendung, die man im Vollbildmodus betreiben möchte. C++ //Der folgende Code gilt für Win32 Anwendungen. //Er ist im IDM_HELP_ABOUT Handler der WM_COMMAND Nachricht //einer per Visual Studio 2008 Win32 Projekt für intelligente Geräte (Windowsanwendung) //untergebracht. Der DialogBox Aufruf ist auskommentiert. //Rückgängig gemacht werden sollte der Fullscrenn Code bspw. über ein Kontextmenu oder beim //beenden der Applikation //DialogBox(g_hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, About); //Fenster muss für SHFullScreen im Vordergrund sein SetForegroundWindow(hWnd); //SIP und Taskbar in den Hintergrund legen SHFullScreen(hWnd,SHFS_HIDESIPBUTTON|SHFS_HIDETASKBAR); //Die Menübar soll auch weg HWND hwndMenu = SHFindMenuBar(hWnd); //Erst die Menübarhöhe holen. Gilt auch für das Starmenü RECT rcMenue; GetWindowRect(hwndMenu, &rcMenue); //Menübar verschieben SetWindowPos(hwndMenu, NULL,-1, -1,0, 0,SWP_NOZORDER | SWP_NOACTIVATE); //Jetzt muss unser Fenster noch neu positioniert werden RECT rect; GetWindowRect(hWnd,&rect); rect.top -= rcMenue.bottom - rcMenue.top; rect.bottom += rcMenue.bottom - rcMenue.top; MoveWindow(hWnd,rect.left,rect.top,rect.right,rect.bottom,TRUE); C# Eine Anwendung zu erstellen, welche eine Vollbilddarstellung liefert ist mit Bordmitteln des .NET Compact Frameworks bereits einfach realisierbar. Dies geht sogar so einfach, dass nicht einmal ein Snippet hierfür notwendig ist. Setzen Sie für die Vollbilddarstellung Ihrer Form einfach die zugehörige Eigenschaft WindowsState auf den Wert FormWindowState.Maximized. Hierdurch »verschwindet« die Titelzeile Ihrer Anwendung. Damit sich jedoch kein vielleicht im Hintergrund startendes Programm in den Vordergrund setzt und Ihre Anwendung überlagert, sollten Sie gleichfalls die Eigenschaft TopMost der Form auf den Wert true setzten. Wollen Sie gleichfalls die Menüzeile, welche Ihr Anwendungsmenü darstellt und meist auch die virtuelle Tastatur aktiviert, deaktivieren, ist auch dies ganz einfach. Leider gibt es für das MainMenu keine Eigenschaft »Enabled«, um dies zu ermöglichen. Stattdessen reicht es, wenn die Eigenschaft Menu der zugehörigen Form auf den Wert null gesetzt wird, was auch dynamisch aus der Anwendung möglich ist. Hierdurch wird die Zuweisung eines MainMenu zur Form entfernt, wodurch Sie eine komplette Vollbilddarstellung Ihrer Anwendung erhalten. Wenn Sie nun Menu wieder das Objekt vom Typ MainMenu zuweisen, haben Sie wieder Ihren Ursprungszustand hergestellt. Patrick & Peter December 19 Türchen Nummer 19Wer dem Weihnachtstress ein wenig aus dem Weg gehen möchte vermeidet es am besten die Geschenke auf althergebrachte Weise in der Stadt oder sonstigen Einkaufläden zu besorgen. Nichts ist entspannter als ein Einkaufbummel im Internet. Und wenn man die Einkauftour rechtzeitig beginn bekommt auch keinen Stress mit den Lieferzeiten. Um aber trotzdem an sein Paket zu kommen muss man natürlich seine eigene Adresse mitteilen. Gleiches gilt auch für Anwendung die auf Netzwerkdienste zugreifen. Oftmals muss die Anwendung die eigene IP Adresse nicht kennen, da es durch die zugrunde liegenden Protokolle automatisch passiert. Ab und an kann es aber vorkommen dass diese Information benötigt wird. Dazu kann das folgende Codeschnipsel verwendet werden: C++ #include "iphlpapi.h" #pragma comment(lib, "Iphlpapi.lib") ... //Wir listen mal etwas mehr auf als nur die aktuellen IPs. //Adapter Information anlegen PIP_ADAPTER_INFO pAdapterInfo; ULONG lRetLen = sizeof(PIP_ADAPTER_INFO); //Benötigte Speichergrösse holen DWORD dwRet = GetAdaptersInfo(NULL,&lRetLen); //Informationen zu allen Adaptern holen pAdapterInfo = (PIP_ADAPTER_INFO) malloc (lRetLen); dwRet = GetAdaptersInfo(pAdapterInfo,&lRetLen); if(dwRet == ERROR_SUCCESS) { //Informationen nur für den aktuellen Adapter PIP_ADAPTER_INFO pCurrentAdapter = pAdapterInfo; while (pCurrentAdapter != NULL ) { //Systemindex printf("Adapterindex: %i\n",pCurrentAdapter->Index); //Beschreibung printf("Beschreibung: %s\n",pCurrentAdapter->Description); //IP Addresse printf("IP: %s\n",pCurrentAdapter->IpAddressList.IpAddress.String); //Gateway printf("Gateway: %s\n",pCurrentAdapter->GatewayList.IpAddress.String); //Subnet printf("Subnet: %s\n",pCurrentAdapter->IpAddressList.IpMask.String); //Erweiterte IP Infos anlegen PIP_PER_ADAPTER_INFO pPerAdapterInfo; lRetLen = sizeof(PIP_PER_ADAPTER_INFO); //Benötigte Speichergrösse holen dwRet = GetPerAdapterInfo(pCurrentAdapter->Index,NULL,&lRetLen); //Erweiterte IP Infos abfragen pPerAdapterInfo = (PIP_PER_ADAPTER_INFO) malloc (lRetLen); dwRet = GetPerAdapterInfo(pCurrentAdapter->Index,pPerAdapterInfo,&lRetLen); //DNS1 printf("DNS: %s\n",pPerAdapterInfo->DnsServerList.IpAddress.String); //DNS2 if(pPerAdapterInfo->DnsServerList.Next != NULL) printf("DNS2: %s\n",pPerAdapterInfo->DnsServerList.Next->IpAddress.String); //Interface anlegen MIB_IFROW IfRow; memset( &IfRow, NULL, sizeof( MIB_IFROW ) ); //Index zum abfragen setzen IfRow.dwIndex = pCurrentAdapter->Index; //Interface abfragen GetIfEntry(&IfRow); //MAC Adresse printf("MAC: %02x-%02x-%02x-%02x-%02x-%02x\n",IfRow.bPhysAddr[0], IfRow.bPhysAddr[1], IfRow.bPhysAddr[2], IfRow.bPhysAddr[3], IfRow.bPhysAddr[4], IfRow.bPhysAddr[5]); //Den Namen(ClassID) brauchen wir auch... printf("ClassID: %s\n",pCurrentAdapter->AdapterName); //Nächster Adapter pCurrentAdapter = pCurrentAdapter->Next; } } C# In C# geht dies ebenfalls ganz einfach, auch wenn es wild aussieht: using System.Net; ... IPHostEntry enty = Dns.GetHostEntry(Dns.GetHostName()); foreach (IPAddress ipadress in enty.AddressList) { Console.WriteLine("IP: " + ipadress.ToString()); } Patrick & Peter December 18 Türchen Nummer 18December 17 Türchen Nummer 17Bei einem leckerem warmen Kakao trifft man manchmal Freunde, die man lange nicht mehr gesehen, oder jedoch aus den Augen verloren hat. Also schnell die Telefonnummern ausgetauscht, bevor man sich wieder verliert. Wenn man aber seine eigene Nummer nicht auswendig kennt und sich auch nicht selbst als Kontakt aufgeführt hat (Wobei dies zum Versenden seiner eigenen Kontaktdaten sehr praktisch ist) muss man sich mühsam über die Menüdialoge der Telefonapplikation zur eigenen Rufnummer vorarbeiten. Wäre es also nicht schön wenn man die Rufnummer aus dem eigenen Code heraus anzeigen lassen könnte? Hier das passende Codeschnipsel dazu: Es gilt jedoch zu beachten, dass die vorgestellte Funktionalität möglicherweise nicht bei allen funktioniert, da dies ggfs. sogar providerabhängig ist. C++ //Da dies ein Snippet ist inkludiere ich hier einfach die komplette CPP Datei des SDK Samples #include "C:\Programme\Windows Mobile 6 SDK\Samples\Common\CPP\Win32\GetPhoneNumber\GetPhoneNumber.cpp" ... //Der Aufruf setzt vorraus das die Telefonnummer in die SIM einprogrammiert //ist. Wird ggf. nicht von jeder SIM bzw. Provider unterstützt. //Durch verwendung von AT Kommandos ist diese Nummer auch //editierbar. SHGetPhoneNumber(szNumber, MAX_LOADSTRING, 1); _tprintf(L"Nummer: %s\n",szNumber); C# In .NET ist auch diese Aufgabe wieder einmal nicht ganz trivial zu lösen. Um Ihnen jedoch Menge Tipparbeit zu ersparen, laden Sie sich bitte die MSDN Bibliothek zur Phonefunktionalität unter http://www.microsoft.com/downloads/details.aspx?FamilyId=40CF068B-66CA-4F26-87A1-15B67F2F7AE6&displaylang=en herunter, fügen die dort enthaltene Klasse Phone.cs ihrem Projekt hinzu. Da der hinzugefügte Code jedoch direkte Speicheroperationen durchführt, ist es notwendig in ihren Projekteigenschaften die Option "Unsicheren Code zulassen" zu aktivieren. Der zu erstellende Code reduziert sich somit auf eine Zeile auführbaren Code. ... using Microsoft.Wireless; ... Console.WriteLine(Sim.GetPhoneNumber().Address); ... Patrick & Peter December 16 Türchen Nummer 16Gerade in der Weihnachtszeit kommt man in der Stadt kaum voran, ohne auf irgendwelchen dynamischen Plakatwänden auf die aktuellsten Angebote aufmerksam gemacht zu werden. Anstatt jedoch Werbung darzustellen, ist es unter Windows Mobile manchmal durchaus sinnvoll, den Benutzer mithilfe einer Notification auf eine bestimmte Gegebenheit hinzuweisen. C++ // {070A46B7-1C4F-441e-AAE2-5BD96D03477A} static const GUID CLSID_MyBubble = { 0x70a46b7, 0x1c4f, 0x441e, { 0xaa, 0xe2, 0x5b, 0xd9, 0x6d, 0x3, 0x47, 0x7a } }; ... //SHNOTIFICATIONDATA Struktur anlegen SHNOTIFICATIONDATA bubble = {0}; bubble.cbStruct = sizeof(bubble); bubble.dwID = 1; //Anzeigen bis der Benutzer es ausblendet bubble.npPriority = SHNP_INFORM; bubble.csDuration = -1; bubble.hicon = NULL; //Eine eigene GUID angeben bubble.clsid = CLSID_MyBubble; bubble.grfFlags = 0; //Inhalt festlegen bubble.pszTitle = L"Türchen Nummer 16"; bubble.pszHTML = L"<html><body>Das ist Türchen Nummer <b> 16</b><br><br> in unserem Adventskalender.</body></html>"; //Softkey festlegen bubble.rgskn[0].pszTitle = L"Ausblenden"; bubble.rgskn[0].skc.wpCmd = 1; SHNotificationAdd(&bubble); ... //Wir blenden es jetzt selber aus SHNotificationRemove(&CLSID_MyBubble,1); C# In .NET gibt es in der Microsoft.WindowsCE.Forms Bibliothek eine Notification-Klasse, die einen Wrapper für verwalteten Code darstellt, jedoch funktioniert diese ein wenig anders. Anstatt nun den Softkey einstellen zu können, verwendet dieses Beispiel den speziellen Namen des HTML-Inputbuttons "cmd:1", welcher die Notification schließt. Ausgelöst wird die Notifikation durch eine Schaltfläche. Hier nun das Snippet: ... Notification nf = new Notification(); ... private void btnNotify_Click(object sender, EventArgs e) { nf.InitialDuration = 0; nf.Critical = false; nf.Caption = "Türchen Nummer 16"; nf.Text = "<html><body>Das ist Türchen Nummer <b> 16</b><br><br> in unserem Adventskalender.<br><input type=button name='cmd:1' value='Schliessen'></body></html>"; nf.Visible = true; ... //Wir blenden es nun selbst aus nf.Visible = false; } Patrick & Peter December 15 Türchen Nummer 15Je nachdem wie groß der Familien und Freundeskreis muss man sich gerade während der Vorweihnachtszeit eine ganze Menge an Geschenkideen, Weihnachtsmarktbesuchen, Weihnachtsfeiern und sonstigen
Termin notieren. Wohl dem der ein Windows Phone sein eigen nennt und jederzeit Zugriff auf seine Termine, Notizen und Kontaktdaten hat. Nichts ist jedoch ärgerlicher als das man bei der Eingabe von Text noch manuell die Tastatur anzeigen lassen muss nachdem man in ein Textfeld geklickt hat. Wie man ein solches Verhalten in seiner eigenen Applikation verhindern kann zeigen die folgenden Codeschnipsel: C++ //Keyboard anzeigen (bspw. wenn wir den Focus bekommen) SHSipPreference(hwndEdit,SIP_UP); ... //Keyboard ausblenden(bspw. wenn wir den Focus verlieren) SHSipPreference(hwndEdit,SIP_DOWN); C# Damit wir den Status des Softkeyboards, bzw. die SIP sauber setzen können, gilt es dieses Mal eine mobile Windows Forms Anwendung zu erstellen. Zusätzlich ist es notwendig einen Verweis auf die Bibliothek "Microsoft.WindowsCE.Forms" hinzuzufügen. Wurden 2 Schaltflächen ebenfalls hinzufgefügt, so sieht der zu erstellende Code, wie folgt aus: using Microsoft.WindowsCE.Forms; ... InputPanel panel = new InputPanel(); private void btnSHowSIP_Click(object sender, EventArgs e) { panel.Enabled = true; } private void btnHideSIP_Click(object sender, EventArgs e) { panel.Enabled = false; } ... Patrick & Peter December 14 Türchen Nummer 14Gerade in dieser kalten Jahreszeit kann es passieren, dass man draußen in der Kälte ohne Handschuhe nichts mehr fühlt, da die Finger taub werden. Auch unter Windows Mobile möchte man manchmal die Eingaben über das Touchscreen, solange es sich um ein Professional oder Classic Gerät handelt, verhindern und das Touchscreen quasi "taub" stellen. Dabei gilt bei diesem Snippet zu beachten, dass das vorgestellte Beispiel OEM spezifisch ist, so dass es u.U. nicht auf jedem Gerät funktioniert. C++ //Funktionsdeklaration typedef VOID (*TOUCHPANELDISABLE)(); ... //Touch.DLL(Treiber) ist OEM spezifisch! //Touch.DLL laden HINSTANCE hTouch=LoadLibrary(L"touch.dll"); //Funktionszeiger auf die TouchPanelDisable Funktion holen TOUCHPANELDISABLE pTouchPanelDisable=(TOUCHPANELDISABLE)GetProcAddress(hTouch, _T("TouchPanelDisable")); //Touchpanel deaktivieren pTouchPanelDisable(); //Touch.DLL entladen FreeLibrary(hTouch); //Ein wenig warten Sleep(30000); //Warmstart durchführen. //TouchPanelEnable ist nicht aufrufbar da von GWES die Callback benötigt werden würde. ExitWindowsEx(EWX_REBOOT,0); C# Aufsetzend auf unser vorheriges Türchen würde das Beispiel in verwaltetem Code, wie folgt, aussehen: [DllImport("touch")] private static extern void TouchPanelDisable(); static void Main(string[] args) { TouchPanelDisable(); Thread.Sleep(30000); ExitWindowsEx(EWX_REBOOT, 0); } Patrick & Peter December 13 Türchen Nummer 13Gerade in der Weihnachtszeit ist es möglich, dass die Kälte einen eben angesprungenen Wagen wieder ausgehen lässt, wodurch dieser neu gestartet werden muss. Auch Windows Mobile Geräte sind vor einem Warmstart manchmal nicht gefeit. C++ ExitWindowsEx(EWX_REBOOT,0); C# Auch im .NET Compact Framework gibt es eigentlich kaum mehr zu schreiben, als die zugehörigen P/Invoke Signaturen. Somit hier das Snippet: [DllImport("aygshell.dll")] private static extern bool ExitWindowsEx(uint uFlags, int dwReserved); const uint EWX_REBOOT = 0x00000002; const uint EWX_POWEROFF = 0x00000008; static void Main(string[] args) { ExitWindowsEx(EWX_REBOOT, 0); } Patrick & Peter December 12 Türchen Nummer 12Türchen Nummer 12
So, die erste Hälfte der Türchen ist jetzt offen.
Wer jetzt noch nicht in Weihnachtsstimmung ist kann wohl nur noch durch intensive Beschallung dazu versetzt werden. Nichts leichter als das. Mit dem folgenden Codeschnipsel kann man sich auch unter Windows Mobile von Weihnachtsmusik berieseln lassen: C++
//WAV Datei in Endlosschleife und asynchron vom API Aufruf abspielen
sndPlaySound(L"\\Windows\\JingleBells.wav",SND_FILENAME|SND_LOOP|SND_ASYNC); ...
//OK, genug! WAV stoppen.
sndPlaySound(NULL,SND_FILENAME); ------------------------------------
C#
Damit die Schnippsel gleich bleiben, wird hier eine Lösung mittels P/Invoke angeboten, welche auch für das .NET CF 2.0 gilt. Das .NET CF 3.5 allerdings birng eine neue Klasse Soundplayer-Klasse (System.Media.SoundPlayer http://msdn.microsoft.com/en-us/library/system.media.soundplayer.aspx) mit, welche alternativ verwendbar wäre.
Nun aber zum Snippet.
[DllImport("coredll.dll")]
public static extern int PlaySound( string szSound, IntPtr hModule, int flags); static void Main(string[] args)
{ PlaySound("\\Windows\\JingleBells.wav", IntPtr.Zero,(int) (PlaySoundFlags.SND_FILENAME | PlaySoundFlags.SND_LOOP | PlaySoundFlags.SND_ASYNC));
... //Das reicht PlaySound(null, IntPtr.Zero, (int)(PlaySoundFlags.SND_FILENAME)); } Patrick & Peter December 11 Türchen Nummer 11Haben Sie dieses Jahr keine Lust auf den alten Vorwurf man würde immer das gleiche verschenken? Soll es diese Jahr ein einzigartiges Geschenk werden? Tja, da können wir leider auch nicht weiterhelfen. Wohl aber wenn es um eine einzigartige ID geht, die in einem Windows Mobile Programm verwendet werden soll. Und zwar eine sogenannte GUID (Globally Unique Identifier). Oftmals finden diese GUIDs während der Entwicklungszeit Verwendung, aber ab und an können diese auch mal zur Laufzeit benötigt werden. Mit folgendem Codeschnipsel kann eine solche GUID erstellt werden: C++ #include "objbase.h" #pragma comment(lib, "ole32.lib") ... //GUID erstellen GUID myGUID; HRESULT hr = CoCreateGuid(&myGUID); //GUID in String umwandeln WCHAR szGUID[MAX_PATH] = L"\0"; StringFromGUID2(myGUID,szGUID,MAX_PATH); wprintf(L"Neue GUID: %s\n",szGUID); C# In C# eine Guid zu erzeugen ist glücklicherweise auch nicht so schwer. Hier das Snippet: static void Main(string[] args) { Guid guid = Guid.NewGuid(); Console.WriteLine(guid.ToString()); } Patrick & Peter December 10 Türchen Nummer 10Eine oft ausgeübte Weihnachtstradition ist das Wichteln. Wem ein einfaches "Zettel aus dem Hut" ziehen zu langweilig ist kann
ja auch ein kleines Programm schreiben um die Wichtel automatisch und zufällig auszuwählen. Doch oftmals ist das "zufällig" eher wenig vom Zufall bestimmt. Wie man jedoch richtige Zufallszahlen erzeugen kann zeigt das folgende Codeschnipsel: C++ #include <wincrypt.h>
...
//Erzeuge 100 Zufallszahlen
for(int i=0;i<100;i++) { DWORD dwRandomSeed = 0; HCRYPTPROV hCrypt; if (CryptAcquireContext(&hCrypt, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { if (CryptGenRandom(hCrypt, 2, (BYTE*)&dwRandomSeed)) { //Zufallszahlen nur zwischen 0 & 1000 wprintf(L"RandomNumber %d\n",dwRandomSeed%1000); } CryptReleaseContext(hCrypt, 0); } } ------------------------------------ C#
Im .NET CF kann man sich diesmal glücklich schätzen, einmal ohne P/Invoke auszukommen, da der Security-Namespace einen entsprechende Klasse bereits bereitstellt.
Hier das zugehörige Snippet:
using System.Security.Cryptography;
...
static void Main(string[] args)
{ RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); for (int i = 0; i < 100; i++)
{ byte[] randomBytes = new byte[4]; // Generiere 4 zufällige Bytes.
rng.GetBytes(randomBytes); // Konvertiere 4 bytes in ein Integer (32 bit).
int seed = (randomBytes[0] & 0x7f) << 24 | randomBytes[1] << 16 | randomBytes[2] << 8 | randomBytes[3]; // Nur Zahlen zwischen unter 1000; seed = seed % 1000; Console.WriteLine(seed.ToString()); } }
Patrick & Peter December 09 Türchen Nummer 9Nicht nur Weihnachtsgeschenke sollten einzigartig sein, sondern auch Geräte um diese beispielsweise für Kopierschutzfunktionen unterscheiden zu können.
Daher hat jedes Windows Mobile Gerät eine eindeutige und einmalige ID. Diese über die KernelIOControl Funktion abzufragen ist allerdings nur für privilegiert signierte Applikationen zulässig. Als Ausgleich dafür gibt es jedoch die GetDeviceUniqueID Funktion. Allerdings liefert diese nicht dieselbe ID zurück wie der KernelIoControl Aufruf, sondern anhand von der Applikation übergeben Zusatzdaten einen eindeutigen Hash der ID. Ruft man mit seiner Anwendung also die GetDeviceUniqueID Funktion immer mit den gleichen Parametern auf erhält man auch immer den gleichen einzigartigen Hash auf dem Gerät, selbst wenn es resettet oder sogar geflasht wird. Hier nun der Codeschnipsel dazu: C++
#include "getdeviceuniqueid.h" …
//GetDeviceUniqueID gibt nicht die eigentlich ID zurück sondern eine
//eigene ID die aus der Device ID sowie von der Anwendung zusätzlich //übergebenen Daten besteht. Solange die Zusatzdaten gleich sind erhält //Die Anwendung auch immer die selbe ID zurück, selbst nach einem Firmwareupdate #define APPDATA "MEINAPPDATAUID" #define APPDATALEN 14 …
//Device ID Variable anlegen
BYTE bDeviceID[GETDEVICEUNIQUEID_V1_OUTPUT]; DWORD cbSize = sizeof(bDeviceID); //DeviceID Hash abfragen HRESULT hr = GetDeviceUniqueID(reinterpret_cast<PBYTE>(APPDATA), APPDATALEN, GETDEVICEUNIQUEID_V1, bDeviceID, &cbSize); ---------------------------------------------------
C# Eigentlich könnte man sich bereits denken, dass man an dieser Stelle auch P/Invoke benötigen wird, um diese Funktionalität aus verwaltetem Code heraus zu lesen. ;-)
Hier somit das Snippet: class Program
{ [DllImport("coredll.dll")] private extern static int GetDeviceUniqueID([In, Out] byte[] appdata, int cbApplictionData, int dwDeviceIDVersion, [In, Out] byte[] deviceIDOuput, out uint pcbDeviceIDOutput); private static byte[] GetDeviceID(string AppString)
{ // Call the GetDeviceUniqueID byte[] AppData = Encoding.Unicode.GetBytes(AppString); int appDataSize = AppData.Length; byte[] DeviceOutput = new byte[20]; uint SizeOut = 20; GetDeviceUniqueID(AppData, appDataSize, 1, DeviceOutput, out SizeOut); return DeviceOutput; } static void Main(string[] args)
{ byte[] id = GetDeviceID("MEINAPPDATAUID"); } } Patrick & Peter December 08 Türchen Nummer 8Wenn man mal ehrlich ist bedeutet die Adventszeit für die meisten Leute puren Stress. Da man also eh schon unter Zeitdruck steht sollte man wenigstens nicht lange nach seiner Applikation suchen müssen. Mit einer Verknüpfung findet man die Anwendung einfach und schnell zum Beispiel über das Startmenü. Daher hier der Codeschnipsel um eine Verknüpfung anzulegen: C++ //Startmenu Ordner auslesen WCHAR szShortcutPath[MAX_PATH*2] = L"\0"; SHGetSpecialFolderPath(NULL,szShortcutPath,CSIDL_PROGRAMS,0); wcscat(szShortcutPath,L"\\MeinShortcut.lnk"); //Modulnamen auslesen WCHAR szModulePath[MAX_PATH*2] = L"\0"; GetModuleFileName(GetModuleHandle(NULL), szModulePath, MAX_PATH); //Shortcut erstellen SHCreateShortcut(szShortcutPath,szModulePath); Die einfachste Lösung in diesem Fall ist es, ebenfalls SHCreateShortcut via P/Invoke anzusprechen. Somit sieht das Snippet, wie folgt, aus: C# ... using System.Runtime.InteropServices; [DllImport("coredll.dll")] private static extern int SHCreateShortcut(StringBuilder szShortcut, StringBuilder szTarget); static void Main(string[] args) { StringBuilder shortcut = new StringBuilder(@"\MeinShortcut.lnk"); StringBuilder target = new StringBuilder(@"\windows\calc.exe"); SHCreateShortcut(shortcut, target); } ... In diesem Beispiel wurde das dynamische Auslesen des Anwendungsnamens und der Windows-Ordners ausgelassen, da dies bereits in anderen Snippets behandelt wurde. Wer jedoch gerne mal ein Shortcut in verwaltetem Code “auf die harte Tour” erstellen möchte, sollte sich folgenden Link einmal anschauen. Patrick & Peter December 07 Türchen Nummer 7Beim Versenden der Nikolaus SMS gestern jemanden vergessen?
C# December 06 Türchen Nummer 6 (Nikolaus)Heute ist Nikolaustag! Wenn das mal kein Grund ist heute allen seinen Freunden und Bekannten eine Nikolaus-SMS zu senden. Womit wir auch schon direkt beim Codeschnipsel wären. Denn heute zeigen wir das senden von SMS über die Windows Mobile API: C++ #include "sms.h" #pragma comment(lib, "sms.lib") bool SMSSendSMS(LPCWSTR pcNumber,LPCWSTR pcText) { //SMS API initialisieren HRESULT hr = NULL; SMS_HANDLE SMSHandle = NULL; HANDLE SMSEvent = NULL; hr = SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND, &SMSHandle, &SMSEvent); SMS_ADDRESS adr; //SMS Adresse erstellen memset (&adr, 0, sizeof (adr)); adr.smsatAddressType = SMSAT_INTERNATIONAL; lstrcpy(adr.ptsAddress, pcNumber); //SMS Text Struktur erstellen TEXT_PROVIDER_SPECIFIC_DATA tpsd; tpsd.dwMessageOptions = PS_MESSAGE_OPTION_NONE; tpsd.psMessageClass = PS_MESSAGE_CLASS1; tpsd.psReplaceOption = PSRO_NONE; tpsd.dwHeaderDataSize = 0; //SMS senden hr = SmsSendMessage(SMSHandle, NULL, &adr, NULL, (BYTE *)pcText, wcslen(pcText) * sizeof (TCHAR), (BYTE *)&tpsd, 12, SMSDE_OPTIMAL, SMS_OPTION_DELIVERY_NONE,NULL); //SMS API deinitialsieren hr = SmsClose(SMSHandle); return true; } … SMSSendSMS(L"+49xxxxxxxxx",L"Alles Gute zum Nikolaustag!"); C# Das Windows Mobile SDK bietet dem verwalteten Entwickler hier auch wieder eine Bibliothek. Diese (Microsoft.WindowsMobile.PocketOutlook) muss somit als Verweis dem Projekt hinzugefügt werden. Der Rest ist ein Kinderspiel: using Microsoft.WindowsMobile.PocketOutlook; ... static void Main(string[] args) { SmsMessage sms = new SmsMessage("+49xxxxxxxxx", "Alles Gute zum Nikolaus") sms.Send(); } In diesem Sinne alles Gute zum Nikolaustag! Patrick & Peter December 05 Türchen Nummer 5Wer in der Adventszeit mal in die Innenstadt zum Einkaufen geht könnte meinen die Welt steht Kopf. Da man das aber nun mal nicht verhindern kann heißt es also sich anzupassen. Und wenn man schon dabei ist: Warum nicht auch mal die eigene Windows Mobile Anwendung auf den Kopf stellen? Mit folgenden Codeschnipseln ist dies schnell getan: C++ //DEVMODE Struktur anlegen und säubern DEVMODE devMode; memset(&devMode,0,sizeof(DEVMODE)); devMode.dmSize = sizeof(DEVMODE); //Bildschirmorientierung auslesen devMode.dmFields = DM_DISPLAYORIENTATION; ChangeDisplaySettingsEx(NULL, &devMode, NULL, CDS_TEST, NULL); //Aktuelle orientierung speichern DWORD dwOld = devMode.dmDisplayOrientation; //Bildschirmorientierung setzen devMode.dmFields = DM_DISPLAYORIENTATION; switch(devMode.dmDisplayOrientation) { case DMDO_0: devMode.dmDisplayOrientation = DMDO_180; break; case DMDO_90: devMode.dmDisplayOrientation = DMDO_270; break; case DMDO_180: devMode.dmDisplayOrientation = DMDO_0; break; case DMDO_270: devMode.dmDisplayOrientation = DMDO_90; break; } ChangeDisplaySettingsEx(NULL,&devMode,NULL,CDS_RESET,NULL); //Warten Sleep(15000); //Ursprüngliche orientierung wieder setzen devMode.dmFields = DM_DISPLAYORIENTATION; devMode.dmDisplayOrientation = dwOld; ChangeDisplaySettingsEx(NULL,&devMode,NULL,CDS_RESET,NULL); C# Um dies in einer verwalteten Anwendung zu erreichen, gilt es, die Bibliothek „System.WindowsCE.Forms“ als Verweis hinzuzufügen. using Microsoft.WindowsCE.Forms; … static void Main(string[] args) { ScreenOrientation currentOrientation = SystemSettings.ScreenOrientation; switch (currentOrientation) { case ScreenOrientation.Angle0: SystemSettings.ScreenOrientation = ScreenOrientation.Angle180; break; case ScreenOrientation.Angle180: SystemSettings.ScreenOrientation = ScreenOrientation.Angle0; break; case ScreenOrientation.Angle270: SystemSettings.ScreenOrientation = ScreenOrientation.Angle90; break; case ScreenOrientation.Angle90: SystemSettings.ScreenOrientation = ScreenOrientation.Angle270; break; default: break; } System.Threading.Thread.Sleep(1500); SystemSettings.ScreenOrientation = currentOrientation; } Patrick & Peter December 04 Türchen Nummer 4„Von drauß` vom Walde komme ich her…“ heißt es im einem der bekanntesten Weihnachtsgedichten. Schön wenn man so genau weiß, wo man „herkommt“. Denn oft genug muss man seinen eigenen Anwendungsordner kennen, um beispielsweise den Ablageort für Konfigurationsdateien oder den Speicherort für Logdateien zu bestimmen. Nicht einfacher als das. Mit den folgenden Codeschnipseln kann man ganz einfach und ohne großen Aufwand den Ordner bestimmen aus dem die Anwendung gestartet wurde. C++ //Vollständigen Modulnamen auslesen WCHAR szPath[MAX_PATH] = L"\0"; if(GetModuleFileName(GetModuleHandle(NULL), szPath, MAX_PATH)) { //Das Backslash suchen(rückwärts) und abschneiden WCHAR *pszPath = wcsrchr(szPath, '\\'); if(pszPath) *(pszPath + 1) = '\0'; //In szPath steht nun der Ordner wprintf(L"Pfad: %s\n",szPath); } C# In C# ist es ein bisschen weniger Code, da zum Filtern des reinen Ordnernamens, hier bereits eine Klasse existiert. using System.Reflection; … static void Main(string[] args) { string executable = Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName; Console.Write(“Pfad: “+Path.GetDirectoryName(executable)); } Patrick & Peter December 03 Türchen Nummer 3Die Adventszeit liegt ja bekanntermaßen in der dunklen Jahreszeit. Umso angenehmer das man mit einem Windows Phone ein wenig Licht erzeugen kann, beispielsweise in Form einer Taschenlampenapplikation. Was nützt einem jedoch die beste Taschenlampe wenn nach ein paar Sekunden die Hintergrundbeleuchtung erlischt oder sich der Bildschirm komplett ausschaltet?
Mit einem kleinen Griff in die Windows Mobile Trickkiste kann man dies jedoch verhindern. Ein kleiner Hinweis vorweg: Die benötigten APIs sind auf jedem Gerät gleich, lediglich der anzusprechende Treiber kann je nach Hersteller variieren. Als erstes muss man der Hintergrundbeleuchtung mitteilen das Sie doch bitte nicht mehr ausgehen soll. Dies bewerkstelligen wir über die SetPowerRequirement Funktion. Anschließend müssen wir uns noch um das Standbyverhalten kümmern, schließlich soll das Gerät ja nicht einschlafen. Dazu müssen wir den sogenannten Idle-Timer zurücksetzen. Die Funktion dazu nennt sich SystemIdleTimerReset. Hier nun die Schnipsel dazu: C++ #include "pmpolicy.h"
#include "pm.h" …
//Bedingung für die Hintergrundbeleuchtung setzen
//BKL1 ist exemplarisch und kann je nach OEM variieren HANDLE hPower = SetPowerRequirement(L"BKL1:", D0, POWER_NAME | POWER_FORCE, NULL, 0); //Zusätzlich muss jetzt noch die IDLE Time umgangen werden //Aktuellen Batterie Status holen SYSTEM_POWER_STATUS_EX pex; memset(&pex,0,sizeof(SYSTEM_POWER_STATUS_EX)); GetSystemPowerStatusEx(&pex,TRUE); //Idle Timeout je nach AC Status abfragen //Dies funktioniert nur wenn der Wert "DisableGwesPowerOff" //unter "[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power]" != 0 ist. DWORD dwIdleTimeout = 30; BOOL bRet = FALSE; if(pex.ACLineStatus == AC_LINE_ONLINE) bRet = SystemParametersInfo(SPI_GETEXTERNALIDLETIMEOUT,0,&dwIdleTimeout,0); else bRet = SystemParametersInfo(SPI_GETBATTERYIDLETIMEOUT,0,&dwIdleTimeout,0); //Idle Timer verringern oder auf 30 Sekunden setzen if(dwIdleTimeout == 0) dwIdleTimeout = 30; else if(dwIdleTimeout > 5) dwIdleTimeout -= 5; dwIdleTimeout*=1000; //Idle Timer reset (Normalerweise in einem Thread...) /*while (1) { SystemIdleTimerReset(); Sleep(dwIdleTimeout); }*/
//Bedingung für die Hintergrundbeleuchtung wieder freigeben ReleasePowerRequirement(hPower); -------------------------------------------------------------------------------- Die Funktionalität in C# darzustellen ist nicht ganz so einfach, da jede angesprochene Funktion mittels P/Invoke angesprochen werden muss.
Somit ist der Quellcode in C# ein wenig länger: C#
… public class SYSTEM_POWER_STATUS_EX { public byte ACLineStatus; public byte BatteryFlag; public byte BatteryLifePercent; public byte Reserved1; public uint BatteryLifeTime; public uint BatteryFullLifeTime; public byte Reserved2; public byte BackupBatteryFlag; public byte BackupBatteryLifePercent; public byte Reserved3; public uint BackupBatteryLifeTime; public uint BackupBatteryFullLifeTime; }
public enum DevicePowerState : int { Unspecified = -1,
D0 = 0, // Full On: full power, full functionality D1, // Low Power On: fully functional at low power/performance D2, // Standby: partially powered with automatic wake D3, // Sleep: partially powered with device initiated wake D4, // Off: unpowered } class Program { [DllImport("coredll")] private static extern uint GetSystemPowerStatusEx(SYSTEM_POWER_STATUS_EX lpSystemPowerStatus, bool fUpdate); [DllImport("coredll", SetLastError=true)]
private static extern IntPtr SetPowerRequirement(string DeviceName, DevicePowerState State, uint dwDeviceFlags, string Name, ulong Reserved); [DllImport("coredll.dll", SetLastError=true)]
static extern int ReleasePowerRequirement(IntPtr handle); [DllImport("coredll.dll", SetLastError=true)]
static extern void SystemIdleTimerReset(); [DllImport("coredll.dll")]
internal static extern int SystemParametersInfo( int uiAction, int uiParam, ref int pvParam, int fWinIni); public const int SPI_SETBATTERYIDLETIMEOUT = 251;
public const int SPI_GETBATTERYIDLETIMEOUT = 252; public const int SPI_SETEXTERNALIDLETIMEOUT = 253; public const int SPI_GETEXTERNALIDLETIMEOUT = 254; static void Main(string[] args) { IntPtr handle = SetPowerRequirement("BKL1:", DevicePowerState.D0, 1, null, 0);
int dreisig = 30; // reset the system's idle time every 10 seconds so it doesn't suspend Timer timer = new Timer(IdleReset, null, 0, 10000); SYSTEM_POWER_STATUS_EX status = new SYSTEM_POWER_STATUS_EX();
if (GetSystemPowerStatusEx(status, false) == 1) { if (status.ACLineStatus == (byte)1) //1 == Plugged in { SystemParametersInfo(SPI_GETEXTERNALIDLETIMEOUT, 0, ref dreisig, 0); } else //Plugged out { SystemParametersInfo(SPI_SETBATTERYIDLETIMEOUT, 0, ref dreisig, 0); } }
ReleasePowerRequirement(handle);
} private static void IdleReset(object state) { // no suspend SystemIdleTimerReset(); } } … Patrick & Peter
P.S.: Der gewiefte Bastler stellt 4 Geräte in einen Adventskranz und startet die Anwendung, so dass spätestens am 4. Advent auch alle Kerzen… ich meine natürlich Displays (solange die Batterie ausreicht) freudig leuchten.
|
||||||
|
|