Programmieren in C und C++

Aus GoPalWiki
Wechseln zu: Navigation, Suche

Ziel dieser Seite ist, unabhängig der gewählten Programmierumgebung die zur Verfügung stehende API verstehen und kleine Programme entwickeln, die auf dem GoPal PNAs lauffähig sind.

Voraussetzungen

Rund um das 'Hallo Welt'

Nach dem Motto Keep it simple! (Halte es einfach!) ist der erste Schritt eines Entwicklers häufig ein erstes eigenes Programm zu schreiben, um die Funktionsfähigkeit der Toolchain, der gesamten Entwicklungsumgebung, zu prüfen. Also muss ein dann selbst geschriebenes Programm her, das ein Lebenszeichen von sich gibt. Das Lebenszeichen ist dann häufig die Ausgabe einer Zeichenkette Hallo Welt! auf dem Bildschirm des Target-Device (Zielgerät). In unserem Fall also soll solch eine Zeichenkette auf dem Bildschirm des GoPal-Gerätes erscheinen.

Ein solches Basis-Programm dann später um sinnvolle Funktionen zu erweitern, stellen sich Entwickler immer gaaaaaanz trivial vor. Ist es ja auch, wenn man weiß wie.

Aber Schritt für Schritt... Bleiben wir vorerst beim Hallo Welt!.

Das geschenkte 'Hallo Welt' von Microsoft

Je nach verwendeter Entwicklungsumgebung kommt bei dem Hello World-Programm, das die Entwicklungsumgebung erzeugt, anderer Source Code heraus und andere Standardbibliotheken werden verwendet. Daher gehen wir hier nochmals auf die einzelnen Entwicklungsumgebungen ein.

Microsoft eMbedded Visual C++

Im Hilfe-Menu unter About... steht etwas von Version 4.00.1610.0. Ich beschreibe hier die englische Version. Ich benutze einfach das Standard SDK (Pocket PC2003), das bei der Installation dabei war. Unter der Verwendung eines anderen nachinstallierten SDKs sind ähnliche Schritte durchzuführen, jedoch mögen die Menu/Punkte dann natürlich ein wenig anders lauten.

  1. Im File-Menu unter New wählt man da Tab Projects aus.
  2. Wir erstellen eine WCE Pocket PC 2003 Application mit Project name HelloWorld. Dabei dürfte die Standardeinstellung unter Location zu C:\Program Files\Microsoft eMbedded C++ 4.0\Common\EVC\MyProjects\HelloWorld erweitert werden.
  3. Ein neuer Arbeitsbereich wird mittels ausgewähltem Create new workspace angelegt und als CPU wählen wir mindestens, dass Win32 (WCE ARMV4) unterstützt wird.

Streng genommen handelt es sich hier nicht um eine ausgewählte CPU, sondern um ein Instruction Set. Die CPU muss dieses Instruction Set beherrschen, damit das spätere Kompilat darauf laufen kann.

Abschließend hier OK auswählen.

Jetzt nochmals A typical Hello World application auswählen und noch mit Finish bestätigen. Und nochmal OK...

Im FileView den Baum expandieren und unter Source Files die Datei HelloWorld.cpp öffnen.

Und schon sehen wir in der Datei, dass die Aygshell verwendet werden soll.

Wurde als CPU auch der Emulator ausgewählt, so kann man durchaus mal testen, dass im Emulator das Programm funktioniert. Oben nicht die Auswahl des Emulators vergessen!

Man kann es auch, so wie es ist, für das ARMV4 Instruction Set kompilieren und wenn man einen PDA oder Windows Mobile Phone besitzt, die daraus erhaltene HelloWorld.exe mal laufen lassen. Im Visual Studio oben nicht die Auswahl von Win32 (WCE ARMV4) Release vergessen. Übertragen des HelloWorld.exe mehr oder weniger umständlich per direktem Deploy auf das Device, per Active Sync oder sonstwie SD card und dann aufs Target Device.

Probiert man dieses HelloWorld.exe nach dem Kompilieren auf einem unmodifizierten GoPal-Gerät aus, kommt nur eine Fehlermeldung, dass irgendwelche Dateien oder Libraries nicht gefunden werden konnten. Und wir stecken mitten im Problem der reduzierten Core Runtime License im Vergleich zur Professional Runtime License oder des reduzierten API sets. Irreführend ist hier die Meldung, dass das HelloWorld selbst nicht gefunden werden kann. Wer sich mal wieder die Fehlermeldung ausgedacht hat? :-)

Und da wir nicht mit der Aygshell rumschummeln wollen, kommt jetzt die Frage auf, wie wir doch zu einem HelloWorld.exe kommen, das läuft.

