VxD tutorial

VxD tutorial

1. Background Knowledge

In order to understand the examples given in this article, knowledge of C, assembly and Windows device drivers is required.


2. Development tools

Requires Micros
(Using VC ++ 6.0 as an example)

3. Purpose

Use the development tools listed above to write a dynamically loaded VxD, which can be used to read memory
Address 0
Interrupt vector table at.

4. Get started

first step:

Create a new project with VC ++, named MiniVxd, type Win32 App or Console App
Because V
C ++ does not provide direct generation

VxD's App Wizard, so I have to choose one of the two apps above, and then do manual repair
Change it
, What needs to be modified

There is only one place, which is to add an option to the Link command line

/ VXD

The second step:

Create a new empty file and enter the following code in it:

TITLE MINIVXD-By Ding Kai

.386p

include vmm.inc


MINIVXD_DYNAMIC EQU 1

; Define the device driver block DDB, because it is dynamically loaded, so the device ID and loading order can be taken
Undecided
Meaning.

DECLARE_VIRTUAL_DEVICE MINIVXD, 1, 0, MsgDispatch, UNDEFINED_DEVICE_ID,


Undefined_Init_Order, 0, 0

VXD_LOCKED_CODE_SEG

; Define the message dispatch table. In this example, only one message is needed, namely W32_DEVICEIOCONTROL, which is used to
Win32Ap
Use in p

; DeviceIoControl API function communicates with this VxD.


BeginProc MsgDispatch

Control_Dispatch W32_DEVICEIOCONTROL, W32DeviceIoControl, sCall,


clc

ret

EndProc MsgDispatch


VXD_LOCKED_CODE_EN
END


Save the file, the file name is VxdStub.asm, and then compile the file with the following command:

Aml -coff -W2 -DBLD_COFF -DIS_32 -c -Cx -DMASM6 -I \ 95DDK \ INC32
VxdStub.asm


This will generate vxdstub.obj, add this OBJ file to the project file.


third step:

Create a C language file, the file name is MiniVxd.c, enter the following code in the file:
#define WIN40SERVICES

#pragma warning (disable: 4229)

#include

#define WANTVXDWRAPS

#pragma intrinsic (memcpy)

#pragma VxD_LOCKED_DATA_SEG


// Here you can define any global variables needed in VxD.

char AnyData [200];

#pragma VxD_LOCKED_CODE_SEG

///////////////////////////////////////////

DWORD __stdcall GetMemory (DWORD dwDDB, PDIOCPARAMETERS pD)

{

BYTE * p;

// The parameter check is omitted here, which must not be the case in practical applications !!

p = (PBYTE) pD-> lpvOutBuffer;

memcpy (p, 0, 1024); // Read the interrupt vector table, a total of 1024 bytes

return 1024;

}

////////////////////////////////////////////////// /////

/ * Win32 interface * /

////////////////////////////////////////////////// /////

int __stdcall

W32DeviceIoControl (DWORD dwIoCtrlCode, / * ecx * /

DWORD dwDDB, / * ebx * /

DWORD pDIOCParams) / * esi * /

{

PDIOCPARAMETERS pD;

pD = (PDIOCPARAMETERS) pDIOCParams;

if (dwIoCtrlCode == DIOC_OPEN ||

dwIoCtrlCode == DIOC_CLOSEHANDLE)

return 0L;

// Init returned value

if (pD-> lpcbBytesReturned)

* (DWORD *) pD-> lpcbBytesReturned = 0;

if (dwIoCtrlCode == 1) {// 1 = DeviceIoControl code

GetMemory (dwDDB, pD);

}

else

return ERROR_NOT_SUPPORTED;

return 0;

}

Add this file to the project file.


the fourth step:

Create a module definition file, this file will define the segment attributes of the resulting VxD file and output the device
drive
Program blocks,

Enter the following code and save the file as MiniVxd.def, and add it to the project file.

VXD MINIVXD DYNAMIC

DESCRIPTION 'Written by Ding Kai'

SEGMENTS

_LTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE

_LDATA CLASS 'LCODE' PRELOAD NONDISCARDABLE

_TLS CLASS 'LCODE' PRELOAD NONDISCARDABLE

_BSS CLASS 'LCODE' PRELOAD NONDISCARDABLE

_ITEXT CLASS 'ICODE' DISCARDABLE

_IDATA CLASS 'ICODE' DISCARDABLE

_PTEXT CLASS 'PCODE' NONDISCARDABLE

_PDATA CLASS 'PDATA' NONDISCARDABLE SHARED

HEAPSIZE 10240

STACKSIZE 40960

EXPORTS

MINIVXD_DDB @ 1


Of course, some of these paragraphs do not exist in this program, listed here is to use this file as a template to
For
After use.


the fifth step:

The last one needed to add this project file, VxdWarps.clb, this file is in the DDK's LIB directory.


The sixth step:

The four required files have been added. They are:

vxdstub.obj

minivxd.c

minivxd.def

vxdwarps.clb


Please check the file path before making make, be sure to include the DDK directory in the Include
INC3

2 Directory.

After confirming that it is correct, you can press F7 to make.


The result will be generated in the DEBUG directory or Release directory (depending on the settings of the project file)
MiniVxd.vxd
file.


At this point, a small Vxd file is generated, you can use the following code in the program to communicate with this VxD
News.



BYTE buffer [1024];

DeviceIoControl (hVxd, 1, NULL, NULL, buffer, 1024, NULL, 0);


As a result, the current interrupt vector table will appear in the buffer array.

This article no longer provides a complete test program, please contact the author if necessary

5. Summary


This article uses a simple example to explain how to write a dynamically installable VxD under Windows 95.
Seed
And use this method

The written VxD is also applicable under Windows 98. However, this method cannot be used in Windows NT





*****************************
**
*******************************************

Title: VxD World-Virtual Windows World


Readers engaged in Windows system programming must have heard of VxD-this is in Windows (Note: refers to Windows
3.1
/ 95/98,

Non-Windows NT) omnipotent super weapon in the world. So far, more and more people have
Into the
Solve the wishes of Windows,

This also makes VxD technology more and more important. VxD, which means Virtual Something Driver, here
Of x
Refers to Something. such as

Say keyboard driver VKD, mouse driver VMD, etc. In the minds of many people, only hardware developers
Only
VxD is used, actually not

Of course, for software development, VxD is also omnipotent. This is not obvious under Windows 3.1,
While in W
indows95, with

