精华内容
下载资源
问答
  • 同学让我帮忙处理数据,但是有365天的数据也就是365个文件夹,整体文件好几十个G,但是每个文件夹中有用的文件只有几个且很小,于是就准备写一个脚本文件对其进行批处理,反正也没学过,就搜集资料搞吧,搞好了详细...

    同学让我帮忙处理数据,但是有365天的数据也就是365个文件夹,整体文件好几十个G,但是每个文件夹中有用的文件只有几个且很小,于是就准备写一个脚本文件对其进行批处理,反正也没学过,就搜集资料搞吧,搞好了详细写给大家看嘛。

    同学的文件分布是这样的:

    其中有用的文件是这样的,且每个文件夹中都类似:

    明确了需求,直接把最后源码先看,再具体一行行讲:

    @echo off
    mkdir result
    for /f  %%i in ('"dir /ad/b/on *.*"') do (  
    echo  %%i
    xcopy %%i\met_*.* result /s /e /d /y 
    )
    pause

    下面是讲解,对应每行

    (1)@为不输出当前行,echo off是取消显示命令
    (2)mkdir是创建文件夹
    (3)for循环中,脚本语言使用%%variable做变量,要加两个百分号,变量名大小写区分,/f的话就是遍历整体里面的内容,若不加则无法遍历file,后面的dir是当前路径,加参数(只列出当前用到的),也就是只按顺序输出文件夹名:

    /A          显示具有指定属性的文件。
      attributes   D  目录                R  只读文件
                   H  隐藏文件            A  准备存档的文件
                   S  系统文件            -  表示“否”的前缀
     /B          使用空格式(没有标题信息或摘要)。
     /O          用分类顺序列出文件。
      sortorder    N  按名称(字母顺序)     S  按大小(从小到大)
                   E  按扩展名(字母顺序)   D  按日期/时间(从先到后)
                   G  组目录优先           -  颠倒顺序的前缀

    (4)输出一下文件夹名
    (5)将文件夹中符合met_.格式的文件复制到result文件夹中,*是通配符,可以代表任何字符。后面的参数意义为:

    /s 复制非空的目录和子目录。如果省略“/s”,则“xcopy”将在单个目录中工作。
    /e 复制所有子目录,包括空目录。将“/e”与“/s”和“/t”命令行选项一起使用。
    /y 禁止提示确认要覆盖已存在的目标文件。
    /d [:MM-DD-YYYY] 只复制那些在指定日期或指定日期之后更改过的源文件。如果不包括“MM-DD-YYYY”值,“xcopy”会复制比现有“Destination”文件新的所有“Source”文件。该命令行选项使您可以更新更改过的文件。
    

     

    展开全文
  • Windows Shell提取媒体信息

    千次阅读 2010-04-15 22:50:00
    这个Project有三个有趣而可以参考的地方:使用COM接口操作Windows Shell,并提取多媒体文件的标签信息编写Dll,并提供对DLL中的类显示调用的支持最小化编译时的依赖,即正确地使用#include、理清C/CPP文件和H文件的...

    这个Project有三个有趣而可以参考的地方:

    1. 使用COM接口操作Windows Shell,并提取多媒体文件的标签信息
    2. 编写Dll,并提供对DLL中的类显示调用的支持
    3. 最小化编译时的依赖,即正确地使用#include、理清C/CPP文件和H文件的关系

    为了照顾这个Project研究的逻辑思考过程,将这三点按上述顺序排列,虽然我觉得后面的更好玩一点。Moreover, the term Project here refers to its meaning in Visual Studio, rather than the meaning in Engineering of zhuangbility - -||

    最后我们将把这个Shell的API按提取多媒体文件标签的这个需要打一个包,形成一个新的库文件以供其他使用。

    1. Shell操作

    Windows Shell顾名思义就是Windows系统的外衣,能看到的日常操作都由Shell负责,而很多Shell提供的功能都作为系统API放到了DLL文件里可供调用(shell32.dll)。

    因为这次要做的是提取多媒体文件的标签信息,并且对这个信息的要求不高,即不需要提取很全面,例如mp3文件只需ID3v1标签即可满足我们的要 求。而我们可以看到,Windows Explorer已经把这些信息提取出来了。类似的,对图片和视频文件它也能提供标签信息。需要注意的是,Windows Shell仅仅提供mp3 的ID3v1标签提取,对ID3v2不予支持。即如果媒体只有ID3v2标签,此处读出来的就是空字符串。

     

    好了,确定了范围和方向之后,就是如何使用COM接口调用Shell组件读取信息这一步了。

    这里有几个概念,Shell即是外壳,Shell的基础是桌面,桌面之下衍生出很多子文件夹,以及系统的“网络”、“控制面板”、“C:/”等文件 夹,这些文件夹里又有很多层子文件夹。因此,我们想要获得一首歌的标签信息,需要首先获得桌面文件夹的对象,然后找到对应的目录,然后找到那个目录中的对 应文件,然后才能提取文件的信息。

    这里需要用到几个接口和结构体:

    • IShellFolder接口,用来定位某个文件夹,并对其下的文件和文件夹进行操作。
    • IShellFolder2接口,从IShellFolder接口继承而来,提供了一些新的功能。
    • ItemIDList, 每个文件夹或者文件都维护自己的ItemIDList,里面记录了它们的所有属性,比如文件名、类型、大小、修改时间。也就是说每个文件逻辑上都对应一个 二维表,表有一个ID列,有一个值列,每行的记录用链表实现,Windows提供了ItemIDList这样的一个结构。


     

    • EnumIDList,一个文件夹下所有的对象(文件和文件夹),形成了一个有序链表。对这个链表进行遍历即可找到所有的文件。链表的每个节点就是上面的ItemIDList

    可以以这样的树状结构来看上述概念:


    每个实际的文件夹对应一个IShellFolder,每个IShellFolder可以获得一个EnumIDList,遍历每个EnumIDList可以获得每个ItemIDList,每个ItemIDList就已经与文件一一对应。

    上面已经提到,所有文件夹的父文件夹是桌面,于是先获得桌面的IShellFolder2接口对象。

    IShellFolder* psfDesktop;
    IShellFolder2* psf2Desktop;
    SHGetDesktopFolder( &psfDesktop );
    psfDesktop->QueryInterface( IID_IShellFolder2, (void**) &psf2Desktop );
    psfDesktop->Release();

    这里使用SHGetDesktopFolder()函数获得了桌面的IShellFolder接口对象,然后通过COM的QueryInterface()方法实例化了IShellFolder2接口对象。
    为什么?首先我们肯定需要一个对应的IShellFolder2接口来提取信息,这个接口是否可以留到调用它的增强功能之前再实例化我没有确认,不过既然它继承了IShellFolder并提供了更多的功能,我就打算从最开始就实例化它。
    为什么要用IShellFolder来实例化这个IShellFolder2?QueryInterface()函数按照COM原理是从IUnknown 继承来的,因此理论上只要任何一个COM对象都可以通过QueryInterface( IID_IShellFolder2, (void**) &psf2Desktop );来实例化IShellFolder2。使用IShellFolder来担任此工作也是因为SHGetDesktopFolder()使用较方便。
    另外,SHGetDesktopFolder获得的psfDesktop一定是与桌面绑定的,而此时我们实例化的psf2Desktop是否已经与桌面相关了我没有确认。

    接下来的工作就是定位到文件上,我们需要获得文件的ItemIDList。

    LPITEMIDLIST pTargetPathID;
    IShellFolder2* psf2Folder;
    // 定位文件所在的文件夹,wFilePath为文件夹路径
    psf2Desktop->ParseDisplayName( ::GetActiveWindow(), NULL, wFilePath, NULL, &pTargetPathID, NULL );
    // 将定位得到的文件夹路径绑定到IShellFolder2接口的对象上去
    psf2Desktop->BindToObject( pTargetPathID, NULL, IID_IShellFolder2, (void**) &psf2Folder );
    // 此时psf2Folder就已经指向对应的文件夹了,接下来我们需要找到文件。
    // 枚举这个文件夹下的内容放到pEnum这个链表里
    LPENUMIDLIST pEnum;
    psf2Folder->EnumObjects( ::GetActiveWindow(), SHCONTF_NONFOLDERS, &pEnum );
    STRRET retFile;
    char szFilename[ MAX_PATH ];
    while ( pEnum->Next( 1, &pFileItemID, &uEleFetched ) == S_OK ) {
    ZeroMemory( szFilename, MAX_PATH );
    // 按照完整文件名格式获得文件名
    psf2Folder->GetDisplayNameOf( pFileItemID, SHGDN_FORPARSING, &retFile );
    StrRetToBuf( &retFile, pFileItemID, szFilename, MAX_PATH );
    if ( m_sTargetFile.compare( szFilename ) == 0 ) break;
    }

    此时我们获得了指定文件的ItemIDList,既然属性都在里面,那就可以开始提取了。

    // get title, column 21
    ::CoInitialize( NULL );
    HRESULT hr = psf2Folder->GetDetailsOf(pFileItemID, 21, &shDetail );
    if ( hr == S_OK ) {
    ZeroMemory( szContent, MAX_PATH );
    StrRetToBuf( &(shDetail.str), pFileItemID, szContent, MAX_PATH );
    m_sTitle = string( szContent );
    }

    这样就获得了音乐文件的标题,存入了m_sTitle成员变量里。GetDetailsOf()函数中的数字即是ID号,至于当前文件夹支持多少ID号,可以给第一个参数以NULL,然后使用循环打印m_sTitle就能知道当前ID对应什么信息。即:

    for ( int id = 0; id < 1000; ++id ) {
    psf2Folder->GetDetailsOf( NULL, id, &shDetail );
    // 打印shDetail内容
    }

    另外,使用GetDetailsEx()函数可以不用使用ID号,但我做了XP到Win7的迁移后发现GetDetailsEx()好像也没有能跨 越平台障碍,所以索性还是用GetDetailsOf()了。注意上面提取标题时的::CoInitialize( NULL );这表示初始化COM对象。没有这一句,所有的文件夹都只能提取出前几个ID对应的文件名、类型、修改时间、大小等基本信息,无法提取出标题、专辑等特 别的信息。一个文件能提取出什么样的信息与所使用的IShellFolder2有关。

    此外注意GetDetailsOf()的平台差异,WinXP上提取出来的东西比较贫乏,Vista和Win7能提取的标签就很丰富,但是与 WinXP相同的部分在ID编号上有变化。所以这个方法需要对XP和Vista做两套平台的库文件,并需要在运行时检查系统的版本号,动态载入不同的库文 件。

    2. Dll调用

    Dll(即dynamic link library)在编译后至少会有a.dll和a.lib两个文件。这样导入DLL就有三种方式:

    1. 使用lib直接链接;
    2. 使用lib并启用delay load;
    3. 使用dll动态导入。

    粗略地说,lib中记录了dll的函数入口,编译自己程序时链接器里加入lib即可在运行时使用dll内的函数。这样的程序在启动时就会载入 dll,如果目标机器上不存在,那么就会给出“应用程序不能运行,需要重新安装”之类的提示。而delay load是VC6之后较新的版本提供的功能,即将dll的载入延迟到需要调用它的函数的时候。如果目标机器没有dll,那程序依然能够启动,但是要执行函 数的时候会发生不友好的异常错误。而使用dll动态导入,就是在代码里载入dll的导出函数,程序可以在需要时载入它,一些实现不同语言、添加插件等功能 就可以使用这种方式来实现。下面主要说第三种方式。

    使用C语言即可调用系统API来动态导入dll。首先LoadLibrary()载入Dll返回句柄,GetProcAddress()使用句柄返 回函数指针,FreeLibrary()使用句柄释放dll。这三个DLL套装的详细用法和示例可以查阅MSDN。但是,它们只能导出函数,而在C++里 需要导出一个类时,就得用其他办法了。

    首先,按照DLL的一贯做法,导出函数和导出类都要有__declspec(dllexport),在导入的地方声明这些函数时,相对地要有__declspec(dllimport)。因此我们使用了这样的一个宏定义:

    #ifndef _NMP_API_
    #ifdef _WINSHELLLIBRARY_EXPORTS_
    #define _NMP_API_ __declspec(dllexport)
    #else
    #define _NMP_API_ __declspec(dllimport)
    #endif
    #endif

    然后就可以使用如下的方式声明导出类:

    class _NMP_API_ CAudioInfo : public CMediaInfo { ... };

    使用如下方式声明导出函数(extern "C"的作用见本节最后):

    extern "C" _NMP_API_ CAudioInfo* GetAudioInfo();
    extern "C" _NMP_API_ CAudioInfo* GetAudioInfoByFilename( const char* );

    在这个头文件对应的CPP实现文件里首先加上:

    #define _WINSHELLLIBRARY_EXPORTS_
    #include "MediaInfo.h"

    然后按照原有方式实现CAudioInfo类,按照如下方式实现导出的函数:

    extern "C" _NMP_API_ CAudioInfo* GetAudioInfo() {
        return ( new CAudioInfo() );
    }

    按照原本方法,在头文件里添加对应的指针:

    typedef CAudioInfo* (*LPNMPGetAudioInfo)();
    typedef CAudioInfo* (*LPNMPGetAudioInfoByFilename)( const char* );

    这样,通过导出函数,我们就能获得对应的类的指针,这样既可实现导出类。并且此时这个头文件我们就可以用到需要调用dll的地方了。在调用DLL的cpp里,如下:

    #include "..//WinShellLibrary//MediaInfo.h"
    ...
    HMODULE hMediaDll = LoadLibrary( "..//RELEASE//WinShellLibrary.dll" );
    char szAnsiName[] = "E://AAA//BBB.mp3";
    LPNMPGetAudioInfoByFilename pfnAudio;
    pfnAudio = (LPNMPGetAudioInfoByFilename) ::GetProcAddress( hMediaDll, "GetAudioInfoByFilename" );
    CAudioInfo* audio = pfnAudio( szAnsiName );
    // 此处添加调用该类的对象的应用
    delete image;
    FreeLibrary( hMediaDll );

    上述代码段中,通过函数指针pfnAudio来执行函数的调用。

    仅仅这样,把上述思想应用到实际时,编译依然会报错。还缺什么呢?DLL文件作为一个独立的Project可以正常地Build,但是调用DLL的 文件却无法链接成功。在链接时无法找到对CAudioInfo类的成员函数,这里我做了一个测试,一个类成员函数仅做类内声明,在类外却并不实现它的话, 这个cpp编译是正常的,但如果这个成员函数被调用了,linker就会提示找不到。这说明类成员函数仅仅声明是可以通过编译的,但是调用时链接器无法找 到它。反观上面的调用DLL的cpp,我们也是仅仅把头文件包含进来,这不是一样的效果吗?

    那么,怎么才能让成员函数在外面被调用?

    这里又有很多种办法,我采取了其一,其他的方法可以参考最后的参考资料。参考《C++ Primer》第四版,15.2.4,类内的虚函数编译后会有一个VTable表,因此加了virtual关键字的非纯虚函数,在编译时一定会被要求有实 现,链接时可以通过VTable里的指针来找到对应函数。

    所以,将所有要导出的成员函数,包括析构函数,都加上virtual关键字(因为delete操作会调用析构函数),之后就可以正常编译了。

    3. 最小化编译依赖

    这需要我们理顺Project里各个.h和.cpp文件之间的关系。比如我们建立这样两个类,放到四个文件里:

    A.h

    A.cpp

    B.h

    B.cpp

    #pragma once
    #include "b.h"

    class A {
    public:
    A(void);
    ~A(void);

    B* m_b;
    };

    #include "StdAfx.h" #include "A.h"

    A::A(void) { }

    A::~A(void) { }

    #pragma once
    #include "a.h"

    class B {
    public:
    B(void);
    ~B(void);

    A* m_a;
    };

    #include "StdAfx.h"
    #include "B.h"

    B::B(void) { }

    B::~B(void) { }

    很简单的两个类,每个类内有一个指向对方类一个对象的指针。也许这两个类的设计有点问题,但也确有这种可能——比如数据库两个表是一对一的关系,而 我们使用C++来对这两个表进行面向对象的抽象,那可能就会形成这种类的设计思路。按照以前的想法,很正常啊,A类里要有一个B类的指针,那就在开始把 b.h包含进来,B类要有个A类指针,那就也把a.h包含进来吧。

    编译——6个错误。再看一遍源码,哪有语法错误啊,这让人怎么改?

    于是我们需要明白.h和.cpp文件的意义,参考《Exceptional C++》的Item 26到Item 30。首先,.h文件是头文件,header文件,头文件是干什么的?包含用的,头文件不会参与编译,只有在.cpp里用.h时,.h里内容才有意 义。#include "A.h"意义是原封不动地把a.h文件的内容在这一行完全展开。既然编译器只会去编译.cpp文件,并且在cpp中将.h文件展开,那我们自己展开来看 看?以a.cpp为例,A.h要展开,又遇到了b.h要展开,好吧继续展开,b.h又要展开a.h?因为有#pragma once的预编译指令,于是展开工作到此结束。

    最后,在a.cpp完全展开之后,”b.h”留在展开的内容的最上面,好了,b.h文件内容是什么呢,有个A* m_a,A是什么?A不是个类吗,不是包含过了吗?很遗憾,在最后展开的文件里,A的内容在下面呢,因为#pragma once作祟,最后需要的a.h没有展开,那就去掉a.h的#pragma once呢?那A就会是一个重复定义的类,同样收到一堆错误。

    诶?重复定义?是不是可以有办法解决了?定义是完全写出类或者全局函数的内容,声明则是通知编译器这个东西类型和名字是什么。也就是说,把 declaration放到前面,把implementation放到后面,不就结了?前面指的当然就是类的头文件里,后面指的就是CPP文件。于是修改 代码如下:

    A.h

    A.cpp

    B.h

    B.cpp

    #pragma once
    class B;
    class A {
    public:
    A(void);
    ~A(void);

    B* m_b;
    };

    #include "StdAfx.h"

    #include "b.h"
    #include "A.h"

    A::A(void) { }

    A::~A(void) { }

    #pragma once
    class A;
    class B {
    public:
    B(void);
    ~B(void);

    A* m_a;
    };

    #include "StdAfx.h"

    #include "a.h"
    #include "B.h"

    B::B(void) { }

    B::~B(void) { }

    在.h文件中,只留下最简单的声明,在cpp文件中如果用到了再包含要使用的东西。这样即成功编译。其实在上例中,就算去掉.cpp文件中对对方类的包含也能通过,因为没有对m_a,m_b成员进行操作。

    在这个提取文件信息的项目中,我自己的机器是Win7+VS2008,但是工作的机器是XP+VC6。对IShellFolder2的操作是在 Windows SDK里才有的,VC6出的比较早,最后的更新是到Windows 2003的一个SDK。Windows SDK也是后来更名的,之前叫做Platform SDK。机房机器装的VC6没有办法使用一些Shell相关的函数和接口,也没有shlwapi.h和shlwapi.lib等文件了。

    于是我采用了这个减少编译依赖的方法去做。首先,因为按照第二点的思路制作的DLL文件仍然需要在调用它的Project里包含DLL的.h文件, 这是库文件的必然。但是VC6没法认这个有一些Shell接口成员声明的.h文件。按照《Code Complete》(代码大全)第二版一书6.2节关于隐藏类实现达成良好封装的叙述,将所有有关Shell操作的接口形成一个单独的实现类 CMediaImp,将CMediaImp的声明放到这个类里,将此类的成员放到该类的实现文件中。这样在.h文件里就没有了Shell的内容,但cpp 在编译时能正常找到Shell的操作。

    此时将这个库编译成DLL,并随库提供DLL的.h头文件,交给使用该库的程序员,他在工作的机器环境VC6上就能正常编译使用这个库了。反之,如 果不这么做的话,DLL是正常了,但是该程序员在引用了随库的头文件时依然会遇到编译无法通过,缺少Shell接口相关声明的问题。

    4. 小结

    至此,项目结束。附上一些较好的参考材料:

    1.  
      1. DLL导出类,显示链接到DLL中的类
      2. 一步一步教你DLL,第四部分,DLL动态导入
      3. DLL很简单,第一部分第二部分第三部分第四部分
      4. Shell操作,在应用程序中集成外壳的上下文菜单
      5. 《Exceptional C++》, Item 26 ~ Item 30

     

     

     

    本文转载自:http://hi.baidu.com/ecluytj/blog/item/de28cdbfbb2e4d0318d81f4d.html

    展开全文
  • 1.C语言创建程序 1.1C语言创建(分为4个步骤) 编辑 ...编译器的输出结果成为目标代码,存放它们的文件称为目标文件。扩展名为.o或者.obj。 (该部分编译是指汇编器编译汇编语言或者编译器编译...

    转自:https://www.jianshu.com/p/7c609b70acbd

    1.C语言创建程序

    1.1C语言创建(分为4个步骤)
    • 编辑
    • 编译
    • 链接
    • 执行

    编辑:就是创建和修改C程序的源代码-我们编写的程序称为源代码。
    编译:就是将源代码转换为机器语言。编译器的输出结果成为目标代码,存放它们的文件称为目标文件。扩展名为.o或者.obj。
    (该部分编译是指汇编器编译汇编语言或者编译器编译高级语言)
    链接:链接器将源代码由编译器产生的各种模块组合起来,再从C语言提供的程序库中添加必要的代码模块,将它们组成一个可执行的文件。在windows下扩展名为.exe,Unix下无扩展名。
    执行:运行程序。

    C.png
    1.2什么是源代码,目标文件,可执行文件。

    源代码 ——源文件就是存放程序代码的文件。通常我们编辑代码的文件就是源文件。

    • 源代码相对目标代码和可执行代码而言的。
    • 源代码就是用汇编语言和高级语言写出来的地代码。

    目标文件——指源代码经过编译程序产生的能被cpu直接识别二进制代码。

    • 目标代码指计算机科学中编译器或汇编器处理源代码后所生成的代码,它一般由机器代码或接近于机器语言的代码组成。
    • 目标文件包括着机器代码(可直接被计算机中央处理器履行)和代码在运行时使用的数据,如重定位信息,如用于链接或调试的程序符号(变量和函数的名字),另外还包括其他调试信息。
    gcc -c main.c 
    编译main.c ,生成目标文件main.o,但不进行link. 
    gcc -o main.o
    链接成可执行文件main

    可执行文件——可执行代码就是将目标代码连接后形成的可执行文件,当然也是二进制的。 连接程序系统库文件连接就生成可执行文件。

    例如:*.obj是程序编译之后生成的目标文件,连接程序再将这个文件与系统库文件连接就生成可执行文件
    
    1.3链接器的作用
    Screen Shot 2017-06-12 at 4.32.06 PM.png

    根据上面的图,我们可以看到链接器还额外链接了2个部分。

    目标代码文件中所缺少的第一个元素是一种叫做启动代码(Start-up code)的东西,此代码相当于您的程序和操作系统之间的接口。例如你可以在dos 或Linux下运行一个 IBM PC 兼容机,在两种情况中硬件是相同的,所以都会使用同样的目标代码,但是 DOS与Linux要使用不用的启动代码,因为这两种系统处理程序的方式不同的。

    所缺少的第二个元素是库例程的代码。几乎所有C程序都利用标准库中所包含的例程(称为函数)。例如,程序中的函数printf()。目标代码文件不包含这一函数的指令。实际代码存储在另一个称为“库”的文件中,库文件中包含许多函数的目标代码。

    链接器的作用是将这3个元素(目标代码、系统的标准启动代码和库代码)结合在一起,并将他们存放在单个文件,即可执行文件中。对库代码来说,链接器只从库中提取您所使用的函数所需的代码。

    可以得出结论:目标文件和可执行文件都是由机器语言指令组成的。但目标文件只包含您所编写的代码转换成的机器语言,而可执行文件还包含您所使用的库例程以及启动代码。

    下面这幅图能大致说明一下链接的情况。


    1.png

    这是一个main.o目标代码,内部有main,foo,bar三个函数。

    U main表示main这个符号在crtl1.o中用到了,但是没有定义。因此需要main.o提供定义并和crtl1.o链接在一起。main整个程序的入口实际上是_crtl1.o中的 _start,它做了一些初始化工作(启动历程),然后调用C代码中提供的main.c函数。libc是运行时候动态链接libc共享库(库中包含常用的函数)。

    所以程序的入口点其实是_start,main函数实际上是被_start调用。

    1.4gcc命令图
    QQ截图20170613151139.png

    2.ELF文件(该部分分析目标文件和可执行文件的,涉及部分汇编指令)

    ELF文件格式是一个开放标准,各种UNIX系统的可执行文件都采用ELF格式,它有三种不同的类型:

    • 可重定位的目标文件
    • 可执行文件
    • 共享库

    ELF文件格式提供了两种不同的视角,在汇编器和链接器看来,ELF文件是由Section HeaderTable描述的一系列Section的集合,而执行一个ELF文件时,在加载器(Loader)看来它是 由Program Header Table描述的一系列Segment的集合。如下图所示。


    Screen Shot 2017-06-10 at 10.01.54 PM.png

    左边是从汇编器和链接器的视角来看这个文件,开头的ELF Header描述了体系结构和操作系统 等基本信息,并指出Section Header Table和Program Header Table在文件中的什么位 置,Program Header Table在汇编和链接过程中没有用到,所以是可有可无的,Section Header Table中保存了所有Section的描述信息。右边是从加载器的视角来看这个文件,开头 是ELF Header,Program Header Table中保存了所有Segment的描述信息,Section Header Table在加载过程中没有用到,所以是可有可无的。注意Section Header Table和ProgramHeader Table并不是一定要位于文件开头和结尾的,其位置由ELF Header指出,上图这么画只是为了清晰。

    目标文件需要链接器做进一步处理,所以一定有Section Header Table;可执行文件需要加载运行,所以一定有Program Header Table;而共享库既要加载运行,又要在加载时做动态链接, 所以既有Section Header Table又有Program Header Table。

    • section:C语言内存中的.text,.data,.bss.....
    • Segment:是指在程序运行时加载到内存的具有相同属性的区域,由一个或多个Section组成,比如有两个Section都要求加载到内存后可读可写,就属于同一个Segment。有些Section只对汇编器和链接器有意义,在运行时用不到,也不需要加载到内 存,那么就不属于任何Segment 。

    2.1重定位目标文件

    在进行该部分之前,我们先查看一下网上的部分重定位目标文件的资料。

    资料一:

    汇编器所产生的目标文件至少包括三个区,即文本区(text),数据区(data)和bss区。文本区一般包括程序的代码和常量,数据区通常存放全局变量等内容,bss区用于存放未初始化的变量或作为公共变量存储空间。在一个目标文件中,其text区从地址0开始,随后是data区,再后面是bss区。而要运行程序,必须装载到内存中,所以这些区的地址需要在内存中重新安排,也就是重定位。

    资料二:

    编译器编译后产生的目标文件是可重定位的程序模块,并不能直接运行,链接就是把目标文件和其他分别进行编译生成的程序模块(如果有的话)及系统提供的标准库函数连接在一起,生成可运行的可执行文件的过程。
    重定位是链接器在完成符号解析后(知道了各个输入模块的代码段和数据段的大小)的一个步骤,其作用顾名思义就是重新定位,确定比如指令,全局变量等在运行时的存储器地址。

    资料三:

    比如说两个编译后的可重定位目标文件obj1.o和obj2.o
    在obj1.o里面定义了一个全局变量glob(在obj1里面记录了glob相对于该文件数据段的相对地址), 而obj2.0里面又引用了这个全局变量glob。
    链接的重定位就是要确定在链接后的可执行程序中glob的地址,而不是相对于obj1的地址,从而使obj2也能通过地址调用glob。
    当然重定位并不只是全局变量,还包括外部函数,指令等运行时地址的确定

    资料四:

    当你在程序中写上一个全局变量或者是一个函数时,这个定位过程会经历几个阶段:
    1.在这个目标文件中的相对定位,一个目标文件中对此文件中的所有函数,变量进行符号描述,比如一个变量A,它所占的相对地址是多少?是全局的?或者是静态的,或者是外部的??
    2.在连接多个目标成一个可执行文件时,会再次对这个变量进行重定位,也就是在这个可执行文件中进行对此变量进行描述,同目标文件中的描述差不多,只不过此变量不再有外部,内部之分,都成了本地变量,并且会将所有全局变量存放在一定的逻辑地址中,这是通过连接脚本文件与各个目标文件中的相对地址共同决定的
    3.最终的操作系统加载这个可执行文件时,会对这些变量与函数地址再次进行重定位,其方式就是首先分析这个可执行文件中的不同段,读出相应的描述表,然后通过逻辑地址与物理地址进行映射出,最终就将可执行的二进制码加进了真实的物理内存了,关于分析可执行文件格式与物理地址的转换,不同的CPU与操作系统的实现方式会有不同之处

    接下来我们开始实践部分,首先写一个求一组数的最大值的汇编程序max.s。

    Screen Shot 2017-06-11 at 3.42.29 PM.png

    现在有一个max.o目标文件,我们用readlf工具读取其ELF Header和Section Header Table


    Screen Shot 2017-06-10 at 10.20.59 PM.png

    ELF Header中描述了操作系统是UNIX,体系结构是80386。Section Header Table中有8个Section Header,从文件地址200
    (0xc8)开始,每个Section Header占40字节,共320字节,到文件地址0x207结束。这个目标文件没有Program Header。文件地址是这样定义的:文件开头第一个字节的地址是0,然后每个字节占一个地址。

    QQ图片20170611130217.png

    从Section Header中读出各Section的描述信息。
    Addr是这些段加载到内存中的地址(程序中的地址都是虚拟地址),加载地址要在链接时填写,现在空缺,所以是全0。
    OffSize两列指出了各Section的文件地址,比如.data段从文件地址0x60开始,一共0x38个字节,回去翻一下程序,.data段定义了14个4字节的整数,一共是56个字节,也就是0x38。

    根据以上信息可以描绘出整个目标文件的布局。

    QQ图片20170611132839.png

    ** Section Header Table**:读出各Section的描述信息。
    .shstrtab:保存着各Section的名字,比如.text,.data.....。
    .strtab:保存着程序中用到的符号的名字.比如汇编程序的start_loop:和loop_exit符号。(对应的就是for循环)。
    **.data **:保存程序中已初始化的全局变量和静态变量以及字符串常量。
    .bss:存放程序中未初始化的全局变量和静态变量。
    .text:存放程序执行代码。
    .rel.text:告诉链接器指令中的哪些地方需要做重定位。
    下节分析。

    Screen Shot 2017-06-11 at 2.40.30 PM.png

    我们看一下.text段内容


    Screen Shot 2017-06-11 at 3.44.53 PM.png

    ![Uploading QQ截图20170613113520_952796.png . . .]

    text段代码中,一些跳转指令和内存访问指令中的地址都是符号的相对地址,下一步链接器要修改这些指令,把其中的地址都改成加载时的内存地址,这些指令才能正确执行。

    2.2可执行文件

    现在分析可执行文件max。


    Screen Shot 2017-06-11 at 2.45.59 PM.png
    Screen Shot 2017-06-11 at 2.47.07 PM.png
    Screen Shot 2017-06-11 at 2.47.43 PM.png
    Screen Shot 2017-06-11 at 2.48.05 PM.png

    在ELF Header中,Type改成了EXEC,由目标文件变成可执行文件了多了两个Program Header,少了两个Section Header。

    在Section Header Table中,.text和.data的加载地址分别改成了0x0804 8074和0x0804 90a0。.bss段没有用到,所以被删掉了。.rel.text段就是用于链接过程的,链接完了就没用 了,所以也删掉了。

    多出来的Program Header Table描述了两个Segment的信息。.text段和前面的ELF Header、Program Header Table一起组成一个Segment(FileSiz指出总长度 是0x9e),.data段组成另一个Segment(总长度是0x38)。VirtAddr列指出第一 个Segment加载到虚拟地址0x0804 8000,第二个Segment加载到地址0x0804 90a0。Flg列指出第一个Segment的访问权限是可读 可执行,第二个Segment的访问权限是可读可写。

    Screen Shot 2017-06-11 at 2.54.13 PM.png

    原来目标文件符号表中的Value都是相对地址,现在都改成绝对地址了。

    我们查看一下.txt段内容。


    Screen Shot 2017-06-11 at 3.32.25 PM.png
    现在我们对比一下目标文件和可执行文件的不同。

    目标文件.text和.data段地址


    QQ截图20170613113520.png

    可执行文件.text和.data段地址


    QQ截图20170613113500.png

    目标文件中跳转指令


    Screen Shot 2017-06-11 at 3.35.56 PM.png

    可执行文件中跳转指令


    Screen Shot 2017-06-11 at 3.36.18 PM.png

    目标文件中内存访问指令


    Screen Shot 2017-06-11 at 3.37.08 PM.png

    可执行文件中内存访问指令


    • 可以看到指令中的相对地址都改成绝对地址了。
    • 结合上2部分分析,我们可以看到。
    • .text和.data段代码加载到内存中的地址由空缺0变成了具体地址。
    • .text段代码中一些跳转指令和内存访问指令中的地址由相对地址改成加载时的内存地址,
    • .data段代码也由相对地址改为绝对地址。

    3.静态库和共享库

    :有时候需要把一组代码编译成一个库,这个库在很多项目中都要用到,例如libc就是这样一个库,我们在不同的程序中都会用到libc中的库函数(例如printf)。

    共享库和静态库的区别:在链接libc共享库时只是指定了动态链接器和该程序所需要的库文件,并没有真的做链接,可执行文件调用的libc库函数仍然是未定义符号,要在运行时做动态链接。而在链接静态库时,链接器会把静态库中的目标文件取出来和可执行文件真正链接在一起。

    • 静态库链接后,指令由相对地址变为绝对地址,各段的加载地址定死了。
    • 共享库链接后,指令仍是相对地址,共享库各段的加载地址并没有定死,可以加载到任意位置。

    静态库好处:静态库中存在很多部分,链接器可以从静态库中只取出需要的部分来做链接 (比如main.c需要stach.c其中的一个函数,而stach.c中有4个函数,则打包库后,只会链接用到那个函数)。另一个好处就是使用静态库只需写一个库文件名,而不需要写一长串目标文件名。

    该部分是参照《一站式学习C编程》所做的总结,理解程度比较初步。

    展开全文
  • Windows 2000 或 XP 系统出现故障的时候,通常我们可以通过"最后一次正确配置"、"安全模式"、"系统还原"(只用于XP)来修复。但是有一些情况是不能通过这些办法来修复的,例如:系统引导文件丢失、BOOT.INI 文件丢失...
    Windows 2000 或 XP 系统出现故障的时候,通常我们可以通过"最后一次正确配置"、"安全模式"、"系统还原"(只用于XP)来修复。但是有一些情况是不能通过这些办法来修复的,例如:系统引导文件丢失、BOOT.INI 文件丢失或配置错误、系统DLL丢失等等。这个时候,重新安装操作系统虽然是个有效的方法,但是要花费很长的时间。其实我们还可以通过恢复控制台来做最后的尝试。使用恢复控制台,我们不但可以进行包括启用和禁用系统服务、分区和格式化磁盘、修复引导记录等操作,还可以通过复制源光盘的文件来修复丢失系统文件的错误等等。

    恢复控制台虽然很实用,但是也是有一些限制:

    要使用恢复控制台,必须知道系统管理员的账号和苈耄?

    系统控制台里面只能访问以下的文件夹:引导文件夹、系统目录、可移动存储设备。如果访问其它文件夹,系统会提示"Access Denied"(拒绝访问);

    在恢复控制台,可以把文件从光、软盘复制到硬盘,或者是硬盘复制到硬盘。但是不能从硬盘复制到软盘。

    二. 启动恢复控制台

    要进入恢复控制台,首先要用OS光盘启动,在出现"欢迎使用安装程序"的界面,我们可以看到第二项提示是"要使用/'恢复控制台/'修复 Windows XP安装,请按 R"。

    在按了R键之后,安装程序会对磁盘进行检查。稍等片刻后,屏幕上会列出已经找到的操作系统及其安装目录,并且会自动编号。系统会询问你要登录到哪一个Windows系统,我们只要输入系统前面的序号,然后回车(千万不要直接按回车!系统会重新启动的!),然后会询问管理员的密码,输入之后按回车,这样我们就进入了修复控制台

    三.常用的系统故障恢复命令

    1. Bootcfg:用于对启动文件BOOT.INI的配置和恢复(Win 2K没有该命令)
    2. Disable 和 Enable:用于禁用/启用系统服务或设备驱动程序
    3. Expand:用于从压缩文件中提取文件
    4. FixBoot:重新写入新的分区引导区到指定的系统分区
    5. FixMBR:修复启动磁盘的主引导记录
    6. Help:显示帮助
    7. Listsvc:列出该系统上所有的系统服务和设备驱动程序(配合Disable/Enable使用)
    8. Set:显示和设置环境变量

    四.修复常见系统启动故障

    (一) 系统引导区损坏

    通常是因为被病毒损坏,或者是使用一些第三方的磁盘工具,造成引导扇区内容被破坏。现象是到OS启动的阶段就停着不动,或提示没有可启动的磁盘。其实用恢复控制台可以很简单地恢复这种错误。进入恢复控制台,输入FIXBOOT,系统会提示是否确定要写入新的启动扇区到目标磁盘分区,按Y然后回车,系统写入成功后,按EXIT重新启动就可以了。

    如果还是不行,有可能是主引导记录(MBR,Main Boot Record)损坏了,这时候我们就要用到FixMBR这个命令了,但是这个命令风险比较大,有可能损坏分区信息造成数据丢失,所以不到最后千万不要用!

    使用方法跟FIXBOOT一样,进入恢复控制台,输入FixMBR,系统会有警告信息(,确定要执行的话,按Y然后按回车就可以了,然后再恢复控制台的提示符状态按EXIT重新启动。

    (二) 系统引导文件损坏或丢失
    当系统启动时,提示找不到NTLDR或者NTDETECT.COM时,系统要求重新启动。这种情况多发生在同一分区装多系统,或某些误操作,删除了系统根目录下面的文件。

    其实这两个文件都可以在安装光盘的i386目录下找到,直接复制到系统盘根目录就可以了。首先进入恢复控制台,然后输入(假设光盘盘符是D盘):

    "COPY D://i386//NTLDR C://" 或 "COPY D://i386//NTDETECT.COM C://"

    (如提示文件存在,覆盖就可以)然后敲EXIT重新启动即可。

    (三) 引导配置文件BOOT.INI损坏或丢失

    这种情况多发生在安装多系统时顺序不对,或者某些误操作引起的。保存有系统多重启动菜单的BOOT.INI丢失或被破坏之后,启动菜单会消失(虽然如果你只有一个系统的时候,它会以默认目录启动,但是还是会有一个错误提示,。

    其实这个错误很简单,我们可以使用恢复控制台的BOOTCFG命令来重建BOOT.INI文件。首先进入恢复控制台,然后在提示符下输入:BOOTCFG /REBUILD,然后系统会自动扫描所有磁盘以寻找可以添加到启动菜单的操作系统。扫描完毕之后,系统会列出能找到的操作系统,然后你可以选择哪一个加载到BOOT.INI(按Y表示"是",按N表示"不",按A表示"全部")。然后在提示"输入加载识别符"时输入这个启动项目的名称,提示"输入OS加载选项"时输入启动参数(通常是FASTDETECT,也可以是其它,如NODETECT等),然后敲回车就回到恢复控制台的提示符,敲EXIT重启,启动菜单就重新出现了。

    (四) 系统文件丢失

    有时候会碰到这种情况,系统启动的过程中,提示仔 Windows//System32 下面有某个文件丢失,系统无法启动,要求重新安装这样子。造成这种情况的原因有很多,通常是安装卸载一些软件的时候,被删除或替换了某些重要的系统文件,因而造成系统无法启动,以下是一个例子。

    针对这种情况,我们可以使用恢复控制台的EXPAND或COPY命令来进行修复。进入恢复控制台,然后进入光盘下的i386文件夹,首先要确定我们丢失的文件是不是压缩过的。如果没有压缩过,我们直接复制到目标位置就可以了就可以了,例如NTDLL.DLL;如果是压缩的,就要用到EXPAND命令了,例如NTDSBCLI.DL_(只要后缀名最后一个是下划线的就是压缩过的)。

    COPY的格式是:COPY(空格)来源文件(空格)目标文件夹
    例如:COPY D://i386//NTDLL.DLL C://Windows//System32

    EXPAND格式也是:EXPAND(空格)来源文件(空格)目标文件夹
    例如:EXPAND D://i386//NTDSBCLI.DL_ C://Windows//System32
    展开全文
  • 提取文件夹中所有文件名(Windows

    千次阅读 2017-02-24 12:51:25
    以下为一个很好的方法利用系统批处理文件来进行文件名称的提取。先在需要提取名称的文件夹内新建一个文本文档(即.txt文件)。 打开新件的文本文档,并输入“DIR *.* /B > 文件名称列表.txt”,保存后关闭。 将新建...
  • windows bat批处理复制文件操作

    千次阅读 2019-09-25 12:11:28
    windows bat批处理复制文件操作(提取同类型文件到另一个文件夹) 0.1492019.03.30 10:47:55字数 351阅读 1428 同学让我帮忙处理数据,但是有365天的数据也就是365个文件夹,整体文件好几十个G,但是每个文件夹中...
  • windows批处理字符串提取

    千次阅读 2016-05-05 15:12:56
    Windows \ PPP \a.btx  call :deal aaa %aa% "c c" ddd eee  pause>nul  exit   :deal  echo %%0 = % 0  echo %%1 = % 1  echo %%2 = % 2  echo %%3 = % 3  echo %%4 = % 4  echo %%5 = %...
  • [color=red][b]我的电脑开机后,提示:因以下文件的损坏或丢失WINDOWS无法启动 同上,SYSTEM32\DRIVERS\FASTFAT.SYS你可以通过使用原始启动盘或CD-ROM来启动WINDOWS安装程序,以便修复这个文件,在第一屏时选择R,开始...
  • ELF文件、目标文件、可执行文件的关系 目标文件是源代码经过编译但未进行链接的那些中间文件,在linux中的.o文件,它跟可执行文件的内容与结构很相似,所以一般与可执行格式采用一种方式存储,在linux下,我们可以...
  • 从ROS bag文件提取图像

    万次阅读 2016-03-18 09:02:20
    从ROS bag文件提取图像
  • 一个文件,双击打开时是一张图片,将后缀改成.zip 即可变成一个压缩文件,解压可以提取其中的文件 2、应用 1、可以对大部分人隐藏真实文件 2、相对于创建隐藏文件,该方式更加隐秘 3、发给别人时可以装逼 4、秀操作 ...
  • Windows batch文件学习笔记

    千次阅读 2018-03-02 09:36:38
    Windows batch文件学习笔记 运行程序 call 语法: call [ [Drive:] [Path] FileName [BatchParameters]][:label [arguments]] 参数: [Drive:][Path] FileName 指定要调用的批处理程序的位置和名称。Filename ...
  • Windows补丁文件是*.msu格式, 这是一种压缩文件格式. 正常情况下,只需要双击这个文件就可以...我们所需要的目标文件,就在那个.cab文件包里面. 继续用7-zip打开这个.cab包,发现如下文件结构: 看上去好像正常打开...
  • 下图是我使用Robocopy复制文件的输出日志,可以看出效率是非常不错的,复制59.2G文件2729个文件152个文件夹,只用27分59秒 Robocopy是windows server 2003 resource kit工具箱中的一个工具,需要下载windows server...
  • 公司服务器用来备份数据的硬盘过段时间就会被备份文件占满,弄得我老是要登录到服务器去手工删除那些老的文件,有时忘记了就会导致硬盘空间不足而无法备份。 因为只要保留最近几天的备份,如果可以做一个批处理让...
  • Windows PowerShell读取文本文件

    万次阅读 2013-06-17 14:59:38
    示例: [code] i=0 to=3  #iteration number while ( i−le to ) { cd (Get-Content dir.txt)[$i] rm gb.gro ..\dump2gro_2000.exe ...尽管对文本文件的需要大概在很久以前就消失了,但是系统管理员仍
  • Windows中快速获取文件目录的方法

    千次阅读 2014-01-07 12:35:14
    计算机使用时间越长,硬盘中保存的文件就会越来越多,如果不对这些众多的文件创建合适的目录,日后要寻找某个文件将变得非常困难。为了提高文件定位的效率创建方便快捷的文件目录,下面有如下几则快速生成文件目录的...
  • 这周做了一个新功能,就是全自动地把微信的语音文件提取出来,然后转换成文字,一来是一目了然,二来是方便检索。未曾想到的是,在这个过程中,碰到了几个不大不小的坎,踩到了几个不深不浅的坑,在此逐一记录解决...
  • ophone 1.5陆续更新,苦于移动MM目前资源偏少,android market的免费资源...可惜在ophone在无法安装android market,现在只好采用折中的办法:在pc端安装android market,然后将apk文件copy进入ophone安装。以下将教程
  • Tencent AI Lab Embedding Corpus for Chinese Words and Phrases为超过800万个中文单词和词语(包括标点符号)...Tencent_AILab_ChineseEmbedding.txt(开源下载文件)第一行显示嵌入总数和尺寸大小,下面每一行(第一列...
  • windows下复制文件命令

    万次阅读 2012-06-14 10:04:40
    1、xcopy命令 复制文件和目录树。 XCOPY source [destination] [/A | /M] [/D[:date]] [/P] [/S [/E]] [/V] [/W]  [/C] [/I] [/Q] [/F] [/L] [/G] [/H] [/R] [/T] [/U]
  • 首先先安装 JDK 5 or JDK 6...可惜在ophone在无法安装android market,现在只好采用折中的办法:在pc端安装android market,然后将apk文件copy
  • 本文作者第一次进行目标检测,选定目标为人(hunman)和鱼(fish)的识别,因为是新手,所以选定目标较为简单。从网上下载了图片之后,进行下一步。 2.选择图片 在数据集中,选择的图片应该清晰,有极少数图片像素...
  • 基于YOLO进行物体检测、对象识别,在搭建好开发环境后,先和大家进行实践应用中,体验YOLO3物体/目标检测效果和魅力;同时逐步了解YOLO3的不足和优化思路。 开发环境参数 系统:Windows 编程语言:Python 3.8 ...
  • 0x00 原理 获取到内存文件 lsass.exe ...Windows10/2012 以下的版本: 1、上传 procdump 执行命令转存出 lsass.dmp 文件(需要管理员权限) procdump64.exe -accepteula -ma lsass.exe lsass.dmp 2、拿到 mimika...
  • BMP文件格式及数据提取

    千次阅读 2009-09-27 13:43:00
    所以自己分析了一下文件格式,动手写个了小程序用来提取其中图像数据并生成数组保存。简单的对 BMP 文件格式解释一下。 BMP 文件简介 首先先简单说明一下 BMP 文件是什么。 BMP...
  • Windows下文件夹下面存在很多文件,有时需要用到所有文件的文件名称,我们可以用一个脚本来实现,不用自己一个个复制。实现的方法需要下面几步: ...(这里的b.txt文件就是用来存放目标文件名称的txt文件。) ...
  • 如何从数据库中提取文件

    千次阅读 2007-09-11 09:35:00
    在这篇文章里,我们演示了一个方法,...动态链接库被用来连接数据库,提取图片数据,把图片数据放入文件。ASP页面则负责与SQL数据库和动态链接库通信。我们已经介绍了设置SQL数据库和Web项目的操作过程。 微软SQL S
  • forfile windows下删除文件利器

    千次阅读 2010-04-20 14:30:00
    server2003版本以上自带工具,2000和xp想用,拷贝文件即可 1. 用批处理文件删除当前目录下 7 天以前的扩展名为bkf文件(以当前系统时间为基准) 示例: forfiles /m *.bkf /d -7 /c "cmd /c del @file /f"2. ...
  • 3.正则表达式提取器,提取Connection 正则表达式提取器,提取userId 边界提取器,提取响应代码 4.添加beanshell后置处理程序,写入脚本 FileWriter fstream = new FileWriter("E://jmeter//pk1.c...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 53,150
精华内容 21,260
关键字:

windows无法提取目标文件