• 利用InjectedBundle定制自己的Webkit(二)


     

    由于时间仓促,之前贴的代码中有些许错误,请大家见谅,现已更正,并附上运行结果!

    ----------------我是开始分割线-----------------

     

    在上一篇利用InjectedBundle定制自己的Webkit(一)中,我们完成了一个自己的InjectedBundle,接下来我们就要在Webkit中加载我们自己的InjectedBundle。

    为了测试方便先给出一个示例的InjectedBundle代码,项目名称MyInjectedBundle。

    #include <WebKit2/WKBundleInitialize.h>
    #include <WebKit2/WKBundleFrame.h>
    #include <Webkit2/WKBundlePage.h>
    #include <WebKit2/WKBundle.h>
    #include <Webkit2/WKUrl.h>
    #include <Webkit2/WKString.h>
    #include <windows.h>
    #include <string.h>
    #include <wchar.h>

    #pragma comment(lib, "WebKit.lib")

    // 框架加载完毕后调用
    void didFinishLoadForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void*)
    {
      if (WKBundleFrameIsMainFrame(frame)) // 如果是主框架
      {
        WKURLRef url = WKBundleFrameCopyURL(frame); // 获得URL
        WKStringRef str = WKURLCopyString(url); // 得到URL字符串
        size_t length = WKStringGetLength(str);
        wchar_t* urlBuffer = new wchar_t[length + 1];
        size_t size = WKStringGetCharacters(str, urlBuffer, length); // 转成wchar_t*
        urlBuffer[length] = 0;

        WKRect rect = WKBundleFrameGetContentBounds(frame); // 得到边界范围
        wchar_t info[1024];
        swprintf(info, L"url: %s\nx=%f, y=%f, width=%f, height=%f",
          urlBuffer, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); // 生成结果
        ::MessageBoxW(0, info, L"FrameBound", MB_OK); // 显示
        delete[] urlBuffer;
      }
    }

    // page创建完后调用
    void didCreatePage(WKBundleRef bundle, WKBundlePageRef page, const void*)
    {
      WKBundlePageLoaderClient loaderClient = { 0 };
      loaderClient.version = kWKBundlePageLoaderClientCurrentVersion;
      loaderClient.didFinishLoadForFrame = didFinishLoadForFrame; // 注册回调
      WKBundlePageSetPageLoaderClient(page, &loaderClient);
    }

    extern "C" __declspec(dllexport)
    void WKBundleInitialize(WKBundleRef bundle, WKTypeRef initializationUserData)
    {
      WKBundleClient client = { 0 };
      client.version = kWKBundleClientCurrentVersion;
      client.didCreatePage = didCreatePage;
      WKBundleSetClient(bundle, &client);
    }

    以上InjectedBundle首先注册page创建后的回调didCreatePage,然后在page创建之后注册框架加载完毕的回调didFinishLoadForFrame,当框架加载完毕之后再获得框架的url和区域坐标显示出来。

    在InjectedBundle编译生成完毕之后得到MyInjectedBundle.dll,之后我们开始写客户进程调用的代码。

     

    创建一个空项目WebkitDemo,准备工作同InjectedBundle,不过配置类型是应用程序(.exe)

    然后开始写代码

    首先需要建立一个让View对象依附的Window,Window是怎样的不重要,主要是需要一个载体和消息队列,所以这里直接不显示Window了。

    #include <Windows.h>
    #include <Webkit2/WKType.h>
    #include <WebKit2/WKStringCF.h>
    #include <Webkit2/WKString.h>
    #include <Webkit2/WKUrl.h>
    #include <Webkit2/WKView.h>
    #include <Webkit2/WKContext.h>
    #include <Webkit2/WKPage.h>
    #include <shlwapi.h>
    #include <WebKit2/WKURLCF.h>

    #pragma comment(lib, "Shlwapi.lib")
    #pragma comment(lib, "CoreFoundation.lib")
    #pragma comment(lib, "WebKit.lib")

    LRESULT CALLBACK runLoopProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
    {
      if (message == WM_CREATE)
      {
        createView(window);  // 创建view这里之后介绍
        return 0;
      }
      return ::DefWindowProc(window, message, wParam, lParam);
    }

    int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpstrCmdLine, int nCmdShow)
    {
      MSG msg;
      BOOL bRet;

      WNDCLASS wc;
      wc.style = 0;
      wc.lpfnWndProc = runLoopProc;
      wc.cbClsExtra = 0;
      wc.cbWndExtra = 0;
      wc.hInstance = hInstance;
      wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
      wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
      wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
      wc.lpszMenuName = 0;
      wc.lpszClassName = L"MyWebkitToolRunLoop";

      if (!RegisterClass(&wc))
        return 0;

      HWND hwnd = ::CreateWindow(L"MyWebkitToolRunLoop", L"MyWebkitTool",
        WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0);

      if (!hwnd)
        return 0;

      // 开始消息循环

      while ( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
      {
        if (bRet == -1)
        {
          break;
        }
        else
        {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
        }
      }
      return 0;
    }

    然后在WM_CREATE事件的响应中创建view,创建view需要一个WKContextRef参数,这个参数能够指定view在什么上下文中创建,即利用哪个WebProcess进程来创建。所以我们就可以在WKContextRef中设置使用我们的InjectedBundle。

    WKStringRef getBundlePath()

    {
      const wchar_t* THIS_FILE_NAME = L"WebkitDemo.exe";  // 客户程序文件名
      const wchar_t* BUNDLE_FILE_NAME = L"MyInjectedBundle.dll";  // InjectedBundle文件名
      HMODULE thisModule = ::GetModuleHandleW(THIS_FILE_NAME);
      if (!thisModule)
        return 0;

      WCHAR pathStr[MAX_PATH];
      if (!::GetModuleFileNameW(thisModule, pathStr, (DWORD)wcslen(pathStr)))  // 得到客户程序文件路径
        return 0;

      ::PathRemoveFileSpecW(pathStr);  // 去掉文件名
      if (!::PathAppendW(pathStr, BUNDLE_FILE_NAME))  // 加上InjectedBundle文件名
        return 0;

      CFStringRef cf = CFStringCreateWithCharacters(0, (const UniChar*)pathStr, (CFIndex)wcslen(pathStr));

      WKStringRef res = WKStringCreateWithCFString(cf);

      CFRelease(cf);

      return res;
    }

    void goToURL(const wchar_t* wcurl, WKViewRef view)
    {
      CFStringRef string = CFStringCreateWithCharacters(0, (const UniChar*)wcurl, (CFIndex)wcslen(wcurl));
      CFStringRef escapedString = CFURLCreateStringByAddingPercentEscapes(0, string, 0, 0, kCFStringEncodingUTF8);
      CFRelease(string);
      CFURLRef cfURL = CFURLCreateWithString(0, escapedString, 0);
      CFRelease(escapedString);
      WKURLRef url = WKURLCreateWithCFURL(cfURL);  // 创建一个URL对象
      CFRelease(cfURL);
      WKPageRef page = WKViewGetPage(view);
      WKPageLoadURL(page, url);       // 开始加载
      WKRelease(url);
    }

    void createView(HWND window)
    {
      RECT webViewRect = { 0, 0, 0, 0 };
      WKStringRef path = getBundlePath();
      WKContextRef context = WKContextCreateWithInjectedBundlePath(path);  // 利用InjectedBundle创建context对象
      WKRelease(path);

      WKViewRef view = WKViewCreate(webViewRect, context, 0, window);    // 创建view对象
      WKViewSetIsInWindow(view, true);
      // 这里可以用WKPageSetXXXClient注册一些回调函数
      goToURL(L"http://www.baidu.com", view);
    }

    我们需要给将WKContextCreateWithInjectedBundlePath传入bundle文件的路径,为方便我们将MyInjectedBundle.dll和客户端程序放在一起,利用getBundlePath()得到bundle文件的路径。在创建完成之后调用goToURL让Webkit加载页面,这里以百度为例。

    以上代码用到了Shlwapi和CoreFoundation,所以加上头文件和链接库。

    编译运行,大功告成了!

     

    之后附上源码文件InjectedBundleDemo.zip

    小小尝试,欢迎大家多提意见和建议!下一期准备为大家介绍利用定制的Webkit来爬取动态页面内容和动态生成的链接。

  • 相关阅读:
    二十三种设计模式 python实现
    python logging的输出
    redis
    Django1.11序列化与反序列化
    Django1.11基础视图
    Django1.11模型类数据库操作
    Django1.11创建
    泛型全面分析和应用(二)
    泛型全面分析和应用(一)
    注解的基本盘点 -- 《Java编程思想》
  • 原文地址:https://www.cnblogs.com/Jiajun/p/2800120.html
Copyright © 2020-2023  润新知