With the introduction of the concept of threads and localization, it becomes more and more difficult to control all the resources of the system. Give a simple
example
, Due to 32-bit Windows

The application has an independent 4GB linear address space, commonly used Windows applications under Windows 3.1
Between orders
Communication and shared variables

The method is no longer fully applicable. I want to do something "out of the ordinary", such as intercepting messages from other applications
Become
It's getting harder. It's just you

When you are uneasy, using VxD technology may make you "bright and cheerful"-since the operating system can control
Wholly-owned
Source, then with the operating system

VxD (or simply VxD as part of the operating system) with the same highest authority will also be able to help
Help you
Complete "extraordinary behavior."

Why is VxD so magical? The most fundamental reason is that VxD runs on Ring 0 of the system, and is used
SDK (
Or well known now

VC ++, BC ++, Borl and C ++ Builder, VB, Delphi)
Walk in
Ring 3. At 80x86

In protected mode, VxD running at Ring 0 has the highest system permissions (the operating system also runs at
Ring 0)
.

To truly master VxD technology, you must:

Have a clear understanding of the 80x86 protection mode;

In-depth understanding of the operating mechanism of Windows;


Familiar with the operating mechanism of VxD itself.

As for the protection mode, there are currently some reference books on the domestic market, I hope you can read it a few times. although
Say no
Can make people feel better, but also

Can have a relatively clear understanding of the protection mode.


Let's get to know Windows itself first.


Virtual Windows world


Several applications running under Windows95: DOS application, Win16 application, Win32 application
program
. Among them DOS application

Most programs run in character mode, while Win16 / Win32 applications run in graphics mode. for
Make
These modes of operation are very similar

Tongting applications can "peace coexist", Windows uses a virtual machine (Virtual machine)
the way
. Every DOS application

Running in an independent DOS VM makes this stunned DOS application feel in control
all
Resources, it can be wanton

On the full screen it thinks, read and write on the hard disk that it thinks it is exclusive (Microsoft is setting up
Gauge DO
I can't think of a PC at S

Hardware is developing so rapidly). And all Windows applications, whether they are 16-bit or 32-bit
It is
Run on the same System

VM. This is because Windows applications are more compliant, readers familiar with Windows programming must be right
Handle
(Handle) The concept

I am very impressed. Every move of a Windows application is through various handles (
HWND, H
DC, HANDLE,

HMOUDLE, etc.) is achieved through the intermediary of the handle, so that the system has the opportunity to be in the same
System VM
Coordinate multiple Windows

application. Don't believe it? OK, let's do an experiment (I assume you have installed SofTIce for
Win95
), Start Windows95, start

After the end, do not run any DOS applications, press Control + D to activate SofTIce (in some
Softic
e default installation, activated

Softice's hotkey is Alt + D), and then type in the command:

: Vm


VM Handle Status High Addr VM ID Client Regs

C39200E8 00001862 C3800000 00000001 C3449F70


From the output of Softice, you can see that there is only one VM at this time, and its ID is 1.


Press Control + D to return to the Windows desktop, at this time, run several Windows applications, whether it is
16 bit
, Or 32-bit. repeat

The operation just now, press Control + D to activate Softice, and type in the instruction VM. We see the Softice output
Knot
The result is the same. That is to say

Although there are multiple Windows applications running, but there is only one VM in the system, this VM is
System VM
.


Press Control + D to return to the Windows desktop. At this time, run "MS-DOS mode" from the "Start Menu"
,press
Activate under Control + D

Softice, then type in the instruction:

: Vm


VM Handle Status High Addr VM ID Client Regs

C8D200E8 00000802 C8C00000 00000002 C0F5FF70

C39200E8 00005A62 C3800000 00000001 C0F06F70


From the output of Softice, you can see that there are two VMs in the system, one of which is what we just saw
of

System VM (VM ID = 1), in addition, there is another VM with ID = 2, this VM is DOS VM,

In other words, the "MS-DOS mode" just now runs in this DOS VM.


Press Control + D to return to the Windows desktop, and then run a few "MS-DOS mode" from the "Start Menu"


, Press Control + D to activate Softice, and type in the VM command, you will find that the VM has increased, the number of which is equal to
Place

In some cases, the number of "MS-DOS mode" is increased by 1.


Do you believe it now? All DOS applications are running in their respective DOS VM, and all

Windows applications are running in the same System VM. This is a very important concept.





**********************************************
**
***************************

VxD world-Windows protection mode


In general, 80x86 (80386 and later generation CPUs) can operate in three modes: real mode
,

Protected mode, V86 mode. The real mode is the ancient MS-DOS operating environment. Win95 only uses two


Various modes: protected mode and V86 mode.


Why go into protected mode

The protection mode has many advantages. One of the most direct benefits is: your program can use more internal
Saved
!


Do n’t think this is a big deal, I believe everyone who has written programs under MS-DOS has
One

A worry: how to open a large enough array in the program? If you do n’t move, you will get a stack overflow
do

Too. Don't complain about Turbo C, MS Fortran, Turbo Pascal, they are also overwhelmed.
These ones


The troubles all stem from "your program is running in real mode". The most 16-bit programs running in real mode
only

Can access 1M of memory. You may ask: Doesn't my machine have 64M memory? Yes if you
Yes

Run the program under MS-DOS (or the CPU is running in real mode), then you only use 1M memory,


The rest of the memory is "laid off", in this case, your 386/486/586 / Pâ…¡ is only equivalent to one
Run


8086 fast.


But the protection mode gave us a surprise. In theory, in protected mode, the CPU can address 4096M (


That is 4GB) of memory. That said, just compile your program into a 32-bit executable program (of course you have to borrow
help

32-bit compiler), you can make full use of memory in the program, the direct result of this is: you can
To

There is no need to eat anymore because the stack overflows or a 5000 × 5000 array cannot be opened.


It is the realization of 4GB memory access that makes the operating system have a more intelligent material foundation, multi-tasking
real

Only now can we mention the agenda for consideration.


Go deeper


From the hardware structure, 386 is controlled by three registers CR0, CR1, CR2 CPU operation. For example

The 0th bit of CR0 is used to judge whether the current CPU works in the protection mode or the real mode. Learned

People in 8088/8086 assembly language must be familiar with the 16 bits of AX, BX, CX, DX, SI, DI, SP, BP

Of the registers, in 80386, these registers were expanded to 32 bits, namely EAX, EBX, ECX, EDX

, ESI, EDI, ESP, EBP, if the CPU is running in real mode, then you can only use these 32-bit


The first 16 bits of the memory, and the last 16 bits are wasted.


The concept of segments is the key to our understanding of the protection model. In real mode, 16 bits are stored in the segment register
of

Segment address, at this time, the segment address is involved in addressing: shift the segment address to the left by 4 bits, plus the offset address
20 people


Physical address. In protected mode, a 16-bit segment selector (Segment) is stored in the segment register

Selector), this value is not directly involved in addressing, but only a pointer to the segment description table (
Segment

Descriptor Table). The segment description table (Segment Descriptor Table) stores
Paragraph description