Was muss ich also ändern, damit es auf dem GoPal-Gerät auch ohne Microsoft Aygshell.dll und ohne DLLs von Dritten läuft?

So und los geht's...

Angepasstes HelloWorld.exe, so dass Aygshell nicht verwendet wird auf E3210 M10.

Aus dem HelloWorld.cpp sind die folgenden Zeilen auszukommentieren oder zu löschen:

// #include <aygshell.h>
// HWND				g_hwndCB;					// The command bar handle
// static SHACTIVATEINFO s_sai;
// LRESULT CALLBACK	About			(HWND, UINT, WPARAM, LPARAM);
// HWND				CreateRpCommandBar(HWND);
//When the main window is created using CW_USEDEFAULT the height of the menubar (if one
// is created is not taken into account). So we resize the window after creating it
// if a menubar is present
/*	if (g_hwndCB)
   {
       RECT rc;
       RECT rcMenuBar;
 
       GetWindowRect(hWnd, &rc);
       GetWindowRect(g_hwndCB, &rcMenuBar);
       rc.bottom -= (rcMenuBar.bottom - rcMenuBar.top);
 
       MoveWindow(hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, FALSE);
   }
*/
/*    case IDM_HELP_ABOUT:
          DialogBox(g_hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
          break;
*/
/*         g_hwndCB = CreateRpCommandBar(hWnd);
           // Initialize the shell activate info structure
           memset (&s_sai, 0, sizeof (s_sai));
           s_sai.cbSize = sizeof (s_sai);
*/
//			CommandBar_Destroy(g_hwndCB);
//			SHHandleWMActivate(hWnd, wParam, lParam, &s_sai, FALSE);
//			SHHandleWMSettingChange(hWnd, wParam, lParam, &s_sai);
/* HWND CreateRpCommandBar(HWND hwnd)
{
       SHMENUBARINFO mbi;
 
       memset(&mbi, 0, sizeof(SHMENUBARINFO));
       mbi.cbSize     = sizeof(SHMENUBARINFO);
       mbi.hwndParent = hwnd;
       mbi.nToolBarId = IDM_MENU;
       mbi.hInstRes   = g_hInst;
       mbi.nBmpId     = 0;
       mbi.cBmpImages = 0;
 
       if (!SHCreateMenuBar(&mbi)) 
               return NULL;
 
       return mbi.hwndMB;
}
// Mesage handler for the About box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
       SHINITDLGINFO shidi;
 
       switch (message)
       {
                case WM_INITDIALOG:
                       // Create a Done button and size it.  
                       shidi.dwMask = SHIDIM_FLAGS;
                       shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
                       shidi.hDlg = hDlg;
                       SHInitDialog(&shidi);
                       return TRUE; 
 
                case WM_COMMAND:
                       if (LOWORD(wParam) == IDOK)
                       {
                               EndDialog(hDlg, LOWORD(wParam));
                               return TRUE;
                       }
                       break;
       }
   return FALSE;
}
*/

Um die Möglichkeit zu schaffen, das Programm auch ohne OK-Button oder schließen des Fensters zu verlassen, fügen wir noch eine Zeile ein. Unser Hello World läuft nämlich jetzt im Vollbildmodus ohne Fensterdekoration.

Vor der Zeile

case WM_DESTROY:

fügen wir noch folgendes ein

case WM_LBUTTONDOWN:

Das sieht also dann in etwa so aus:

case WM_LBUTTONDOWN:
case WM_DESTROY:
// CommandBar_Destroy(g_hwndCB);
   PostQuitMessage(0);
   break;

Das CommandBar_Destroy hatten wir vorher gerade frisch auskommentiert.

Der WM_LBUTTONDOWN sorgt dafür, dass jetzt beim Berühren des Touchscreen an beliebiger Position das Hello World beendet wird.

Und siehe da: Die Aygshell wird nicht benötigt. Die Befehle, Konstanten, etc., die in unserem Beispiel mit SH... beginnen, sind diejenigen, die in der Aygshell enthalten sind. Daher wurden solche Absätze und Zeilen, die solche Aygshell Anteile besaßen, einfach wie oben beschrieben entfernt.

Das so erhaltene Programm kann als Basis für die weiteren Eigenentwicklungen verwendet werden.

Viel Spaß beim Ausprobieren...

Obwohl hier Microsoft verwendet wurde: Entwicklungsumgebung etc. war kostenlos! Es läuft und man kann eigentlich alles daraus bauen, wenn man noch rausbekommt wie... GoPal-gerätespezifische Fragen bitte an Fragen zur Programmierung

