//////////////////////////////////////
/*
這個例子比較複雜,需要放到專門的編譯器中去看,建議使用VS查看
在後面的帖子中有一個簡單的例子說明回調函數的用法,比較清楚,可以參看
*/
//主函數調用:
int _tmain(int argc, _TCHAR* argv[])
{
Init();
........
AddFileDriver(MiddleWare,NULL);
......
_getch();
return 0;
}
//
//AddFileDriver的代碼
//加載底層驅動程序。典型的回調函數使用<驅動程序DiskCommand通過AddFileDriver獲取參數Parameter信息>
//個人認爲可以改成更通用的形式:AddFileDriver(int8 (* DiskCommand)(void *Parameter), void *RsvdForLow)
//其中參數:Parameter可以獲取調用時的動態信息,比如:命令字、要操作的扇區
// RsvdForLow可以獲取程序運行過程中的靜態信息,比如:固定的磁盤信息
// 注意:Parameter,RsvdForLow傳遞到DiskCommand函數中具體使用時都要強制類型轉化
//Parameter 用於向驅動函數<DiskCommand指向的函數>傳遞信息,Parameter->RsvdForLow 獲取外部的其他輔助信息
void AddFileDriver(int8 (* DiskCommand)(int8 Command, void *Parameter), void *RsvdForLow)
{
//1.檢測是否有初始化程序<防止傳遞空參數>,初始化是否成功<物理磁盤是否可操作>
//2.獲取當前第一個空閒邏輯盤並掛接<也就是將物理磁盤轉化爲系統內部表示法以便操作>
//3.檢測是否掛接成功。失敗:退出。成功:讀取物理磁盤0扇區(啓動扇區)的值
//4.根據啓動扇區數據重新設置邏輯盤參數(第一次有意義的初始化)
//**此時需要判斷磁盤是否正確格式化了,如果沒有格式化(Format),那麼此時需要運行Format程序
int8 Buf[SECSIZE] = {0}; //存儲一個扇區的內容
Disk_Info *Disk = NULL;
Disk_RW_Parameter Pa;
Pa.TempVal = 0; //初始化,無意義
Pa.SectorIndex = 0; //初始化,無意義
Pa.RsvdForLow = RsvdForLow; //相關輔助信息,有可能用到,如果調用函數RsvdForLow = NULL,那麼這個參數也就無意義
Pa.Buf = Buf; //便於調用驅動函數運行時產生的數據,一般是磁盤讀寫數據的緩存
Pa.Drive = EMPTY_DRIVE; //初始化,當前使用的邏輯盤符
if (DiskCommand == NULL) // 參數無效退出
{
return;
}
//先掛接再初始化,把流程中所說的1和2有所顛倒,爲了統一接口
Disk = GetEmptyDiskInfoAddr();
if (Disk == NULL)
{
return;
}
//Disk是指向全局變量DiskInfo_G的指針,對其賦值可以把信息保留到程序運行完畢
//因此把程序運行過程中一直需要使用的信息傳給Disk->RsvdForLow和Disk->DiskCommand
//Disk->DiskCommand 一般指向驅動函數,Disk->RsvdForLow 指向主函數中傳遞過來的信息
Disk->RsvdForLow = RsvdForLow; //接收輔助信息
Disk->DiskCommand = DiskCommand; //指向驅動程序
Pa.Drive = Disk->Drive;
if (Disk->DiskCommand(DISK_INIT, &Pa) != DISK_INIT_OK) // 底層驅動初始化不成功退出
{
return;
}
//讀取0扇區的數據,初始化DiskInfo_G[Drive]
Pa.SectorIndex = 0;
if(Disk->DiskCommand(DISK_READ_SECTOR, &Pa) != DISK_READ_OK)
{
return;
}
#ifdef NF_FORMAT //如果沒有沒有格式化便格式化
if(FSMount(Disk, Buf) != RETURN_OK)
{
Format(Disk->Drive);
if(FSMount(Disk, Buf) != RETURN_OK)
return;
}
#else
FSMount(Disk, Buf);
#endif
}
//****************Disk_RW_Parameter的結構如下***********************//
typedef struct _Disk_RW_Parameter
{
int32 TempVal; /* 用於傳遞函數需要的參數或者將函數調用後產生的值傳出來 */
/* 常用於傳遞一個需要操作的函數或者傳回函數調用後產生的值*/
uint32 SectorIndex; /* 操作的扇區,當向下傳遞一個參數的時候也可以使用TempVal替代 */
void *RsvdForLow; /* 保留給底層驅動程序,由_Disk_Info中拷貝過來 */
int8 *Buf; /* 數據存儲位置 */
int8 Drive;
}Disk_RW_Parameter;
//************************************************************************************************************************************//
//
//MiddleWare的代碼
int8 MiddleWare(int8 Command, void *Parameter)
{
uint8 state; //用於返回值,表示函數操作完畢之後的狀態
Disk_RW_Parameter * Dp; //用於接收加載驅動時傳遞的參數
Dp = (Disk_RW_Parameter *)Parameter;
// 如果所要求的命令沒有在這裏實現,則state = BAD_DISK_COMMAND
switch (Command)
{
default:
state = BAD_DISK_COMMAND;
break;
case DISK_INIT:
// 初始化驅動程序,必須實現 //
// Parameter沒有使用 //
//state= DISK_INIT_OK 或 DISK_INIT_NOT_OK
//state = PHDisk_Initialize();爲了明確顯示採用以下方式:
if(PHDisk_Initialize() == PH_DISK_INIT_OK)
state = DISK_INIT_OK;
else
state = DISK_INIT_NOT_OK;
break;
case DISK_READ_SECTOR:
// 讀物理扇區,必須實現 //
// Dp->Buf:存儲讀到的數據 //
// Dp->SectorIndex:物理扇區索引 //
// state=DISK_READ_OK或DISK_READ_NOT_OK//
if(PHDiskReadSec(Dp->SectorIndex, Dp->Buf) == PH_DISK_READ_OK)
state = DISK_READ_OK;
else
state = DISK_READ_NOT_OK;
break;
.......
.......
}
return state;
}
注意紅色和大字體部分,可以放到VS中去看,網頁上顯示的比較亂!
之所以貼出代碼是因爲這段代碼實在是太經典了,用好
void AddFileDriver(int8 (* DiskCommand)(int8 Command, void *Parameter), void *RsvdForLow)
中的void *Parameter和void *RsvdForLow,可以實現強大的變參函數。而且函數接口具有良好的擴展性。
//使用函數指針構造回調函數
//在一個單鏈表中查找一個指定的值(*value)
search_list(Node *node, void const *value, int (*compare)(void const *, void const *) )
{
while(node != NULL)
{
if(compare(&node->value, value) == 0 )
break;
node = node->link;
}
return node;
}
//函數的定義
int compare_ints (void const *a, void const *b)
{
if(*(int*)a == *(int*)b )
return 0;
else
return 1;
}
//回調函數的使用
desired_node = search_list(root, &desired_value, compare_ints);
***這是一個簡單的例子,雖然說明了回調函數的構造方法和使用,但是隻是個簡單的應用,還沒有完全體現出來回調函數的強大功能。這個函數的擴展性很好,大家可以看見傳遞的參數都是(void*),也就是一個通用性指針,可以指向任意類型。但是沒有體現出來被調用函數可以從回調函數接收參數這一點,但是在第一個例子中具有這種思想(在回調函數AddFileDriver(...)中給(Disk_RW_Parameter Pa) 賦值,然後再傳給被調用函數MiddleWare(...),也就是*DiskCommand指向的函數)