Descriptor (Segment Descriptor). There is a description of the segment in the segment descriptor, for example: the segment is in memory
of

Position, segment size, segment type (whether data segment or code segment), etc.


When the CPU is running in protected mode, there are often at least three segment description tables in the memory: the global description table (

Global Descriptor Table (abbreviated as GDT), Local Descriptor Table (abbreviated as GDT)
LDT)


, Interrupt Descriptor Table (Interrupt Descriptor Table, referred to as IDT). Speaking of which, I want to mention
Awake reader


Note: Remember the meaning of the three words GDT, LDT, and IDT, which we will often use later.


The segment description table cannot exceed 64K (Why? If the answer is not up, then look at the previous explanation)
,

Each segment descriptor (that is, an item in the segment description table) is 8 bytes long, so each segment description
Table most


It can only contain 8192 segment descriptors.


In the future "Walking into the VxD World", we will make the segment description table, segment descriptors and paging machine into
One

Step by step explanation.




*************************
**
***************************

VxD World-About "Description Table"


We mentioned earlier that in protected mode, there are often at least three tables in memory: GDT, LDT, and IDT. Satoshi
Bright

You may ask: Where are these tables in memory?


Figure 1 Schematic diagram of several important registers


The positions of these three tables are recorded by three registers. The three registers are: GDTR, LDTR,


IDTR. We also need to explain a register, that is TR (Task Register), this register
Device

Related to task management in protection mode. In 386, these registers are shown in Figure 1.


We can see that both GDTR and IDTR contain 32-bit physical addresses and 16-bit permission levels, for a total of 48


Bit. The 32-bit addresses in GDTR and IDTR are linear addresses, not a combination of segment: offset (Seg: Offset)


Form, it indicates where the segment starts, if the system has not started the memory paging management mechanism,
that

This linear address is the physical address, but once the system starts the memory paging management mechanism, GDTR and


The 32-bit linear address in IDTR no longer points to the physical address.


The CPU reserves the first descriptor in the GDT. Any attempt to access the internal descriptor through the first descriptor in the GDT


The saved operation is illegal (remember the terrible blue screen under Win95?). If you point to the GDT segment selection


If the Index field of the selector is 0, it points to an empty segment selector. The schematic of the segment selector is shown in Figure 2:


TI: Talbe Indicator

RPL: Requestor Privilege Level


Figure 2 Segment selector


This means that in protected mode, the far NULL pointer is illegal. In real mode, the NULL pointer
there is
meaningful.


In memory (of course, the CPU works in protected mode), there is only one copy of GDT and IDT, which is


In other words, they are global. Any task that changes these two tables will have an impact on other tasks.



Let's talk about each item in IDT, IDT, that is, each descriptor has 256 interrupts defined
in

one of. Remember the interrupt vector table in the MS-DOS environment in real mode? In protected mode, interrupt
Trace

The statement IDT replaces the interrupt vector table. Although the interrupt description table can hold 8192 interrupt descriptors, but


The CPU can only use the first 256. So the length limit of the interrupt description table should be 7FFh (23


× 28-1).


T = 0: Interrupt gate descriptor

1: Trap gate descriptor


Figure 3 Trap / Interrupt gate descriptor


In fact, there can be two kinds of descriptors in the interrupt description table (IDT): Interrupt gate descriptor, Trap
gate trace


Predicate. Figure 3 shows the structure of the Trap / Interrupt gate descriptor. The word gate is mentioned here,
general


Translated as "door". The "interrupt gate" directly represents the process of interrupt calling: the interrupt calling is like
Over one


Like the door, this door is the interrupt descriptor, because the interrupt descriptor has the DPL and other permissions to check the flag
,

So if you want to call the corresponding interrupt service routine through this door, you need a certain qualification.


Trap and Interrupt gate are very similar. Generally speaking, Trap gate is used to catch system anomalies, and


Interrupt gate is used to respond to interrupts. In the specific implementation, there is only one difference: Interrupt
gate meeting
will

IF is set to 0, which can shield the hardware interrupt. But Trap gate will not change the value of IF.


The location of the LDT is stored in the register LDTR. Unlike GDT, there can be multiple LDTs ​​in memory. Every


Tasks can have their own LDT. From Figure 1, we can see that LDTR does not contain "address / permission"
Bit

. LDTR contains a segment selector. This segment selector points to an item in GDT, but it is not a general


A common item, this item is a descriptor that points to a certain LDT. You may immediately realize that GDT can contain


Descriptors that point to different LDTs, yes, exactly. In Windows protected mode, every MS-


DOS applications have their own LDT, and all Windows applications share an LDT. Yes no


Did you remember something? By the way, this is somewhat similar to the concept of VM (Virtual Machine) in Windows!





**********************************
**
***************************

VxD world-paging mechanism


About the paging mechanism


Under this mechanism, memory is divided into fixed-length "pages." In protected mode, the "page" is
Accept

To be protected, and can be "virtual" (to put it bluntly, "virtual" things mean that there is not,
and

The application mistakenly thought there were. For example, you only have 64MB of RAM, but your program thinks it silly
can

With 4GB of RAM, this is called "virtual").


Remember the concept of "segment" in the protected mode mentioned earlier? In protected mode, the length of the segment is OK
change

The size of the "page" cannot be changed here, although both of them are protected by the protection mode.
"

And "virtual". So what is the size of the "page"? The answer is 4KB.


If there is now a 32-bit linear address, let's take a look at how to obtain a physical address from this linear address
.


Figure 1 Linear addresses are mapped to physical addresses through pages


Please remember that Figure 1 shows the content, because it is so important. CR3 in the figure refers to the value of register CR3.
Such as

If you installed Softice for Win95, then press Ctrl + D to activate Softice, and then type the command:


CPU; you will see the value of CR3.


Example of linear address conversion to physical address


