Wine GDI 绘图调用路径

为了解Wine 中针对GDI 绘图,完整的调用链路。以下以一个简单的示例demo 进行验证如下。
以创建画笔并绘制一条线段为例;
1、创建画笔

HPEN hPen = CreatePen(style, width, color);

此时会调用 gdi32.dll(objects.c) 中函数

/***********************************************************************
 *           CreatePen    (GDI32.@)
 */
HPEN WINAPI CreatePen( INT style, INT width, COLORREF color )
{
    if (style < 0 || style > PS_INSIDEFRAME) style = PS_SOLID;
    return NtGdiCreatePen( style, width, color, NULL );
}

在win32u.spec 文件中 NtGdiCreatePen 函数定义为

@ stdcall -syscall NtGdiCreatePen(long long long long)

声明中有 -syscall 也即其有系统调用过程,会转到内核层进行调用。
也即会调用win32u.so (pen.c) 中函数。

/***********************************************************************
 *           NtGdiCreatePen    (win32u.@)
 */
HPEN WINAPI NtGdiCreatePen( INT style, INT width, COLORREF color, HBRUSH brush )
{
    if (brush) FIXME( "brush not supported\n" );
    if (style == PS_NULL) return GetStockObject( NULL_PEN );
    return create_pen( style, width, color );
}

2、画线
此时会调用 gdi32.dll(dc.c) 中函数

/***********************************************************************
 *           LineTo    (GDI32.@)
 */
BOOL WINAPI LineTo( HDC hdc, INT x, INT y )
{
    DC_ATTR *dc_attr;

    TRACE( "%p, (%d, %d)\n", hdc, x, y );

    if (is_meta_dc( hdc )) return METADC_LineTo( hdc, x, y );
    if (!(dc_attr = get_dc_attr( hdc ))) return FALSE;
    if (dc_attr->emf && !EMFDC_LineTo( dc_attr, x, y )) return FALSE;
    return NtGdiLineTo( hdc, x, y );
}

继续调用到wrappers.c 中函数

BOOL WINAPI NtGdiLineTo( HDC hdc, INT x, INT y )
{
    if (!unix_funcs) return FALSE;
    return unix_funcs->pNtGdiLineTo( hdc, x, y );
}

此处 unix_funcs 是一个结构体,它定义了一些函数原型指针;

struct unix_funcs
{
    /* win32u functions */
    ...
    INT      (WINAPI *pNtGdiIntersectClipRect)( HDC hdc, INT left, INT top, INT right, INT bottom );
    BOOL     (WINAPI *pNtGdiInvertRgn)( HDC hdc, HRGN hrgn );
    BOOL     (WINAPI *pNtGdiLineTo)( HDC hdc, INT x, INT y );
    BOOL     (WINAPI *pNtGdiMaskBlt)( HDC hdc, INT x_dst, INT y_dst, INT width_dst, INT height_dst,
                                      HDC hdc_src, INT x_src, INT y_src, HBITMAP mask,
                                      INT x_mask, INT y_mask, DWORD rop, DWORD bk_color );
    BOOL     (WINAPI *pNtGdiModifyWorldTransform)( HDC hdc, const XFORM *xform, DWORD mode );
    BOOL     (WINAPI *pNtGdiMoveTo)( HDC hdc, INT x, INT y, POINT *pt );
    INT      (WINAPI *pNtGdiOffsetClipRgn)( HDC hdc, INT x, INT y );
    ...
}

该结构体的初始化函数为:

extern void wrappers_init( unixlib_handle_t handle )
{
    const void *args;
    if (!__wine_unix_call( handle, 1, &args )) unix_funcs = args;
}

在该模块dllmain 中被调用:

BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved )
{
    switch (reason)
    {
    case DLL_PROCESS_ATTACH:
        LdrDisableThreadCalloutsForDll( inst );
        if (__wine_syscall_dispatcher) break;  /* already set through Wow64Transition */
        if (!NtQueryVirtualMemory( GetCurrentProcess(), inst, MemoryWineUnixFuncs,
                                   &win32u_handle, sizeof(win32u_handle), NULL ))
        {
            __wine_unix_call( win32u_handle, 0, &__wine_syscall_dispatcher );
            wrappers_init( win32u_handle );
        }
        break;
    }
    return TRUE;
}

继续追踪代码至ntdll.so 的virtual.c 文件中。了解其获取了win32u.so中关于内存和函数调用相关的函数指针;

NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr,
                                      MEMORY_INFORMATION_CLASS info_class,
                                      PVOID buffer, SIZE_T len, SIZE_T *res_len )
 {
        case MemoryWineUnixFuncs:
        case MemoryWineUnixWow64Funcs:
            if (len != sizeof(unixlib_handle_t)) return STATUS_INFO_LENGTH_MISMATCH;
            if (process == GetCurrentProcess())
            {
                void *module = (void *)addr;
                const void *funcs = NULL;

                status = get_builtin_unix_funcs( module, info_class == MemoryWineUnixWow64Funcs, &funcs );
                if (!status) *(unixlib_handle_t *)buffer = (UINT_PTR)funcs;
                return status;
            }
            return STATUS_INVALID_HANDLE;
            ...
  }

