2、lib7z-Memory从内存或网络解压数据(分析源码,修改源码)

来源:互联网 发布:淘宝商城客服 编辑:程序博客网 时间:2024/05/22 05:10

希望你有好运气能编译上一节我提供的Dec7z的源码,如果你成功了,接下来,我就分享一下分析7z源码并修改到符合目的的过程。

首先进入

BOOL Extra7zFileToPath(WCHAR* sTargetPath,                       HWND hwnd,                       BOOL bUpdate,                       char* pAllData,                       DWORD iLength)

这个函数,
CFileInStream archiveStream;//包含HANDLE句柄,Read和Seek  CLookToRead lookStream;  CSzArEx db;//压缩包内的文件名称和大小?  SRes res;  ISzAlloc allocImp;//包括malloc和free函数  ISzAlloc allocTempImp;//Win32 HeapAlloc和HeapFree
这里主要的代码我都做了注释,关键点一是这个:

 //(*函数指针Read,Seek,Skip等赋值  FileInStream_CreateVTable(&archiveStream);//这个好办  //这个尚未清楚  //也是用的上面的Read和Seek,但多了p->pos的操作  LookToRead_CreateVTable(&lookStream, False);  //*)

通过分析7z解压的源码,就知道7z解压的最后手段无非就是fopen,fwrite在Windows系统下用宏判断定义了,读取和Seek函数,

无非就是CreateFile(打开文件),ReadFile(读物数据到内存 ),SetFilePointer(设置文件指针),CloseHandle(关闭打开的文件句柄)。

7z用了结构体的面向技术,最关键的两个是

typedef struct{  ISeekInStream s;  CSzFile file;} CFileInStream;
typedef struct{  SRes (*Read)(void *p, void *buf, size_t *size);  /* same as ISeqInStream::Read */  SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);} ISeekInStream;
最关键的读取和移动文件指针函数是 

WRes File_Read(CSzFile *p, void *data, size_t *size);/* writes *size bytes */WRes File_Write(CSzFile *p, const void *data, size_t *size);WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);WRes File_GetLength(CSzFile *p, UInt64 *length);

其中ISeekInStream的Read函数指针通过分析正指向File_Read函数,而File_Read函数正调用了Win32的ReadFile函数,Seek函数指针对应Win32的SetFilePointer,7z这样做非常高明,这样7z的结构体不但有数据成员,同时也通过函数指针拥有了函数成员,达到了和c++相同的面向对象的功能,又通过宏判断以适应X86,Arm等平台和编译器。

假如我们首先获取了全部数据指针char* pAllData和数据长度iLength,用memcpy代替File_Read,用自动位置的代替File_Seek不就实现了不用CreateFileA打开文件,直接解压的目的了吗? 这个想法理论上是成立的,我在修改中也遇到了不少问题。首先ReadFile函数用memcpy来代替是可以的,但ReadFile不但读取了数据,也同时改变了HANDLE文件的位置(Seek的偏移),所以需要一个全局的位置,记住文件的偏移。我建立了func.h和func.c文件,里面定义了一些全局变量和修改过的函数。

#ifndef FUNC_H#define FUNC_H#ifndef kChunkSizeMax    #define kChunkSizeMax (1 << 22)#endif#include "7zFile.h"#include "Types.h"extern char* g_pAllData;extern DWORD g_iAllDataLength;extern DWORD g_iSeekPos;extern HANDLE* g_handle;void g_func_GetLowAndHeightFromDWORD(DWORD v, DWORD* iLow, LONG *iHeight);UInt64 g_func_GetDWORDByLowAndHight(DWORD sizeLow,DWORD sizeHigh);WRes g_func_File_Close(CSzFile *p);WRes g_func_File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);WRes g_func_File_Read(CSzFile *p,                      void *data,                      size_t *size);#endif // FUNC_H

最关键的是这两个

WRes g_func_File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);WRes g_func_File_Read(CSzFile *p,                      void *data,                      size_t *size);
我们要用这两个函数分别替换File_Seek和File_Read函数,才能达到去除文件HANDLE,内存直接解压的目的。g_ISeekPos是全局的文件指针偏移

我是先拿File_Read函数开刀,下面是关键的函数替换修改点(7zFile.c):