Assuming the existing linear address 8000DD88h, let's take a look at where the physical address points. Sexually
site

8000DD88h should be analyzed as follows:


800 0D D88

Page Table Index Page Index Page Offset

(Page table index) (page index) (page offset address)


Now we need to know the starting address of the page table directory. So we look at register CR3. Hypothetical deposit
Device

CR3 = 891000h, then 891000h + 200h? 4 = 891800h (think about why? 4, the answer


Note [1] at the end of the article), now we want to check the value at the linear address 891800h, at
Softice


, Type the following command:


: d 891800


Suppose that the result is 493227h. This value is ANDed with 0FFFFF000h, and we get what we are looking for


The starting physical address of the Page Table is 493000h, then 493000h + 4? 0Dh = 49302Ch (think


One thought why? 4, see note [2]), then we need to know the value at physical address 49302Ch, but
Is in


In Softice, we cannot directly obtain the value stored at the physical address, only through its corresponding linear
address


To view. In Softice, type the following command:


: phys 49302C


Suppose you get a linear address 89302Ch (sometimes you will get two different values, we take the first value
.

Think about why there are two different values? The answer is in note [3]), now we look at the linear
site

The value at 89302Ch, which is also the value at physical address 49302Ch. In Softice, type the following command:



: d 89302C


Assuming that you get 3F5000h, this is the physical address of the Page we are looking for. then,

3F5000h + 0D88h = 3F5D88h, this is the physical address corresponding to the linear address 8000DD88h. I


We want to check the value at physical address 3F5D88h in Softice, then we have to find its corresponding linear address.
in

In Softice, type the following command:


: phys 3F5D88


Suppose we get the linear address 7F5D88h, let us look at the value at this linear address 7F5D88h. in


In softice, type the following command:


: d 7F5D88


Let's write down the result, and then look at the value at linear address 8000DD88h.


: d 8000DD88


You will find that the linear address 7F5D88h and the linear address 8000DD88h point to exactly the same value.


Generally speaking, in order to improve the efficiency of address translation, there will be a page translation buffer (TLB) in the CPU


), Which stores information about the most recent page transition.


MMU (Memory Management Unit)


Since 386, the CPU has a memory management unit (Memory Management Unit, referred to as

MMU).


MMU is responsible for the functions provided: virtual memory, reorganization segment, process separation, page protection, address translation.


The linear address to physical address conversion we performed in the above example is done by the MMU.



Mode conversion


We briefly mention the switching process between the protection mode and the real mode, there is a concept.


Switch from real mode to protection mode: check whether the current CPU is capable of running in protection mode; check
Measurement

Protected mode environment (DPMI or VCPI); establish IDT; establish GDT; disable interrupts, including NMI; load


GDTR; set IDTR; set bit 0 of CR0; clear instruction queue; load segment register; allow interrupt.



Switch from the protection mode to the real mode: interrupt is prohibited, including NMI; page conversion is prohibited; set the data segment to send
Save

The value of the device is the selector in quasi-real mode; reset bit 0 of CR0; clear the command queue; set
IDTR = 0000

: 03FF; interrupt is allowed.


[1] Because in the Page Table Directory, each entry occupies 4 bytes.


[2] Because in the Page Idex Table, each entry occupies 4 bytes.


[3] Because the mapping relationship between linear addresses and physical addresses is not one-to-one, this means that the same physical location
site,


There may be multiple linear addresses pointing to it.






********************************
**
***************************

VxD World-Win95 linear address allocation


Figure 1 is a diagram describing the Win95 linear address space. Below we explain this picture in detail.


Figure 1 Windows 95 linear address allocation diagram


0 ~ 4MB:


The part marked on the figure is the DOS memory area, but this is not true. We know 16-bit DOS application


The sequence can only access 0 ~ 1MB of memory space, so why should we also count the 3MB memory of 1MB ~ 4MB

What about the DOS memory area? The answer lies in the paging mechanism we talked about earlier. Let ’s review it again: a page
surface

(Page) is 4kB, a page table (Page Talbe) has 1024 pages, a page table directory (


Page Table Directory) has 1024 page tables. Then a page table directory item can be mapped
1024 ×


4k = 4MB linear address. In fact, DOS can only use the memory space of 0MB ~ 1MB, then 1MB ~ 4MB

To whom is the address space left? Regarding this question, the author once asked Karen Hazzah, Walter


Oney and Geoff Chappell, their answer is: this part is empty. Win95 saves trouble in order to save time,
on

The linear address space of 1MB ~ 4MB is also used as the DOS memory area, so Win95 switches between DOS VM

At this time, it can be done in units of Page Table Directory Entry. such
although


3MB of addresses were wasted, but the efficiency of DOS VM switching was exchanged.


What does the â‘  marked in Figure 1 mean? Haha, Win95 is very interesting, in order to make the system, Win16
should

The application can cooperate with the DOS application, so it is between 0 and 1MB, in fact, it is next to 1MB


In the face, put a Win16 global heap (actually a very small part of Win16 global heap). Speaking of which, pen
By

I think of a Quick Edit 4.0 editor under DOS that everyone loves. This editor has a
very much


Interesting function: Can share clipboard with Windows. At the time, we guessed that it must have used undisclosed
DPMI

Calling, now from Figure 1, it must be â‘  part of the Win16 global heap to help it.


Please also note that there is a â‘¡ in Figure 1, this part we call the high-end part of the Win16 global heap. why
Want

Place a Win16 global heap here? What is it used for?


The answer to this question is: To efficiently switch DOS VM.


Each DOS VM has a backup in the address space greater than 3GB, Win95 is performed between the DOS VM

When switching, simply switch the first item of the page table directory. So if one
VxD

To access a DOS VM, there is no need to wait until the DOS VM becomes the current VM before it can be accessed.


Go directly to â‘¡ access the backup of that DOS VM. The address of this DOS VM backup is called High-

linear address.


In later articles, we will talk about how to access the DOS VM in VxD in detail.





***********************
**
***************************

VxD World-Win95 Memory Secret


In the last issue of "Entering the VxD World", we talked about the allocation of 0 to 4MB linear memory space in Win95. below


We then talk about the allocation of the remaining 4MB to 4GB of linear memory space.


4MB ~ 2GB

The code, data and resources of the Win32 application are stored in this section of memory.
each

All Win32 applications are private. This is the place that best reflects the role of the protection mode paging mechanism
square

. Two Win32 applications, read and write to the same linear address (in the range of 4MB ~ 2GB), the actual