按照上述方法查找,能够查到win32u.so painting.c 中实现的NtGdiLineTo 函数

/***********************************************************************
 *           NtGdiLineTo    (win32u.@)
 */
BOOL WINAPI NtGdiLineTo( HDC hdc, INT x, INT y )
{
    DC * dc = get_dc_ptr( hdc );
    PHYSDEV physdev;
    BOOL ret;

    if(!dc) return FALSE;

    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pLineTo );
    ret = physdev->funcs->pLineTo( physdev, x, y );

    if(ret)
    {
        dc->attr->cur_pos.x = x;
        dc->attr->cur_pos.y = y;
    }
    release_dc_ptr( dc );
    return ret;
}

可以看到,通过一个 gdi_dc_funcs 结构体(physdev->funcs)来运行时指定待执行的指针函数;
gdi_driver.h 文件中定义该结构体的函数指针如下:

struct gdi_dc_funcs
{
    INT      (CDECL *pAbortDoc)(PHYSDEV);
    BOOL     (CDECL *pAbortPath)(PHYSDEV);
    BOOL     (CDECL *pAlphaBlend)(PHYSDEV,struct bitblt_coords*,PHYSDEV,struct bitblt_coords*,BLENDFUNCTION);
    BOOL     (CDECL *pAngleArc)(PHYSDEV,INT,INT,DWORD,FLOAT,FLOAT);
    BOOL     (CDECL *pArc)(PHYSDEV,INT,INT,INT,INT,INT,INT,INT,INT);
    BOOL     (CDECL *pArcTo)(PHYSDEV,INT,INT,INT,INT,INT,INT,INT,INT);
    BOOL     (CDECL *pBeginPath)(PHYSDEV);
    DWORD    (CDECL *pBlendImage)(PHYSDEV,BITMAPINFO*,const struct gdi_image_bits*,struct bitblt_coords*,struct bitblt_coords*,BLENDFUNCTION);
    BOOL     (CDECL *pChord)(PHYSDEV,INT,INT,INT,INT,INT,INT,INT,INT);
    BOOL     (CDECL *pCloseFigure)(PHYSDEV);
    BOOL     (CDECL *pCreateCompatibleDC)(PHYSDEV,PHYSDEV*);
    BOOL     (CDECL *pCreateDC)(PHYSDEV*,LPCWSTR,LPCWSTR,const DEVMODEW*);
    BOOL     (CDECL *pDeleteDC)(PHYSDEV);
    BOOL     (CDECL *pDeleteObject)(PHYSDEV,HGDIOBJ);
    BOOL     (CDECL *pEllipse)(PHYSDEV,INT,INT,INT,INT);
    INT      (CDECL *pEndDoc)(PHYSDEV);
    INT      (CDECL *pEndPage)(PHYSDEV);
    BOOL     (CDECL *pEndPath)(PHYSDEV);
    BOOL     (CDECL *pEnumFonts)(PHYSDEV,LPLOGFONTW,FONTENUMPROCW,LPARAM);
    INT      (CDECL *pExtEscape)(PHYSDEV,INT,INT,LPCVOID,INT,LPVOID);
    BOOL     (CDECL *pExtFloodFill)(PHYSDEV,INT,INT,COLORREF,UINT);
    BOOL     (CDECL *pExtTextOut)(PHYSDEV,INT,INT,UINT,const RECT*,LPCWSTR,UINT,const INT*);
    BOOL     (CDECL *pFillPath)(PHYSDEV);
    BOOL     (CDECL *pFillRgn)(PHYSDEV,HRGN,HBRUSH);
    BOOL     (CDECL *pFontIsLinked)(PHYSDEV);
    BOOL     (CDECL *pFrameRgn)(PHYSDEV,HRGN,HBRUSH,INT,INT);
    ...
}

此处,其找到了 dc.c 文件中dibdrv_LineTo 函数,完成线段的绘制。

/***********************************************************************
 *           dibdrv_LineTo
 */
BOOL CDECL dibdrv_LineTo( PHYSDEV dev, INT x, INT y )
{
    dibdrv_physdev *pdev = get_dibdrv_pdev(dev);
    DC *dc = get_physdev_dc( dev );
    POINT pts[2];
    HRGN region = 0;
    BOOL ret;

    pts[0] = dc->attr->cur_pos;
    pts[1].x = x;
    pts[1].y = y;

    lp_to_dp(dc, pts, 2);

    if (pdev->pen_uses_region && !(region = NtGdiCreateRectRgn( 0, 0, 0, 0 ))) return FALSE;

    reset_dash_origin(pdev);

    ret = pdev->pen_lines(pdev, 2, pts, FALSE, region);
    add_pen_lines_bounds( pdev, 2, pts, region );

    if (region)
    {
        ret = pen_region( pdev, region );
        NtGdiDeleteObjectApp( region );
    }
    return ret;
}

此函数中 lp_to_dp/pen_region 等子函数会继续调用到winex11.so 中,后续再细究。

小提示:
1、文件头中有如下标识
#pragma makedep unix
则表明该文件导出的是.so 文件,可以直接与linux 内核函数进行互操作;

2、Nt 开头的函数一般都需要从用户态转成内核态;会在对应的dll
以及so 动态库中均存在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值