呐,上回书说到写代码过程中遇到不少问题,在这里就一点一点慢慢道来吧。
首先,定义的结构体、全局变量和函数如下:
// Flight.c : 定义应用程序的入口点。
//
#include "stdafx.h"
//订单
typedef struct OrderForm{
TCHAR IdNum[32]; //订单用户身份证号
int Order_Number; //订单号
int Tickets_Num; //订票数量
TCHAR Flight_Number[16]; //航班号
TCHAR Departure[16]; //出发地
TCHAR Destination[16]; //目的地
TCHAR Date[16]; //出发日期
TCHAR TakeOff_Time[16]; //起飞时间
TCHAR Landing_Time[16]; //降落时间
struct OrderForm *Next; //所有订单链表next
struct OrderForm *psgNext; //用户订单链表next
}OrderForm;
//乘客订单链表
typedef struct PsgOrderLink{
int OrderNum; //订单数目
OrderForm *head; //头结点
OrderForm *tail; //尾结点
}PsgOrderLink;
//所有订单列表
typedef struct AllOrderLink{
int AllOrderNum; //所有订单数目
OrderForm *head; //头结点
OrderForm *tail; //尾结点
}AllOrderLink;
//乘客
typedef struct Passenger{
TCHAR Name[16]; //姓名
TCHAR IdNum[32]; //身份证号码
TCHAR PassWord[32]; //密码
int TicketNum; //订单数目
PsgOrderLink OrderLink; //用户所有订单
struct Passenger *Next;
}Passenger;
//乘客链表
typedef struct PsgLinkList{
int PsgNum; //账户数量
Passenger *head; //头结点
Passenger *tail; //尾结点
}PsgLinkList;
//航班
typedef struct Flight{
double Fare; //票价
int Seat_Number; //座位数
int Vacant_Seat; //空余座位数
TCHAR Discount[16]; //折扣
TCHAR Flight_Number[16]; //航班号
TCHAR Departure[16]; //出发地
TCHAR Destination[16]; //目的地
TCHAR Date[16]; //出发日期
TCHAR TakeOff_Time[16]; //起飞时间
TCHAR Landing_Time[16]; //降落时间
struct Flight *Next;
}Flight;
//航班链表
typedef struct FlightLinkList{
Flight *head; //头结点
Flight *tail; //尾结点
int Flight_Number; //航班数目
}FilghtLinkList;
// 全局变量:
HICON hIcon;
HINSTANCE hInst; //当前实例
static TCHAR szBuffer[256]; //缓冲区
static PsgLinkList psglink; //所有账户_链表
static Passenger *passenger; //登陆账户信息
static AllOrderLink allorder; //所有订单_链表
static FilghtLinkList flightlink; //所有航班_链表
//函数声明
BOOL AccountLogIn(HWND); //账户登陆
BOOL AccountRegister(HWND); //注册账户
BOOL ReadFlightData(HWND); //读入航班信息
BOOL ReadAccountData(HWND); //读入账户资料
BOOL ReadAccountOrder(HWND,Passenger*); //读入所有订单、账户订单
BOOL SearchFlight(HWND); //查询航班
BOOL BookTickets(HWND); //订票
BOOL _Book_Tickets(HWND,Flight*,int); //订票
BOOL Recommend(HWND,Flight*,int); //航班建议
BOOL ReturnTickets(HWND); //退票
BOOL EntryFlight(HWND); //录入航班
BOOL ModifyFlight(HWND); //修改航班信息
BOOL PrintFlight(HWND, Flight*); //输出航班信息
BOOL WriteFlightData(HWND); //保存航班信息
BOOL WriteAccountData(HWND); //保存账户资料
BOOL WriteOrderData(HWND); //保存订单信息
BOOL CALLBACK LogInDlgProc(HWND, UINT, WPARAM, LPARAM); //登陆窗口窗口过程
BOOL CALLBACK MainDlgProc(HWND, UINT, WPARAM, LPARAM); //主界面窗口过程
BOOL CALLBACK NameDlgProc(HWND, UINT, WPARAM, LPARAM); //获取新注册用户姓名窗口过程
BOOL CALLBACK FlightNumDlgProc(HWND, UINT, WPARAM, LPARAM); //获取用户输入机票数量窗口过程
BOOL CALLBACK EntryFlightProc(HWND, UINT, WPARAM, LPARAM); //录入航班窗口过程
BOOL CALLBACK ModifyFlightProc(HWND, UINT, WPARAM, LPARAM); //修改航班信息窗口过程
主函数如下:
//主函数
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int iCmdShow)
{
hInst = hInstance;
InitCommonControls();
hIcon=LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1));
return DialogBox(hInst, MAKEINTRESOURCE(IDD_LOGINDLG), NULL, (DLGPROC)LogInDlgProc);
}//WinMain
登陆界面窗口过程:
//登陆窗口_窗口过程
BOOL CALLBACK LogInDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
//WM_INITDIALOG是当其对话框和子控件全部创建完毕,将要显示内容的时候发送的消息
//因此可以在WM_INITDIALOG消息响应函数中添加对编辑框控件的初始化和修改
case WM_INITDIALOG:
{
if (hIcon)
SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
ReadAccountData(hwndDlg); //登陆对话框初始化时读入账户资料
ReadFlightData(hwndDlg); //登陆对话框初始化时读入航班信息
}//WM_INITDIALOG
return TRUE;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}//WM_CLOSE
return TRUE;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_LOGIN:
AccountLogIn(hwndDlg); //登陆
break;
case IDC_REGISTER:
AccountRegister(hwndDlg); //注册
break;
}//switch
}//WM_COMMAND
return TRUE;
}//switch
return FALSE;
}//LogInDlgProc()
我在对话框初始化时调用了下述函数来 加载程序标题栏的图标:
if (hIcon)
SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
接下来就该读取文件了。
那么问题就来了,挖掘机…额,不对,应该怎么读入账户资料和航班信息?
我把 账户资料和航班信息储存在了.txt文件里,每一行是结构体的一个成员,一个一个读入。
两个读入函数代码如下:
//读入账户信息
BOOL ReadAccountData(HWND hwndDlg){
FILE *fp;
passenger = (Passenger *)malloc(sizeof(Passenger)); //为登录账户分配内存
if (!passenger){
MessageBox(hwndDlg, TEXT("内存申请错误!"), TEXT("错误"), MB_OK | MB_ICONERROR);
EndDialog(hwndDlg, 0);
return FALSE;
}//if
psglink.head = NULL;
psglink.tail = NULL;
psglink.PsgNum = 0;
if ((fp = fopen(".\\AccountData.txt", "r+")) == NULL){ //打开文件
MessageBox(hwndDlg, TEXT("账户文件读入错误!"), TEXT("错误"), MB_OK | MB_ICONERROR);
EndDialog(hwndDlg, 0);
}//if
while (!feof(fp)){
Passenger *p = (Passenger *)malloc(sizeof(Passenger));
if (!p){
MessageBox(hwndDlg, TEXT("内存申请错误!"), TEXT("错误"), MB_OK | MB_ICONERROR);
EndDialog(hwndDlg, 0);
return FALSE;
}//if
//读入账户姓名、身份证号、密码
if (fscanf(fp, "%s%s%s", p->Name, p->IdNum, p->PassWord) == EOF){
free(p); break;
}
p->Next = NULL;
if (psglink.head == NULL) //读入第一个账户信息时,头、尾结点均指向p
psglink.head = p;
else
psglink.tail->Next = p; //否则,尾结点Next指向p
psglink.tail = p; //尾结点指向p
psglink.PsgNum++; //已注册账户个数
}//while
fclose(fp); //关闭文件
return TRUE;
}//ReadAccountData(HWND)
//读入航班信息
BOOL ReadFlightData(HWND hwndDlg){
int flag = 0;
FILE *fp;
flightlink.Flight_Number = 0;
flightlink.head = NULL;
flightlink.tail = NULL;
if ((fp = fopen(".\\FlightData.txt", "r")) == NULL){ //打开文件
MessageBox(hwndDlg, TEXT("账户文件读入错误!"), TEXT("错误"), MB_OK | MB_ICONERROR);
EndDialog(hwndDlg, 0);
}//if
while (!feof(fp)){
Flight *p = (Flight *)malloc(sizeof(Flight));
if (!p){
MessageBox(hwndDlg, TEXT("内存申请错误!"), TEXT("错误"), MB_OK | MB_ICONERROR);
EndDialog(hwndDlg, 0);
return FALSE;
}//if
//读入航班信息
if (fscanf(fp, "%s%lf%s%d%d%s%s%s%s%s",
p->Flight_Number,
&p->Fare,
p->Discount,
&p->Seat_Number,
&p->Vacant_Seat,
p->Departure,
p->Destination,
p->Date,
p->TakeOff_Time,
p->Landing_Time) == EOF)
{
free(p);
break;
}
p->Next = NULL;
if (flightlink.head == NULL) //添加至链表
flightlink.head = p;
else
flightlink.tail->Next = p;
flightlink.tail = p;
flightlink.Flight_Number++;
}//while
fclose(fp); //关闭文件
return TRUE;
}//ReadFlightData()
编译运行的时候会报错,报错信息是大体是说fopen()函数不安全,根据提示信息,解决办法是在文件头部做如下定义:
#define _CRT_SECURE_NO_WARNINGS
或者是在项目->属性->c/c++->预处理器->预处理器定义,后面加上_CRT_SECURE_NO_WARNINGS就行了。
结下来又遇到问题了:程序退出把数据写入txt文件时每行末尾都会有一个\n,读入数据的时候用的是feof()函数,而feof()返回的其实是”最后一次读操作的内容”,与数据库中的eof()不同,eof()读取的是当前指针位置。因此在读到文件末尾的时候,程序会多执行一次循环,为了防止这种情况,我采取的是检查fscanf()返回值的方法,在此以读入账户资料时为例:
//读入账户姓名、身份证号、密码
if (fscanf(fp, "%s%s%s", p->Name, p->IdNum, p->PassWord) == EOF){
<span style="white-space:pre"> </span>free(p); break;
}
下面继续贴函数代码:
//登陆用户验证
BOOL AccountLogIn(HWND hwndDlg){
int flag = 0; //flag!=0则该用户已注册,flag==0则该用户尚未注册
Passenger *p;
GetDlgItemText(hwndDlg, IDC_IDEDIT, passenger->IdNum, 256); //获取用户输入ID
GetDlgItemText(hwndDlg, IDC_PSWEDIT, passenger->PassWord, 256); //获取用户输入密码
p = psglink.head;
while (p){ //在用户链表查找登陆用户ID
if (!lstrcmp(passenger->IdNum, p->IdNum)){
flag++; //该用户已注册
if (!lstrcmp(passenger->PassWord, p->PassWord)){ //密码匹配,登陆成功
lstrcpy(passenger->Name, p->Name);
MessageBox(hwndDlg, TEXT("登陆成功,单击确定进入程序主界面"), TEXT("登陆成功"), MB_OK | MB_ICONINFORMATION);
break;
}//if
else{ //密码错误,退出循环
MessageBox(hwndDlg, TEXT("密码错误,请重新输入"), TEXT("密码错误"), MB_OK | MB_ICONERROR);
return FALSE;
}//else
}//if
p = p->Next;
}//while
if (!flag){ //用户尚未注册
if (lstrlen(passenger->PassWord)==0)
MessageBox(hwndDlg, TEXT("请输入密码!"), TEXT("信息"), MB_OK | MB_ICONINFORMATION);
else
MessageBox(hwndDlg, TEXT("该账户尚未注册,请先注册"), TEXT("信息"), MB_OK | MB_ICONINFORMATION);
}//if
else{ //关闭登陆界面,弹出主界面
EndDialog(hwndDlg, TRUE);
if (ReadAccountOrder(hwndDlg, passenger)) //读取用户订单
DialogBox(hInst, MAKEINTRESOURCE(IDD_MAINDLG), NULL, (DLGPROC)MainDlgProc);
}//else
return TRUE;
}//AccountLogIn(HWND)
//用户注册
BOOL AccountRegister(HWND hwndDlg){
GetDlgItemText(hwndDlg, IDC_IDEDIT, passenger->IdNum, 256); //获取用户输入ID
GetDlgItemText(hwndDlg, IDC_PSWEDIT, passenger->PassWord, 256); //获取用户输入密码
if (lstrlen(passenger->PassWord) == 0){
MessageBox(hwndDlg, TEXT("请输入注册用户密码!"), TEXT("信息"), MB_OK | MB_ICONINFORMATION);
return FALSE;
}
DialogBox(hInst, MAKEINTRESOURCE(IDD_NAMEDLG), NULL, (DLGPROC)NameDlgProc); //获取新注册用户姓名
if (lstrlen(passenger->Name) == 0)
return FALSE;
passenger->Next = NULL;
if (psglink.head == NULL) //该注册账户为第一个账户时,头、尾结点均指向passenger
psglink.head = passenger;
else
psglink.tail->Next = passenger; //将新注册账户资料添加至账户链表
psglink.tail = passenger; //链表尾指针指向链表尾
psglink.PsgNum++; //注册用户数目加1
passenger->TicketNum = 0;
passenger->OrderLink.head = NULL; //乘客订单链表初始化
passenger->OrderLink.tail = NULL;
passenger->OrderLink.OrderNum = 0;
MessageBox(hwndDlg, TEXT("注册成功!请单击确定进入主界面"), TEXT("注册成功"), MB_OK | MB_ICONINFORMATION);
EndDialog(hwndDlg, TRUE); //关闭登陆界面
DialogBox(hInst, MAKEINTRESOURCE(IDD_MAINDLG), NULL, (DLGPROC)MainDlgProc); //弹出主界面
return TRUE;
}//AccountRegister(HWND)
//获取新注册用户姓名_窗口过程
BOOL CALLBACK NameDlgProc(HWND hNameDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch (uMsg)
{
case WM_INITDIALOG:
{
if (hIcon)
SendMessage(hNameDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
}
return TRUE;
case WM_CLOSE:
{
wsprintf(passenger->IdNum, TEXT("\0"));
EndDialog(hNameDlg, 0);
}//WM_CLOSE
return TRUE;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_GETNAMEOK:
{
GetDlgItemText(hNameDlg, IDC_GETNAME, passenger->Name, 16);
EndDialog(hNameDlg, TRUE);
break;
}//GETNAME
case IDC_GETNAMECANCEL:
{
wsprintf(passenger->IdNum, TEXT("\0"));
EndDialog(hNameDlg, TRUE);
break;
}//GETNAMECANCEL
}//stitch
}//WM_COMMAND
return TRUE;
}//stitch
return FALSE;
}//NameDlgProc()
//读取订单信息
BOOL ReadAccountOrder(HWND hwndDlg, Passenger *passenger){
FILE *fp;
allorder.AllOrderNum = 0; //订单链表初始化
allorder.head = NULL;
allorder.tail = NULL;
passenger->TicketNum = 0; //乘客订单链表初始化
passenger->OrderLink.head = NULL;
passenger->OrderLink.tail = NULL;
passenger->OrderLink.OrderNum = 0;
if ((fp = fopen(".\\OrderForm.txt", "r")) == NULL){ //打开存储订单信息文件
MessageBox(hwndDlg, TEXT("订单文件读入错误!"), TEXT("错误"), MB_OK | MB_ICONERROR);
return FALSE;
}
while (!feof(fp)){
OrderForm *p = (OrderForm *)malloc(sizeof(OrderForm));
if (!p){
MessageBox(hwndDlg, TEXT("内存申请错误!"), TEXT("错误"), MB_OK | MB_ICONERROR);
EndDialog(hwndDlg, 0);
return FALSE;
}//if
if (fscanf(fp, "%s%d%d%s%s%s%s%s%s",
p->IdNum, //读取订单信息
&p->Order_Number,
&p->Tickets_Num,
p->Flight_Number,
p->Departure,
p->Destination,
p->Date,
p->TakeOff_Time,
p->Landing_Time) == EOF)
{
free(p);
break;
}
p->Next = NULL;
p->psgNext = NULL;
if (allorder.head == NULL){ //将结点添至订单链表末尾
allorder.head = p;
}
else
allorder.tail->Next = p;
allorder.tail = p;
allorder.AllOrderNum++; //订单数目增加
if (!lstrcmp(p->IdNum, passenger->IdNum)){ //若该订单为当前登录账户订单,则添至用户订单链表末尾
if (passenger->OrderLink.head == NULL){ //将结点添至用户订单链表末尾
passenger->OrderLink.head = p;
}
else
passenger->OrderLink.tail->psgNext = p;
passenger->OrderLink.tail = p;
passenger->TicketNum += p->Tickets_Num;
passenger->OrderLink.OrderNum++; //订单数目增加
}//if
}//while
fclose(fp);
return TRUE;
}//ReadAccountOrder()
至此,登录对话框部分已经完成。下篇博客来完成主界面的部分功能…..