In fact, they are reading and writing to different physical addresses. Let's calculate how many pieces of memory this section corresponds to


Page Directory Entry. We know that a Page Directory Entry corresponds to 4MB linear memory
,that


4MB ~ 2GB of memory corresponds to 1024/2-1 = 511 Page Directory Entry. Just
says


Win95 realized the "independent" Win32 application by manipulating these 511 Page Directory Entry
Linear


Address space. For example, now there are 511 drawers, God told A: these drawers are all
gold

Coins, and told B to say: these drawers are all silver coins. And stipulate that only God can open the drawer. Actually smoke
Drawer


There may be only one copper plate, or nothing. At this time, A wants to see if it is in a drawer
gold


, So God temporarily put a gold coin in that drawer behind his back, and then opened the drawer to let A see it, so
A just

Believe it. This trick also works on B. B also believes that the 511 drawers are all the silver coins he wants. Protect
Protect

The operating system in mode (of course, Win95 here) is equivalent to God, and the deceived A and B are equivalent to
Luck

Win32 application running in protected mode. The hand of God that manipulates the drawer is the paging mechanism.


2GB ~ 3GB

This part of the memory space is called "application shared memory area", where Ring 3 applications are stored
Cheng

The sequence requires shared data and code. Including Win95 system DLL (such as User32.dll, Kernel32.
dll etc.


), Memory-mapped files, Win16 applications, and memory allocated by DPMI calls.


Win32 applications can be implemented by mapping the data to be shared to a linear memory space between 2GB and 3GB


Data sharing between programs.


Win16 applications need to share a linear address space, which is a legacy of Windows 3.1.
for

In order to make Win16 applications previously running on Windows 3.1 have the same running effect under Win95,
to

Microsoft decided to put the Win16 application in this shared memory area to run. It should be said that this is a comparison
Fit

Rational decision: not only save time and effort, but also ensure good compatibility with the previous generation of products.


From the perspective of the paging mechanism, think about how this "shared memory area" is implemented. 2GB ~ 3GB


The linear space corresponds to 256 Page Directory Entry. No matter who is running, Win95 will not change
Change this


256 Page Directory Entry, which means that the physical address corresponding to the linear address space of 2GB ~ 3GB
site

it's the same. In this way, Win95 achieves a total of 2GB ~ 3GB of linear address space memory without doing anything


enjoy.


3GB ~ 4GB

This part of the memory space is called "system memory area". Only Ring 0 VMM and VxD can access this part


Memory space. This part of the memory is also shared. Although the Ring 3 app cannot directly share this part
Inside

Storage space (that is, the method described in the SDK cannot be achieved), but we still call it shared memory
Area

, At least from the perspective of the paging mechanism, the Page Directory Entry corresponding to this part of memory is not
changing


. In fact, Win32 applications still have a way to access this part of the memory space. The general approach is that in VxD
Minute

Allocate a piece of memory, and then pass the 32-bit address pointer pointing to that piece of memory to the Win32 application, so
can

In order to directly access that memory in the Win32 application. When we talked about 0 ~ 4MB memory space,
mention

To the High-linear address, the place where the DOS VM is backed up, is in the 3GB ~ 4GB memory space.



For Windows application developers, implementing memory sharing is sometimes very important. For VxD open


For developers, it should also be noted that for shared memory pages that need to be accessed frequently, the VMM must be called

_LinPageLock service to lock, otherwise Out-Of-Context Memory Reference will appear
.




********************
**
***************************

VxD World-Hardware Virtualization and Virtual Device Driver


· Hardware Virtualization ·



Recall that in real-mode DOS, real-mode MS-DOS applications call the runtime library
of

fgets or -kbhit functions, these functions will directly call the MS-DOS service through the soft interrupt int 21h (
MS

-The DOS service will eventually call the BIOS service by calling the soft interrupt int 16h), or by soft interrupt
int 16h


Call the BIOS service. The BIOS directly manipulates the keyboard or interrupt controller through IN / OUT instructions.


We use the following picture to understand what is the realization of hardware virtualization (see Figure 1).


Two different MS-DOS applications may want to access the keyboard at the same time, they both feel that they are directly manipulating
Write

hardware. In fact, from a global perspective, the keyboard access of these two MS-DOS applications is serialized by VKD.


These two MS-DOS applications are actually manipulating "virtual hardware".


A key foundation of hardware virtualization is: the "port trapping" function of the 80386 chip, which makes
VKD


You can capture ring 3 application access to the keyboard. We remember when we talked about protected mode memory management earlier
,mention


To the concept of "virtual memory", the realization of "virtual memory" was also because the 80386 chip had "

paging trap "function.


· Virtual Device Driver ·


In the Windows system, it is VMM and VxDs that implement hardware virtualization (VMM itself is a collection of some VxDs

). In Win95, there are two kinds of VxD: static VxD (statically loaded VxD) and dynamic VxD (dynamic


State loaded VxD). In Windows 3.1, there is only one type of VxD: static VxD. Static VxD
load


Need to put VxD in SYSTEM.INI or the registry. For example, you wrote a file named Fool.VxD
of

static VxD, then you can add a sentence in the following location in SYSTEM.INI:


...

[386Enh]

device = Fool.VxD

...


Or add the following item in the following location of the registration form:

\ HKEY—LOCAL—MACHINE \ System \ CurrentControlSet \ services \ VxD \ Fool.VxD



In this way, Win95 will automatically load Fool.VxD when it starts.

Compared with Windows 3.1, Win95 has added dynamic VxD (dynamically loaded VxD). in


In Win95, the main manipulation of dynamic VxD is configuration manager and input / output

The two function modules of Supervisor (these two modules are static VxDs themselves).



Figure 1 Virtual keyboard


VKD: Virtual keyboard device VPICD: Virtual PIC device





*****************************
***************************

VxD World-VxD file format



We know that the executable file .EXE under MS-DOS is in MZ format, which means that the first two of the .EXE file

The byte is the character string "MZ". The .EXE of Windows 3.1 is in NE (New Executable) format,

Win95 and WinNT. EXE is PE (Portable Executable) format, VxD is LE (Linear


Executable) format. But there is one thing to note, there is always a small section embedded in the heads of NE, PE and LE


DOS program, its role is: when you run these kinds of .EXE files under DOS, it will prompt you "This


program cannot be run in DOS mode ".


DEBUG TEST.VXD


