callback function(回调函数)
call与callback简单的说,我们调用别人的API叫call,调用的第三方api调用我们的函数叫回调(callback)
回调机制
比如,要拷贝一个文件,将1.pdf拷贝成1_copy.pdf。。。
方法:调用Windows API(系统函数库)
里面有一个CopyFile函数
CopyFile("1.pdf", "2.pdf" ... );
这种调用就叫Call,即,调用别人的函数
何时需要 Callback ?
考虑:拷贝一个很大的文件(比如,1G的视频文件)。。。。
这个拷贝过程需要一段时间。。。
如果用CopyFile(…),则需要默默等待、直到它完成。。。
缺点:用户体验差,缺少交互性
这时候,我们希望增加交互性:显示拷贝的进度这意味着,我们希望系统在拷贝文件的时候,能够通知我们的应用程序。。。
比如,我们提供一个函数
void CopyProgress(int total, int copied)
{
}
我们希望: 系统能够时不时的调用我们的这个函数,将total/copied数据通知给我们。。。
这时,我们将这个函数的地址作为参数传给API即可
CopyFileEx(source, dst, &CopyProgress, NULL, NULL, 0);
示例代码:
#include<windows.h>
#include<stdio.h>
// 将LARGE_INTTEGER类型转成unsigned long long
unsigned long long translate(LARGE_INTEGER num)
{
unsigned long long result = num.HighPart;
result <<= 32;
result += num.LowPart;
return result;
}
// 回调函数
// 注:要求将此函数用关键字CALLBACK修饰(这是Windows API的要求)
DWORD CALLBACK CopyProgress(
LARGE_INTEGER TotalFileSize,
LARGE_INTEGER TotalBytesTransferred,
LARGE_INTEGER StreamSize,
LARGE_INTEGER StreamBytesTransferred,
DWORD dwStreamNumber,
DWORD dwCallbackReason,
HANDLE hSourceFile,
HANDLE hDestinationFile,
LPVOID lpData)
{
//文件的总字节数 TotalFileSize
unsigned long long total = translate(TotalFileSize);
//已完成的字节数 TotalBytesTransferred
unsigned long long copied = translate(TotalBytesTransferred);
//打印进度
//printf("进度:%I64d / %I64d \n", copied, total);// 64位整数用 %I64d
//printf("进度: %d / %d \n", (int)copied, (int)total); // 文件大小于2G时,可以转成int
printf("%d%% \n", 100*copied / total);//按百分比显示拷贝进度
return PROGRESS_CONTINUE;
}
int main()
{
const char* source = "c:\\test\\2.rmvb";
const char* dst = "c:\\test\\2_copy.rmvb";
printf("start copy ...\n");
// 将函数指针传给CopyFileEx
BOOL result = CopyFileEx(source, dst, &CopyProgress, NULL, NULL, 0);
printf("operation done : %s \n", result ? "success" : "failed");
return 0;
}
2.回调函数中的上下文
使用回调函数时,总是会透传一个void参数,指向一个上下文对象。
"透传":透明的,不关心其类型与内容,什么样的进来、什么样的出去。。。void
注意:上下文对象的生命期在回调函数被触发时,该上下文对象必须是有效的。。
我们希望在函数回调时显示其他信息,文件名......
上下文
然而,在回调函数的参数里,并没有源文件名和目标文件名。。。也就是说,无法得知当前正在拷贝的哪个文件。。。
CopyFileEx function在回调函数中参数 “ lpData”即上下文,类型为“ LPVOID”也就是void*类型,
CopyProgressRoutine callback function
上下文对象:该对象携带了所有必要的上下文信息。。。
可以为任意类型的数据,完全有用户自己决定。。。
比如:
struct Context
{
char username[32];
char source[128];
char dst[128];
};
示例代码:
#include<windows.h>
#include<stdio.h>
#include<string.h>
struct Context {
char username[32];
char source[128];
char dst[128];
};
// 将LARGE_INTTEGER类型转成unsigned long long
unsigned long long translate(LARGE_INTEGER num)
{
unsigned long long result = num.HighPart;
result <<= 32;
result += num.LowPart;
return result;
}
// 回调函数
// 注:要求将此函数用关键字CALLBACK修饰(这是Windows API的要求)
DWORD CALLBACK CopyProgress(
LARGE_INTEGER TotalFileSize,
LARGE_INTEGER TotalBytesTransferred,
LARGE_INTEGER StreamSize,
LARGE_INTEGER StreamBytesTransferred,
DWORD dwStreamNumber,
DWORD dwCallbackReason,
HANDLE hSourceFile,
HANDLE hDestinationFile,
LPVOID lpData)
{
//文件的总字节数 TotalFileSize
unsigned long long total = translate(TotalFileSize);
//已完成的字节数 TotalBytesTransferred
unsigned long long copied = translate(TotalBytesTransferred);
//转换上下文对象
Context* ctx = (Context*)lpData;
//打印进度
//printf("进度:%I64d / %I64d \n", copied, total);// 64位整数用 %I64d
//printf("进度: %d / %d \n", (int)copied, (int)total); // 文件大小于2G时,可以转成int
printf("[%s]\t%s\t->\t%s\t%d%% \n",ctx->username,ctx->source,ctx->dst,(int)100*copied / total);//按百分比显示拷贝进度
return PROGRESS_CONTINUE;
}
int main() {
//定义上下文对象
Context con;
strcpy_s(con.username, "NiceBlueChai");
strcpy_s(con.source, "E:\\与孩子一起学编程(中文完整版).pdf");
strcpy_s(con.dst, "e:\\1234.pdf");
const char* source = "E:/与孩子一起学编程(中文完整版).pdf";
const char* dst = "e:/1234.pdf";
//const char* source = "F:\\迅雷下载\\taxt/123.mkv";
//const char* dst = "F:\\迅雷下载\\taxt/出租车司机.mkv";
printf("start copy...\n");
system("color 70");
BOOL result = CopyFileEx(source, dst, &CopyProgress,&con,NULL,0);
printf("operation done:%s\n", result ? "success" : "filed");
getchar();
return 0;
}
❤️我的目标是:someday,即便你花钱看我的文章,也会觉得心满意足