首页
社区
课程
招聘
[求助]后台截图,如何强制Ddraw更新被遮挡部分
发表于: 2015-8-1 09:44 9026

[求助]后台截图,如何强制Ddraw更新被遮挡部分

2015-8-1 09:44
9026
Ddraw后台截图网上找了很久,也没有完整的,这里分享一下自己的代码。不过在实测的时候,发现,被遮挡部分虽然可以截取到画面,但画面是固定的,不会更新。只有没被遮挡的部分才会更新。在这里求助高手!

void *g_pBlt=NULL;
LPDIRECTDRAW		 g_lpDD = NULL;
LPDIRECTDRAWSURFACE  g_pDrawSurface = NULL; // Our rendering device
BYTE Blt_Begin[5];//用于保存入口的5字节 

//*/
//窗口截屏在这里进行。
HRESULT __stdcall New_Blt( LPDIRECTDRAWSURFACE p0, LPRECT p1,LPDIRECTDRAWSURFACE p2, LPRECT p3,DWORD p4, LPDDBLTFX p5)   
{
	HRESULT hr;
	__asm pushad
	//恢复hook
    memcpy(g_pBlt,Blt_Begin,5);//先还原入口的5字节 
	
	hr = IDirectDrawSurface_Blt(p0,p1,p2,p3,p4,p5);
	if(g_NeedGetBmp)
	{
		//复制整个表面到我们自己的表面
		IDirectDrawSurface_Blt(g_pDrawSurface,NULL,p2,NULL,DDBLT_WAIT,NULL);
		g_NeedGetBmp=0;//只调用一次
	}
	if(g_ReHook)//需要再次hook
	{
		//再次hook
		*(BYTE*)g_pBlt=0xe9; 
		*(DWORD*)((BYTE*)g_pBlt+1)=(DWORD)New_Blt-(DWORD)g_pBlt-5; 
	}

	   __asm popad 

	return hr;
}
HBITMAP GameDDraw_GetWindowBmp(HWND hWnd,RECT *rc);

