美文网首页开源时代C语言C++
callback function(回调函数)

callback function(回调函数)

作者: NiceBlueChai | 来源:发表于2017-10-04 16:05 被阅读30次

    简单的说,我们调用别人的API叫call,调用的第三方api调用我们的函数叫回调(callback)

    call与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

    注意:上下文对象的生命期在回调函数被触发时,该上下文对象必须是有效的。。

    我们希望在函数回调时显示其他信息,文件名......


    上下文

    然而,在回调函数的参数里,并没有源文件名和目标文件名。。。也就是说,无法得知当前正在拷贝的哪个文件。。。

    在回调函数中参数 “ lpData”即上下文,类型为“ LPVOID”也就是void*类型,

    CopyFileEx function
    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,即便你花钱看我的文章,也会觉得心满意足


    相关文章

      网友评论

        本文标题:callback function(回调函数)

        本文链接:https://www.haomeiwen.com/subject/fblcyxtx.html