LE文件格式最早出自OS/2 2.0。这种格式的文件可以同时包含16位和32位代码,
è¿™

正是VxD需要的,因为VxD在加载的初始化阶段,需要进行一些实模式的操作,这


需要16位代码,而VxD的主要运行阶段是在32位环境中,这又需要32位代码,所
以

LE文件格式正好适合于VxD。


VxD不仅有16位和32位两种代码,而且,它把数据段和代码段搅和在一起,只是
通

过段前的标识来表明该段在运行时的特性。VxD之所以这样,是因为VxD所用的
Flat

mode的代码和Data selector有同样的基本地址和限制,这样当VxD想访问数据或
代

码时,用哪个段寄存器都可以。VxD中常用的Segment Class见表1。


在VxD中,LCODE、PCODE和PDATA段包含了主要的数据和代码。LCODE段所包

含的数据和代码必须总在内存中,我们在VxD中处理硬件中断的代码和相关数据
å¿…

须位于LCODE段中,否则,在处理硬件中断时就会出现可怕的Page fault。


ICODE段包含的是VxD初始化时要完成的工作,当VxD完成初始后,ICODE段中的


代码和数据将被VMM抛弃。


RCODE中包含的是16位代码和数据,用作实模式初始化阶段。


SCODE中包含Static Code和Data,一般来说,SCODE对于动态加载的VxD尤其有


用。试想一下,如果可动态加载的VxD包含了一个回调函数,当你卸载这个VxD后


,又需要这个回调函数继续发生作用,那你就得把这个回调函数放到SCODE中。
再

者,如果你需要知道某个动态加载的VxD被加、卸载了几次,那可以在SCODE中放


个记数器,每次该VxD被加载时都把该记数器加一。


由于VxD的文件格式比较特殊,所以你必须使用可以产生LE格式的链接器(
Linker

).如果你要开发Windows 3.1的VxD,那得用Windows 3.1 DDK带的链接器。但
是

如果开发Win95的VxD,那用MSVC 2.0及其以后版本的链接器就可以了。有一点需


要注意,MSVC 4.1的链接器由于存在一些小BUG,不能用于生成VxD。


Table 1

Segment Class 描 述

LCODE Page-locked code and data

PCODE Pageable code

PDATA Pageable data

ICODE Initialization-only code and data

SCODE Static code and data

RCODE Real-mode initialization






**********************************
***************************

VxD世界——VxD开发的利器SoftIce/VToolsD


SoftIce的安装


对于从事Windows系统开发的人来说,SoftIce是必不可少的利器。目前SoftIce
的最


新版本是3.24,有Win 95的版本,也有Win NT的版本。SoftIce 3.24 for Win
95可以



从国内的一些FTP站下载。


在SoftIce的安装过程中,有一步是很重要的,那就是对显卡的测试。新版本的


SoftIce提供对更多种显卡的支持。如果说显卡不被SoftIce支持,那就意味着


SoftIce将无法正常工作。


在安装过程中,如果Test通不过,那就试着选一下Universal Video Driver或者
Use


monochrome card/monitor。如果还通不过的话,那就在Display Adapter
Selection


列表框中选择Stand VGA再试。如果还不行的话,那就试着去下载一份new
SoftIce

video driver(在国内的FTP站上也可以找到),如果运气好的话,你的显卡会
被

SoftIce支持。


安装过程结束后,重新启动计算机,按下Ctrl+D进入SoftIce,如果SoftIce能
正常


工作,那我们的安装就成功了。你可以敲入help来看一下SoftIce的简要帮助,
或者


,按下Ctrl+D返回Windows桌面。


VToolsD的安装


在VToolsD出世之前,VxD的开发者面对DDK浩如烟海的古怪的asm代码,能忍受下


来的人不多。VToolsD就是把幸福带给VxD开发者的天使。就凭这一点,VToolsD
å°±

令VxD开发者趋之若鹜。VToolsD是Vireo公司的作品,目前国内的FTP站上有

VToolsD 2.03 for Win95下载(如果运气好的话,还可以找到2.04、2.05b)。
从国


内FTP站下载的VToolsD在安装时有一些需要注意的地方。


首先,在安装过程中会遇到对话框,询问你是否需要MASM6.11c。VToolsD并不包


含MASM 6.11c,但是,可以从DDK for Win95中找到MASM 6.11c。当你的程序中


需要嵌入汇编代码时,MASM 6.11c就不可缺少了(如果你只用C语言开发VxD,那


就用不着了)。


图1所示是对调试器的选择,请选择SoftIce。


图1 调制器的选择


在SoftIce的路径设置对话筐中,请选择SoftIce的Util16子目录。


再者,在安装过程中,图2所示的两个选项是不能选的,否则,安装过程将无法
正常结
束。


图2 不要选择的两个选项


在VToolsD安装完成之后,重新启动计算机,然后,进到VToolsD安装目录下的


examples\c\simple\下,敲如下的命令:


nmake-f simple.mak


如果一切正常的话,将生成simple.vxd。


如果不行的话,那就在DOS提示符下敲set命令,检查一下环境变量的设置。下面
是

我的系统中相应环境变量的设置,你需要根据自己系统的实际情况调整一下路径
Set up
:


VTOOLSD=E:\VTD95

PATH= ï¼…PAHï¼…;C:\MSDEV\BIN; E:\VTD95\BIN

INCLUDE=C:\MSDEV\INCLUDE; E:\VTD95\INCLUDE

LIB=C:\MSDEV\LIB;E:\VTD95\LIB


把这些环境变量的设置加到autoexec.bat中去,以使其每次开机都自动设置。






*********************************
**
*******************************

VxD世界——用VToolsD开发一个简单的VxD


这一次,我们讲一下如何用VToolsD开发一个最简单的VxD,以及用SoftIce进行
源程序
级的调试。


VToolsD的使用


在VtoolsD中,有一个最重要的VxD开发工具:QuickVxD。QuickVxD可以为我们自


动生成VxD源程序框架,而且QuickVxD提供了许多VxD的特性选项,例如可以选择


要生成的VxD是动态加载的或是静态加载的,要使用的编程语言是C还是C++等
等。



我们要利用QuickVxD自动生成的是一个可动态加载的、基于C语言的VxD框架。之


所以选用动态加载的VxD,是为了调试VxD的方便。每次修改代码,重新编译连接


之后,要使VxD重新生效,如果采用静态加载的VxD,那就不得不重新启动电脑,


而若采用了动态加载的VxD,那只须使用VToolsD带的另一个开发工具VxDLoad就