BOOL InitDraw(HWND hWnd)
{
    DDSURFACEDESC desc; 
	HRESULT (WINAPI *pDirectDrawCreate)( GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter );
	pDirectDrawCreate=GetProcAddress(GetModuleHandle(TEXT("ddraw.dll")), "DirectDrawCreate");
	if(pDirectDrawCreate==NULL)
	{
		MessageBox(NULL,"error0","",MB_OK);
		return FALSE;
	}

    //创建DirectDraw 
    if (FAILED( pDirectDrawCreate(NULL,&g_lpDD,NULL))) 
    {
		MessageBox(NULL,"error1","",MB_OK);
		return FALSE;
    }
	if(FAILED(IDirectDraw_SetCooperativeLevel(g_lpDD,hWnd, DDSCL_NORMAL)))
	{
		MessageBox(NULL,"error2","",MB_OK);
		return FALSE;

	}

    //创建后台表面 
 
    ZeroMemory(&desc,sizeof(desc)); 
    desc.dwSize=sizeof(desc); 
    desc.dwFlags= DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS; 
    desc.dwWidth=640;
    desc.dwHeight=480;
    desc.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY | DDSCAPS_OFFSCREENPLAIN; 

    if (FAILED(IDirectDraw_CreateSurface(g_lpDD,&desc, &g_pDrawSurface, NULL))) 
    { 
		MessageBox(NULL,"error3","",MB_OK);
		IDirectDraw_Release(g_lpDD);
		return FALSE;
    }
	return TRUE;
  
}
void GamdDDraw_HookBlt(HWND hWnd)
{
	if(g_pDrawSurface==NULL)
	{
		if(InitDraw(hWnd))
		{
			DWORD oldpro=0; 
			DWORD  d3d9_adr=(DWORD)GetModuleHandleA("ddraw.dll");
			g_pBlt=(LPVOID)((DWORD)GetClassVirtualFnAddress(g_pDrawSurface,5));

			g_ReHook=1;//设置自动挂接
			memcpy(Blt_Begin,g_pBlt,5);//保存IDirect3DDevice9::Present入口的5个字节 
			VirtualProtect(g_pBlt,5,PAGE_EXECUTE_READWRITE,&oldpro); 
			*(BYTE*)g_pBlt=0xe9; 
			*(DWORD*)((BYTE*)g_pBlt+1)=(DWORD)New_Blt-(DWORD)g_pBlt-5; 
			//MessageBox(NULL,"成功","",MB_OK);
			
		}
	}
}
void GameDDraw_UnHookBlt()
{
	if(g_pDrawSurface)
	{
		//释放离开屏幕的表面

		g_ReHook=0;
		Sleep(500);//等待释放
		IDirectDrawSurface_Release(g_pDrawSurface);
		IDirectDraw_Release(g_lpDD);
		g_pDrawSurface=NULL;
		g_lpDD=NULL;
		//g_pBlt=NULL;
	}
}
HBITMAP GameDDraw_GetWindowBmp(HWND hWnd,const RECT *rc)
{
	int Width,Height;
	static volatile int only_one=0;
	HBITMAP hBmp;
	HDC hDC;
	DDSURFACEDESC desc;
	int CUT,num;
	if(NULL==g_pDrawSurface)
		GamdDDraw_HookBlt(hWnd);
	if(NULL==g_pDrawSurface)
		return NULL;

	if(rc==NULL)
	{
		Width=640;
		Height=480;
		CUT=FALSE;
	}
	else
	{
		Width=rc->right-rc->left;
		Height=rc->bottom-rc->top;
		CUT=TRUE;
	}
	while(only_one)Sleep(g_Delay);
	only_one=1;//多线程防止冲突
	//强制更新窗口//试了好几个方法,不知道怎么更新窗口才有效
	hDC=GetDC(hWnd);
	g_SendMessage(hWnd,WM_PRINT,hDC,PRF_CLIENT);
	ReleaseDC(hWnd,hDC);
	//InvalidateRect(hWnd,NULL,TRUE);
	g_NeedGetBmp=1;//开始截图
	num=30;
	while(--num)
	{
		if(g_NeedGetBmp==0)
			break;
		Sleep(g_Delay);
	}
	if(num==0)
		return NULL;
	//开始处理表面
	ZeroMemory(&desc,sizeof(desc)); 
	desc.dwSize = sizeof(desc);

	if (SUCCEEDED(IDirectDrawSurface_Lock(g_pDrawSurface,NULL, &desc, DDLOCK_READONLY | DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR , NULL)))
	//截图出来了,转换表面到bmp
	//if (SUCCEEDED(IDirectDrawSurface_GetSurfaceDesc(g_pDrawSurface,&desc))) 		//g_sc.CaptureSurface(p0, p2 );
	{
		if(CUT)
		{
			char *p1,*p2,*s=malloc(Width*Height*4);
			int y;
			p1=((char *)desc.lpSurface);
			p1+=(4*((rc->top)*640+rc->left));
			p2=s;
			for(y=0;y<Height;y++)
			{
				memcpy(p2,p1,Width*4);
				p2+=Width*4;
				p1+=640*4;
			}
			hBmp=CreateBitmap(Width,Height,1,32,s);
			free(s);
		}
		else
		{
			//怎么转换为bmp呢
			hBmp=CreateBitmap(Width,Height,1,32,desc.lpSurface);
		}
		IDirectDrawSurface_Unlock(g_pDrawSurface,desc.lpSurface);
#ifdef _DEBUG
		SaveBmpToFile(hBmp,NULL);
#endif
		only_one=0;
		return hBmp;
	}

	only_one=0;
	return NULL;
}
//*/

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (8)
雪    币: 248
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
被遮挡就不能截出即时图,算什么后台。。。
2015-8-1 11:20
0
雪    币: 320
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
所以不知道问题在哪里,求高手帮忙看看,哪里出的问题
2015-8-1 11:22
0
雪    币: 248
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
天使后台插件有公开源码,自己去找来看吧
2015-8-1 11:29
0
雪    币: 320
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
专门找了天使的源代码,不过貌似没有找到DDRAW后台截图的代码,它的DDRAW模式应该是DX2吧,和GDI的截图采用的方法一致,貌似用了PrintWindow函数,这个函数理论上是可以后台截图的,不过我实测效果不太好。
2015-8-1 12:12
0
雪    币: 126
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
hook主表面的DirectDraw_CreateSurface_Blt函数,然后再该函数下直接取数据,这个数据是无遮挡的(当然窗口不能最小化的情况下). 读取的数据有可能是一个区域的,不是完整的窗口,这个时候就需要自己拼接了.
有没有更好的直接读取完整未遮挡的主屏数据,我也请教下. 资料查了下,好像有个flip...数据完整后翻转,还没研究过.
2015-8-2 16:42
0
雪    币: 320
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
主表面是显示屏,所以,不能操作主表面,这里我着重操作复制来源的表面。这个表面完全复制过来的效果就是,被遮挡部分画面静止不动,没有遮挡部分会实时更新。你说的拼接,我测试了很久也无法通过这个表面找到更多的关联表面。可以肯定的是,这个来源表面有一个关联DC,我在复制前,操作这个DC,在上面输出文字,都会反映到最终的截图上面。可目前没办法从这个表面找到其他相关的表面。也无法从这个表面找到上一层的设备来源。只知道,这个表面在被遮挡后,遮挡部分就不再更新了。我尝试hook gdi32的bitblt函数,还是无法取到画面。试着屏蔽getclipbox,还是不行。不知道程序内部如何操作导致这个表面被遮挡部分不再更新。你说的翻转我试过,测试结果是找不到或不存在。
2015-8-2 20:14
0
雪    币: 126
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
我是直接通过hook主表面获取数据的 之前看过一篇文章也是这么操作的.
不晓得你的使用场景是什么情况 是自己写个dll注入到DX窗口截屏 还是其他场景
2015-8-2 23:47
0
雪    币: 248
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
直接把逆向大漠插件就是了,以前没啥资料,就这么弄的
2015-8-3 21:28
0
游客
登录 | 注册 方可回帖
返回
//