dll的共享变量&预编译指令#pragma

 长期以来一直对dll的共享变量&预编译指令#pragma没有完全理解清楚,今天不惜花费读驱动程序技术文档的血本时间对该部分知识进行梳理总结,一劳永逸!


dll中共享数据
#pragma data_seg("jwsSharedVars")  //定义一个新的共享数据段,名称为jwsSharedVars。
int g_Share = 0;  //在新的共享数据段内定义一个变量g_Share,并初始化,一定要初始化,原因见后面。
#pragma data_seg() //共享段定义结束

#pragma comment(linker, "/Section:jwsSharedVars,rws") //告诉编译器,我们的这个新共享数据段内的所有变量
                                                                                                        //可以进行rws, 及read, write, shared。/*除了使用编译此方式设置外,也可以通过一下方式实现:

定义一个DLL的*.def文件,内容如下:SECTIONS
     .MYSEC READ WRITE SHARED */
 

   控制应用程序的运行实例个数。

     可以通过一下方式实现对运行程序运行实例的个数进行控制。
    
 

   // 应用程序的入口文件前面加上
#pragma data_seg("flag_data")
int app_count = 0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:flag_data,RWS")

//然后程序启动的地方加上

 if(app_count>0)    // 如果计数大于0,则退出应用程序。
 {
  MessageBox(NULL, "已经启动一个应用程序", "Warning", MB_OK);

  printf("no%d application", app_count);

  return FALSE;
 }
 app_count++;


3. 与应用层程序进行数据交换
//一个简单的例子:

    #pragma data_seg(".MYSEC")
    char MySharedData[4096]={0};
    #pragma data_seg()
    
    void __stdcall SetData(LPSTR s)
    {
     strcpy(MySharedData, s);
    }
    
    void __stdcall GetData(LPSTR s)
    {
     strcpy(s, MySharedData);
    }
    2、一定要写一个DLL的*.DEF文件,并加入以下内容(一点都不能省略):
    SECTIONS
     .MYSEC READ WRITE SHARED     
    EXPORTS     
    SetData @1
    GetData @2
4. 关于data_sec使用的一些注意事项
爱上对方

#pragma data_seg("flag_data")
   int count=0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:flag_data,RWS")
这种方法只能在没有def文件时使用,如果通过def文件进行导出的话,那么设置就要在def文件内设置而不能
在代码里设置了。
SETCTIONS
flag_data READ WRITE SHARED
-----
在主文件中,用#pragma data_seg建立一

个新的数据段并定义共享数据,其具体格式为:

#pragma data_seg ("shareddata") //名称可以

//自己定义,但必须与下面的一致。

HWND sharedwnd=NULL;//共享数据

#pragma data_seg()

仅定义一个数据段还不能达到共享数据的目的,还要告诉编译器该段的属性,有两种方法可以实现该目的 (其效果是相同的),一种方法是在.DEF文件中加入如下语句: SETCTIONS shareddata READ WRITE SHARED 另一种方法是在项目设置链接选项(Project Setting --〉Link)中加入如下语句: /SECTION:shareddata,rws

第一点:什么是共享数据段?为什么要用共享数据段??它有什么用途??
在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;而在Win32环境中,情况却发生了变化,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。

因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。

#pragma data_seg预处理指令用于设置共享数据段。例如:

#pragma data_seg("SharedDataName") HHOOK hHook=NULL; //必须在定义的同时进行初始化!!!!#pragma data_seg()

在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。再加上一条指令#pragma comment(linker,"/section:.SharedDataName,rws"),[注意:数据节的名称is case sensitive]那么这个数据节中的数据可以在所有DLL的实例之间共享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。

当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里(以下简称"地址空间")。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。(这项技术又叫code Injection技术,被广泛地应用在了病毒、黑客领域!呵呵^_^)

第二点:在具体使用共享数据段时需要注意的一些问题!

Win32 DLLs are mapped into the address space of the calling process. By default, each process using a DLL has its own instance of all the DLLs global and static variables. (注意: 即使是全局变量和静态变量也都不是共享的!) If your DLL needs to share data with other instances of it loaded by other applications, you can use either of the following approaches:

· Create named data sections using the data_seg pragma.

· Use memory mapped files. See the Win32 documentation about memory mapped files.

Here is an example of using the data_seg pragma:

#pragma data_seg (".myseg")
int i = 0;
char a[32] = "hello world";
#pragma data_seg()

data_seg can be used to create a new named section (.myseg in this example). The most typical usage is to call the data segment .shared for clarity. You then must specify the correct sharing attributes for this new named data section in your .def file or with the linker option /SECTION:.MYSEC,RWS. (这个编译参数既可以使用pragma指令来指定,也可以在VC的IDE中指定!)