可以卸出或重新加载内存中的VxD。之所以采用C语言而不是C++,是因为其简
洁

易懂。请按照如图1~图4进行选择。按下Generate Now按钮,我们就获得了动态
加

载的、基于C语言的VxD的源程序。


如果您是按照上一篇文章中讲过的VToolsD的编译环境设置系统,那我们就可以
ç¼–

译刚才生成的这个最简单的VxD了。在DOS提示符下输入指令:


nmake -f myfirst.mak


看一下当前目录下是否生成了myfirst.vxd,如果有,那我们下面准备对这个
VxD进行


源程序级的调试。如果没有,那么很可能是您的编译环境没有正确配置,请找来
上

一篇文章好好读读。


用VxDLoad加载myfirst.vxd(见图5)


按下Load按钮,会出现VxD load successfully消息框。


用SoftIce调试VxD


对于SoftIce选单作如下选择:

(1)File→Open Module选择我们刚才生成的myfirst.vxd。

(2)Module→Translate,如果Symbol Loader提示无法加载一些asm文件,那就
跳过
所有的asm文件。

(3)Module→Load。


按下Ctrl+D,进入SoftIce运行环境中(如果您还没有按照上一篇文章中安装


SoftIce的话,那就无法再进行下面的测试)。输入如下指令:

:file ?


myfirst.c

:file myfirst.c


这时,在SoftIce中,您将会看到myfirst.c的源程序。


图1选项页面之一

图2选项页面之二

图3选项页面之三

图4可以生成VxD源程序了

图5用VxD Load加载myfirst.vxd





*********************************
**
*******************************

VxD世界——VxD的结构


在上一次“走进VxD”世界中,我们用VToolsD生成了一个最简单的可以动态加载
çš„

VxD——MYFIST程序。我们以这个例子为基础,不断地丰富其功能,并以此讲解
一些V
xD的基本技术。

下面是MYFIRST主要组成之一:MYFIRST.C。


// MYFIRST.c - main module for VxD MYFIRST

#define DEVICE_MAIN

#include "myfirst.h"

#undef DEVICE_MAIN

Declare_Virtual_Device(MYFIRST)

VOID_cdecl V86_Api_Handler(VMHANDLE hVM, PCLIENT_STRUCT pcrs) { }

VOID _cdecl PM_Api_Handler(VMHANDLE hVM, PCLIENT_STRUCT pcrs) { }


DefineControlHandler(SYS_DYNAMIC_DEVICE_INIT,

OnSysDynamicDeviceInit);


DefineControlHandler(SYS_DYNAMIC_DEVICE_EXIT,

OnSysDynamicDeviceExit);


DefineControlHandler(W32_DEVICEIOCONTROL, OnW32Deviceiocontrol);



BOOL _cdecl ControlDispatcher(

DWORD dwControlMessage,

DWORD EBX, DWORD EDX,

DWORD ESI, DWORD EDI, DWORD ECX)

{ START_CONTROL_DISPATCH

ON_SYS_DYNAMIC_DEVICE_INIT(OnSysDynamicDeviceInit);

ON_SYS_DYNAMIC_DEVICE_EXIT(OnSysDynamicDeviceExit);

ON_W32_DEVICEIOCONTROL(OnW32Deviceiocontrol);

END_CONTROL_DISPATCH

return TRUE;}


BOOL OnSysDynamicDeviceInit()

{ return TRUE; }

BOOL OnSysDynamicDeviceExit()

{ return TRUE; }


DWORD OnW32Deviceiocontrol(PIOCTLPARAMS p)

{ return 0; }


记得在前面的文章中提到过,VxD可以与应用程序实现相互通信。我们在用

QuickVxD生成这个例子时,又选中了支持动态加载和Real/V86 Mode API及

Protected Mode API等选项。上面程序中,函数V86_Api_Handler用来实现VxD与


16位的DOS应用程序通信,函数PM_Api_Handler用来实现VxD与16位的Windows

应用程序或DOS-Extended(DPMI)应用程序通信,函数OnW32Deviceiocontrol用
来

实现VxD与32位的Windows应用程序通信。函数OnSysDynamicDeviceInit和

OnSysDynamicDeviceExit自然是用来控制VxD动态加载和卸载啦。


上面的代码中有两个宏DefineControlHandler和ControlDispatcher,用来把这
些函数


与VxD的消息机制联系起来。好像我们搞清楚了,不,再仔细看一下,宏

DefineControlHandler和ControlDispatcher都只是定义了三个函数

OnW32Deviceiocontrol、OnSysDynamicDeviceInit和OnSysDynamicDeviceExit的
消

息映射关系。我们很自然地想到,函数V86_Api_Handler和PM_Api_Handler呢

,为什么能肯定VxD一定用这两个函数与16位应用程序通信呢?


让我们在VToolsD的include子目录下找一找,我们会发现VToolsC.h中有这两个
函数


的定义。下面的代码摘自VToolsC.h。


#define Declare_Virtual_Device_Ex(VName, RefData) \


extern _C_ void _cdecl V86_Api_Handler(VMHANDLE hVM, PCLIENT_

STRUCT pRegs); \

extern _C_ void _cdecl PM_Api_Handler(VMHANDLE hVM, PCLIENT_

STRUCT pRegs); \

extern _C_ void (?VXD_SERVICE_TABLE[])(); \

_EXC_ DDB The_DDB = { 0, DDK_VERSION, VName##_DeviceID, VName

##_Major, \

VName##_Minor, 0, {′ ′,′ ′,′ ′,′ ′,′ ′,′ ′,′ ′,′ ′},
\

VName##_Init_Order, (DWORD) LocalControlDispatcher, \

(DWORD) LocalV86handler, \

(DWORD) LocalPMhandler, 0, 0, RefData, (DWORD) VXD_SERVICE_TABLE, \

0, \

0, \

_SIG_} ;


// This is the standard macro for declaring a DDB, using all default
value
s.


#define Declare_Virtual_Device(VName) Declare_Virtual_Device_

Ex(VName,0)


从上面的代码中,我们可以看到,函数V86_Api_Handler和PM_Api_Handler被

宏Declare_Virtual_Device声明已在DDB(Device Descriptor Block)中,自然
不

用再在MYFIRST.C中进行消息映射了。






****************************************
**
*******************************

VxD世界__VxD的设备描述块与VxD API


VxD设备描述块