So böse ist Microsoft zumindest hier also gar nicht: Hallo non-Microsoftler! Willkommen auf der dunklen Seite... Come to the dark side... We have cookies! :-)

Microsoft Visual Studio 2005 mit Smart Devices

Visual Studio 2005 bietet natürlich eine integrierte Entwicklungsumgebung (IDE), die deutlich moderner und komfortabler als das eMbedded Visual C++ ist. Als echter vi, sed, awk und ed User ist das zwar Nebensache, aber wir wollen ja auch den nicht Gurus oder gerade diesen Gurus auch ein wenig Komfort zuteil werden lassen.

Gehen wir von Standard Hello World des Visual Studio 2005 aus und versuchen es auf den abgespeckten GoPal-Geräten zum Laufen zu überreden, so sind noch ein paar andere Hürden zu nehmen. Auch wenn manches ähnlich aussieht...

Also nochmal das Ganze:

'Hallo Welt' von Anfang an

Von einem leeren Zustand etwas neu zu entwickeln wird auch als Entwicklung from scratch bezeichnet.

In C++ mit Hilfe der WTL

Der folgende Ansatz funktioniert in allen Entwicklungsumgebungen gleichermaßen, da man ja überall von einem leeren Editor ausgehen kann. Die Menü-Bezeichnungen kommen von der englischen Version eines Visual Studio 2005 und können in anderen Entwicklungsumgebungen variieren.

In diesem Abschnitt wird die WTL in der Version 8.0 eingesetzt.

Die Windows Template Library (WTL) ist eine C++ library für die Entwicklung von Windows-Applikationen und Benutzerschnittstellen-Komponenten. Diese Bibliothek erweitert die Microsoft eigene ATL (Active Template Library) und stellt eine Menge von Klassen zur Verfügung wie Kontroll-Elemente, Dialoge, Fensterklassen, GDI Objekte, und sogar noch mehr.

Die WTL ist Open Source Software und muss natürlich erst mal installiert werden.

Voraussetzung ist auch hier ein installiertes Standard SDK für Windows CE 5.0.

File -> New -> Project -> Visual C++ -> Smart Device -> Win32 Smart Device Project

Name eingeben (z.B. HelloWelt), Location auswählen (z.B. c:\proj ), danach OK klicken.

Es erscheint nun der Win32 Smart Device Project Wizard bei dem folgende Einstellungen vorgenommen werden müssen:

  • Platforms -> Pocket PC 2003 entfernen, STANDARDSDK_500 hinzufügen
  • Application Settings -> Application type: Windows application auswählen
  • Application Settings -> Additional options: Empty project aktivieren

Danach Finish klicken.

Wir haben nun ein geeignetes leeres Projekt.

Zum sinnvollen Arbeiten brauchen wir allerdings eine C++ Datei:

Im Solution-Explorer, rechter Mausclick auf Source-Files -> Add -> New Item -> Code -> C++ File (.cpp)

Name: HalloWelt.cpp eintragen und Add klicken.

Include und library Pfade für WTL 8.0 entweder global oder für das Project setzen (beides z.B. c:\wtl80\include) und folgenden Code in HalloWelt.cpp einfügen.

#include <atlbase.h>
#include <atlapp.h> 
 
extern CAppModule _Module; 
 
#include <atlgdi.h> 
#include <atlmisc.h> 
 
CAppModule _Module; 
 
class CMainWindow : public CWindowImpl<CMainWindow , CWindow , CWinTraits<WS_VISIBLE> > 
{ 
       // Message map is used to map Windows messages to handlers. 
       BEGIN_MSG_MAP(CMainWindow) 
               MESSAGE_HANDLER(WM_PAINT, OnPaint)
               MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
               MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 
       END_MSG_MAP() 
 
       LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
       { 
               CPaintDC dc(m_hWnd); 
               CRect rect; 
               GetClientRect(rect); 
               dc.DrawText(_T("Hello, Wtl!"), -1, rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER); 
               return 0; 
       } 
 
       LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
       { 
               PostMessage(WM_CLOSE); 
               return 0; 
       } 
 
       LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
       { 
               PostQuitMessage(0); 
               return 0; 
       } 
}; 
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int) { 
       // Init module. 
       _Module.Init(0, hInstance, 0); 
       // Create main window. 
       CMainWindow wnd; 
       wnd.Create(NULL, CWindow::rcDefault, _T("Hello, Welt!")); 
       wnd.ShowWindow(SW_SHOW); 
       // Run message loop. 
       CMessageLoop loop; 
       int res = loop.Run(); 
       // Terminate. 
       _Module.Term(); 
       return res; 
}

Und damit geht es mit der WTL...