Difference between revisions of "MFC"
(→Sockets) |
(→Debug output) |
||
(12 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
+ | Her har jeg samlet nogle forskellige links omkring applikations udvikling med visual C++ og MFC. Alternativt kan man også se på [[Cpp]] og [[Linux development]] siderne. | ||
+ | |||
=Debug output= | =Debug output= | ||
Ofte har man brug for at få lavet noget debug output men en <code>MessageBox()</code> er måske lidt i overkanten og en log fil er måske for besværlig - så til dette formål har VC++ metoden <code>OutputDebugString()</code> som skriver til output vinduet i den aktuelle debugger - og er der ingen debugger tilknyttet så skrives dataene ikke. | Ofte har man brug for at få lavet noget debug output men en <code>MessageBox()</code> er måske lidt i overkanten og en log fil er måske for besværlig - så til dette formål har VC++ metoden <code>OutputDebugString()</code> som skriver til output vinduet i den aktuelle debugger - og er der ingen debugger tilknyttet så skrives dataene ikke. | ||
+ | |||
+ | Ligeledes kan man bruge TRACE (samt TRACE0,TRACE1,TRACE2 osv) til debug output | ||
Læs mere på [http://www.unixwiz.net/techtips/outputdebugstring.html Understanding Win32 "OutputDebugString"] | Læs mere på [http://www.unixwiz.net/techtips/outputdebugstring.html Understanding Win32 "OutputDebugString"] | ||
Line 38: | Line 42: | ||
Husk at gøre sådan at du kan få fat i din custom dialogs data enten med en <code>getData()</code> function eller at gøre control variablen public. | Husk at gøre sådan at du kan få fat i din custom dialogs data enten med en <code>getData()</code> function eller at gøre control variablen public. | ||
− | = | + | ==Ændre stilen== |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | =Ændre stilen= | ||
Hvis du i programmet har brug for at ændre indstillinger for din dialogbox kan det gøres med ModifyStile (kan f.eks. kaldes fra OnInitDialog() ). (Følgende kode System-menuen til dialogboksen) | Hvis du i programmet har brug for at ændre indstillinger for din dialogbox kan det gøres med ModifyStile (kan f.eks. kaldes fra OnInitDialog() ). (Følgende kode System-menuen til dialogboksen) | ||
this->ModifyStyle(0, WS_SYSMENU); | this->ModifyStyle(0, WS_SYSMENU); | ||
+ | |||
=Controls= | =Controls= | ||
Line 83: | Line 69: | ||
CComboBox *com = (CComboBox*) GetDlgItem(IDC_HANDLE); | CComboBox *com = (CComboBox*) GetDlgItem(IDC_HANDLE); | ||
com->ResetContent(); | com->ResetContent(); | ||
+ | |||
+ | ==Dataudveksling== | ||
+ | |||
+ | I MFC er der to metoder til at udveksle data mellem programkoden og dialogboxens controls. Enten med kald til til nogle control funktioner såsom | ||
+ | GetDlgItemText(IDC_CONTROL, char*); | ||
+ | |||
+ | eller | ||
+ | CButton::GetWindowText(char *); | ||
+ | |||
+ | |||
+ | Den anden måde er at lave en variabel som man bruger til udveksle data mellem control og programkode. Dataene udveksles mellem program og dialogboks når at man kalder funktionen UpdateData(); denne funktion modtager en parameter i form af en boolean som fortæller om data skal fra program til dialogboks eller omvendt; | ||
+ | |||
+ | //Sender data fra variablen til dialogboksen | ||
+ | UpdateData( false ); | ||
+ | |||
+ | //Opdaterer variablen med data fra dialogboksen, dvs. brugerens input | ||
+ | UpdateData( true ) | ||
+ | |||
+ | [[Image:Mfc1.png|MFC data exchange]] | ||
+ | |||
+ | |||
==Radio button og checkbox== | ==Radio button og checkbox== | ||
Line 120: | Line 127: | ||
CString t = "test"; | CString t = "test"; | ||
someLowLevelFunction( (char *) t ); | someLowLevelFunction( (char *) t ); | ||
+ | |||
+ | =Keyboard shortcuts= | ||
+ | Keyboard shortcuts hedder i MFC Accelerators. Sådanne accelerators tilføjes til et MFC projekt i resource-editor'en. | ||
+ | |||
+ | Læg mærke til at som default kan et dialog-baseret projekt ikke håndtere accelerators - men det kan heldigvis [http://www.borngeek.com/code/accelerator-keys.html tilføjes] | ||
=Kommunikation= | =Kommunikation= | ||
Line 125: | Line 137: | ||
Her er nogle korte eksempler på hvordan at man kan arbejde med winsockets ... vær opmærksom på at man bør tjekke returværdien for alle kaldene for at finde ud af hvorvidt at kaldet var problemfrit ! | Her er nogle korte eksempler på hvordan at man kan arbejde med winsockets ... vær opmærksom på at man bør tjekke returværdien for alle kaldene for at finde ud af hvorvidt at kaldet var problemfrit ! | ||
− | Hvis du f.eks. vil have besked, når at der at der kommer data ind på socket'en, kan du lave en klasse som arver fra <code> | + | Hvis du f.eks. vil have besked, når at der at der kommer data ind på socket'en, kan du lave en klasse som arver fra <code>CSocket</code> og implementere <code>OnAccept()</code>, <code>OnConnect()</code>, <code>OnClose()</code>, <code>OnRecieve()</code> eller <code>OnSend()</code> |
− | ===Server=== | + | ===TCP Server=== |
Dette er en simpel echo server: | Dette er en simpel echo server: | ||
char buf[1024]; | char buf[1024]; | ||
Line 133: | Line 145: | ||
int listenPort = 4000; | int listenPort = 4000; | ||
− | + | CSocket socket; | |
− | + | CSocket workSocket; | |
socket.Create(listenPort); | socket.Create(listenPort); | ||
socket.Listen(); | socket.Listen(); | ||
Line 146: | Line 158: | ||
} | } | ||
− | ===Client=== | + | ===TCP Client=== |
Og en tilhørende klient | Og en tilhørende klient | ||
CString message = "besked"; | CString message = "besked"; | ||
int status,len; | int status,len; | ||
− | + | CSocket socket; | |
socket.Create(); | socket.Create(); | ||
socket.Connect("localhost", 4000); | socket.Connect("localhost", 4000); | ||
Line 157: | Line 169: | ||
status = socket.Send(LPCTSTR(message), len); //status indeholder antallet af sendte bytes eller SOCKET_ERROR | status = socket.Send(LPCTSTR(message), len); //status indeholder antallet af sendte bytes eller SOCKET_ERROR | ||
− | = | + | ===UDP sender=== |
+ | CString message = "Besked"; | ||
+ | CSocket sender; | ||
+ | sender.create(2048, SOCK_DGRAM); //UDP port 2048 | ||
+ | |||
+ | sender.SendTo(message, message.GetLength(), 2048, "10.1.2.3"); | ||
+ | sender.Close(); | ||
+ | |||
+ | ===UDP reciever=== | ||
+ | char buf[100]; | ||
+ | int size; | ||
+ | |||
+ | CSocket reciever; | ||
+ | reciever.Create(2048, SOCK_DGRAM); | ||
+ | size = reciever.Recieve(buf,100); | ||
+ | reciever.Close(); | ||
+ | |||
+ | ==Serielle porte== | ||
*[http://www.codeproject.com/managedcpp/howtocomport.asp et eksempel på en CSerialPort klasse] | *[http://www.codeproject.com/managedcpp/howtocomport.asp et eksempel på en CSerialPort klasse] | ||
*[http://www.samspublishing.com/library/content.asp?b=Visual_C_PlusPlus&seqNum=86&rl=1 Serial Communications] | *[http://www.samspublishing.com/library/content.asp?b=Visual_C_PlusPlus&seqNum=86&rl=1 Serial Communications] | ||
*[http://www.control.com/1026217270/index_html serial port in visual c++] | *[http://www.control.com/1026217270/index_html serial port in visual c++] | ||
*[http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2503/ Codeguru:CSerial] | *[http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2503/ Codeguru:CSerial] | ||
− | |||
=MFC tråde= | =MFC tråde= | ||
Line 270: | Line 298: | ||
Dette gøres ved at vælge properties for filen der fejler i solution exploreren, finde precompiled headers sektionen og sætte "Create/use precompiled headers" = "Not using precompiled headers" | Dette gøres ved at vælge properties for filen der fejler i solution exploreren, finde precompiled headers sektionen og sætte "Create/use precompiled headers" = "Not using precompiled headers" | ||
− | = | + | =System interaktion= |
==Registrerings-databasen== | ==Registrerings-databasen== | ||
Line 292: | Line 320: | ||
I winXP/2003 er der tilføjet et ny API til [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp credential håndthering] - [http://www.pluralsight.com/wiki/default.aspx/Keith.GuideBook/HowToPromptForAPassword.html how to prompt for a password] | I winXP/2003 er der tilføjet et ny API til [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp credential håndthering] - [http://www.pluralsight.com/wiki/default.aspx/Keith.GuideBook/HowToPromptForAPassword.html how to prompt for a password] | ||
− | = | + | =Database & SQL= |
MFC har nogle indbyggede klasser til at kommunikere med databaser via ODBC interfacet : <code>CDatabase</code> & <code>CRecordset</code> | MFC har nogle indbyggede klasser til at kommunikere med databaser via ODBC interfacet : <code>CDatabase</code> & <code>CRecordset</code> |
Latest revision as of 14:17, 1 March 2007
Her har jeg samlet nogle forskellige links omkring applikations udvikling med visual C++ og MFC. Alternativt kan man også se på Cpp og Linux development siderne.
Contents
Debug output
Ofte har man brug for at få lavet noget debug output men en MessageBox()
er måske lidt i overkanten og en log fil er måske for besværlig - så til dette formål har VC++ metoden OutputDebugString()
som skriver til output vinduet i den aktuelle debugger - og er der ingen debugger tilknyttet så skrives dataene ikke.
Ligeledes kan man bruge TRACE (samt TRACE0,TRACE1,TRACE2 osv) til debug output
Læs mere på Understanding Win32 "OutputDebugString"
Standart Dialogbokse
En almindelig besked laves med MessageBox("besked" [,"titel"] [,options])
hvor at options feltet bruges til at fastlægge hvilke knapper der skal være og om der skal være et ikon.
Lav en ja/nej boks:
int res = MessageBox("Are you sure", "Question", MB_YESNO); if (res == MB_YES) ...
Af andre standart dialog bokse kan der nævnes:
CFileDialog
CFontDialog
CColorDialog
CPageSetupDialog
CPrintDialog
CFindReplaceDialog
Eksempel:
CString result; CFileDialog file(true); if (file.DoModal() == IDOK) { result = file.GetFileName(); }
Custom Dialogbokse
En ny custom dialogbox laves som en alm. boks og når at du skal kalde den, gøres det som med de indbyggede dialogbokse. Læg mærke til at det er ikke nødvendigt at kalde UpdateData()
ved afslutning , det sker nemlig automatisk ved kald af OnOK()
.
Husk at gøre sådan at du kan få fat i din custom dialogs data enten med en getData()
function eller at gøre control variablen public.
Ændre stilen
Hvis du i programmet har brug for at ændre indstillinger for din dialogbox kan det gøres med ModifyStile (kan f.eks. kaldes fra OnInitDialog() ). (Følgende kode System-menuen til dialogboksen)
this->ModifyStyle(0, WS_SYSMENU);
Controls
Diverse tips til ændring af controls findes på
Ændre control udfra ID
Når at man i VC++ laver programmer med dialogeditoren, kan man i sin kode bruge GetDlgItem()
til at hente en reference til et givent control. Læg mærke til at funktionene returnerer en pointer til et CWnd object.
Herefter kan man f.eks. skjule et object med:
GetDlgItem(IDC_HANDLE)->ShowWindow( false );
eller man kan disable det med
GetDlgItem(IDC_HANDLE)->EnableWindow( false );
Hvis at man skal bruge nogle specifikke funktioner for f.eks. en comboboks kan man lave en type cast på pointeren (her fjernes alle elementer fra boksen):
CComboBox *com = (CComboBox*) GetDlgItem(IDC_HANDLE); com->ResetContent();
Dataudveksling
I MFC er der to metoder til at udveksle data mellem programkoden og dialogboxens controls. Enten med kald til til nogle control funktioner såsom
GetDlgItemText(IDC_CONTROL, char*);
eller
CButton::GetWindowText(char *);
Den anden måde er at lave en variabel som man bruger til udveksle data mellem control og programkode. Dataene udveksles mellem program og dialogboks når at man kalder funktionen UpdateData(); denne funktion modtager en parameter i form af en boolean som fortæller om data skal fra program til dialogboks eller omvendt;
//Sender data fra variablen til dialogboksen UpdateData( false ); //Opdaterer variablen med data fra dialogboksen, dvs. brugerens input UpdateData( true )
Radio button og checkbox
Status på radiobutton og checkbox kan ændres på to måder:
CButton *button = (CButton*) GetDlgItem(IDC_HANDLE); button->SetCheck(BST_CHECKED);
eller
CheckDlgButton(IDC_HANDLE, BST_CHECKED);
Waiting Cursor
Hvis du har brug for at vise timeglasset så længe at en bestemt function kører kan du erklære en variabel af typen CWaitCursor
i starten af din funktion.
Strenge
MFC har bygget al håndteringen af tekststrenge ind i classen CString
, hvilket er noget nemmere at arbejde med end char arrays; f.eks. er det helt legalt at bruge ==
til at se om to strenge er identiske. Ligeledes kan man bruge =
til tildeling og +=
til at tilføje. Strengens længe kan aflæses med CString::GetLength()
Formaterede strenge
Hvis du har brug for at formatere en streng, kan du bruge CString::Format()
som virker på nogen lunde samme måde som standart C funktionen sprintf()
.
int time,minut,sekund; CString tid; ... tid.Format("%d:%d:%d", time, minut, sekund);
Konvertering
CString kan konveretes til andre typer direkte vha.:
CString t = "test"; someLowLevelFunction( (LPCTSTR) t );
eller
CString t = "test"; someLowLevelFunction( (char *) t );
Keyboard shortcuts
Keyboard shortcuts hedder i MFC Accelerators. Sådanne accelerators tilføjes til et MFC projekt i resource-editor'en.
Læg mærke til at som default kan et dialog-baseret projekt ikke håndtere accelerators - men det kan heldigvis tilføjes
Kommunikation
Sockets
Her er nogle korte eksempler på hvordan at man kan arbejde med winsockets ... vær opmærksom på at man bør tjekke returværdien for alle kaldene for at finde ud af hvorvidt at kaldet var problemfrit !
Hvis du f.eks. vil have besked, når at der at der kommer data ind på socket'en, kan du lave en klasse som arver fra CSocket
og implementere OnAccept()
, OnConnect()
, OnClose()
, OnRecieve()
eller OnSend()
TCP Server
Dette er en simpel echo server:
char buf[1024]; int status; int listenPort = 4000; CSocket socket; CSocket workSocket; socket.Create(listenPort); socket.Listen(); while (1) { socket.Accept(workSocket); status = workSocket.Recieve(buf, 1023); //status indeholder antallet af læste bytes, eller SOCKET_ERROR if (status != SOCKET_ERROR) { workSocket.Send(buf,status); } workSocket.Close(); }
TCP Client
Og en tilhørende klient
CString message = "besked"; int status,len; CSocket socket; socket.Create(); socket.Connect("localhost", 4000); len = message.GetLength(); status = socket.Send(LPCTSTR(message), len); //status indeholder antallet af sendte bytes eller SOCKET_ERROR
UDP sender
CString message = "Besked"; CSocket sender; sender.create(2048, SOCK_DGRAM); //UDP port 2048 sender.SendTo(message, message.GetLength(), 2048, "10.1.2.3"); sender.Close();
UDP reciever
char buf[100]; int size; CSocket reciever; reciever.Create(2048, SOCK_DGRAM); size = reciever.Recieve(buf,100); reciever.Close();
Serielle porte
- et eksempel på en CSerialPort klasse
- Serial Communications
- serial port in visual c++
- Codeguru:CSerial
MFC tråde
I MFC skelnes der mellem GUI threads og workerthreads, hvor at en workerthread er en baggrundstråds som udfører et bestemt stykke arbejde uden direkte kontakt med brugergrænsefladen.
En workerthread oprettes og startes med et kald til AfxBeginThread()
og den første parameter til denne er en pointer til den funktion som skal udføres i tråden - denne funktion skal skal have signaturen UINT mythreadfunction(LPVOID)
(navnet må selvfølgelig gerne ændres), læg mærke til at det skal være en alm. stand-alone funktion eller være static
medlem af en klasse! Læg mærke til parameteren til tråd-funktionen er en void pointer(LPVOID) , så denne kan bruges til at overføre en pointer til hvad som helst.
En workerthread må ikke manipulere et GUI-object direkte - hvis at gui'en skal opdateres med data fra tråden, skal guien have en besked om at der skal opdateres. Dette kan gøres med CWnd::PostMessage(), hvor at man bruger en custom message ID - husk at implementere message-handlers for denne messageID.
Synkronisering
Når at flere tråde skal arbejde på de samme data, er det vigtigt at trådene er synkroniserede - til dette har MFC en række klasser, der alle arver fra CSyncObject
. Læs evt. Multithreading: How to Use the Synchronization Classes
Eksempel
Her er et eksempel på en implementation af en trådklasse.
//Tråden kan startes med: //ThreadTest *t = new ThreadTest(AfxGetMainWnd()); //t->start(); class ThreadTest { private: CWnd * mainwindow; int count; protected: virtual void run(); public: ThreadTest(CWnd *wnd) {mainwindow = wnd; count=0; } void start(); friend UINT threadWrapper(LPVOID thread); }; UINT threadWrapper(LPVOID thread) { ThreadTest *t = (ThreadTest*) thread; t->run(); return 0; } void ThreadTest::start() { AfxBeginThread(threadWrapper, (LPVOID) this); } void ThreadTest::run() { while (1) { CString *str = new CString(); str->Format("Davs %d", count++); Sleep(300); mainwindow->PostMessage(UWM_MYMESSAGE, 0, (LPARAM)str); } }
I dialog-header filen kan man have følgende:
#define UWM_MYMESSAGE 0x8100 afx_msg LRESULT OnShowString(WPARAM,LPARAM);
Og i koden
BEGIN_MESSAGE_MAP(CDMEStatusViewerDlg, CDialog) // more message handlers ON_MESSAGE(UWM_MYMESSAGE, OnShowString) END_MESSAGE_MAP() LRESULT CMyDialog::OnShowString(WPARAM wParam, LPARAM lParam) { CString *s = (CString*) lParam; GetDlgItem(IDC_EDITFIELD)->SetWindowText(*s); delete s; return 0; }
Links
- http://www.murrayc.com/learning/windows/multithreading.shtml
- http://www.apostate.com/programming/mfc-threads/
- http://www.pluralsight.com/articlecontent/cpprep0397.htm
- http://www.codeproject.com/threads/threadex.asp
- Threads in MFC Part I: Worker Threads
- Threads in MFC Part II: Synchronization Objects
- Threads in MFC Part III: Exceptions, Suspense, Murder, and Safety
- CWorkerThread implementation
VC++ configuration
Linker Errors, CString, ATL, MFC, and YOU!
MFC Includes
Hvis du starter et nyt win32 projekt og har brug for MFC, så skal du includere afxwin.h
ogt sørge for at dit project er sat til at må bruge MFC dynamic eller static link libraries.
Extra include dirs
Ekstra include directories angives i projekt properties vinduet -> C/C++ -> General -> Additional include directories (står sikkert øverst)
Extra link libraries
Extra libraries angives i project properties -> Linker -> Input -> Additional Dependencies (øverst). Dette skal sikkert kombineres med project properties -> Linker -> General -> Additional Library Directories (nederst).
Fatal error C1010
Hvis du importerer noget kode (eller skriver selv) som er standart c++ - kan du risikere at du får fejlen "fatal error C1010: unexpected end of file while looking for precompiled header directive".
Dette er fordi at VC++ tror at den skal bruge precompiled headers for at compilere filen, men højst sandynligt skal den ikke dette så derfor skal det disables.
Dette gøres ved at vælge properties for filen der fejler i solution exploreren, finde precompiled headers sektionen og sætte "Create/use precompiled headers" = "Not using precompiled headers"
System interaktion
Registrerings-databasen
Kommunikation med registreringsdatabasen foregår i MFC via klassen CRegKey
.
Her er et eksempel på brugen af denne klasse : http://www.netalive.org/codersguild/posts/2088.shtml
WMI
System info kan trækkes via WMI interfacet som er baseret på COM/DCOM.
WMI interfacet kan afprøves med værktøjet wbemtest.exe
(DCOM indstillinger kan justeres med dcomcnfg.exe
)
Active Directory
Data fra et A/D kan trækkes / manipuleres via Active Directory System Interface
Credentials
I winXP/2003 er der tilføjet et ny API til credential håndthering - how to prompt for a password
Database & SQL
MFC har nogle indbyggede klasser til at kommunikere med databaser via ODBC interfacet : CDatabase
& CRecordset
PostgreSQL
libpq
PostgreSQL's standard C interface hedder libpq.
En almindelig libpq kan bygges ifølge instruktionerne.
Hvis at du skal bygge libpq med SSL/TLS support er det nemmeste at hente en openssl binary og installere denne - Dernæst skal du ændre %postgresqldir%\src\interfaces\libpq\win32.mak og tilføje
/I "pathtossl\include"
til CPP_PROJ sektionen, og til LINK32_FLAGS tilføjes der
/LIBPATH:"pathtossl\lib\vc\static"
libpqxx
Vil du derimod hellere have et C++ interface kan man bruge libpqxx.