用汇编语言描述MYFIRST.VxD的设备描述块(DDB Device Descriptor Block)如下
(

其实,如果是用DDK来开发VxD,那我们在每个VxD的源程序中都会见到这些代码


,只是VToolsD替我们封装了这些费解的东西):


Declare_Virtual_Device MYFIRST,1,0,MYFIRST_Control,MYFIRST_


Device_ID,MYFIRST_Init_Order,MYFIRST_V86_API_Handler,

MYFIRST_PM_API_Handler


对于DDB的8个入口来说,只有前面4个是必须的,后面4个的缺省值为0,如果我
们

的MYFIRST.VxD不输出V86 API,那么上面的代码应这样写:


Declare_Virtual_Device MYFIRST,1,0,MYFIRST_Control,MYFIRST_

Device_ID,MYFIRST_Init_Order,,MYFIRST_PM_API_Handler


一般来说,MYFIRST_Init_Order是可以设为缺省值0的,因为我们一般不需要特


殊的初始化顺序。


你一定会奇怪MYFIRST_Control是怎么回事。读一下下面的代码,大概就明白了
.


BeginProc MYFIRST_Control


Begin_Control_Dispatch MYFIRST_Control


Control_Dispatch Sys_Dynamic_Device_Init, OnSysDynamicDeviceInit


Control_Dispatch Sys_Dynamic_Device_Exit, OnSysDynamicDeviceExit


.........


End_Control_Dispatch MYFIRST_Control


EndProc MYFIRST_Control


对比一下VToolsD为我们生成的C程序:


BOOL _cdecl ControlDispatcher(

DWORD dwControlMessage,

DWORD EBX,DWORD EDX,

DWORD ESI, DWORD EDI,

DWORD ECX)

{ START_CONTROL_DISPATCH

ON_SYS_DYNAMIC_DEVICE_INIT(OnSysDynamicDeviceInit);

ON_SYS_DYNAMIC_DEVICE_EXIT(OnSysDynamicDeviceExit);

END_CONTROL_DISPATCH

return TRUE;}


Windows是基于消息机制的操作系统,这一点在VxD中也体现了出来。MYFIRST_


Control就是接收Windows消息的入口点。Windows发给MYFIRST_Control的消息


与发给Windows应用程序的消息不完全一样,前者包含了一些系统信息。MYFIRST


_Control在收到消息后,调用相应的控制过程。


VxD API


在前面的文章中,我们说MYFIRST.VxD将支持Real/V86 Mode API及Protected

Mode API,这使得MYFIRST.VxD可以与V86应用程序或Win16应用程序通信。

MYFIRST.VxD输出的V86 API和PM API就是


VOID _cdecl V86_Api_Handler(VMHANDLE hVM, PCLIENT_STRUCT pcrs);


VOID _cdecl PM_Api_Handler(VMHANDLE hVM, PCLIENT_STRUCT pcrs);


一个问题很快就摆在我们面前:如何在我们的应用程序中调用到这两个API?


读一下这段代码:


DWORD NEAR PASCAL GetAPIEntry(WORD VxD_ID)

{DWORD Entry_Point;


_asm{

mov AX, 1684h

mov BX, WORD PTR SS: [VxD_ID]

sub DI, DI

mov ES, DI

int 2Fh

mov WORD PTR SS: [Entry_Point][0], DI

mov WORD PTR SS: [Entry_Point][2], ES

} return Entry_Point;}


这段代码可以用在MS_DOS应用程序或是Win16应用程序中,函数GetAPIEntry将分


别返回V86_Api_Handler的地址或PM_Api_Handler的地址。


等一下,函数GetAPIEntry的入口参数VxD_ID是怎么回事?嗯,问得好。如果你
一

直在读我的文章,那你会发现我们在前面有一个失误:在用QuickVxD生成

MYFIRST.VxD的源程序时,把MYFIRST.VxD的DeviceID置成了UNDEFINED_

DEVICE_ID。通过在VToolsD\include\Vmm.h中查找,可以看到:


#define UNDEFINED_DEVICE_ID 0x00000


也就是说所有UNDEFINED_DEVICE_ID的VxD的DeviceID都置成了0。如果我们

向函数GetAPIEntry传递MYFIRST_DeviceID,那我们很可能无法获得

MYFIRST.VxD中的API的地址,因为我们的DeviceID不是惟一的,Windows无法在


众多DeviceID为0的VxD中找到我们的MYFIRST.VxD。 then what should we do?


解决方案有两个:


方案一:

再用QuickVxD重新生成MYFIRST.VxD的源程序。记着在Device Parameters页中填


写Device ID为某个值,这个值尽量大一些,因为比较小的DeviceID都让
Microsoft或


是别的硬件开发商注册了(注册是需要银子的),为了保证不与系统中现存的
VxD

的DeviceID发生冲突,我们只好把DeviceID设得大一些,比如说0xAAAA。


Option II:

编辑一下MYFIRST.H,把MYFIRST_DeviceID改了,改过之后的MYFIRST.h如下

:


#include 〈vtoolsc.h〉

#define MYFIRST_Major 1

#define MYFIRST_Minor 0

#define MYFIRST_DeviceID 0xAAAA

#define MYFIRST_Init_Order UNDEFINED_INIT_ORDER


好了,我们已经准备好与我们的MYFIRST.VxD通信了。

Solar road light is a crystalline silicon solar battery power supply, valve control type sealed and maintenance-free battery, gel battery) to store electrical energy, super bright LED as light source, lamps and lanterns and controlled by the intelligent charging and discharging controller, is used to instead of the traditional public power lighting lamps.

Solar Street Light Battery

Solar road light working principle

1. Using the microcomputer intellective controller to transfer the light energy to electrical energy.Easy to install since to wiring and trenching,energy conservation and environmental protection.

2. The microcomputer intellective controller is composed by advanced special-purpose integrated circuit,high conversion efficiency,prevent over-load and over-discharge,Output short circuit protection,extend work life,on the safe side,convenient usage.

3. High effective Non-maintaining accumulator cell,strong storage,durable usage.

4. Automatic track type time controller,Automatically adjust the working time along with different illumination time in various seasons.For extending the work time energy conservation intellective controller with automatic shut-off the light at Deep-night.

Solar Street Light

 

 


Solar Street Light

Solar Street Light ,Solar Street Light Pole,Solar Street Light Battery,Solar Street Light Advantages

Jiangsu chengxu Electric Group Co., Ltd , http://www.chengxulighting.com

Posted on