static SRes FileInStream_Read(void *pp, void *buf, size_t *size){  CFileInStream *p = (CFileInStream *)pp;  //return (File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ;  return (g_func_File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ;  //return SZ_OK;}static SRes FileInStream_Seek(void *pp, Int64 *pos, ESzSeek origin){  CFileInStream *p = (CFileInStream *)pp;  //return File_Seek(&p->file, pos, origin);  return g_func_File_Seek(&p->file, pos, origin);}
这样,7z真实的Read和Seek函数便到了我们控制的函数里。

WRes g_func_File_Read(CSzFile *p,                      void *data,                      size_t *size){    size_t originalSize = *size;    if (originalSize == 0)      return 0;    *size = 0;    do    {      DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;      DWORD processed = 0;      DWORD iCurrentPos = 0;      UInt64 iSeekPos = 0;      //BOOL res = ReadFile(p->handle, data, curSize, &processed, NULL);      //获取当前文件指针位置      ////iCurrentPos = SetFilePointer(*g_handle,NULL, NULL, FILE_CURRENT);      //memcpy(data,g_pAllData+iCurrentPos,curSize);      memcpy(data,g_pAllData+g_iSeekPos,curSize);      processed = curSize;      data = (void *)((Byte *)data + processed);      originalSize -= processed;      *size += processed;      iSeekPos = (UInt64)processed;      g_func_File_Seek(p,&iSeekPos,SZ_SEEK_CUR);      if (processed == 0)        break;    }    while (originalSize > 0);    return 0;}

真实的Read函数里直接用memcpy代替ReadFile函数,实现内存拷贝功能
WRes g_func_File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin){    LARGE_INTEGER value;    DWORD moveMethod;    DWORD iCurrentPos = 0;//    Int64 zzz;    value.LowPart = (DWORD)*pos;    value.HighPart = (LONG)((UInt64)*pos >> 16 >> 16);//    zzz = *pos;    switch (origin)    {      case SZ_SEEK_SET: moveMethod = FILE_BEGIN;g_iSeekPos=(DWORD)(*pos);break;      case SZ_SEEK_CUR: moveMethod = FILE_CURRENT;g_iSeekPos+=(DWORD)(*pos);break;      case SZ_SEEK_END: moveMethod = FILE_END;g_iSeekPos=g_iAllDataLength;break;      default: return ERROR_INVALID_PARAMETER;    }    //value.LowPart = SetFilePointer(p->handle, value.LowPart, &value.HighPart, moveMethod);    //iCurrentPos = SetFilePointer(*g_handle,NULL, NULL, FILE_CURRENT);    //printf("XXOK--iCurrentPos:%d--g_iSeekPos:%d\n",iCurrentPos,g_iSeekPos);    //g_iSeekPos = iCurrentPos;//    if (value.LowPart == 0xFFFFFFFF)//    {//      WRes res = GetLastError();//     if (res != NO_ERROR)//        return res;//    }    *pos = g_iSeekPos;    //*pos = ((Int64)value.HighPart << 32) | value.LowPart;    //printf("--zzz:%d--*pos:%d\n",zzz,*pos);    return 0;}
真实的Seek函数直接用g_iSeekPos代替SetFilePointer,注意读取文件数据到内存的同时,文件偏移也变了,所以在

WRes g_func_File_Read(CSzFile *p,                      void *data,                      size_t *size)
里有这么一句



而对于获取文件大小的File_GetLength函数,则直接赋值即可。

WRes File_GetLength(CSzFile *p, UInt64 *length){  #ifdef USE_WINDOWS_FILE  *length = (UInt64)g_iAllDataLength;  return 0;/*  DWORD sizeHigh;  DWORD sizeLow = GetFileSize(p->handle, &sizeHigh);  if (sizeLow == 0xFFFFFFFF)  {    DWORD res = GetLastError();    if (res != NO_ERROR)      return res;  }  *length = (((UInt64)sizeHigh) << 32) + sizeLow;  return 0;*/    #else    long pos = ftell(p->file);  int res = fseek(p->file, 0, SEEK_END);  *length = ftell(p->file);  fseek(p->file, pos, SEEK_SET);  return res;    #endif}

通过这节的大刀阔斧的修改、增加源码,lib7z解压原理已经很清楚了,修改也已完毕,下一节就让我们试试吧。










0 0
原创粉丝点击