There are restrictions to consider before using a shared data segment:

· Any variables in a shared data segment must be statically initialized. In the above example, i is initialized to 0 and a is 32 characters initialized to hello world.

· All shared variables are placed in the compiled DLL in the specified data segment. Very large arrays can result in very large DLLs. This is true of all initialized global variables.

· Never store process-specific information in a shared data segment. Most Win32 data structures or values (such as HANDLEs) are really valid only within the context of a single process.

· Each process gets its own address space. It is very important that pointers are never stored in a variable contained in a shared data segment. A pointer might be perfectly valid in one application but not in another.

· It is possible that the DLL itself could get loaded at a different address in the virtual address spaces of each process. It is not safe to have pointers to functions in the DLL or to other shared variables.
5.pe 文件中的数据段属性及编译器所支持的所有预编译指令总结及说明
The following table shows some of the more common section names and explains each section's purpose.

Section Name Purpose
.bss Uninitialized data
.CRT Read-only C run-time data
.data Initialized data
.debug Debugging information
.didata Delay imported names table
.edata Exported names table
.idata Imported names table
.rdata Read-only run-time data
.reloc Relocation table information
.rsrc Resources
.text .exe's or DLL's code
.tls Thread-local storage
.xdata Exception handling table


In addition to the standard sections created by the compiler and the linker, you can create your own sections when you compile using the following directive:

#pragma data_seg("sectionname")


So, for example, I can create a section called "Shared" that contains a single LONG value, as follows:

#pragma data_seg("Shared")LONG g_lInstanceCount = 0;#pragma data_seg()


When the compiler compiles this code, it creates a new section called Shared and places all the initialized data variables that it sees after the pragma in this new section. In the example above, the variable is placed in the Shared section. Following the variable, the #pragma dataseg() line tells the compiler to stop putting initialized variables in the Shared section and to start putting them back in the default data section. It is extremely important to remember that the compiler will store only initialized variables in the new section. For example, if I had removed the initialization from the previous code fragment (as shown in the following code), the compiler would have put this variable in a section other than the Shared section:

#pragma data_seg("Shared")LONG g_lInstanceCount;#pragma data_seg()The Microsoft Visual C++ 6.0 compiler offers an allocate declaration specifier, however, that does allow you to place uninitialized data in any section you desire. Take a look at the following code:***************************************************************************// Create Shared section & have compiler place initialized data in it.#pragma data_seg("Shared")// Initialized, in Shared sectionint a = 0; // Uninitialized, not in Shared sectionint b; // Have compiler stop placing initialized data in Shared section.#pragma data_seg()// Initialized, in Shared section_ _declspec(allocate("Shared")) int c = 0; // Uninitialized, in Shared section _ _declspec(allocate("Shared")) int d; // Initialized, not in Shared sectionint e = 0; // Uninitialized, not in Shared sectionint f;   ************************************************************************************************************


--摘自<programming application for Microsoft Windows>

#pragma directives -- 摘自msdn library

Each implementation of C and C++ supports some features unique to its host machine or operating system. Some programs, for instance, need to exercise precise control over the memory areas where data is placed or to control the way certain functions receive parameters. The #pragma directives offer a way for each compiler to offer machine- and operating system-specific features while retaining overall compatibility with the C and C++ languages. Pragmas are machine- or operating system-specific by definition, and are usually different for every compiler.

Pragmas can be used in conditional statements, to provide new preprocessor functionality, or to provide implementation-defined information to the compiler. The Microsoft C and C++ compilers recognize the following pragmas:

alloc_text auto_inline bss_seg check_stack
code_seg comment component conform1
const_seg data_seg deprecated function
hdrstop include_alias init_seg1 inline_depth
inline_recursion intrinsic managed message
once optimize pack pointers_to_members1
pop_macro push_macro runtime_checks section
setlocale unmanaged vtordisp1 warning

1. Supported only by the C++ compiler.

#pragma token-stringThe token-string is a series of characters that gives a specific compiler instruction and arguments, if any. The number sign (#) must be the first non-white-space character on the line containing the pragma; white-space characters can separate the number sign and the word pragma. Following #pragma, write any text that the translator can parse as preprocessing tokens. The argument to #pragma is subject to macro expansion.

If the compiler finds a pragma it does not recognize, it issues a warning, but compilation continues.

Some pragmas provide the same functionality as compiler options. When a pragma is encountered in source code, it overrides the behavior specified by the compiler option. For example, if you specified /Zp8, you can override this compiler setting for specific portions of the code with pack:

cl /Zp8 ...<file> - packing is 8// ...#pragma pack(push, 1) - packing is now 1// ...#pragma pack(pop) - packing is 8</file> 

你可能感兴趣的:(Microsoft,dll,compiler,linker,variables,Pointers)