这几天我已经不忍再看新闻,一个大老爷们,却流了好几次泪,而且没办法为灾区做点什么,我深感惭愧。

不过看到qq群里转发的某些2b言论,我觉得我还是有话要说。
1.捐款不是义务,爱心不能强迫。捐了的我们可以表示感谢,不捐的也没有理由去攻击他,请别让爱心变了味。
2.不要搞什么捐款排行榜,爱心大小不是以金钱多少来衡量的。
3.捐多捐少都是一份心意,请停止你的风凉话。某些人心里有点扭曲,坐在电脑前,整天盯着看谁捐了多少,甚至无聊到列出个清单,对比印尼海啸和这次的捐款,后面还附上恶心的评论。有这闲工夫您不能做点实事,为灾区尽一份力吗?还有指责姚明和刘德华捐的少的,也请闭上你的嘴,灾区人民需要这些有榜样作用、能带动捐款的人,而不需要你们这些捐款金额评论员。
4.趁机造谣,夹带私货的愤青,请不要出来恶心人了。像什么“三星阻止员工向灾区捐款”,“诺基亚,sony都没捐,我们支持国货吧”之类的,我看到就无比厌恶。你可以讨厌这些外国牌子,你可以支持国货,你也可以讨厌韩国,但请不要这么下作。三星,sony,诺基亚捐了没有上网一查就能知道,我就纳闷这么明显的谎言竟然还一直有人转发,您就不能多一点独立思考吗,转发一条很2的消息之前您就不能先求证一下吗?
5.不要骂人作秀。捐款能提升企业形象,适当作秀无可厚非,只要灾区人民得到了好处,企业宣传下自己有何不可。

 

VS2005的SP1又耗时又占空间是出了名的。想要要今后安装一步到位就要制作集SP1的安装关盘了。

主要有2个命令

1.解压原盘

msiexec.exe /a G:\VS\vs_setup.msi TARGETDIR=F:\VSSETUP /L*vx install.log

我解压到了 F:\VSSETUP 目录

2.集成补丁集

msiexec.exe /a F:\VSSETUP\vs_setup.msi /p "F:\TOOLS\VS2005\VS80sp1-KB926604-X86-CHS\VS80sp1-KB926604-X86-CHS.msp" /L*vx patch.log

这样 F:\VSSETUP 里面的文件就是集成了sp1的补丁的VS2005安装程序

从原盘新建iso镜像,删除镜像VS目录,将F:\VSSETUP目录添加到镜像,重命名VS,另存为新ISO文件,刻盘。

其中要注意的是VS目录里面有文件的路径很深。务必需要支持长文件名。还有就是标卷一定不要改动,依据原来的。

说明,如果运行vs_setup.msi出错,则把原来的以下个文件拷贝到F:\VSSETUP。

 

image

安装后的版本信息:

image

我安装的是专业版,合并后的文件夹大小为2.5G,占用空间为1.43G,不过这比几个小时的等待要好的多。

image

DXUT暂停函数

函数
描述

DXUTPause
将框架的内部计数器和(或)渲染过程设为暂停状态

DXUTRenderingPaused
检查当前设备的渲染状态是否处在暂停状态

DXUTIsTimePaused
检查当前设备的计时器是否处在暂停状态

DXUTPause

Sets the paused state of DXUT internal timer and/or rendering process.

  VOID DXUTPause(  
BOOL bPauseTime ,
BOOL bPauseRendering
) ;
Parameters
bPauseTime
[in] If TRUE, DXUT internal timer is paused. If FALSE, the timer is resumed from a paused state. See Remarks.
bPauseRendering
[in] If TRUE, DXUT will not call the LPDXUTCALLBACKD3D10FRAMERENDER callback function; DXUT will continue to call the LPDXUTCALLBACKFRAMEMOVE callback function. If FALSE, rendering will resume from a paused state. See Remarks.
Return Values

No return value.

Remarks

The timer controls the fTime and fElapsedTime parameters passed to the LPDXUTCALLBACKFRAMEMOVE or render callback functions.

When time is paused, fTime will remain unchanged and fElapsedTime will be 0.0f. The LPDXUTCALLBACKFRAMEMOVE and the render callback functions will still be called. This allows a camera to still move while time is paused.

When rendering is paused, the render callback function and the Direct3D Present method will not be called, and DXUT will periodically yield time to other processes by calling Sleep.

DXUTIsRenderingPaused

Indicates whether rendering has been paused in DXUT.

  BOOL DXUTIsRenderingPaused()  ;
Parameters

None.

Return Values

TRUE if rendering has been paused in DXUT.

DXUTIsTimePaused

Indicates whether time has been paused in DXUT.

  BOOL DXUTIsTimePaused()  ;
Parameters

None.

Return Values

TRUE if time has been paused in DXUT.

DXUT用户输入函数

函数
描述

DXUTIsKeyDown
检查当该函数调用时,键盘上指定的某个键是否按下。

DXUTIsMouseButtonDown
检查当该函数调用时,指定的鼠标键是否按下。

DXUTIsKeyDown

Indicates whether a specified keyboard key is up or down at the time the function is called.

  BOOL DXUTIsKeyDown(  
BYTE vKey
) ;
Parameters
vKey
[in] Virtual key code of a keyboard key. For example VK_F1, VK_LSHIFT, VK_RCONTROL, VK_RMENU, and 41 (representing the 'A' key).
Return Values

TRUE if the specified vKey keyboard key is down; FALSE otherwise.

DXUTIsMouseButtonDown

Indicates whether a specified mouse button is up or down at the time the function is called.

  BOOL DXUTIsMouseButtonDown(  
BYTE vButton
) ;
Parameters
vButton
[in] Virtual key code of a mouse button. Allowed values are VK_LBUTTON, VK_RBUTTON, VK_MBUTTON, VK_XBUTTON1, and VK_XBUTTON2.
Return Values

TRUE if the specified vButton mouse button is down; FALSE otherwise.

DXUT统计函数

函数
描述

DXUTGetFPS
获取当前每秒提交的帧数

DXUTGetFrameStats
获取一个指向字符串的指针,该字符串包括每秒帧数、分辨率、后台缓冲区格式、深度缓冲区格式。

DXUTGetDeviceStats
获取一个指向字符串的指针,该字符串包括当前设备类型、顶点运算行为和设备名。

DXUTGetFPS

Get the current number of frames being presented per second.

  FLOAT DXUTGetFPS()  ;
Parameters

None.

Return Values

The current number of frames being presented per second.

DXUTGetFrameStats

Get a pointer to a string containing the current number of frames per second (optionally), resolution, back buffer format, and depth stencil format.

  LPCWSTR DXUTGetFrameStats(  
bool bIncludeFPS
) ;
Parameters
bIncludeFPS
[in] If true, the string returned will contain the frames per second. Otherwise, it will not.
Return Values

Pointer to a string containing the current number of frames per second (optionally), resolution, back buffer format, and depth stencil format.

DXUTGetDeviceStats

Get a pointer to a string containing the current device type, vertex processing behavior, and device name.

  LPCWSTR DXUTGetDeviceStats()  ;
Parameters

None.

Return Values

Pointer to a string containing the current device type, vertex processing behavior, and device name.

DXUT时间函数

函数
描述

DXUTGetTime
获取当前时间(秒)

DXUTGetElapsedTime
获取从上一帧到当前帧所经过的时间

DXUTSetConstantFrameTime
启用或禁用固定帧时间

DXUTGetTime

Get the current time, in seconds.

  DOUBLE DXUTGetTime()  ;
Parameters

None.

Return Values

The current time, in seconds.

Remarks

DXUT internally uses the best practices for high resolution timing information as described in the "Game Timing and Multicore Processors" article in the DirectX SDK.

DXUTGetElapsedTime

Get the time elapsed since the last frame.

  FLOAT DXUTGetElapsedTime()  ;
Parameters

None.

Return Values

Time elapsed, in seconds, since the last frame.

Remarks

DXUT internally uses the best practices for high resolution timing information as described in the "Game Timing and Multicore Processors" article in the DirectX SDK.

DXUTSetConstantFrameTime

Enables or disables a constant frame time.

  HRESULT DXUTSetConstantFrameTime(  
BOOL bEnabled ,
FLOAT fTimePerFrame
) ;
Parameters
bEnabled
[in] If TRUE, a constant frame time will be enabled.
fTimePerFrame
[in] Time per frame, in seconds. The default value is 0.0333f, so the fTime parameter of LPDXUTCALLBACKFRAMEMOVE and the render callback functions will advance one second for every 30 frames.
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

This function simulates a fixed-frame rate render loop by sending a constant value for elapsed time to the LPDXUTCALLBACKFRAMEMOVE and render callback functions. The default rate is one second for every 30 frames. The application itself will continue to render at an unregulated rate (which may be far higher than the specified frame rate).

This function is useful for saving the rendered output to a video format for playback, allowing animation at a rate independent of the actual rate at which frames were rendered.

DXUT计时器函数

函数
描述

DXUTSetTimer
添加一个新的计时器

DXUTKillTimer
卸载一个已有的计时器

DXUTSetTimer

Starts a DXUT timer that will trigger a callback function at regular intervals.

  HRESULT DXUTSetTimer(  
LPDXUTCALLBACKTIMER pCallbackTimer ,
FLOAT fTimeoutInSecs ,
UINT * pnIDEvent ,
void * pCallbackUserContext
) ;
Parameters
pCallbackTimer
[in] Pointer to a timer callback function. The callback function is to be called at the specified fTimeoutInSecs timeout intervals. May not be NULL.
fTimeoutInSecs
[in] Interval, in seconds, between successive calls to the timer callback function. The default value is 1.0f.
pnIDEvent
[in] Optional pointer to a variable to receive the event ID for the new timer. This event ID will be passed to the timer callback function to indicate which timer generated the event, allowing the application to use a single callback function for multiple timers. The default value is NULL.
pCallbackUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

Timers created with DXUTSetTimer can be destroyed with DXUTKillTimer.

LPDXUTCALLBACKTIMER

A timer to be called at specified time intervals by DXUT.

  VOID LPDXUTCALLBACKTIMER(  
UINT idEvent ,
void* pUserContext
) ;
Parameters
idEvent
[in] Specifies a nonzero timer event ID. Indicates which timer generated the event, allowing the application to use a single callback function for multiple timers.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

DXUT will call this function at the start of the frame, before calling LPDXUTCALLBACKFRAMEMOVE or LPDXUTCALLBACKD3D10FRAMERENDER.

DXUTKillTimer

Uninstalls an existing timer.

  HRESULT DXUTKillTimer(  
UINT nIDEvent
) ;
Parameters
nIDEvent
[in] The event ID for the timer being destroyed. This ID is provided to the application by the DXUTSetTimer method.
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

Timers created with DXUTSetTimer can be destroyed with DXUTKillTimer .

管理DXUT框架的函数

函数
描述

DXUTResetFrameworkState
将框架状态重置为初始默认状态,之前设置的框架状态改变将失效。

DXUTShutdown
触发程序终止和清空框架

DXUTGetExitCode
获取框架的退出代码

DXUTResetFrameworkState

Resets DXUT state to its initial default state. All previous DXUT state changes are lost.

 VOID DXUTResetFrameworkState() ;
Parameters

None.

Return Values

No return value.

Remarks

This function does not normally need to be called. It is useful for testing purposes because the application process does not have to be restarted to reset DXUT back to its initial state.

DXUTShutdown

Triggers program termination and DXUT cleanup.

 VOID DXUTShutdown( 
int nExitCode
) ;
Parameters
nExitCode
[in] The exit code returned via the DXUTGetExitCode function. The default value is 0
Return Values

No return value.

Remarks

It is not necessary for the application to call this function, since the application lifespan is handled by DXUT. However, the application can call this function to cleanly terminate the application process.

DXUTGetExitCode

Get DXUT exit code.

 INT DXUTGetExitCode() ;
Parameters

None.

Return Values

A DXUT exit code, which will be one of the following possible values.

Value
Description

0
Successful execution.

1
An undetermined error occurred.

2
No Direct3D device could be found with the specified device settings.

3
A media file could not be found.

4
The Direct3D device has a non-zero reference count, meaning that some objects were not released.

5
An error occurred when attempting to create an Direct3D device.

6
An error occurred when attempting to reset an Direct3D device.

7
An error occurred in the device creation callback function.

8
An error occurred in the device creation callback function.

9
The incorrect version of Direct3D or D3DX is installed.

10
The last device used upon exit was a REF device type.

11
The device was removed.

Remarks

The return value of this function is typically used in the application as the return code of the application's WinMain function. Command-line tests can then be performed on applications using this return code.

The following is an example of command-line usage that uses DXUT exit code:

start /wait BasicHLSL.exe
echo %errorlevel%

检索Direct3D变量的函数

函数
描述

DXUTGetD3DObject
获取一个指向IDirect3D9对象的指针

DXUTGetD3D9Device
获取一个指向代表当前设备的IDirect3DDevice9接口指针

DXUTGetDeviceSettings
获取用来创建当前设备的结构体DXUTDeviceSettings

DXUTGetPresentParameters
获取当前设备的提交(presentation)参数

DXUTGetD3D9BackBufferSurfaceDesc
获取一个指向当前设备后台缓冲区表面的D3DSURFACE_DESC结构体的指针

DXUTGetD3D9DeviceCaps
获取一个指向当前设备的D3DCAPS9结构体的指针

DXUTGetD3DObject

Get a pointer to the IDirect3D9 object.

 IDirect3D9 * DXUTGetD3DObject() ;
Parameters

None.

Return Values

Pointer to the IDirect3D9 object. NULL is returned if no IDirect3D9 object has been created. See Remarks.

Remarks

This function exposes access to a global IDirect3D9 object. The reference count on this object interface is not incremented, so a calling function should not release the IDirect3D9 interface pointer returned by this function.

DXUTGetDeviceSettings

Get the DXUTDeviceSettings structure used to create the current device.

 DXUTDeviceSettings DXUTGetDeviceSettings() ;
Parameters

None.

Return Values

DXUTDeviceSettings structure used to create the current device.

Remarks

This settings structure can describe either a Direct3D 9 device or a Direct3D 10 device as defined the structure's DXUTDeviceVersion member.

If no device exists, then the DXUTDeviceSettings structure is filled with zeros.

DXUTGetPresentParameters

Get the presentation parameters of the Direct3D 9 device.

 D3DPRESENT_PARAMETERS DXUTGetPresentParameters() ;
Parameters

None.

Return Values

The presentation parameters of the Direct3D 9 device.

Remarks

If no Direct3D 9 device exists, then the D3DPRESENT_PARAMETERS structure is filled with zeros.

DXUTGetD3D9BackBufferSurfaceDesc

Get a pointer to a D3DSURFACE_DESC surface description of the current Direct3D 9 back buffer.

 CONST D3DSURFACE_DESC * DXUTGetD3D9BackBufferSurfaceDesc() ;
Parameters

None.

Return Values

Pointer to a D3DSURFACE_DESC surface description of the current Direct3D 9 back buffer.

Remarks

If no Direct3D 9 device exists, then the D3DSURFACE_DESC structure is filled with zeros.

DXUTGetD3D9DeviceCaps

Get a pointer to the D3DCAPS9 capabilities of the current device.

 CONST D3DCAPS9 * DXUTGetD3D9DeviceCaps() ;
Parameters

None.

Return Values

Pointer to the D3DCAPS9 capabilities of the Direct3D 9 device.

Remarks

If no Direct3D 9 device exists, then the D3DCAPS9 structure is filled with zeros.

下面列出允许改变DXUT行为和获取内部变量的函数,这些函数在使用DXUT框架的Direct3D程序中是非常实用的。

管理窗口的DXUT函数

函数
描述

DXUTGetHINSTANCE
获取应用程序实例的句柄

DXUTGetHWND
获取当前设备窗口的句柄

DXUTGetHWNDFocus
获取当前获得焦点的窗口的句柄

DXUTGetHWNDDeviceFullScreen
获取全屏模式设备窗口的句柄

DXUTGetHWNDDeviceWindowed
获取窗口模式设备窗口的句柄

DXUTGetWindowClientRect
获取应用程序设备窗口的客户区矩形

DXUTGetWindowTitle
获取指向应用程序窗口标题的指针

DXUTIsWindowed
检查应用程序是否处在窗口模式下

DXUTGetHINSTANCE

Get a handle to the application instance.

HINSTANCE DXUTGetHINSTANCE();
Parameters

None.

Return Values

A handle to the application instance.

DXUTGetHWND

Get a handle to the current device window.

HWND DXUTGetHWND();
Parameters

None.

Return Values

A handle to the current device window.

DXUTGetHWNDFocus

Get the handle of the focus window.

HWND DXUTGetHWNDFocus();
Parameters

None.

Return Values

Handle to the focus window.

Remarks

The focus window informs Direct3D when the application is switched to a background window with an ALT+TAB key entry, a mouse click, or by other means. It is typically the same window as the device window.

DXUTGetHWNDDeviceFullScreen

Get the handle of the device window used when the application is fullscreen.

HWND DXUTGetHWNDDeviceFullScreen();
Parameters

None.

Return Values

Returns a handle of the device window used when the application is fullscreen.

DXUTGetHWNDDeviceWindowed

Get the handle of the device window used when the application is windowed.

HWND DXUTGetHWNDDeviceWindowed();
Parameters

None.

Return Values

Returns a handle of the device window used when the application is windowed.

DXUTGetWindowClientRect

Get the current client RECT of the application window.

CONST RECT * DXUTGetWindowClientRect();
Parameters

None.

Return Values

Returns the current client RECT of the application window.

DXUTGetWindowTitle

Get a pointer to a string containing the application window title.

LPCWSTR DXUTGetWindowTitle();
Parameters

None.

Return Values

Pointer to a string containing the application window title.

DXUTIsWindowed

Indicates whether the application is in windowed mode.

BOOL DXUTIsWindowed();
Parameters

None.

Return Values

If TRUE, the application is in windowed mode. If the application is not in windowed mode, or no device exists, then FALSE is returned.

管理设备的DXUT函数

函数
描述

DXUTSetCursorSettings
为全屏模式下光标的用法设置选项

DXUTSetMultimonSettings
为框架如何在多显示器配置中工作设置选项

DXUTToggleFullscreen
使应用程序在窗口模式和全屏模式间切换

DXUTToggleREF
使应用程序在硬件抽象层和参考设备间切换

DXUTSetCursorSettings

Sets options for cursor usage in full-screen mode.

HRESULT DXUTSetCursorSettings(
bool bShowCursorWhenFullScreen,
bool bClipCursorWhenFullScreen
);
Parameters
bShowCursorWhenFullScreen
[in] Cursor visibility flag. If true, the cursor will be visible when the application is running in full-screen mode.
bClipCursorWhenFullScreen
[in] Cursor clipping flag. If true, the cursor will be restricted from exiting the screen boundaries when the application is running in full screen mode.
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

DXUTSetMultimonSettings

Sets options for how DXUT functions on multiple monitors.

VOID DXUTSetMultimonSettings(
BOOL bAutoChangeAdapter
);
Parameters
bAutoChangeAdapter
[in] If TRUE and the application window is relocated to a different monitor, DXUT will automatically change to use the new monitor's adapter, and device callback functions will be called to recreate the scene.
Return Values

No return value.

DXUTToggleFullscreen

Switches the application between windowed and full-screen modes.

HRESULT DXUTToggleFullscreen();
Parameters

None.

Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

This function works regardless of which Direct3D API version the application is using.

Toggling between windowed and full-screen modes will result in swap chain being resized if using Direct3D 10, or the device being reset or recreated if using Direct3D 9.

DXUTToggleREF

Switches the application between HAL and reference device types.

HRESULT DXUTToggleREF();
Parameters

None.

Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

This function works regardless of which Direct3D API version the application is using.

DXUT框架与错误处理

Direct3D API的设计使程序能比较容易地处理各种错误,尽管大多数Direct3D API函数返回HTRSULT值,但只有一部分函数返回设备错误,如D3DERR_DEVICELOST或D3DERR_DRIVERINTERNALERROR。但是通常的Direct3D应用程序使用多种API函数,当传递的参数不合要求时,将返回D3DERR_INVALIDCALL。

当开发Direct3D应用程序时,应该检查所有的API调用是否成功,如果出现一个没有预测到的失败调用,应用程序应立即给出通知或记录该错误。使用这种方法,开发人员能很快发现哪些API函数的调用是不正确的。一个正确调用Direct3D API函数的应用程序应能安全地忽略大多数Direct3D API函数的失败调用,除了一些关键性的API函数,如Present()或TestCooperativeLevel(),这些函数返回的错误应用程序不能忽略。

通过仅处理最重要的Direct3D错误,可以提高运行速度并使应用程序代码更健壮,因为代码中需要处理错误的地方并不多。对于为数不多的几个API函数的失败调用,必须予以适当处理。

框架中错误的处理对应Direct3D API中如何设计错误的处理,对于各种各样的错误,如丢失媒体(missing media),应用程序能通知用户并终止。对于每一帧都将调用的大多数API函数,错误仅在调试时向开发人员显示一个错误消息框来处理,而在发布时这些错误都被忽略了。框架用在DXUT.h中定义的几个宏来完成这一操作:

#if defined(DEBUG) || defined(_DEBUG)
#ifndef V
#define V(x) { hr = (x); if( FAILED(hr) ) { DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, true ); } }
#endif
#ifndef V_RETURN
#define V_RETURN(x) { hr = (x); if( FAILED(hr) ) { return DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, true ); } }
#endif
#else
#ifndef V
#define V(x) { hr = (x); }
#endif
#ifndef V_RETURN
#define V_RETURN(x) { hr = (x); if( FAILED(hr) ) { return hr; } }
#endif
#endif

当使用vs.net时,如果想跳到出错代码所在的行,只需简单地双击调试输出窗口中输出的错误信息行即可。

选择最可行的设备

DXUT使用高度灵活的方法从枚举集合中选择最好的设备,这个设备枚举和分级系统可以通过调用函数DXUTFindValidDeviceSettings()独立于框架使用,该函数的声明如下:

Finds valid device settings to be used to create a new device.

HRESULT DXUTFindValidDeviceSettings(
DXUTDeviceSettings * pOut,
DXUTDeviceSettings * pIn,
DXUTMatchOptions * pMatchOptions
);
Parameters
pOut
[out] Pointer to a DXUTDeviceSettings structure that contains valid settings for the new device.
pIn
[in] Pointer to a DXUTDeviceSettings structure that contains desired settings for the new device. The default value is NULL.
pMatchOptions
[in] Pointer to a DXUTMatchOptions structure that contains flags describing how to use the device settings when choosing valid output device settings. Optimal device settings will be created based upon the match values in DXUTMatchOptions. If NULL, the function acts as if all members of this structure were DXUTMT_IGNORE_INPUT, meaning that the function will return valid device settings as close as possible to default device settings. See Remarks. The default value is NULL.
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

This function attempts to find valid device settings based upon the input device settings, given by pIn. For each device setting, a match option in the DXUTDeviceSettings structure specifies how the function makes decisions. The function works for both Direct3D 9 and Direct3D 10 device settings.

This function is internally by DXUT used when toggling between full screen and windowed modes, when selecting between HAL and REF device types, and inside DXUTCreateDevice.

DXUTMatchOptions

Describes match options for finding valid device settings using the DXUTFindValidDeviceSettings function. Each member of this structure corresponds to a setting described by the DXUTDeviceSettings structure.

Default values are used when a member is set to DXUTMT_IGNORE_INPUT. See Remarks.

typedef struct DXUTMatchOptions {
DXUT_MATCH_TYPE eAPIVersion;
DXUT_MATCH_TYPE eAdapterOrdinal;
DXUT_MATCH_TYPE eOutput;
DXUT_MATCH_TYPE eDeviceType;
DXUT_MATCH_TYPE eWindowed;
DXUT_MATCH_TYPE eAdapterFormat;
DXUT_MATCH_TYPE eVertexProcessing;
DXUT_MATCH_TYPE eResolution;
DXUT_MATCH_TYPE eBackBufferFormat;
DXUT_MATCH_TYPE eBackBufferCount;
DXUT_MATCH_TYPE eMultiSample;
DXUT_MATCH_TYPE eSwapEffect;
DXUT_MATCH_TYPE eDepthFormat;
DXUT_MATCH_TYPE eStencilFormat;
DXUT_MATCH_TYPE ePresentFlags;
DXUT_MATCH_TYPE eRefreshRate;
DXUT_MATCH_TYPE ePresentInterval;
} DXUTMatchOptions, *LPDXUTMatchOptions;
Members
eAPIVersion
Match type for the API version.
eAdapterOrdinal
Match type for the display adapter ordinal.
eOutput
Match type for the adapter output ordinal.
eDeviceType
Match type for the enumerated type of the device. If set to DXUTMT_IGNORE_INPUT, then the default value is D3DDEVTYPE_HAL.
eWindowed
Match type for the windowed or full-screen mode. if set to DXUTMT_IGNORE_INPUT, then the default value is windowed mode (TRUE).
eAdapterFormat
Match type for the adapter surface format. If set to DXUTMT_IGNORE_INPUT, then the default value is the desktop display mode, or D3DFMT_X8R8G8B8 if the desktop display mode is less than 32 bits.
eVertexProcessing
Match type for the vertex processing flags D3DCREATE_HARDWARE_VERTEXPROCESSING, D3DCREATE_MIXED_VERTEXPROCESSING, or D3DCREATE_SOFTWARE_VERTEXPROCESSING. if set to DXUTMT_IGNORE_INPUT, then the default value is D3DCREATE_HARDWARE_VERTEXPROCESSING.
eResolution
Match type for the display mode resolution. if set to DXUTMT_IGNORE_INPUT, then the default value is 640 x 480 pixels for windowed mode, or the desktop resolution for full-screen mode.
eBackBufferFormat
Match type for the back buffer format. if BackBufferFormat is set to DXUTMT_IGNORE_INPUT, then the default value is to match the adapter format.
eBackBufferCount
Match type for the number of back buffers. if BackBufferCount is set to DXUTMT_IGNORE_INPUT, then the default value is 2 for triple buffering.
eMultiSample
Match type for the quality level. if set to DXUTMT_IGNORE_INPUT, then the default value is to disable multisampling (MultiSampleQuality = 0).
eSwapEffect
Match type for the swap effect. if set to DXUTMT_IGNORE_INPUT, then the default value is D3DSWAPEFFECT_DISCARD.
eDepthFormat
Match type for the depth format of the automatic depth-stencil surface that the device will create. If both eDepthFormat and eStencilFormat are set to DXUTMT_IGNORE_INPUT, then the default value is D3DFMT_D16 if the backbuffer format is 16 bits or less, or D3DFMT_D32 otherwise.
eStencilFormat
Match type for the stencil format of the automatic depth-stencil surface that the device will create. if both eDepthFormat and eStencilFormat are set to DXUTMT_IGNORE_INPUT, then the default value is D3DFMT_D16 if the backbuffer format is 16 bits or less, or D3DFMT_D32 otherwise.
ePresentFlags
Match type for the presentation parameters flags. if set to DXUTMT_IGNORE_INPUT, then the default value is D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL.
eRefreshRate
Match type for the rate at which the display adapter refreshes the screen. If set to DXUTMT_IGNORE_INPUT, then the default value is 0, indicating windowed mode.
ePresentInterval
Match type for the presentation interval. if set to DXUTMT_IGNORE_INPUT, then the default value is D3DPRESENT_INTERVAL_IMMEDIATE for windowed mode, or D3DPRESENT_INTERVAL_DEFAULT for full-screen mode.
Remarks

For each member of this structure, match options are specified using the constant values of the DXUT_MATCH_TYPE enumeration, as in the following code example.

matchOptions.eResolution = DXUTMT_CLOSEST_TO_INPUT;

To use default device settings instead, use the DXUTMT_IGNORE_INPUT flag as follows:

matchOptions.eResolution = DXUTMT_IGNORE_INPUT;

DXUT_MATCH_TYPE

Describes how to match input device settings when creating a new device with a function.

typedef enum DXUT_MATCH_TYPE
{
DXUTMT_IGNORE_INPUT = 0,
DXUTMT_PRESERVE_INPUT,
DXUTMT_CLOSEST_TO_INPUT,
} DXUT_MATCH_TYPE, *LPDXUT_MATCH_TYPE;
Constants
DXUTMT_IGNORE_INPUT
Ignore the device setting input, and return a device setting as close as possible to a default device setting.
DXUTMT_PRESERVE_INPUT
Return without changing the device setting that was given as input to the function.
DXUTMT_CLOSEST_TO_INPUT
Return a device setting as close as possible to the device setting that was given as input to the function.

假设想要获取一个硬件抽象层设备,其后台缓冲区格式是D3DFMT_A2B10G10R10,如果系统中的硬件抽象层设备不支持这种后台缓冲区格式,但有一个安装好的参考设备支持,那么该函数可以使用该参考设备或根据硬件抽象层设备改变后台缓冲区格式,这都将通过枚举类型DXUT_MATCH_TYPE来控制如何采用设备格式。

(2)帧事件

框架也提供了帧事件,它在渲染过程中的每一帧被调用,应用程序应该注册并实现这些回调函数,如下表所示:

应用程序回调函数
注册回调函数
框架调用时机
场景渲染

LPDXUTCALLBACK-
FRAMEMOVE
DXUTSetCallback-
FrameMove
在每一帧开始时调用一次
这个回调函数是应用程序处理场景更新的最好位置,但它不应包括实际的渲染调用,渲染调用应放在帧渲染回调函数中。

LPDXUTCALLBACK-
D3D9FRAMERENDER
DXUTSetCallback-
D3D9FrameRender
在每一帧结束或窗口需要重画时调用
所有对场景的渲染调用都应在此回调函数中完成,在这个回调函数返回后,框架将调用Present()来显示交换链中下一缓冲区的内容。

DXUTSetCallbackFrameMove

Sets the frame update callback function.

VOID DXUTSetCallbackFrameMove(
LPDXUTCALLBACKFRAMEMOVE pCallbackFrameMove,
void* pUserContext
);
Parameters
pCallbackFrameMove
[in] Pointer to a LPDXUTCALLBACKFRAMEMOVE callback function. If the callback function is supplied, it will be called at the beginning of every frame to facilitate updates to the scene. If NULL, DXUT will not notify the application about new frames.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

The LPDXUTCALLBACKFRAMEMOVE callback function is the appropriate location for the application to handle updates to the scene. However, LPDXUTCALLBACKFRAMEMOVE is not intended to contain actual rendering calls, which should instead be placed in the LPDXUTCALLBACKD3D9FRAMERENDER or LPDXUTCALLBACKD3D10FRAMERENDER callback function. These callbacks is called when rendering with either Direct3D 9 or Direct3D 10 respectively.

The LPDXUTCALLBACKFRAMEMOVE callback function will be called once per frame, while the render callback function will be called whenever the scene needs to be rendered, which might be more than once per frame on rare occasion if a WM_PAINT message occurs.

LPDXUTCALLBACKFRAMEMOVE

Application-defined callback function that allows for updating the scene. This function is called by DXUT once each frame, before the application renders the scene.

VOID LPDXUTCALLBACKFRAMEMOVE(
DOUBLE fTime,
FLOAT fElapsedTime,
void* pUserContext
);
Parameters
fTime
[in] Time elapsed since the application started, in seconds.
fElapsedTime
[in] Time elapsed since the last frame, in seconds.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

The LPDXUTCALLBACKFRAMEMOVE callback function is the appropriate location for the application to handle updates to the scene. However, LPDXUTCALLBACKFRAMEMOVE is not intended to contain actual rendering calls, which should instead be placed in the LPDXUTCALLBACKD3D9FRAMERENDER or LPDXUTCALLBACKD3D10FRAMERENDER callback function. These callbacks is called when rendering with either Direct3D 9 or Direct3D 10 respectively.

The LPDXUTCALLBACKFRAMEMOVE callback function will be called once per frame, while the render callback function will be called whenever the scene needs to be rendered, which might be more than once per frame on rare occasion if a WM_PAINT message occurs.

DXUTSetCallbackD3D9FrameRender

Sets the Direct3D 9 frame render callback function.

VOID DXUTSetCallbackD3D9FrameRender(
LPDXUTCALLBACKD3D9FRAMERENDER pCallback,
void* pUserContext
);
Parameters
pCallback
[in] Pointer to a LPDXUTCALLBACKD3D9FRAMERENDER callback function. If the callback function is supplied, it will be called once per frame for the application to render the current scene using the Direct3D 9 device. If NULL, DXUT will not prompt the application to render the scene.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

This function only needs to be called if the application supports rendering with Direct3D 9 device.

The LPDXUTCALLBACKD3D9FRAMERENDER callback function is the appropriate location for the application to render the current scene using the Direct3D 9 device. The LPDXUTCALLBACKFRAMEMOVE callback function will be called once per frame, while LPDXUTCALLBACKD3D9FRAMERENDER will be called when the scene needs to be rendered, which might be more than once per frame.

LPDXUTCALLBACKD3D9FRAMERENDER

Application-defined callback function that allows for rendering the scene using a Direct3D 9 device. This function is called by DXUT at the end of every frame, and whenever the application needs to paint the scene.

VOID LPDXUTCALLBACKD3D9FRAMERENDER(
IDirect3DDevice9 * pd3dDevice,
DOUBLE fTime,
FLOAT fElapsedTime,
void* pUserContext
);
Parameters
pd3dDevice
[in] Pointer to the Direct3D 9 device used for rendering.
fTime
[in] Time elapsed since the application started, in seconds.
fElapsedTime
[in] Time elapsed since the last frame, in seconds.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

The LPDXUTCALLBACKD3D9FRAMERENDER callback function is the appropriate location for the application to render the current scene using the Direct3D 9 device. The LPDXUTCALLBACKFRAMEMOVE callback function will be called once per frame, while LPDXUTCALLBACKD3D9FRAMERENDER will be called when the scene needs to be rendered, which might be more than once per frame.

DXUT will call this function after the LPDXUTCALLBACKFRAMEMOVE callback function.

在函数LPDXUTCALLBACKFRAMEMOVE中,通常进行数据变换,比如设置坐标变换矩阵。在函数LPDXUTCALLBACKD3D9FRAMERENDER中,主要进行图形的渲染,类似于Direct3D API中的Render()函数。

(3)消息事件

框架通过下表中的回调函数和相应的注册函数来传递窗口消息、键盘事件和鼠标事件,编写应用程序对这些事件做出适当反应。

应用程序回调函数
注册回调函数
描述

LPDXUTCALLBACKMSGPROC
DXUTSetCallbackMsgProc
处理来自DXUT消息泵的窗口消息

LPDXUTCALLBACKKEYBOARD
DXUTSetCallbackKeyboard
处理来自DXUT消息泵的键盘事件

LPDXUTCALLBACKMOUSE
DXUTSetCallbackMouse
处理来自DXUT消息泵的鼠标事件

DXUTSetCallbackKeyboard

Sets the keyboard event callback function.

VOID DXUTSetCallbackKeyboard(
LPDXUTCALLBACKKEYBOARD pCallbackKeyboard,
void* pUserContext
);
Parameters
pCallbackKeyboard
[in] Pointer to a LPDXUTCALLBACKKEYBOARD keyboard event callback function. If supplied, the callback function will be called for keyboard events. If NULL, DXUT will not notify the application about keyboard events.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

The pCallbackKeyboard keyboard event callback function will be called when any keyboard event occurs.

This callback mechanism is provided to simplify handling keyboard messages through the windows message pump, but does not preclude the application from handling those messages directly through the LPDXUTCALLBACKMSGPROC callback.

LPDXUTCALLBACKKEYBOARD

Application-defined keyboard event callback function, called by DXUT.

VOID LPDXUTCALLBACKKEYBOARD(
UINT nChar,
bool bKeyDown,
bool bAltDown,
void* pUserContext
);
Parameters
nChar
[in] A virtual-key code for the key. See Virtual-Key Codes for a listing.
bKeyDown
[in] TRUE if key is down. FALSE if the key is up
bAltDown
[in] TRUE if the ALT key is also down.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

The pCallbackKeyboard keyboard event callback function will be called when any keyboard event occurs.

This callback mechanism is provided to simplify handling keyboard messages through the windows message pump, but does not preclude the application from handling those messages directly through the LPDXUTCALLBACKMSGPROC callback.

DXUTSetCallbackMouse

Sets the mouse event callback function.

VOID DXUTSetCallbackMouse(
LPDXUTCALLBACKMOUSE pCallbackMouse,
BOOL bIncludeMouseMove,
void* pUserContext
);
Parameters
pCallbackMouse
[in] Pointer to an LPDXUTCALLBACKMOUSE mouse event callback function. If supplied, the callback function will be called for mouse events. If NULL, DXUT will not notify the application about mouse events.
bIncludeMouseMove
[in] If TRUE, the mouse movement events are passed to the pCallbackMouse callback function. Default value is FALSE.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

The pCallbackMouse mouse event callback function will be called when any mouse events occurs

This callback mechanism is provided to simplify handling mouse messages through the Windows message pump, but does not preclude the application from handling those messages directly in the LPDXUTCALLBACKMSGPROC callback function.

LPDXUTCALLBACKMOUSE

Application-defined mouse event callback function, called by DXUT when it receives mouse events.

VOID LPDXUTCALLBACKMOUSE(
bool bLeftButtonDown,
bool bRightButtonDown,
bool bMiddleButtonDown,
bool bSideButton1Down,
bool bSideButton2Down,
INT nMouseWheelDelta,
INT xPos,
INT yPos,
void* pUserContext
);
Parameters
bLeftButtonDown
[in] The left mouse button is down.
bRightButtonDown
[in] The right mouse button is down.
bMiddleButtonDown
[in] The middle mouse button is down.
bSideButton1Down
[in] Windows 2000/Windows XP: The first side button is down.
bSideButton2Down
[in] Windows 2000/Windows XP: The second side button is down.
nMouseWheelDelta
[in] The distance and direction the mouse wheel has rolled, expressed in multiples or divisions of WHEEL_DELTA, which is 120. A positive value indicates that the wheel was rotated forward, away from the user; a negative value indicates that the wheel was rotated backward, toward the user.
xPos
[in] x-coordinate of the pointer, relative to the upper-left corner of the client area.
yPos
[in] y-coordinate of the pointer, relative to the upper-left corner of the client area.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

The pCallbackMouse mouse event callback function will be called when any mouse events occurs

This callback mechanism is provided to simplify handling mouse messages through the Windows message pump, but does not preclude the application from handling those messages directly in the LPDXUTCALLBACKMSGPROC callback function.

在窗口和设备创建好之后,应用程序需要使用消息循环处理窗口消息、更新和渲染场景、处理设备事件。应用程序可以实现自己的消息循环,也可以使用DXUT消息循环,注册相应的回调函数,可以让DXUT处理设备、帧消息事件。

进入消息循环

为使用DXUT框架的消息循环,可以调用DXUTMainLoop()函数:

Starts the main execution loop of DXUT.

HRESULT DXUTMainLoop(
HACCEL hAccel
);
Parameters
hAccel
[in] Handle to an accelerator table to use in translating keyboard messages from the Windows message queue, or NULL if not using an accelerator table. The default value is NULL.
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

This function starts the message loop that will run for the lifetime of the application. During execution, DXUTMainLoop calls the registered callback functions to ask the application to update and render the frame, as well as handle any device or input events.

Custom Main Loop

For some advanced applications a custom main loop may be a better design. It is possible to use DXUT with a custom main loop. An example of how to do this is shown below.

INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, INT )
{
DXUTSetCallbackD3D9DeviceAcceptable( IsDeviceAcceptable );
DXUTSetCallbackD3D9DeviceCreated( OnCreateDevice );
DXUTSetCallbackD3D9DeviceReset( OnResetDevice );
DXUTSetCallbackD3D9FrameRender( OnFrameRender );
DXUTSetCallbackD3D9DeviceLost( OnLostDevice );
DXUTSetCallbackD3D9DeviceDestroyed( OnDestroyDevice );
DXUTSetCallbackMsgProc( MsgProc );
DXUTSetCallbackKeyboard( KeyboardProc );
DXUTSetCallbackFrameMove( OnFrameMove );
DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );

DXUTInit( true, true );
DXUTCreateWindow( L"Example" );
DXUTCreateDevice( true, 640, 480 );

// Custom main loop
HWND hWnd = DXUTGetHWND();
BOOL bGotMsg;
MSG msg;
msg.message = WM_NULL;
PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );

while( WM_QUIT != msg.message )
{
// Use PeekMessage() so we can use idle time to render the scene
bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );

if( bGotMsg )
{
// Translate and dispatch the message
if( 0 == TranslateAccelerator( hWnd, NULL, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
else
{
// Render a frame during idle time (no messages are waiting)
DXUTRender3DEnvironment();
}
}

return DXUTGetExitCode();
}

This example calls DXUTRender3DEnvironment to have DXUT update and render the scene and handle device events. While it is possible for the application to completely replicate this functionality, it is not recommended.

DXUTRender3DEnvironment

Renders the 3D environment.

VOID DXUTRender3DEnvironment();
Parameters

None.

Return Values

No return value.

Remarks

This method does not normally need to be called. It is useful only when the application does not use DXUTMainLoop but still wants DXUT to assist with rendering.

This method checks whether the device is lost. If so, the method attempts to reset the device and then calls the LPDXUTCALLBACKFRAMEMOVE and LPDXUTCALLBACKD3D10FRAMERENDER callback functions.

If the application window is minimized or the application is paused, CPU time is yielded to other processes.

处理事件

框架使用回调函数机制来使应用程序对事件做出反应。应用程序只需对框架注册和设置相应的函数指针,则当事件发生时,框架就会调用相应的函数。框架不需要注册所有的回调函数,所以应用程序只须对所需要的回调函数进行注册即可。通过为回调函数设置参数pUserContext,回调函数可以从应用程序接受内容,比如将该参数设置为一个指向类对象的指针。

DXUT框架可以处理以下事件类型:

(1)设备事件

当应用程序使用Direct3D设备渲染图形时,该设备有可能处于丢失状态。这种情况的发生有多种原因,例如按下Alt + Tab键离开一个全屏模式的应用程序,或者按下Ctrl + Alt + Del键,或者启动了另一个全屏3D应用程序。发生这种情况时,当调用一些函数(如Present)时,Direct3D API通过返回D3DERR_DEVICELOST通知应用程序设备丢失。

当设备丢失时,应用程序负责释放所有不能在设备丢失时存在的Direct3D资源对象,如在D3DPOOL_DEFAULT内存池中创建的对象。如果没有释放这些对象,那么该设备从丢失状态返回时就不能被重新设置。当设备丢失时,应用程序必须等待。当设备返回时,应用程序必须调用函数IDirect3DDevice9::Reset(),并重新创建所有不能在Reset()函数中存在的对象。

通过DXUT框架,这个过程可以通过在应用程序中使用回调函数来简化,这些回调函数处理各种设备事件:设备改变、创建、重新设置、丢失或销毁。当设备丢失时,框架会有提示;当它从丢失状态返回时,框架会适当调用相应的回调函数,重新设置该设备,即框架使用应用程序的回调函数在适当的时间释放和重新创建设备对象。应用程序需要做的是注册并实现相关回调函数,各回调函数的类型、注册、调用时机等细节见下表:

注册函数
应用程序回调函数
框架调用时机
创建资源
释放资源

DXUTSetCallback-
DeviceChanging
LPDXUTCALLBACK-
MODIFYDEVICESETTINGS
在创建Direct3D设备之前调用,应用程序可以返回FALSE,拒绝改变该设备。
x
x

DXUTSetCallback-
D3D9DeviceCreated
LPDXUTCALLBACK-
D3D9DEVICECREATED
当应用程序初始化和重新创建设备时,在Direct3D设备创建之后立即调用。
创建D3DPOOL_MANAGED资源,因为这些资源无论什么时候被销毁都需要重新加载,但这些资源被重新设置时不需要重新加载。在这里创建的资源需要在LPDXUTCALLBACK-DEVICEDESTROYED中释放。
x

DXUTSetCallback-
D3D9DeviceReset
LPDXUTCALLBACK-
D3D9DEVICERESET
当Direct3D设备丢失又被重新设置后立即调用。
创建D3DPOOL_DEFAULT资源,因为这些资源无论什么时候丢失或重新设置时都需要重新加载,在这里创建的资源需要在LPDXUTCALLBACK-DEVICELOST中释放。
x

DXUTSetCallback-
D3D9DeviceLost
LPDXUTCALLBACK-
D3D9DEVICELOST
当Direct3D设备变为丢失状态且在Reset调用之前,立即调用。
x
释放在回调函数LPDXUTCALLBACK-D3D9DEVICERESET中创建的资源,这些资源通常包括所有的D3DPOOL_DEFAULT资源。

DXUTSetCallback-
D3D9DeviceDestroyed
LPDXUTCALLBACK-
D3D9DEVICEDESTROYED
当应用程序终止或重新创建设备时,Direct3D设备被销毁后,立即调用。
x
释放在回调函数LPDXUTCALLBACK-
D3D9DEVICECREATED中创建的资源,这些资源通常包括所有的D3DPOOL_MANAGED资源。

当设备在窗口和全屏模式间切换时常常需要重新设置,但有时它必须通过Direct3D重新创建。

调用这些回调函数是可选的,但如果应用程序没有使用函数DXUTSetCallbackD3D9DeviceDestroyed()和DXUTSetCallbackD3D9DeviceCreated()注册销毁回调函数和创建回调函数,则改变设备或在硬件抽象层设备和参考设备间切换都不能进行。

类似地,如果没有用函数DXUTSetCallbackD3D9DeviceLost()和DXUTSetCallbackD3D9DeviceReset()注册丢失回调函数和重置回调函数,则当设备丢失或重置设备时,框架无法通知应用程序。这样一来,所有不在D3DPOOL_MANAGED内存中的设备对象都不能重新设置。

修改可用的设备

应用程序可以通过DXUTSetCallbackDeviceChanging()设置回调函数来修改Direct3D设备的创建设置:

Sets a callback function that allow the application to change the device settings before the device is created.

VOID DXUTSetCallbackDeviceChanging(
LPDXUTCALLBACKMODIFYDEVICESETTINGS pCallbackModifyDeviceSettings,
void* pUserContext
);
Parameters
pCallbackModifyDeviceSettings
[in] Pointer to a LPDXUTCALLBACKMODIFYDEVICESETTINGS callback function. If the callback function is supplied, it will be called before the Direct3D device is created. If NULL, DXUT will not notify the application about device changes.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

Before a device is created by DXUT, the LPDXUTCALLBACKMODIFYDEVICESETTINGS callback will be called to allow the application to examine or change the device settings before the device is created. This allows an application to modify the device creation settings as it sees fit.

This callback also allows applications to reject changing the device altogether. Returning false from inside this callback will notify DXUT to keep using the current device instead of changing to the new device.

LPDXUTCALLBACKMODIFYDEVICESETTINGS

Application-defined callback function, called by DXUT to allow changes in device settings before the device is created.

bool LPDXUTCALLBACKMODIFYDEVICESETTINGS(
DXUTDeviceSettings * pDeviceSettings,
void* pUserContext
);
Parameters
pDeviceSettings
[in] Pointer to a DXUTDeviceSettings structure that contains the settings for the new device.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

Program the application to return true to continue creating the device. If not, the application should return false to continue using the current device if one exists.

Remarks

Before a device is created by DXUT, the LPDXUTCALLBACKMODIFYDEVICESETTINGS callback will be called to allow the application to examine or change the device settings before the device is created. This allows an application to modify the device creation settings as it sees fit.

This callback also allows applications to reject changing the device altogether. Returning false from inside this callback will notify DXUT to keep using the current device instead of changing to the new device.

Anything in pDeviceSettings can be changed by the application. DXUT will not prevent the failure of device creation caused by changes to device settings.

DXUTDeviceSettings

A union of settings describing how to create the Direct3D 9 or Direct3D 10 device.

typedef struct DXUTDeviceSettings {
DXUTDeviceVersion ver;
union {
DXUTD3D9DeviceSettings d3d9;
DXUTD3D10DeviceSettings d3d10;
};
} DXUTDeviceSettings, *LPDXUTDeviceSettings;
Members
ver
Indicates whether the settings structure is for a Direct3D 9 or Direct3D 10 device.
d3d9
Device settings for Direct3D 9 device. Only valid if ver is DXUT_D3D9_DEVICE.
d3d10
Device settings for Direct3D 10 device. Only valid if ver is DXUT_D3D10_DEVICE.
Remarks

The DXUTDeviceSettings can only describe a single device because the DXUTD3D9DeviceSettings and DXUTD3D10DeviceSettings member variables are unioned together. The DXUTDeviceVersion indicates which of these structures is valid.

DXUTD3D9DeviceSettings

Describes the settings used to create a Direct3D 9 device.

typedef struct DXUTD3D9DeviceSettings {
UINT AdapterOrdinal;
D3DDEVTYPE DeviceType;
D3DFORMAT AdapterFormat;
DWORD BehaviorFlags;
D3DPRESENT_PARAMETERS pp;
} DXUTD3D9DeviceSettings, *LPDXUTD3D9DeviceSettings;
Members
AdapterOrdinal
Ordinal number that denotes the display adapter.
DeviceType
Enumerated type of the device.
AdapterFormat
Adapter surface format.
BehaviorFlags
Behavior flags. This member can be a combination of one or more of the D3DCREATE values.
pp
Presentation parameters structure.

DXUT fills this structure with valid values, and then passes the structure to the callback function where the application can modify it. Be sure to validate any changes your application makes in this callback function. Here is an example that changes the depth-stencil format.

bool CALLBACK ModifyDeviceSettings( 
DXUTDeviceSettings* pDeviceSettings,
void* pUserContext )
{
if( pDeviceSettings->ver == DXUT_D3D9_DEVICE )
{
IDirect3D9* pD3D = DXUTGetD3DObject();

if( SUCCEEDED( pD3D->CheckDeviceFormat(
pDeviceSettings->d3d9.AdapterOrdinal, pDeviceSettings->d3d9.DeviceType,
pDeviceSettings->d3d9.AdapterFormat, D3DUSAGE_DEPTHSTENCIL,
D3DRTYPE_SURFACE, D3DFMT_D24S8 ) ) )
{
if( SUCCEEDED( pD3D->CheckDepthStencilMatch(
pDeviceSettings->d3d9.AdapterOrdinal, pDeviceSettings->d3d9.DeviceType,
pDeviceSettings->d3d9.AdapterFormat, pDeviceSettings->d3d9.pp.BackBufferFormat,
D3DFMT_D24S8 ) ) )
{
pDeviceSettings->d3d9.pp.AutoDepthStencilFormat = D3DFMT_D24S8;
}
}
}

return true;
}

如果应用程序需要的深度模板格式是D3DFMT_D24S8,那么程序需要确定设备支持它。

回调函数ModifyDeviceSettings()返回一个布尔值,如果应用程序返回TRUE,DXUT框架继续像在正常情况下那样进行设备创建。如果返回FALSE,框架不能改变设备,如果已有一个设备,则继续使用当前设备。如果框架提出的请求是改变到一个应用程序不能使用的设备,应用程序可以拒绝该请求。例如,在一个多显示器配置中,默认情况下在显示器之间拖动窗口将使框架改变设备。但如果应用程序不能使用其他设备,它就必须拒绝这种改变并继续使用当前设备。

降级到软件顶点处理

Be careful if your hardware supports pixel processing (transforms and lighting) but does not support vertex processing. One common mistake is to reject devices based on the vertex shader version in the (LPDXUTCALLBACKISD3D9DEVICEACCEPTABLE or LPDXUTCALLBACKISD3D10DEVICEACCEPTABLE) callback functions. The correct solution is to implement the checking in the ModifyDeviceSettings callback function as shown here.

bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, 
void* pUserContext )
{
if( pDeviceSettings->ver == DXUT_D3D9_DEVICE )
{
D3DCAPS9 caps;
DXUTGetD3D9DeviceCaps( pDeviceSettings, &caps );

// If device doesn't support HW T&L or doesn't support 1.1 vertex
// shaders in HW, then switch to SWVP.
if( (pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0 ||
pCaps->VertexShaderVersion < D3DVS_VERSION(1,1) )
{
pDeviceSettings->d3d9.BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}

else
{
pDeviceSettings->d3d9.BehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
}

return true;
}

创建一个设备

通常可以用标准的Direct3D方法CreateDevice()创建一个Direct3D设备,这个方法需要一个有效的显示适配器、设备类型(硬件抽象层设备或参考设备)、窗口句柄、运行标志(软件/硬件顶点运算模式和其他驱动标志)和提交参数。更重要的是,结构体D3DPRESENT_PARAMETERS有许多成员指定了后台缓冲区的设置、多重采样设置、交换效果、窗口模式、深度缓冲区设置、刷新频率、提交间隔和提交标志等。

为所有的参数选择合适的设置是比较繁琐的,DXUT框架使用函数DXUTCreateDevice()简化了Direct3D设备的创建,该函数的声明如下:

Creates a Direct3D 9 or Direct3D 10 device.

HRESULT DXUTCreateDevice(
bool bWindowed,
INT nSuggestedWidth,
INT nSuggestedHeight
);
Parameters
bWindowed
[in] If TRUE, the application will start in windowed mode; if FALSE, the application will start in full-screen mode. The default value is TRUE.
nSuggestedWidth
[in] The requested initial width of the application's back buffer. The actual width may be adjusted to fit device and operating system constraints. The default value is 0.
nSuggestedHeight
[in] The requested initial height of the application's back buffer. The actual height may be adjusted to fit device and operating system constraints. The default value is 0. If both nSuggestedWidth and nSuggestedHeight are zero, the dimension of the client area of the window is used.
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

This function creates a new Direct3D 9 or Direct3D 10 device for the application. DXUT will pick the best device for the application based upon what is availble on the system and which Direct3D API version(s) the application supports. The API version(s) that the application supports is detrimined by which device callbacks are set. This logic can be overridden by calling DXUTSetD3DVersionSupport. If both Direct3D 9 and Direct3D 10 are availble on the system and both are supported by the application, then DXUT will favor Direct3D 10.

Alternately, the application can use the DXUTCreateDeviceFromSettings or handle device creation and pass the desired device to DXUT by using the DXUTSetD3D9Device or DXUTSetD3D10Device function. If neither DXUTCreateDevice, DXUTCreateDeviceFromSettings, nor DXUTSetD3D9Device or DXUTSetD3D10Device have been called before calling DXUTMainLoop, DXUT will automatically call DXUTCreateDevice using the default parameter values.

The application can call this method after a device has already been created to change the current device.

If a device is successfully found, the LPDXUTCALLBACKMODIFYDEVICESETTINGS callback will be called to allow the application to examine or change the device settings before the device is created. This allows an application to modify the device creation settings as it sees fit.

通过这个简单的调用,DXUT框架创建了一个使用默认设置的Direct3D设备,它可以在大多数情况下使用,默认的设备创建设置如下表所示:

Direct3D创建标志
描述
DXUTCreateDevice的默认值

函数CheckDeviceFormat()的参数AdapterFormat
适配器表面格式
当前桌面显示模式,如果桌面显示模式不足32位,则使用D3DFMT_X8R8G8B8

IDirect3D9::CreateDevice()的参数Adapter
显示适配器编号
D3DADAPTER_DEFAULT

D3DPRESENT_PARAMETERS.BackBufferCount
后台缓冲区数目
2,表示有两个后台缓冲区,可实现3倍缓冲。

D3DPRESENT_PARAMETERS.BackBufferFormat
后台缓冲区格式
桌面显示模式,如果桌面显示模式不足32位,则使用D3DFMT_X8R8G8B8

D3DPRESENT_PARAMETERS.AutoDepthStencilFormat
设备将自动创建的深度模板表面的深度格式
如果后台缓冲区格式小于等于16位,则使用D3DFMT_D16,否则使用D3DFMT_D32。

IDirect3D9::CreateDevice()函数的参数DeviceType
设备类型
若D3DDEVTYPE_HAL可行,则使用之,否则使用D3DDEVTYPE_REF,若二者均不可行,则创建失败。

D3DPRESENT_PARAMETERS.MultiSampleQuality
多重采样数量
MultiSampleQuality = 0表示禁用多重采样

D3DPRESENT_PARAMETERS.Flags
提交参数标志
D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL

D3DPRESENT_PARAMETERS.PresentationInterval
提交间隔
窗口模式下为D3DPRESENT_INTERVAL_IMMEDIATE,全屏模式下为D3DPRESENT_INTERVAL_DEFAULT。

D3DPRESENT_PARAMETERS.FullScreen_RefreshRateInHz
显示适配器刷新屏幕的频率
0,表示当前桌面设置的刷新频率。

D3DPRESENT_PARAMETERS.BackBufferWidth和BackBufferHeight
显示器分辨率
在窗口模式下为640 x 480,全屏模式下为桌面分辨率。

D3DPRESENT_PARAMETERS.SwapEffect
交换效果
D3DSWAPEFFECT_DISCARD

IDirect3D9::CreateDevice()的参数BehaviorFlags
顶点运算标志
如果硬件支持,就使用D3DCREATE_HARDWARE_VERTEXPROCESSING,否则使用D3DCREATE_SOFTWARE_VERTEXPROCESSING。

D3DPRESENT_PARAMETERS.Windowed
窗口模式或全屏模式
TRUE,表示窗口模式

IDirect3D9::CreateDevice()的参数hFocusWindow
创建窗口的句柄
DXUTSetWindow函数的参数hWndFocus

D3DPRESENT_PARAMETERS.hDeviceWindow
设备窗口的句柄
DXUTSetWindow的参数hWndDeviceFullScreen或hWndDeviceWindowed

D3DPRESENT_PARAMETERS.EnableAutoDepthStencil
深度模板缓冲区创建标志
TRUE,表示自动创建深度模板缓冲区

选择最好的设备设置

在应用程序中,可以使用DXUTSetCallbackD3D9DeviceAcceptable设置回调函数IsDeviceAcceptable()帮助DXUT框架为应用程序选择最好的设备,该函数的声明如下:

Sets the Direct3D 9 device acceptable callback function.

VOID DXUTSetCallbackD3D9DeviceAcceptable(
LPDXUTCALLBACKISD3D9DEVICEACCEPTABLE pCallback,
void* pUserContext
);
Parameters
pCallback
[in] Pointer to a LPDXUTCALLBACKISD3D9DEVICEACCEPTABLE callback function. If the callback function is supplied, it will be called after the Direct3D 9 device .
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

This function only needs to be called if the application supports rendering with Direct3D 9 device.

The LPDXUTCALLBACKISD3D9DEVICEACCEPTABLE callback function is the appropriate location for the application to.

LPDXUTCALLBACKISD3D9DEVICEACCEPTABLE

Application-defined callback function, called by DXUT to build an enumerated list of all possible Direct3D 9 devices. DXUT then selects the best device for creation among this list. This callback function allows the application to prevent unwanted devices from being added to the list.

bool LPDXUTCALLBACKISD3D9DEVICEACCEPTABLE(
D3DCAPS9* pCaps,
D3DFORMAT AdapterFormat,
D3DFORMAT BackBufferFormat,
bool bWindowed,
void* pUserContext
);
Parameters
pCaps
[in] Pointer to the D3DCAPS9 capabilities of the Direct3D 9 device
AdapterFormat
[in] Format of the Direct3D 9 adapter
BackBufferFormat
[in] Format of the Direct3D 9 backbuffer
bWindowed
[in] Indicates windowed mode. TRUE if the application runs windowed; FALSE if the application runs full-screen.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

Program the application to return TRUE if the device settings are acceptable. If not, the application should return FALSE.

Remarks

This function's parameters describe a set of unique valid device settings that could be used to create a device. The application can examine and reject this set if desired.

All possible unique valid combinations of the following device settings are sent to this callback function:

  • pCaps
  • AdapterFormat
  • BackBufferFormat
  • bWindowed

After the application rejects the unwanted device settings combinations, DXUT picks the best of the remaining combinations and uses that best combination to create the device. Before creating the device, DXUT calls LPDXUTCALLBACKMODIFYDEVICESETTINGS to allow the application to change any of the device creation settings.

应用程序可以使用这个回调函数拒绝任何硬件不支持或不想要的组合。例如,应用程序可以使用下列代码拒绝16位后台缓冲区格式以及所有至少不支持像素渲染2.0版本的设备:

bool CALLBACK IsD3D9DeviceAcceptable(D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, 
bool bWindowed, void* pUserContext)
{
if(pCaps->PixelShaderVersion < D3DPS_VERSION(2, 0))
return false;
	if(BackBufferFormat == D3DFMT_X1R5G5B5 || BackBufferFormat == D3DFMT_R5G6B5)
return false;
	return true;
}

在回调函数IsD3D9DeviceAcceptable()被每种设置的组合调用之后,框架把这些可以接受的组合分级,并从中选出最优的使用。较高级别的组合包括:

(1)D3DDEVTYPE_HAL,为了得到硬件加速。

(2)如果该应用程序运行在全屏模式下,该框架会选择桌面格式所对应的适配器格式,这样从窗口到全屏模式的转换就快多了。例外的是,如果桌面显示模式不足32位,该框架会选择D3DFMT_X8R8G8B8。

(3)适配器格式所对应的后台缓冲区格式。

示例程序中,选择设备设置的核心代码如下:

//--------------------------------------------------------------------------------------
// Rejects any D3D9 devices that aren't acceptable to the app by returning false.
//--------------------------------------------------------------------------------------
bool CALLBACK IsD3D9DeviceAcceptable(D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat,
bool bWindowed, void* pUserContext)
{
// Typically want to skip back buffer formats that don't support alpha blending
    IDirect3D9* pD3D = DXUTGetD3D9Object(); 
	/*
HRESULT CheckDeviceFormat(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT AdapterFormat,
DWORD Usage,
D3DRESOURCETYPE RType,
D3DFORMAT CheckFormat
);
/*/
    if(FAILED(pD3D->CheckDeviceFormat(pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat, 
D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, BackBufferFormat)))
{
return false;
}
    return true;
}

IDirect3D9::CheckDeviceFormat

Determines whether a surface format is available as a specified resource type and can be used as a texture, depth-stencil buffer, or render target, or any combination of the three, on a device representing this adapter.

HRESULT CheckDeviceFormat(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT AdapterFormat,
DWORD Usage,
D3DRESOURCETYPE RType,
D3DFORMAT CheckFormat
);
Parameters
Adapter
[in] Ordinal number denoting the display adapter to query. D3DADAPTER_DEFAULT is always the primary display adapter. This method returns D3DERR_INVALIDCALL when this value equals or exceeds the number of display adapters in the system.
DeviceType
[in] Member of the D3DDEVTYPE enumerated type, identifying the device type.
AdapterFormat
[in] Member of the D3DFORMAT enumerated type, identifying the format of the display mode into which the adapter will be placed.
Usage
[in] Requested usage options for the surface. Usage options are any combination of D3DUSAGE and D3DUSAGE_QUERY constants (only a subset of the D3DUSAGE constants are valid for IDirect3D9::CheckDeviceFormat; see the table on the D3DUSAGE page).
RType
[in] Resource type requested for use with the queried format. Member of D3DRESOURCETYPE.
CheckFormat
[in] Format of the surfaces which may be used, as defined by Usage. Member of D3DFORMAT.
Return Values

If the format is compatible with the specified device for the requested usage, this method returns D3D_OK.

D3DERR_INVALIDCALL is returned if Adapter equals or exceeds the number of display adapters in the system, or if DeviceType is unsupported.

D3DERR_NOTAVAILABLE is returned if the format is not acceptable to the device for this usage.

Remarks

Here are some examples using IDirect3D9::CheckDeviceFormat to check for hardware support of:

  • An off-screen plain surface format - Specify Usage = 0 and RType = D3DRTYPE_SURFACE.
  • A depth-stencil format - The following snippet tests for the passed in depth-stencil format:
    BOOL IsDepthFormatExisting( D3DFORMAT DepthFormat, D3DFORMAT AdapterFormat ) 
    {
    HRESULT hr = pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT,
    D3DDEVTYPE_HAL,
    AdapterFormat,
    D3DUSAGE_DEPTHSTENCIL,
    D3DRTYPE_SURFACE,
    DepthFormat);

    return SUCCEEDED( hr );
    }

    See Selecting a Device (Direct3D 9) for more detail on the enumeration process.

  • Can this texture be rendered in a particular format - Given the current display mode, this example shows how to verify that the texture format is compatible with the specific back-buffer format:
    BOOL IsTextureFormatOk( D3DFORMAT TextureFormat, D3DFORMAT AdapterFormat ) 
    {
    HRESULT hr = pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT,
    D3DDEVTYPE_HAL,
    AdapterFormat,
    0,
    D3DRTYPE_TEXTURE,
    TextureFormat);

    return SUCCEEDED( hr );
    }
  • Alpha blending in a pixel shader - Set Usage to D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING. Expect this to fail for all floating-point render targets.
  • Autogeneration of mipmaps - Set Usage to D3DUSAGE_AUTOGENMIPMAP. If the mipmap automatic generation fails, the application will get a non-mipmapped texture. Calling this method is considered a hint, so this method can return D3DOK_NOAUTOGEN (a valid success code) if the only thing that fails is the mipmap generation. For more information about mipmap generation, see Automatic Generation of Mipmaps (Direct3D 9).

D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING
Query the resource to verify support for post pixel shader blending support. If IDirect3D9::CheckDeviceFormat fails with D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, post pixel blending operations are not supported. These include alpha test, pixel fog, render-target blending, color write enable, and dithering.

初始化DXUT

使用DXUT框架之前,首先需要初始化DXUT,初始化DXUT可以通过函数DXUTInit()完成:

Initializes DXUT.

HRESULT DXUTInit(
BOOL bParseCommandLine,
BOOL bShowMsgBoxOnError,
WCHAR * strExtraCommandLineParams,
bool bThreadSafeDXUT
);
Parameters
bParseCommandLine
[in] If TRUE, DXUT checks for command-line arguments. The application performs the following actions based upon the entered command-line arguments.

Command-line Argument
Action

-forceapi:#
Forces the application to use the specified Direct3D API version. Fails if the application doesn't support this API or if no device is found.

-adapter:#
Forces the application to use this adapter ordinal. Fails if the adapter ordinal does not exist.

-output:#
Applies to Direct3D 10 only. Forces the application to use a particular output on the adapter. Fails if the output does not exist.

-windowed
Forces the application to start in windowed mode.

-fullscreen
Forces the application to start in full-screen mode.

-forcehal
Forces the application to use a HAL device type. Fails if a HAL device does not exist.

-forceref
Forces the application to use a reference device type. Fails if a reference device does not exist.

-forcehwvp
Applies to Direct3D 9 only. Forces the application to use hardware vertex processing. Fails if the device does not support this mode.

-forcepurehwvp
Applies to Direct3D 9 only. Forces the application to use pure hardware vertex processing. Fails if the device does not support this mode.

-forceswvp
Applies to Direct3D 9 only. Forces the application to use software vertex processing.

-forcevsync:#
If # is 0, then vertical sync is disabled. Otherwise, it is enabled.

-width:#
Forces the application to use the window width #. For full-screen mode, DXUT picks the closest possible supported mode.

-height:#
Forces the application to use the window height #. For full-screen mode, DXUT picks the closest possible supported mode.

-startx:#
For windowed mode, forces the application to use the x-coordinate of the window position to the value of #.

-starty:#
For windowed mode, forces the application to use the y-coordinate of the window position to the value of #.

-constantframetime
Forces the application to into a mode where DXUT reports that a constant amount of time has passed between each frame, where # is the time/frame in seconds. This is useful for such scenarios as rendering an movie that can not render in real time

-quitafterframe:#
Forces the application to quit after frame #.

-noerrormsgboxes
Prevents the display of message boxes generated by DXUT, allowing the application to be run without user interaction.

-nostats
Prevents the display of device and frame statistics by always returning blank strings for DXUTGetDeviceStats and DXUTGetFrameStats.

-automation
This is a simple hint to other components that automation is active. The DXUT GUI uses this to enable UI navigation with keyboard by default.

Command-line arguments take precedence over options set by the application when a Direct3D device is first created, but they are ignored afterward to allow the user to interactively change the settings. The default value of this parameter is TRUE.

bShowMsgBoxOnError
[in] If TRUE, DXUT displays a message box if there is an error condition. The default value of this parameter is TRUE.
strExtraCommandLineParams
[in] A string of extra command parameters that will be parsed in addition to the actual command line. It is recommended that be used sparingly. Most the command line options above can be implemented in the application's ModifyDeviceSettings callback or set by using one of DXUT functions. The default value is NULL.
bThreadSafeDXUT
[in] Controls if DXUT enters a critical section when retrieving or modify internal DXUT state. The default value is false.
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

If this function has not been called before DXUTCreateWindow or DXUTSetWindow, DXUT will automatically call this function using the default parameter values.

通常在WinMain()函数中调用DXUTInit()函数进行DXUT初始化工作,如果程序员没有调用DXUTInit()函数,则DXUT框架会自动使用默认参数调用该函数。

如果第一个参数bParseCommandLine设置为TRUE,则DXUT框架就会使用命令行参数,例如通过下面的命令运行上面的AppFrame.exe:

AppFrame.exe -windowed -width:640 -height:480

DXUT框架会尽量使用上面命令行中设置的窗口宽度和高度。

创建一个窗口

在应用程序中使用Windows API函数创建窗口是一个比较复杂的过程,如果操作有误,就会导致bug。尽管这对于一个Direct3D程序员来说可能并不起眼,但在每个应用程序中却都是必须的。而DXUT框架通过函数DXUTCreateWindow()简化了这个过程,该函数的声明如下:

Creates the window for the application.

HRESULT DXUTCreateWindow(
CONST const WCHAR * strWindowTitle,
HINSTANCE hInstance,
HICON hIcon,
HMENU hMenu,
INT x,
INT y
);
Parameters
strWindowTitle
[in] Title bar caption for the window. The default value is L"Direct3D Window".
hInstance
[in] Handle of the application's instance, or NULL to retrieve the handle of the current module. The default value is NULL.
hIcon
[in] Handle to the application's icon, or NULL to use the first icon embedded in the application's executable. The default value is NULL.
hMenu
[in] Handle to the application's menu resource, or NULL to indicate no menu. The default value is NULL.
x
[in] Horizontal coordinate of the window's upper left corner, in screen coordinates. Using a value of CW_USEDEFAULT allows Windows to choose an appropriate location. The default value is CW_USEDEFAULT.
y
[in] Vertical coordinate of the window's upper left corner, in screen coordinates. Using a value of CW_USEDEFAULT allows Windows to choose an appropriate location. The default value is CW_USEDEFAULT.
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

This function creates a new window for the application; alternately, the application can handle window creation and pass the desired window handle to DXUT by using the DXUTSetWindow function. If neither DXUTCreateWindow nor DXUTSetWindow has been called before calling a device creation method, DXUT will call DXUTCreateWindow using the default parameter values. The window width and height are set later using the device settings.

All parameters are optional.

If both x and y are CW_USEDEFAULT and a windowed non-primary Direct3D device is created, the window will automatically be moved to the adapter's monitor to ensure maximum performance.

DXUT框架创建的窗口的句柄可以通过DXUTGetHWND()函数来获取。

如果应用程序要对上面创建的窗口消息做出反应,那么需要使用DXUTSetCallbackMsgProc()来设置一个窗口消息处理函数,该函数声明如下:

Sets the window message callback function.

VOID DXUTSetCallbackMsgProc(
LPDXUTCALLBACKMSGPROC pCallbackMsgProc,
void* pUserContext
);
Parameters
pCallbackMsgProc
[in] Pointer to a LPDXUTCALLBACKMSGPROC callback function. If supplied, DXUT will call this function when it receives window messages. If NULL, DXUT will not notify the application about window messages.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

The LPDXUTCALLBACKMSGPROC callback function allows the application to respond to any Windows messages as it sees fit.

With the use of the LPDXUTCALLBACKMSGPROC pbNoFurtherProcessing parameter, the application can contol the DXUT's level of involvement in processing window messages. If the application sets the pbNoFurtherProcessing parameter to TRUE in the call to LPDXUTCALLBACKMSGPROC, DXUT will not process the message and will immediately return with the value returned by LPDXUTCALLBACKMSGPROC. If the application sets pbNoFurtherProcessing to FALSE, DXUT will handle window management events.

参数pCallbackMsgProc指向一个消息处理回调函数,该回调函数声明如下:

Application-defined function that processes messages from DXUT message pump.

LRESULT LPDXUTCALLBACKMSGPROC(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
bool * pbNoFurtherProcessing,
void* pUserContext
);
Parameters
hWnd
[in] Handle to the window.
uMsg
[in] Specifies the message. See WindowProc for details.
wParam
[in] Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter.
lParam
[in] Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter.
pbNoFurtherProcessing
[out] If TRUE, prevents DXUT from futher handling the message.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

Returns zero if the function has processed window messages successfully; otherwise, returns a nonzero value.

Remarks

This function and its parameters are similar to to the Windows WindowProc function.

With the use of the pbNoFurtherProcessing parameter, the application can control DXUT's level of involvement in processing window messages. If the application sets pbNoFurtherProcessing to TRUE in the call to LPDXUTCALLBACKMSGPROC, DXUT will not process the message and will immediately return with the value returned by LPDXUTCALLBACKMSGPROC. If the application sets pbNoFurtherProcessing to FALSE, DXUT will handle window management events.

在这个回调函数中,因为所有的重要消息都被该框架处理了,所以应用程序可以无需对任何消息做出响应。如果想禁用DXUT框架的消息处理,应用程序可以将pbNoFurtherProcessing设为TRUE。但是,使用这个设置时要格外小心,因为它有可能使框架不能正确运行。

使用自己的窗口

如果想要应用程序创建自己的窗口并同DXUT框架一起使用,那么可以创建一个窗口,然后使用函数DXUTSetWindow()为DXUT框架设置自己创建的窗口,该函数声明如下:

Sets a previously created window for use by DXUT.

HRESULT DXUTSetWindow(
HWND hWndFocus,
HWND hWndDeviceFullScreen,
HWND hWndDeviceWindowed,
BOOL bHandleMessages
);
Parameters
hWndFocus
[in] Handle of the Direct3D focus window. Must not be NULL.
hWndDeviceFullScreen
[in] Handle of the Direct3D device window when in full-screen mode. Must not be NULL.
hWndDeviceWindowed
[in] Handle of the Direct3D device window when in windowed mode. Must not be NULL.
bHandleMessages
[in] If TRUE, DXUT will handle and respond to messages for the window. If FALSE, DXUT will not handle messages for the window, giving the application full responsibility for responding to messages. The default value is TRUE.
Return Values

If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the error codes in DXUTERR.

Remarks

This function relies on an existing window object created by the application. Alternately, the application can call DXUTCreateWindow to have DXUT create a window. If neither DXUTCreateWindow nor DXUTSetWindow has been called before calling a device creation method, DXUT will automatically call DXUTCreateWindow using the default parameter values.

The same Window handle may be used for more than one parameter.

这个函数使用了3个窗口句柄参数,但它们通常都设置为同一个窗口句柄。

如果框架创建了窗口,窗口消息将被自动处理,而要让DXUT框架使用自己创建的窗口,除了为DXUT框架设置窗口之外,还需要向DXUT框架通知窗口接收到的消息,才能使DXUT框架正常运行。应用程序可通过函数DXUTStaticWndProc()将窗口消息从窗口回调函数WindowProc的内部传递给框架,函数DXUTStaticWndProc()的声明如下:

Processes messages sent to a window.

LRESULT_CALLBACK DXUTStaticWndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
Parameters
hWnd
[in] Handle to the window.
uMsg
[in] Specifies the message.
wParam
[in] Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter.
lParam
[in] Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter.
Return Values

If the function has processed window messages successfully, returns zero; otherwise, returns a nonzero value.

Remarks

This method does not normally need to be called. It is useful only when the application use DXUTSetWindow with bHandleMessages set to FALSE but still wants DXUT to assist with handling Windows messages. If this is the case, this function can be called from inside the application's window procedure, or it can be used directly as the window procedure.

DXUT框架用来帮助程序员花更少的时间来解决下列问题:创建窗口、创建Direct3D设备、进行消息循环和处理设备事件。在DXUT框架基础上编写代码,可以快速高效地进行Direct3D程序设计,大多数Direct3D SDK示例程序使用了DXUT框架。

下面的代码是AppFrame示例程序的WinMain函数:

INT WINAPI wWinMain( HINSTANCE, HINSTANCE, LPWSTR, int )
{
// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
    // Set the callback functions
DXUTSetCallbackD3D9DeviceAcceptable(IsD3D9DeviceAcceptable);
DXUTSetCallbackD3D9DeviceCreated(OnD3D9CreateDevice);
DXUTSetCallbackD3D9DeviceReset(OnD3D9ResetDevice);
DXUTSetCallbackD3D9FrameRender(OnD3D9FrameRender);
DXUTSetCallbackD3D9DeviceLost(OnD3D9LostDevice);
DXUTSetCallbackD3D9DeviceDestroyed(OnD3D9DestroyDevice);
DXUTSetCallbackDeviceChanging(ModifyDeviceSettings);
DXUTSetCallbackMsgProc(MsgProc);
DXUTSetCallbackFrameMove(OnFrameMove);
    // TODO: Perform any application-level initialization here
    // Initialize DXUT and create the desired Win32 window and Direct3D device for the application
    DXUTInit( true, true ); // Parse the command line and show msgboxes
DXUTSetHotkeyHandling( true, true, true ); // handle the default hotkeys
DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
DXUTCreateWindow( L"AppFrame Sample" );
DXUTCreateDevice( true, 640, 480 );
    // Start the render loop
DXUTMainLoop();
    // TODO: Perform any application-level cleanup here
    return DXUTGetExitCode();
}

在上面的代码中,DXUT框架做了大部分的工作。它创建了一个窗口和一个Direct3D设备,处理消息循环、并当事件发生时(例如重新设置设备或渲染一帧)调用应用程序提供的回调函数。 DXUT框架是模块化的,所以应用程序可以使用DXUT框架的所有函数或其中的一部分。

下面这组代码时一组注册函数的调用:

    // Set the callback functions
    DXUTSetCallbackD3D9DeviceAcceptable(IsD3D9DeviceAcceptable);
    DXUTSetCallbackD3D9DeviceCreated(OnD3D9CreateDevice);
    DXUTSetCallbackD3D9DeviceReset(OnD3D9ResetDevice);
    DXUTSetCallbackD3D9FrameRender(OnD3D9FrameRender);
    DXUTSetCallbackD3D9DeviceLost(OnD3D9LostDevice);
    DXUTSetCallbackD3D9DeviceDestroyed(OnD3D9DestroyDevice);
    DXUTSetCallbackDeviceChanging(ModifyDeviceSettings);
    DXUTSetCallbackMsgProc(MsgProc);
    DXUTSetCallbackFrameMove(OnFrameMove);

以函数DXUTSetCallbackD3D9DeviceCreated为例,它的声明如下:

Sets the Direct3D 9 device created callback function.

VOID DXUTSetCallbackD3D9DeviceCreated(
LPDXUTCALLBACKD3D9DEVICECREATED pCallback,
void* pUserContext
);
Parameters
pCallback
[in] Pointer to a LPDXUTCALLBACKD3D9DEVICECREATED callback function. If the callback function is supplied, it will be called after the Direct3D 9 device has been created. Device creation will happen during application initialization and if the device is changed. If NULL, DXUT will not notify the application about device creation.
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

No return value.

Remarks

This function only needs to be called if the application supports rendering with Direct3D 9 device.

The LPDXUTCALLBACKD3D9DEVICECREATED callback function is the appropriate location for the application to create Direct3D 9 device resources that will live through a device reset such as D3DPOOL_MANAGED or D3DPOOL_SYSTEMMEM memory and that aren't tied to the back buffer size. Resources created in the LPDXUTCALLBACKD3D9DEVICECREATED callback function should be released in the LPDXUTCALLBACKD3D9DEVICEDESTROYED callback function.

LPDXUTCALLBACKD3D9DEVICECREATED

Application-defined resource creation callback function, called by DXUT after the Direct3D 9 device is created. Passes a pointer to the newly created Direct3D 9 device.

HRESULT LPDXUTCALLBACKD3D9DEVICECREATED(
IDirect3DDevice9 * pd3dDevice,
CONST D3DSURFACE_DESC * pBackBufferSurfaceDesc,
void* pUserContext
);
Parameters
pd3dDevice
[out] Pointer to the newly created Direct3D 9 device.
pBackBufferSurfaceDesc
[out] Pointer to the back buffer surface description
pUserContext
[in] Pointer to a user-defined value which is passed to the callback function. Typically used by an application to pass a pointer to a data structure that provides context information for the callback function. The default value is NULL
Return Values

In general, if no error occurs, program the function to return S_OK. Program the function to return an HRESULT failure code if the function fails. If DXUT receives a failure HRESULT code, it shuts down the application.

Remarks

The LPDXUTCALLBACKD3D9DEVICECREATED callback function is the appropriate location for the application to create Direct3D 9 device resources that will live through a device reset such as D3DPOOL_MANAGED or D3DPOOL_SYSTEMMEM memory and that aren't tied to the back buffer size. Resources created in the LPDXUTCALLBACKD3D9DEVICECREATED callback function should be released in the LPDXUTCALLBACKD3D9DEVICEDESTROYED callback function.

该注册函数的作用在于通知应用程序,在应用程序的初始化期间或当设备改变时,如果需要创建D3DPOOL_MANAGED类型的资源,就会自动调用函数OnD3D9CreateDevice()进行创建。而程序员需要做的就是编写OnD3D9CreateDevice()函数,告诉应用程序创建哪些资源以及如何创建。其他注册函数的作用同样是通知应用程序,使应用程序在特定时机调用注册函数指定的具体回调函数。程序员的核心工作就是实现这些具体的回调函数,事实上,这种构架正是DXUT框架的核心,也可以把它看成是区别于Direct3D API程序的地方。

DXUT框架提供了下列服务,帮助程序员创建一个应用程序:

(1)简化窗口和设备的创建。

(2)声明设备事件(创建、重置、丢失、销毁)和窗口事件(消息、键盘、鼠标)。

(3)在窗口模式和全屏模式间切换,在硬件抽象层设备和参考设备间切换。

(4)高分辨率计时器。

(5)为自动测试提供命令行支持。

(6)通过对话框或API选择设备。

(7)纹理GUI控件组,包括IME-enable文本框。

(8)附加杂类,例如简单的摄像机类。

为使用方便,DXUT框架支持Direct3D设备和窗口一一对应(一个设备只能对应一个窗口)。对于需要同时使用多个设备或显示多个Direct3D窗口的高级应用程序,该框架不支持。不过,大多数应用程序只使用一个窗口和一个Direct3D设备,所以大部分应用程序都能使用该框架。

DXUT(也称sample framework)是建立在Direct3D API之上的Direct3D应用程序框架,有了DXUT这样一个Direct3D程序框架,只需在这个框架的基础上编写相应的代码,从而简化了windows和Direct3D API的使用,可以高效地进行Direct3D程序设计。

生成一个Direct3D程序框架

第一步,运行Direct3D示例程序浏览器:

第二步,单击"EmptyProject"中的"Installl Project"安装工程:

第三步,在弹出的对话框中输入新工程的名称,修改该工程的创建路径,单击Install即可创建工程:

第四步,系统将自动完成工程的创建,然后弹出对话框询问是否查看创建的工程文件夹中的内容:

若选择是,则可以查看新创建的工程文件夹的内容:

使用Direct3D程序框架

通过上面的操作,Direct3D已经为我们创建好了一个应用程序框架,该框架主要包括以下文件:

其中最主要的两个文件是DXUT.h和DXUT.cpp。

除了上面这些通用文件外,Direct3D还生成了一个主程序文件,该文件的名字和工程名字相同,在此即是AppFrame.cpp。该文件主要由以下几个回调函数构成:

bool CALLBACK IsD3D9DeviceAcceptable(D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, 
bool bWindowed, void* pUserContext);
bool CALLBACK ModifyDeviceSettings(DXUTDeviceSettings* pDeviceSettings, void* pUserContext);
HRESULT CALLBACK OnD3D9CreateDevice(IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, 
void* pUserContext);
HRESULT CALLBACK OnD3D9ResetDevice(IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, 
void* pUserContext);
void CALLBACK OnFrameMove(double fTime, float fElapsedTime, void* pUserContext);
void CALLBACK OnD3D9FrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 
bool* pbNoFurtherProcessing, void* pUserContext );
void CALLBACK OnD3D9LostDevice( void* pUserContext );
void CALLBACK OnD3D9DestroyDevice( void* pUserContext );

函数名前使用"CALLBACK"表示声明的是一个回调函数,也就是说,这是DXUT框架为我们设置好的接口,DXUT框架将在合适的时机调用相应的回调函数。现在我们需要做的就是在这些回调函数中填写相应的代码完成所需要的功能。为了使DXUT框架能够调用这些回调函数,还需要在WinMain函数中为DXUT框架设置这些回调函数,代码如下:

    // Set the callback functions
DXUTSetCallbackD3D9DeviceAcceptable(IsD3D9DeviceAcceptable);
DXUTSetCallbackD3D9DeviceCreated(OnD3D9CreateDevice);
DXUTSetCallbackD3D9DeviceReset(OnD3D9ResetDevice);
DXUTSetCallbackD3D9FrameRender(OnD3D9FrameRender);
DXUTSetCallbackD3D9DeviceLost(OnD3D9LostDevice);
DXUTSetCallbackD3D9DeviceDestroyed(OnD3D9DestroyDevice);
DXUTSetCallbackDeviceChanging(ModifyDeviceSettings);
DXUTSetCallbackMsgProc(MsgProc);
DXUTSetCallbackFrameMove(OnFrameMove);

DXUT框架程序的整个“生命周期”可划分为三个阶段:启动、运行和结束。

第一阶段:启动

DXUT框架依次执行IsD3D9DeviceAcceptable()、ModifyDeviceSettings()、OnD3D9CreateDevice()、OnD3D9ResetDevice()这4个函数。

在创建某个Direct3D渲染设备之前,如果需要对渲染设备的特征进行检查,查看设备是否支持需要的功能,可将检查代码写在函数IsD3D9DeviceAcceptable()中。

在某个渲染设备创建之前,如果需要修改该渲染设备的设置,可将代码写在函数ModifyDeviceSettings()中。DXUT框架接下来就根据设置(或者是默认设置)创建最适合当前硬件的Direct3D渲染设备。例如,当硬件不支持某些功能时,可以通过使用参考设备进行模拟,设置使用参考设备代码通常写在该函数中。

DXUT框架创建了Direct3D设备之后,接下来会调用OnD3D9CreateDevice()回调函数,可在OnD3D9CreateDevice()回调函数中创建所有内存池类型为D3DPOOL_MANAGED或D3DPOOL_SYSTEMMEM的资源。以类型D3DPOOL_MANAGED创建的设备由Direct3D系统代替管理(位于显存或系统内存中),以类型D3DPOOL_SYSTEMMEM创建的设备位于系统内存中,在程序退出之前,这些资源常驻内存,不会出现设备丢失的现象。也就是说,以这两种内存类型创建的资源不需要程序员进行管理。

DXUT框架在调用OnD3D9CreateDevice()回调函数之后,将调用OnD3D9ResetDevice()回调函数。我们可在函数OnD3D9ResetDevice()中创建所有内存池类型为D3DPOOL_DEFAULT的资源,这一类资源将尽可能存放在显存中,这样可以提高程序的运行速度。但是,这类资源在程序运行时会出现设备丢失的现象,因此需要程序员自己管理。在设备丢失时释放它的内存,当设备恢复时重新为它分配内存。此外,观察变换矩阵和投影变换矩阵以及在整个程序运行期间保持不变的渲染状态通常也在该回调函数中设置。

如果性能不是很重要,使用D3DPOOL_MANAGED内存类型资源永远是一种安全的选择。

第二阶段:运行

DXUT框架调用回调函数MsgProc()处理各类消息,并在空闲时间反复调用OnFrameMove()和OnFrameRender()两个函数进行场景渲染。

在每一帧中,程序为实现对场景的刷新,为用户输入的响应而编写的代码通常写在函数OnFrameMove()中,例如设置世界变换矩阵实现物体的运动,它相当于“update”的性质,真正进行渲染的代码写在函数OnFrameRender()中。

需要说明的是,在应用程序运行期间,当Direct3D设备变为丢失状态时,DXUT框架会调用OnD3D9LostDevice()函数,释放所有在回调函数OnD3D9ResetDevice()中创建的设备资源。也就是说,这时释放的资源都是D3DPOOL_DEFAULT类型的。当Direct3D设备从丢失状态恢复时,DXUT框架会调用回调函数OnD3D9ResetDevice()重新创建所有类型为D3DPOOL_DEFAULT的资源。也就是说,在程序运行时,如果出现设备丢失现象,OnD3D9LostDevice()和OnD3D9ResetDevice()这一对函数就需要分别调用一次。

第三阶段:退出

在退出程序时,DXUT框架会依次调用OnD3D9LostDevice()和OnD3D9DestroyDevice()回调函数,在函数OnD3D9LostDevice()中释放由函数OnD3D9ResetDevice()创建的资源,在函数OnD3D9DestroyDevice()中释放由函数OnD3D9CreateDevice()创建的资源。

AppFrame.cpp的全部代码如下:

#include "DXUT.h"
#include "resource.h"
//--------------------------------------------------------------------------------------
// Rejects any D3D9 devices that aren't acceptable to the app by returning false.
//--------------------------------------------------------------------------------------
bool CALLBACK IsD3D9DeviceAcceptable(D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat,
bool bWindowed, void* pUserContext)
{
// Typically want to skip back buffer formats that don't support alpha blending
    IDirect3D9* pD3D = DXUTGetD3D9Object();
/*
    HRESULT CheckDeviceFormat(
      UINT Adapter,
      D3DDEVTYPE DeviceType,
      D3DFORMAT AdapterFormat,
      DWORD Usage,
      D3DRESOURCETYPE RType,
      D3DFORMAT CheckFormat
    );
    /*/
if(FAILED(pD3D->CheckDeviceFormat(pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat,
            D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, BackBufferFormat)))
    {
return false;
    }
return true;
}
//--------------------------------------------------------------------------------------
// Before a device is created, modify the device settings as needed.
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings(DXUTDeviceSettings* pDeviceSettings, void* pUserContext)
{
return true;
}
//--------------------------------------------------------------------------------------
// Create any D3D9 resources that will live through a device reset (D3DPOOL_MANAGED)
// and aren't tied to the back buffer size.
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D9CreateDevice(IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
void* pUserContext)
{
return S_OK;
}
//--------------------------------------------------------------------------------------
// Create any D3D9 resources that won't live through a device reset (D3DPOOL_DEFAULT)
// or that are tied to the back buffer size.
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D9ResetDevice(IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
void* pUserContext)
{
return S_OK;
}
//--------------------------------------------------------------------------------------
// Handle updates to the scene.  This is called regardless of which D3D API is used.
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove(double fTime, float fElapsedTime, void* pUserContext)
{
}
//--------------------------------------------------------------------------------------
// Render the scene using the D3D9 device
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D9FrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    HRESULT hr;
// Clear the render target and the zbuffer
    V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0, 5, 5, 5), 1.0f, 0) );
// Render the scene
if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {
        V( pd3dDevice->EndScene() );
    }
}
//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
bool* pbNoFurtherProcessing, void* pUserContext )
{
return 0;
}
//--------------------------------------------------------------------------------------
// Release D3D9 resources created in the OnD3D9ResetDevice callback
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D9LostDevice( void* pUserContext )
{
}
//--------------------------------------------------------------------------------------
// Release D3D9 resources created in the OnD3D9CreateDevice callback
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D9DestroyDevice( void* pUserContext )
{
}
//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
INT WINAPI wWinMain( HINSTANCE, HINSTANCE, LPWSTR, int )
{
// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
// Set the callback functions
    DXUTSetCallbackD3D9DeviceAcceptable(IsD3D9DeviceAcceptable);
    DXUTSetCallbackD3D9DeviceCreated(OnD3D9CreateDevice);
    DXUTSetCallbackD3D9DeviceReset(OnD3D9ResetDevice);
    DXUTSetCallbackD3D9FrameRender(OnD3D9FrameRender);
    DXUTSetCallbackD3D9DeviceLost(OnD3D9LostDevice);
    DXUTSetCallbackD3D9DeviceDestroyed(OnD3D9DestroyDevice);
    DXUTSetCallbackDeviceChanging(ModifyDeviceSettings);
    DXUTSetCallbackMsgProc(MsgProc);
    DXUTSetCallbackFrameMove(OnFrameMove);
// TODO: Perform any application-level initialization here
// Initialize DXUT and create the desired Win32 window and Direct3D device for the application
    DXUTInit( true, true ); // Parse the command line and show msgboxes
    DXUTSetHotkeyHandling( true, true, true );  // handle the default hotkeys
    DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"AppFrame Sample" );
    DXUTCreateDevice( true, 640, 480 );
// Start the render loop
    DXUTMainLoop();
// TODO: Perform any application-level cleanup here
return DXUTGetExitCode();
}

运行效果图:

下载示例工程

昨天发布的字体看起来大家感觉不错,不过由于googlepage有流量限制,后来很多朋友可能都无法下载了

昨天又检查了一下字体,修正了几个字符的问题,目前英文部分常用字体都已经是等宽字体

注意:

雅黑不是等宽字体,但个人感觉中文显示ClearType效果很好;

Consolas是等宽字体,但中文显示偏大,并且中文部分无ClearType效果


前几天已经下载的朋友,麻烦今天重新下载一下,因为修改了几个字符(@&+,.)的等宽问题,根据昨天有些朋友的意见和建议,修改了”|”和”-“


有需要的朋友请从这里下载:RapidShare下载:

http://rapidshare.com/files/34563809/YaHei.Consolas.1.11b.zip.html


记得要打开ClearType,默认的ClearType效果并不好,需要安装一个PowerToys,安装后会在控制面板里多一个控制项,操作很简单,不多说了:


http://download.microsoft.com/download/b/7/0/b7019730-0fa3-47a9-a159-98b80c185aad/setup.exe


综合效果如下图:




有几位朋友问到了插件,这是SlickEdit的一个免费Gadget,目前已经升级到了2.0,可以在官方网站下载:

http://www.slickedit.com/content/view/441


时隔一年了…..陆陆续续还有朋友问我关于这个字体的问题,谢谢大家的关心,这里提供字体编辑工具的下载,请大家在我的修改基础上自行扩展,有什么新发现记得通知我啊:)


下载字体工具请猛击这里


作者RobertLee http://www.cnblogs.com/RobertLee/

曾几何时,服务里面弹出一个窗口到桌面上不再是那么地容易了:以前只要把服务设置为允许和桌面交互就可以直接在服务启动的时候,把一个窗口弹给用户。但是现在在vista(其它的OS 下没有测试,未知)下要弹出这样的窗体,首先会弹出一个提示框提示是否接否一个服务弹出来的消息,点接受后,才会在一个全灰的桌面里面弹出这个窗口。不用想,这样的用户体验,肯定是会被直接PASS。原因很简单,因为不同的用户间的桌面是不一样的,服务用的是System的权限,在vista里面是Session0,而用户的帐户不是这个(肯定大于0)。

看来,想弹出一个窗体,需要另一个程序来作辅助了。解决方案有两种:

  1. 开发一个程序A在启动的时候,随系统启动,并监控指定文件M,服务S有消息的时候,放在文件M里,A 感受了文件变化了,就去读这个文件里的内容,根据规则来作对应的动作。坏处很明显,当用户为了让系统跑得快的时候,这种自启动文件很容易被砍掉,导致了有些功能莫名奇妙地不可用。
  2. 同样地,也是开发一个程序A,用CreateProcessAsUser这个API来创建这个A ,并且发送到用户的桌面上去。好处是可以把这个A和服务S 放在同一个程序文件里面,根据不同的参数来启动不同的功能。这样用户除非是删除整个服务,否则不会有部分功能能用,又有部分功能不能用的问题。

现在来看看第二种方案,要想用CreateProcessAsUser这个API ,有这样几个步骤:

1.取得用户的令牌(Token)

2.指定好虚拟桌面

3.调用API创建这个用户进程。

在取得用户令牌的时候,又有几个方法:

1.从用户的进程上去剥

2.先用WTSGetActiveConsoleSessionId得到用户会话ID,再用WTSQueryUserToken这个API去取。

不过在vista下面,服务里面的WTSGetActiveConsoleSessionId这个API得到的总是0,也就是Session0,用这个创建出来的进程,还是属于一个服务进程。(而且这个API是XP以及以后的系统才会提供的,在早点的系统上就会调用失败)所以我们只好从用户的进程上去找,用户登录的时候,一定会有的进程就是:explorer.exe,这样可以遍历所有的进程去找到这个(如果是多人同时登录这个系统里,我也不知道该怎么办了,不知道有没有高人指点一下)。

这样创建出来的用户进程,在进程列表里面可以看到是活动用户的。但是如果在这里面使用一下文件选择框,或是去取一些系统目录,比如说用户的桌面,用户的收藏夹等,都会得到空。这时候因为没有指定用户环境造成的。使用CreateEnvironmentBlock这个API就可以搞定了。

**//// <summary>
/// 转全角的函数(SBC case)
/// </summary>
/// <param name="input">任意字符串</param>
/// <returns>全角字符串</returns>
///<remarks>
///全角空格为12288,半角空格为32
///其他字符半角(33-126)与全角(65281-65374)的对应关系是:均相差65248
///</remarks>
public string ToSBC(string input)
{
//半角转全角:
char[] c=input.ToCharArray();
for (int i = 0; i < c.Length; i++)
{
if (c[i]==32)
{
                     c[i]=(char)12288;
continue;
                 }
if (c[i]<127)
                     c[i]=(char)(c[i]+65248);
             }
return new string(c);               
         }


/**//// <summary>
/// 转半角的函数(DBC case)
/// </summary>
/// <param name="input">任意字符串</param>
/// <returns>半角字符串</returns>
///<remarks>
///全角空格为12288,半角空格为32
///其他字符半角(33-126)与全角(65281-65374)的对应关系是:均相差65248
///</remarks>
public string ToDBC(string input)
{   
char[] c=input.ToCharArray();
for (int i = 0; i < c.Length; i++)
{
if (c[i]==12288)
{
                     c[i]= (char)32;
continue;
                 }
if (c[i]>65280 && c[i]<65375)
                     c[i]=(char)(c[i]-65248);
             }
return new string(c);
         }

今天刚开始调试windows核心编程中的例程就感觉十分难懂,原因是自己的c++基本功力实在太弱了

首先在windows编程的过程中大量的使用宏的问题。

#pragma 的用法

在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C ++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器
或操作系统专有的,且对于每个编译器都是不同的。
其格式一般为: #Pragma Para
其中Para 为参数,下面来看一些常用的参数。
(1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗
口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:
#Pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正
确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自
己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_
X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。
(2)另一个使用得比较多的pragma参数是code_seg。格式如:
#pragma code_seg( [\section-name\[,\section-class\] ] )
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。
(3)#pragma once (比较常用)
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6
中就已经有了,但是考虑到兼容性并没有太多的使用它。
(4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预
编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所
以使用这个选项排除一些头文件。
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#p
ragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。
(5)#pragma resource \*.dfm\表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体
外观的定义。
(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等价于:
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
#pragma warning(once:4385) // 4385号警告信息仅报告一次
#pragma warning(error:164) // 把164号警告信息作为一个错误。
同时这个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1---4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
常用的lib关键字,可以帮我们连入一个库文件。
(8)·通过#pragma pack(n)改变C编译器的字节对齐方式
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、
long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的
数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分
配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和
整个结构的地址相同。
例如,下面的结构各成员空间分配情况:
struct test
{
      char x1;
      short x2;
      float x3;
      char x4;
};
      结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为
short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了一个
空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然对界地址上,在它
们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构
所有成员中要求的最大对界单元,因而test结构的自然对界条件为4字节,编译器
在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。更改C编译器的
缺省字节对齐方式
     在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配
空间。一般地,可以通过下面的方法来改变缺省的对界条件:
  · 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
      · 使用伪指令#pragma pack (),取消自定义字节对齐方式。
      另外,还有如下的一种方式:
      · __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。
如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
      · __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际
占用字节数进行对齐。
以上的n = 1, 2, 4, 8, 16... 第一种方式较为常见。
应用实例
  在网络协议编程中,经常会处理不同协议的数据报文。一种方法是通过指针偏移的
方法来得到各种信息,但这样做不仅编程复杂,而且一旦协议有变化,程序修改起来
也比较麻烦。在了解了编译器对结构空间的分配原则之后,我们完全可以利用这
一特性定义自己的协议结构,通过访问结构的成员来获取各种信息。这样做,
不仅简化了编程,而且即使协议发生变化,我们也只需修改协议结构的定义即可,
其它程序无需修改,省时省力。下面以TCP协议首部为例,说明如何定义协议结构。
其协议结构定义如下:
#pragma pack(1) // 按照1字节方式进行对齐
struct TCPHEADER
{
      short SrcPort; // 16位源端口号
      short DstPort; // 16位目的端口号
      int SerialNo; // 32位序列号
      int AckNo; // 32位确认号
      unsigned char HaderLen : 4; // 4位首部长度
      unsigned char Reserved1 : 4; // 保留6位中的4位
      unsigned char Reserved2 : 2; // 保留6位中的2位
      unsigned char URG : 1;
      unsigned char ACK : 1;
      unsigned char PSH : 1;
      unsigned char RST : 1;
      unsigned char SYN : 1;
      unsigned char FIN : 1;
      short WindowSize; // 16位窗口大小
      short TcpChkSum; // 16位TCP检验和
      short UrgentPointer; // 16位紧急指针
};
#pragma pack() // 取消1字节对齐方式

//#include "javacall.h"
#include "applet1.h"

//JNIEXPORT jint JNICALL Java_javacall_GetDES (JNIEnv *, jclass, jint p,jint k,jint z)
JNIEXPORT jint JNICALL Java_Applet1_GetDES(JNIEnv *, jclass, jint p,jint k,jint z)
{
int res=GetDES(p,k,z);
      return res;
}

jstring convertANSIToUNI(JNIEnv *env,char* cStr)

{       

                    int slen = strlen(cStr);

                    if(!env||slen==0)

                             return NULL;

                    jchar* buffer = new jchar[slen];

                    int len = MultiByteToWideChar(CP_ACP,0,cStr,strlen(cStr),(LPWSTR)buffer,slen);

                    if(len>0 && len < slen)

                             buffer[len]='\0';

                    jstring js = env->NewString(buffer,len);

                    delete [] buffer;

                    return js;    

}
void convertUniToANSI(JNIEnv *env,jstring oldStr,char* newStr)

{

                     int desc_len=256*2;

                     int len;

                     if(oldStr==NULL||newStr==NULL)

                              return ;

                     wchar_t *w_buffer = new wchar_t[256];

                     wcscpy(w_buffer,(LPWSTR)env->GetStringChars(oldStr,0));

                     env->ReleaseStringChars(oldStr,(jchar *)w_buffer);

                     len = WideCharToMultiByte(CP_ACP,0,w_buffer,1024,newStr,desc_len,NULL,NULL);

                     if(len>0 && len<desc_len)

                     {

                              newStr[len]='\0';

                     }

                     delete[] w_buffer;

}

JNIEXPORT jstring    JNICALL Java_Applet1_ReadIC(JNIEnv * env,jclass,jint l,jint k,jint m)
{
jstring js;
CString CSjs;
CSjs="12345678";
CSjs=ReadICrCS(l,k,m);
      AfxMessageBox(CSjs);
      char *cCSjs;
cCSjs=CSjs.GetBuffer();
      js=convertANSIToUNI(env,cCSjs);

return js;
}

JNIEXPORT jint JNICALL Java_Applet1_WriteIC(JNIEnv *env,jclass,jint l,jint k,jstring iw,jint m)
{
      char chContext[3000];
      convertUniToANSI(env,iw,chContext);
CString iwCS;
iwCS.Format(_T("%s"),chContext);
AfxMessageBox(iwCS);
int res=WriteICrCS(l,k,iwCS,m);
return res;
}

有关UNICODE、ANSI字符集和相关字符串操作的总结!先声明不是我的杰作。我这里只是总结了一部分,如果有错误请指出,同时欢迎大家参与进来! 

Q   UNICODE字符串如何显示 


如果程序定义了_UNICODE宏直接用 

WCHAR   *str=L”unicodestring”; 

TextOut(0,0,str); 

否则就需要转换类型 

#include   <comdef.h> 

WCHAR   *str=L”unicodestring”; 

bstr_t   str1=str; 

TextOut(0,0,(char*)str1); 

Q   如何实现ANSI和UNICODE的相互转换 


将ANSI转换到Unicode 

(1)通过L这个宏来实现,例如:   CLSIDFromProgID(   L”MAPI.Folder”,&clsid); 

(2)通过MultiByteToWideChar函数实现转换,例如: 

char   *szProgID   =   “MAPI.Folder”; 

WCHAR   szWideProgID[128]; 

CLSID   clsid; 

long   lLen   =   MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID)); 

szWideProgID[lLen]   =   ‘\0’;   

(3)通过A2W宏来实现,例如:   

USES_CONVERSION;   

CLSIDFromProgID(   A2W(szProgID),&clsid);   

将Unicode转换到ANSI 

(1)使用WideCharToMultiByte,例如: 

//   假设已经有了一个Unicode   串   wszSomeString…   

char   szANSIString   [MAX_PATH];   

WideCharToMultiByte   (   CP_ACP,   WC_COMPOSITECHECK,   wszSomeString,   -1,   szANSIString,   sizeof(szANSIString),   NULL,   NULL   );   

(2)使用W2A宏来实现,例如: 

USES_CONVERSION; 

pTemp=W2A(wszSomeString);   

注意在转换时可能存在的问题: 

因为ANSI转UNICODE,如果使用A2W或MultiByteToWideChar(第一个参数是CP_ACP)的话,是根据系统默认的转码表,把转入的ANSI字符串看作Multi-Bytes字符串处理的,如果是中文(中文windows默认就是中文),一个大于0x87的byte可能和下一byte一起被看作一个汉字,然后根据汉字的Unicode编码转换为相同的Unicode汉字,如果找不到相应的编码,一般就用一个默认的字符来取代它(一般是问号“?”),由此看,如果随便把一段数据给他转,转化很复杂而且极可能不可逆,而且你加密过的ANSI码是相当混乱的有很多〉0x87的byte,转换就变得不可逆了。 

建议自己直接就这样写: 

CHAR   lpANSI[COUNT]; 

WCHAR   lpUnicode[COUNT]; 

int   i   =   0;   

while(lpANSI[i]   !=   ‘\0’   )   { 

        lpUnicode[i]   =   (WCHAR)lpANSI[i]; 


lpUnicode[i]   =   L’\0’; 

然后按相同的方法转回来,因为对于0~0x87的ANSI字符串,对应的Unicode码就是相同的16位值,至于其他的,你的字符串反正加了密,没必要转换成显示出来是一样的字符,就按同样的方法处理了,其实如果中间的字符串不用显示或别的,直接reutrn   (LPWSTR)lpANSI;过去也可以,   反正接受的时候自己清楚就可以了。 

Q   如何让程序支持UNICODE   

A   

NT系统的内核是unicode代码,通常vc分创建的工程默认都是ansi代码(可以兼容win9x),在nt下ansi程式在调用windows   API的时系统实际又进行了一次ansi到unicode的代码转化,如MoveWindowA实际上又调用MoveWindowW.如果以我们的程序不考虑win9x(早晚是明日黄花)的话,直接用unicode编译,那么程式的代码执行效率一定能增色不少.具体: 

(0).在vc编译选项上,在vc7.0以上在工程的属性页中的“字符集”选上”使用   Unicode   字符集”即可,在vc6.0下可能麻烦一点,得先把vc运行库的unicode版本复制到vc路径下,一般都是和xxx.lib的ansi对应xxxU.lib,默认装vc时是不会装的,将工程属性 

(0).1.改语言定义: 

在project   settings的”C++”页中的”preprocessor   definitions”中改_MBCS为_UNICODE 

(0).2.改入口函数: 

在”link”页中的”project   Options”加入/entry:”wWinMainCRTStartup”即可. 

(1)在代码上,处理字符中的多用TCHAR.H中的宏,如strcpy用_tcscpy代替,用TCHAR代char, 

用TCHAR   m_mystr[]=_T(“xxxx”)代替   char   m_mystr[]=”xxxx”; 

(2)注意调试UNICODE程序时,需要在安装时VC选择所有选项,否则会缺少动态库和相应的.lib文件 

Q   如何取得一个既包含单字节字符又包含双字节字符的字符串的字符个数? 


可以调用Microsoft   Visual   C++的运行期库包含函数_mbslen来操作多字节(既包括单字节也包括双字节)字符串。 

调用strlen函数,无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0之前有多少个字节。 

Q   如何对DBCS(双字节字符集)字符串进行操作? 


函数   描述 

PTSTR   CharNext   (   LPCTSTR   );   返回字符串中下一个字符的地址 

PTSTR   CharPrev   (   LPCTSTR,   LPCTSTR   );   返回字符串中上一个字符的地址 

BOOL   IsDBCSLeadByte(   BYTE   );   如果该字节是DBCS字符的第一个字节,则返回非0值 

Q   为什么要使用Unicode? 


(1)   可以很容易地在不同语言之间进行数据交换。 

(2)   使你能够分配支持所有语言的单个二进制.exe文件或DLL文件。 

(3)   提高应用程序的运行效率。 

Windows   2000是使用Unicode从头进行开发的,如果调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从头开始用Unicode来开发应用程序,就能够使你的应用程序更加有效地运行。 

Windows   CE   本身就是使用Unicode的一种操作系统,完全不支持ANSI   Windows函数 

Windows   98   只支持ANSI,只能为ANSI开发应用程序。 

Microsoft公司将COM从16位Windows转换成Win32时,公司决定需要字符串的所有COM接口方法都只能接受Unicode字符串。 

Q   如何编写Unicode源代码? 


Microsoft公司为Unicode设计了WindowsAPI,这样,可以尽量减少代码的影响。实际上,可以编写单个源代码文件,以便使用或者不使用Unicode来对它进行编译。只需要定义两个宏(UNICODE和_UNICODE),就可以修改然后重新编译该源文件。 

_UNICODE宏用于C运行期头文件,而UNICODE宏则用于Windows头文件。当编译源代码模块时,通常必须同时定义这两个宏。 

Q   Windows定义的Unicode数据类型有哪些? 


数据类型   说明 

WCHAR   Unicode字符 

PWSTR   指向Unicode字符串的指针 

PCWSTR   指向一个恒定的Unicode字符串的指针 

对应的ANSI数据类型为CHAR,LPSTR和LPCSTR。 

ANSI/Unicode通用数据类型为TCHAR,PTSTR,LPCTSTR。 

Q   如何对Unicode进行操作? 


字符集   特性   实例 

ANSI   操作函数以str开头   strcpy 

Unicode   操作函数以wcs开头   wcscpy 

MBCS   操作函数以_mbs开头   _mbscpy 

ANSI/Unicode   操作函数以_tcs开头   _tcscpy(C运行期库) 

ANSI/Unicode   操作函数以lstr开头   lstrcpy(Windows函数) 

所有新的和未过时的函数在Windows2000中都同时拥有ANSI和Unicode两个版本。ANSI版本函数结尾以A表示;Unicode版本函数结尾以W表示。Windows会如下定义: 

#ifdef   UNICODE 

#define   CreateWindowEx   CreateWindowExW 

#else 

#define   CreateWindowEx   CreateWindowExA 

#endif   //   !UNICODE 

Q   如何表示Unicode字符串常量? 


字符集   实例 

ANSI   “string” 

Unicode   L“string” 

ANSI/Unicode   T(“string”)或_TEXT(“string”)if(   szError[0]   ==   _TEXT(‘J’)   ){   } 

Q   为什么应当尽量使用操作系统函数? 


这将有助于稍稍提高应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorer.exe所使用。由于这些函数使用得很多,因此,在应用程序运行时,它们可能已经被装入RAM。 

如:StrCat,StrChr,StrCmp和StrCpy等。 

Q   如何编写符合ANSI和Unicode的应用程序? 


(1)   将文本串视为字符数组,而不是chars数组或字节数组。 

(2)   将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。 

(3)   将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。 

(4)   将TEXT宏用于原义字符和字符串。 

(5)   执行全局性替换(例如用PTSTR替换PSTR)。 

(6)   修改字符串运算问题。例如函数通常希望在字符中传递一个缓存的大小,而不是字节。这意味着不应该传递sizeof(szBuffer),而应该传递(sizeof(szBuffer)/sizeof(TCHAR)。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来分配内存。这就是说,应该调用 

malloc(nCharacters   *sizeof(TCHAR)),而不是调用malloc(nCharacters)。 

Q   如何对字符串进行有选择的比较? 


通过调用CompareString来实现。 

标志   含义 

NORM_IGNORECASE   忽略字母的大小写 

NORM_IGNOREKANATYPE   不区分平假名与片假名字符 

NORM_IGNORENONSPACE   忽略无间隔字符 

NORM_IGNORESYMBOLS   忽略符号 

NORM_IGNOREWIDTH   不区分单字节字符与作为双字节字符的同一个字符 

SORT_STRINGSORT   将标点符号作为普通符号来处理 

Q   如何判断一个文本文件是ANSI还是Unicode? 


判断如果文本文件的开头两个字节是0xFF和0xFE,那么就是Unicode,否则是ANSI。 

Q   如何判断一段字符串是ANSI还是Unicode? 


用IsTextUnicode进行判断。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此   IsTextUnicode有可能返回不正确的结果。 

Q   如何在Unicode与ANSI之间转换字符串? 


Windows函数MultiByteToWideChar用于将多字节字符串转换成宽字符串;函数WideCharToMultiByte将宽字符串转换成等价的多字节字符串。 

Q   如何得到汉字的Unicode编码 


#include   “comdef.h” 

char   *str1=”你好”; 

_bstr_t   str=str1; 

WCHAR   *str2=str; 

str2就是你要的UNICODE码 

Q   如何实现#21592#24037#36873#25321这种编码与汉字之间的转换? 


CString   str=”#21592#24037#36873#25321”; 

str+=’#’; 

CString   str1=””; 

WCHAR   str2[5]={0,0,0,0,0}; 

int   j=0; 

do 


            str1=str.Left(str.Find(‘#’,1)); 

            str=str.Mid(str.Find(‘#’,1)); 

            WCHAR   i=0; 

            sscanf(str1,”#%d”,&i); 

            str2[j]=i; 

            j++; 

}while(str1!=””); 

_bstr_t   str3=str2;

看一下这两个指令是如何影响变量在内存的存储的。

1、pack pragma

pack pragma设置了struct、union或class中各成员的对齐方式,结构成员对齐指的是成员相对于起始地址的偏移量。该指令基本用法如下:
#pragma pack(n)
它指定了结构成员按n(1,2,4,8,16)字节对齐,如果未指定n,则恢复成默认值。需要注意的是,它并不是指结构体中的每个成员都要按n对齐,而是按照每个成员的大小和n相比较小的值对齐。下面引用MSDN中C++ Preprocessor Reference部分关于pack指令的说明:

n (optional)
    Specifies the value, in bytes, to be used for packing. The default value for n is 8. Valid values are 1, 2, 4, 8, and 16. The alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.

即成员member的对齐值 align of member = min( pack setting value, sizeof(member) )

请看下面示例代码:

#include <iostream>
using namespace std;

#pragma pack(show) //显示当前结构成员对齐设置

#pragma pack(8)
struct A
...{
     int n;
     char c;
     short s;
};
struct B
...{
     char c;
     int n;
     short s;
};
#pragma pack()

int _tmain(int argc, _TCHAR* argv[])
...{
     A a;
     B b;

     memset( &a, 0, sizeof(A) );
     memset( &b, 0, sizeof(B) );

     a.c = '1';
     a.n = 2;
     a.s = 3;

     b.c = '1';
     b.n = 2;
     b.s = 3;

     cout << sizeof(A) << endl;
     cout << sizeof(B) << endl;

     return 0;
}

笔者的测试环境为x86体系32位计算机 win2000操作系统,VS2003编译器。
编译器默认的成员对齐值是8字节,通过#pragma pack(show)指令,编译的时候在输出栏会限制默认对齐值。以上程序运行完通过调试的内存察看功能得到a和b的内存存储区域如下:
a的存储区域:0x0012FED0 02 00 00 00 31 00 03 00
b的存储区域:0x0012FEBC 31 00 00 00 02 00 00 00 03 00 00 00
最前面的4字节整数是变量的起始地址,后面是变量的整个存储区域。

现在我们按照 align of member = min( pack setting value, sizeof(member) )的公式分析一下a和b的存储。

a 的第一个成员n为int,大小为4,align of a.n = min(8,sizeof(int) ),对齐值为4。第一个成员相对于结构体起始地址从0偏移开始,前四个字节02 00 00 00即为n的存储区域,因为x86是Little Endian(低字节在前)的字节顺序,所以第一字节是2,后面三个字节0,我们通常写成0x00000002;

a的第二个成员c为char,大小为1,align of a.c=min(8,sizeof(char)),对齐值为1。c紧接着a后面存储从偏移4开始,满足1字节对齐的要求。它的值为'1',ASCII码为0x31,共一个字节31;

a的第三个成员为short,大小为2,align of a.s=min(8,sizeof(short)),对齐值为2。如果紧接第二个成员从偏移5开始存储就不满足2字节对齐,因此跳过1个字节,从偏移6字节的地方开始存储,即最后两个字节03 00;

b的第一个成员c为char,大小为1,align of a.c=min(8,sizeof(char)),对齐值为1。第一个成员从偏移起始地址0字节开始存储,它的值为'1',ASCII码为0x31,共一个字节31;

b 的第二个成员n为int,大小为4,align of a.n = min(8,sizeof(int) ),对齐值为4。如果紧接第二个成员后面从偏移1开始存储就不能4字节对齐,因此跳过3个字节,从偏移4字节的地方开始存储,即第5-8的四个字节02 00 00 00;

b的第三个成员为short,大小为2,align of a.s=min(8,sizeof(short)),对齐值为2。紧接第二个成员从偏移8字节的地方开始存储,即9-10两个字节03 00;

这时有人可能要问,b为什么最后多了两个字节00 00呢?这就是我们下面要讲的,整个结构体的对齐。

2、align指令

align指令可以用于设置各种内置类型、自定义类型如struct、union或class的的对齐方式。指令格式为: __declspec(align( # )) ,#是对齐值,取值为2的1次方至2的8192次方。在声明自定义类型或内置变量时,如果指定了对齐值,则对应变量的起始地址必须是该值的整数倍。除此外,它还会影响结构体的大小。下面引用两段MSDN关于align的描述:

Without __declspec(align( # )) , Visual C++ aligns data on natural boundaries based on the size of the data, for example 4-byte integers on 4-byte boundaries and 8-byte doubles on 8-byte boundaries. Data in classes or structures is aligned within the class or structure at the minimum of its natural alignment and the current packing setting (from #pragma pack or the /Zp compiler option).

从这段可以看出,如果没有设置align(#)值,变量x按照sizeof(x)来对齐起始地址。类或结构体内的成员在类或结构体内部按照min( pack setting value,sizeof(member))来对齐。这个我们在pack指令部分已经分析过。

The sizeof value for any structure is the offset of the final member, plus that member's size, rounded up to the nearest multiple of the largest member alignment value or the whole structure alignment value, whichever is greater.

从这段可以看出,align(#)指令会影响结构体或类的大小。总结公式为:
sizeof(structure) = (结构体最后一个成员的偏移 + sizeof(结构体最后一个成员) ) 上取整 ( n* max( 结构体各成员的对齐值,align(#)设置的值 ) ); 其中n为正整数

根据该公式我们分析一下b为什么后面会多两个填充字节0。
b的最后一个成s偏移为8,大小为2,b中各成员对齐值最大的为4,因为未设置align(#),所以上取整的数值为4n。8+2按4的倍数上取整为12。因此后面需要填充两个字节,这样才能使sizeof(b) == 12。

下面以一代码来说明align(#)指令的用法:

#include <iostream>
using namespace std;

#define CACHE_LINE   32
#define CACHE_ALIGN __declspec(align(CACHE_LINE))

#pragma pack(8)

struct CACHE_ALIGN S1 
...{
    int a, b, c, d;
};

struct S3
...{
    struct S1 s1;                  
    int a;       
};
#pragma pack()

int _tmain(int argc, _TCHAR* argv[])
...{
     CACHE_ALIGN int i = 2;

     cout << sizeof(S1) << endl;
     cout << sizeof(S3) << endl;

     return 0;
}

运行程序输出32和64,按公式sizeof(structure) = (结构体最后一个成员的偏移 + sizeof(结构体最后一个成员) ) 上取整 ( n* max( 结构体各成员的对齐值,align(#)设置的值 ) )分析:

sizeof(S1) = (12+4) 上取整 ( n * max( 4, 32 ) )
sizeof(S1) = (16) 上取整 ( 32 )
sizeof(S1) = 32

S3的大小留待大家练练手。

我们截获函数执行最直接的目的就是为函数增添功能,修改返回值,或者为调试以及性能测试加入附加的代码,或者截获函数的输入输出作研究,破解使用。通过访 问源代码,我们可以轻而易举的使用重建(Rebuilding)操作系统或者应用程序的方法在它们中间插入新的功能或者做功能扩展。然而,在今天这个商业 化的开发世界里,以及在只有二进制代码发布的系统中,研究人员几乎没有机会可以得到源代码。本文主要讨论Detour在Windows二进制PE文件基础 上的API截获技术。对于Linux平台,作这件事情将会非常的简单,由于最初的操作系统设计者引入了LD_PRELOAD。如果你设置  LD_PRELOAD=mylib.so ,那么应用程序在载入 dll时,会先查看mylib.so的符号表,在relocation 的时候会优先 使用mylib.so 里的 symbol 。假如你在mylib.so里有个printf() ,那么这个printf就会替代libc的 printf。 而在mylib.so里的这个printf可以直接访问 libc.so里的printf函数指针来获得真正的 printf的入口地 址。 这样,所有的dll的API HOOK在loader加载dll的时候就已经完成,非常自然,和平台相关的部分全部交给loader去处理。
一、  Detour开发库:
  简介
Detours是一个在x86平台上截获任意Win32函数调用的工具库。中断代码可以在运行时动态加载。Detours使用一个无条件转移指令来替换目 标函数的最初几条指令,将控制流转移到一个用户提供的截获函数。而目标函数中的一些指令被保存在一个被称为“trampoline” (译注:英文意为蹦 床,杂技)的函数中,在这里我觉得翻译成目标函数的部分克隆/拷贝比较贴切。这些指令包括目标函数中被替换的代码以及一个重新跳转到目标函数的无条件分 支。而截获函数可以替换目标函数,或者通过执行“trampoline”函数的时候将目标函数作为子程序来调用的办法来扩展功能。
Detours是执行时被插入的。内存中的目标函数的代码不是在硬盘上被修改的,因而可以在一个很好的粒度上使得截获二进制函数的执行变得更容易。例如, 一个应用程序执行时加载的DLL中的函数过程可以被插入一段截获代码(detoured),与此同时,这个DLL还可以被其他应用程序按正常情况执行(译 注:也就是按照不被截获的方式执行,因为DLL二进制文件没有被修改,所以发生截获时不会影响其他进程空间加载这个DLL)。不同于DLL的重新链接或者 静态重定向,Detours库中使用的这种中断技术确保不会影响到应用程序中的方法或者系统代码对目标函数的定位。
如果其他人为了调试或者在内部使用其他系统检测手段而试图修改二进制代码,Detours将是一个可以普遍使用的开发包。据我所知,Detours是第一 个可以在任意平台上将未修改的目标代码作为一个可以通过“trampoline”调用的子程序来保留的开发包。而以前的系统在逻辑上预先将截获代码放到目 标代码中,而不是将原始的目标代码做为一个普通的子程序来调用。我们独特的“trampoline”设计对于扩展现有的软件的二进制代码是至关重要的。
出于使用基本的函数截获功能的目的,Detours同样提供了编辑任何DLL导入表的功能,达到向存在的二进制代码中添加任意数据节表的目的,向一个新进 程或者一个已经运行着的进程中注入一个DLL。一旦向一个进程注入了DLL,这个动态库就可以截获任何Win32函数,不论它是在应用程序中或者在系统库 中。
  基本原理
1.  WIN32进程的内存管理
众所周知,WINDOWS NT实现了虚拟存储器,每一WIN32进程拥有4GB的虚存空间, 关于WIN32进程的虚存结构及其操作的具体细节请参阅WIN32 API手册, 以下仅指出与Detours相关的几点:
(1) 进程要执行的指令也放在虚存空间中
(2) 可以使用QueryProtectEx函数把存放指令的页面的权限更改为可读可写可执行,再改写其内容,从而修改正在运行的程序
(3) 可以使用VirtualAllocEx从一个进程为另一正运行的进程分配虚存,再使用 QueryProtectEx函数把页面的权限更改为可读可写可执行,并把要执行的指令以二进制机器码的形式写入,从而为一个正在运行的进程注入任意的代码 。
2. 拦截WIN32 API的原理
Detours定义了三个概念:
    (1) Target函数:要拦截的函数,通常为Windows的API。
(2) Trampoline函数:Target函数的部分复制品。因为Detours将会改写Target函数,所以先把Target函数的前5个字节复制保存好,一方面仍然保存Target函数的过程调用语义,另一方面便于以后的恢复。
(3) Detour 函数:用来替代Target函数的函数。
Detours在Target函数的开头加入JMP Address_of_ Detour_ Function指令(共5个字节)把对Target函数 的调用引导到自己的Detour函数, 把Target函数的开头的5个字节加上JMP Address_of_ Target _ Function+ 5共10个字节作为Trampoline函数。请参考下面的图1和图2。
(图1:Detour函数的过程)
crack_01
(图2: Detour函数的调用过程)
crack_02
说明:
  目标函数:
目标函数的函数体(二进制)至少有5个字节以上。按照微软的说明文档Trampoline函数的函数体是拷贝前5个字节加一个无条件跳转指令的话(如果没 有特殊处理不可分割指令的话),那么前5个字节必须是完整指令,也就是不能第5个字节和第6个字节是一条不可分割的指令,否则会造成Trampoline 函数执行错误,一条完整的指令被硬性分割开来,造成程序崩溃。对于第5字节和第6个字节是不可分割指令需要调整拷贝到杂技函数(Trampoline)的 字节个数,这个值可以查看目标函数的汇编代码得到。此函数是目标函数的修改版本,不能在Detour函数中直接调用,需要通过对Trampoline函数 的调用来达到间接调用。
  Trampoline函数:
此函数默认分配了32个字节,函数的内容就是拷贝的目标函数的前5个字节,加上一个JMP Address_of_ Target _ Function+5指令,共10个字节。
此函数仅供您的Detour函数调用,执行完前5个字节的指令后再绝对跳转到目标函数的第6个字节继续执行原功能函数。
  Detour函数:
此函数是用户需要的截获API的一个模拟版本,调用方式,参数个数必须和目标函数相一致。如目标函数是__stdcall,则Detour函数声明也必须 是__stdcall,参数个数和类型也必须相同,否则会造成程序崩溃。此函数在程序调用目标函数的第一条指令的时候就会被调用(无条件跳转过来的),如 果在此函数中想继续调用目标函数,必须调用Trampoline函数(Trampoline函数在执行完目标函数的前5个字节的指令后会无条件跳转到目标 函数的5个字节后继续执行),不能再直接调用目标函数,否则将进入无穷递归(目标函数跳转到Detour函数,Detour函数又跳转到目标函数的递归, 因为目标函数在内存中的前5个字节已经被修改成绝对跳转)。通过对Trampoline函数的调用后可以获取目标函数的执行结果,此特性对分析目标函数非 常有用,而且可以将目标函数的输出结果进行修改后再传回给应用程序。
Detour提供了向运行中的应用程序注入Detour函数和在二进制文件基础上注入Detour函数两种方式。本章主要讨论第二种工作方式。通过 Detours提供的开发包可以在二进制EXE文件中添加一个名称为Detour的节表,如下图3所示,主要目的是实现PE加载器加载应用程序的时候会自 动加载您编写的Detours DLL,在Detours Dll中的DLLMain中完成对目标函数的Detour。
(图3)
crack_03
二、  Detours提供的截获API的相关接口
Detours的提供的API 接口可以作为一个共享DLL给外部程序调用,也可以作为一个静态Lib链接到您的程序内部。
Trampoline函数可以动态或者静态的创建,如果目标函数本身是一个链接符号,使用静态的trampoline函数将非常简单。如果目标函数不能在链接时可见,那么可以使用动态trampoline函数。
  要使用静态的trampoline函数来截获目标函数,应用程序生成trampoline的时候必须使用
DETOUR_TRAMPOLINE宏。DETOUR_TRAMPOLINE有两个输入参数:trampoline的原型和目标函数的名字。
注意,对于正确的截获模型,包括目标函数,trampoline函数,以及截获函数都必须是完全一致的调用形式,包括参数格式和调用约定。当通过 trampoline函数调用目标函数的时候拷贝正确参数是截获函数的责任。由于目标函数仅仅是截获函数的一个可调用分支(截获函数可以调用 trampoline函数也可以不调用),这种责任几乎就是一种下意识的行为。
使用相同的调用约定可以确保寄存器中的值被正确的保存,并且保证调用堆栈在截获函数调用目标函数的时候能正确的建立和销毁。
可以使用DetourFunctionWithTrampoline函数来截获目标函数。这个函数有两个参数:trampoline函数以及截获函数的指针。因为目标函数已经被加到trampoline函数中,所有不需要在参数中特别指定。
  我们可以使用DetourFunction函数来创建一个动态的trampoline函数,它包括两个参数:一个指向目标函数的指针和一个截获函数的指针。DetourFunction分配一个新的trampoline函数并将适当的截获代码插入到目标函数中去。
当目标函数不是很容易使用的时候,DetourFindFunction函数可以找到那个函数,不管它是DLL中导出的函数,或者是可以通过二进制目标函数的调试符号找到。
DetourFindFunction接受两个参数:库的名字和函数的名字。如果DetourFindFunction函数找到了指定的函数,返回该函数 的指针,否则将返回一个NULL指针。DetourFindFunction会首先使用Win32函数LoadLibrary 和 GetProcAddress来定位函数,如果函数没有在DLL的导出表中找到,DetourFindFunction将使用ImageHlp库来搜索有 效的调试符号(译注:这里的调试符号是指Windows本身提供的调试符号,需要单独安装,具体信息请参考Windows的用户诊断支持信息)。 DetourFindFunction返回的函数指针可以用来传递给DetourFunction以生成一个动态的trampoline函数。
我们可以调用DetourRemoveTrampoline来去掉对一个目标函数的截获。
注意,因为Detours中的函数会修改应用程序的地址空间,请确保当加入截获函数或者去掉截获函数的时候没有其他线程在进程空间中执行,这是程序员的责任。一个简单的方法保证这个时候是单线程执行就是在加载Detours库的时候在DllMain中呼叫函数。
三、  使用Detours实现对API的截获的两种方法
建立一个MFC对话框工程,在对话框的OK按钮的单击事件中加入对MessageBoxA函数的调用,编译后的程序名称MessageBoxApp,效果如图。
crack_04
(图4)
  静态方法
建立一个Dll工程,名称为ApiHook,这里以Visual C++6.0开发环境,以截获ASCII版本的MessageBoxA函数来说明。在Dll的工程加入:
DETOUR_TRAMPOLINE(int WINAPI Real_Messagebox(HWND hWnd ,
    LPCSTR lpText,
    LPCSTR lpCaption,
UINT uType), ::MessageBoxA);
生成一个静态的MessageBoxA的Trampoline函数,在Dll工程中加入目标函数的Detour函数:
int WINAPI MessageBox_Mine( HWND hWnd ,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType)
{
  CString tmp= lpText;
  tmp+=” 被Detour截获”;
  return Real_Messagebox(hWnd,tmp,lpCaption,uType);
//  return ::MessageBoxA(hWnd,tmp,lpCaption,uType);  //Error
}
在Dll入口函数中的加载Dll事件中加入:
DetourFunctionWithTrampoline((PBYTE)Real_Messagebox, (PBYTE)MessageBox_Mine);
在Dll入口函数中的卸载Dll事件中加入:
DetourRemove((PBYTE)Real_Messagebox, (PBYTE)MessageBox_Mine);
  动态方法
建立一个Dll工程,名称为ApiHook,这里以Visual C++6.0开发环境,以截获ASCII版本的MessageBoxA函数来说明。在Dll的工程加入:
//声明MessageBoxA一样的函数原型
typedef int  (WINAPI * MessageBoxSys)( HWND hWnd ,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType);
//目标函数指针
MessageBoxSys SystemMessageBox=NULL;
//Trampoline函数指针
MessageBoxSys Real_MessageBox=NULL;
在Dll工程中加入目标函数的Detour函数:
int WINAPI MessageBox_Mine( HWND hWnd ,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType)
{
  CString tmp= lpText;
  tmp+=” 被Detour截获”;
  return Real_Messagebox(hWnd,tmp,lpCaption,uType);
//  return ::MessageBoxA(hWnd,tmp,lpCaption,uType);  //Error
}
在Dll入口函数中的加载Dll事件中加入:
  SystemMessageBox=(MessageBoxSys)DetourFindFunction("user32.dll","MessageBoxA");
  if(SystemMessageBox==NULL)
  {
    return FASLE;
  }
  Real_MessageBox=(MessageBoxSys)DetourFunction((PBYTE)SystemMessageBox, (PBYTE)MessageBox_Mine);
在Dll入口函数中的卸载Dll事件中加入:
DetourRemove((PBYTE)Real_Messagebox, (PBYTE)MessageBox_Mine);
  重写二进制可执行文件
使用Detours自带的SetDll.exe重写二进制可执行文件,可以在需要截获的程序中加入一个新的Detours的PE节表。对于本文就是新建一个批处理文件调用SetDll.exe。
@echo off
if not exist MessageBoxApp.exe (
echo 请将文件解压到MessageBoxApp.exe的安装目录, 然后执行补丁程序
) else (
setdll /d:ApiHook.dll MessageBoxApp.exe
)
Pause
调用后使用depends.exe(微软VC6.0开发包的工具之一)观察MessageBoxApp.exe前后变化, 可以看到Setdll已经重写MessageBoxApp.exe
成功,加入了对ApiHook.dll的依赖关系。
crack_05
      (执行SetDll.exe前)                                                       (执行SetDll.exe后)
执行SetDll.exe重写后的MessageBoxApp.exe,点击确定后可以看到结果如下:
至此,MessageBoxApp.exe对MessageBoxA函数的调用已经被截获,弹出的对话框内容已经明显说明这一点。
crack_06

(1)CPU篇
很多人认为CPU主频越高越强,这条件只存在于同等架构的CPU下对比才能成立。例如3G的奔腾D915能比2.6G的5000+强吗?2.6G的5000+能比2.33G的E6550强吗?不可能,因为架构根本不同,没得比,只能从实测性能去评估。某些人说“Intel的CPU稳定,AMD的玩游戏快,不过就是不稳定,而且兼容性也不够”,真的是那样吗?首先是否稳定是取决于电源供电率是否能保持稳定,还有主板滤波,系统也优化占有很大程度。至于兼容,有人敢说CPU也有兼容性问题的就可以无视了,明显不是小白就是忽悠人,只要没有硬伤,主板芯片和供电也是支持的话兼容是绝对没问题的。而且当时说AMD玩游戏好并不是指AMD的CPU游戏性能就是比Intel高,只是达到同等游戏性能的话用AMD平台花费得更少。而整机性能是否强悍并不是只取决于CPU,还要考虑内存,512M内存+E6550也不见得比PE2180+1G内存流畅,玩游戏的更要把显卡也考虑进去。玩高配置游戏游戏的时候,如果你用QX6600+8600GT,效果比E6550+8800GT差也更加正常,希望大家购机时不要只看CPU,还得根据自己的实际用途和整体性能均衡去考虑。我帮人配机的时候就遇到N个人说什么CPU要强点的,有限预算内把CPU加强就等于要降低其它的性能,根本是破坏整体均衡性能,反而得不偿失。你跑个系统看得出E6550和PE2180这相差700元的CPU的性能差别吗?你玩玩大型游戏的话,差距700元的显卡就足以让你体验什么是游戏性能。
(2)主板篇

大部分JS都说华硕主板好,大品牌的话能差吗?(能说出这话的人就可以证明他没有能耐,只能信品牌。)我可以告诉你们,如果大品牌真的全部都这么好,为什么其他品牌还能有活路?难道消费者全部都是不懂吗?主板的好坏在于主板的布局设计,用料,散热等方面,很大程度上影响主板的寿命。再好的大厂也有垃圾板,再弱的小厂也会有好板,500元在大厂买低端板和在小厂买高端板,这大厂的板能比得上吗?有些人还说“大厂的主板就是稳定”难道二三线厂的主板就不稳定吗?开几个QQ就死机?品牌观啊!难道布局有点不同就非要说这是大厂增加稳定性的设计?简直开玩笑,要是立场对调,不知道那种人还怎么看。现时很多人说什么主板是全固态电容的,全固就一定好吗?那只是一个卖点。日常使用,无论固态还是电解电容都是一样的用,不要被所谓的全固蒙蔽了眼睛,一个固态电容能值几个钱?难道打仗的时候,金铸的大炮就会比铜铸的大炮要好?高性能主板能提升性能?性能取决于CPU,内存,显卡,硬盘这些,主板起作支架作用。平台的主板就像一个人的身体那样,一个身体很壮的人,但手脚不灵活,脑筋也不发达,能多干活吗?主板需求的是稳定,耐用,有些人还强调需要超频。至于高性能,我说:“什么叫高性能的主板?能让CPU和显卡发挥超越设定的性能吗?一派胡言!”最近很多人喜欢做SLI,说双显卡性能高,但必须主板支持。我想问问,是否真的有做SLI的必要?首先SLI就是加大电源和CPU的负荷,退一步说让你8600GT做SLI,还不如买张8800GT或者HD3850,2张8800GT做SLI还不如直接买8800Ultra。假如你做SLI的话,那需要什么用的CPU?这个价位组建的平台还得用那普通的SLI主板吗?早就用那些2000多块的主板了,什么时候才轮得到它?我帮人配机极少用太高端主板,基本是做工用料设计布局都行的就没问题,最紧要的就是稳定耐用。太高端的板也不见得稳定性能高到哪,还不如用多出来的预算在电源,CPU,显卡,内存方面加强,这样才配出同等价位更高性能的配置。
(3)内存篇

内存容量当然越多越好,现在1G内存已经普及了。只要你不是超频发烧友,基本都没必要专门挑选特别好的内存,个人习惯是用宇瞻,金士顿(正货),威刚(红色威红),金邦(白金)这些。黑金刚悍将版的内存也很能超,那是采用D9GMH颗粒的内存条,耐压度强比较适合超频用,不过D9颗粒的内存存在一个致命的弱点,就是兼容性比较差,现在的D9超频条也普遍存在较早夭折的问题。所以不建议普通用户使用,大型游戏时DDR667和DDR800的性能差异也只在2%-3%的差距,可以感觉出实际差别吗?我让你超到DDR1000以上,性能明显不?而且这对散热,内存颗粒,主板支持度有很大关系,搞不好的话使硬件过早老化,系统不稳定等问题。我自己的机子也只是小超玩一把,要记住:性能和寿命往往成反比,看看那些超频发烧友的机子用了多久?有连续3年保持极限超频的机子去用?我也并不是说叫大家绝对不要超频,但超频是必须建立在系统稳定和散热良好的情况下进行,往往会增加不少额外的预算,所以说只要内存能兼容就可以了没有必要太追求过高带宽,反而会为自己的金钱和正常使用带来麻烦。
(4)显卡篇
不少商家卖显卡都说什么512M的独立显卡,那显卡算高性能吗?显存再高也是忽悠人的,你究竟买张什么样的显卡你知道吗?8500GT 512M(DDR2显存)在游戏上的性能连7300GT 256M(DDR3显存)也比不上,就连8800GT这级别的显卡512M与256M性能差异也只在数个百分点之间,其它的低端显卡还比得出性能差别?显卡性能取决于芯片性能,芯片频率,显存频率,至于其稳定性和耐用度也得看显卡布局设计,用料,散热装置。有些主板商说xx主板集成显卡相当于独立显卡的性能,那简直是一派胡言。现在的显卡都得上散热片+风冷,难道你比得上独立显卡性能的就可以不上风冷?如果有这样的技术早就在独立显卡上应用了,显卡厂商就早用来降低散热系统的成本了。就算同样的显示芯片,也永远不可能达到同等芯片频率和显存频率,而且共享内存的延迟远远大于独立显存的延迟,所以指望集成显卡能比得上独立显卡的人,你想都别想,除非是比N年前的MX440这些还可以。
(5)电源篇
一般人总会忽略电源稳定性问题,只认为电源功率够用就可以了,殊不知电源的供电稳定性直接影响硬件(特别是主板)的寿命。例如曾经有个服务器的电源,测试的时候,供电是只是短时间在12.47-12.52V徘徊,=_=!稳定得令人心寒。功率问题还得多说一句,一般电源的额定功率(就是指保证稳定的功率)是占峰值功率的70-80%之间(简称转换率),JS经常说300W电源一般都是指峰值300W电源,其实际功率一般都是220W,如果有独立显卡的话,令人担忧。如果是有独立显卡的用户,要买就买额定300W的电源,而且电源我一般都不用杂牌的。要想系统稳定就得先要电源功率稳定,个人习惯用TT,航嘉,先马,全汉,康舒,海韵等电源。大陆市场似乎很多人用长城,个人随机抽一个长城电源测试,电压是在11.87-12.47V徘徊,功率真的不那么稳定,被誉为:硬件杀手!其它杂牌的就更不用说了。要想系统稳定就先得挑好电源,为何服务器专用电源会这么贵?这不用多说吧?因为一个供电稳定的电源是保证一台机子稳定的重要开端。

一,默认的工作频率
目前
事实上,大多数用户只是把电脑当作一个工具,工作或娱乐的工具,对超频并不感兴趣,应用中也不需要超频。如果因为某个配件据称“包超XXMhz”,因此而选购,就很容易陷入超频的误区。事实上,超频并不是某个设备OC一下就可以实现的事,围绕超频,应该是一系列硬件协调配合的结果,比如CPU能超,那就需要主板适合超频,bios关于超频的内容非常丰富,主板上的供电回路、电容、MOS等相应地就需要提高一个档次,同时,系统的散热和供电就得加强。。。哇,突然间,你是不是发现主板、风扇、电源一下子就贵了好多?如果实践一下,又会发现DDRII667成了OC的瓶颈,是不是又得更换成DDRII800?
原来你预想4K的开销,突然间就跃升到5K或更多,那么事实上你是否需要把电脑超频起来使用呢?更郁闷的是,你超频起来使用,性能未必就能超越不超频的5K配置。
这里并不是说抛弃超频,超频对于部分人来说是一种乐趣、一种成就感,但于大多数人来说,未必有意思。换句话说,你把一只X2 3800+超到5000+,还不如直接选购一只X2 4000+或更高主频的CPU.显卡的超频也类似,需要扎实的基板、强化的散热系统,但还是容易导致花屏和贴图出错。
在默认频率的条件下,硬件是最稳定的,也会是最长寿的。而稳定、长寿,带来的是愉快的心情,同时减少相当多无谓的开销。与心情和经济均有利的事,何乐而不为呢?
二,扎实的主板
因为CPU、内存的寿命和稳定性都是非常统一的,因此,焦点就在于主板。一块做工优良、用料扎实的主板就成为重中之中。好多人在主板上省钱,这是不太明智的。关于主板的选购,牵涉到太多的技术细节,本文就不在此详细展开(有机会详谈)。就个人观点而言,请注意以下二点:
1,ATX标准版型。标准的PCB基板,意味着厂家尽善的电路设计和合格的电气性能。通常而言6层基
板,可以达到公版的设计要求。
2,标准的三相或四相供电回路设计。对于非超频系统,三相供电可以满足应用要求。每一项通常由一个电感线圈、二片MOS、一颗电容构成。此时要注意线圈、MOS的品质,电容的品质和数量。其中电容比较直观,而且主板的稳定与否也与此有关。英特尔对于电容的总容量也有相应的要求。简单地说,6颗以上日系电容意味着稳定。如果采用了固态电容,会意味着更强的稳定性和寿命。固态电容不光不容易暴浆,还带来更低的ESR值。
三,优质的电源。
这个问题好多人都忽略了,当看到机箱上带有电源时,就听从JS的说法,一块儿买了下来。
事实上,机箱上自带的电源,为OEM版电源,与DIY市场上的零售版,在实足的额定功率,电路的设计和用料上都有很大的差别。以全汉为例,技嘉机箱上经常带有全汉OEM的电源,但机箱电源的功率往往虚标(实际功率偏低),电路简化,二级滤波通常不完善。
我们知道,电脑硬件需要的直流工作电流,如果由交流转为直流处理得不够纯净,当电压波动时,会对硬件造成一定的冲击,就会对硬件的寿命生产影响。另一方面,从节省能源的角度说,我们更需要主动PFC电路的电源,转换因子会更高,也就是更省电,但OEM版通常不会有主动PFC的。
这类OEM电源的问题,在金河田、大水牛等著名机箱上也很常见,通常的原则就是一律放弃。
第四,强化散热系统
这也是大家容易忽视的问题。通常大家根据攒机单选购标配设备,JS装机完成后,就满足地回家了。
事实上,机箱内部局部的温室效应将致使硬件处于高温工作环境下,稳定性和寿命都会大打折扣。特别是在夏天,即使低TDP的K8、酷睿也会处于明显的高温之中,更别说显卡GPU、硬盘的温度了。
高温是硬件的杀手,高温也是导致重要电子元器件PN结击穿的罪魁,类似于电子元件PN结击穿的现象,在电容器上的表现就是热击穿,就是俗话中的“电容暴浆”。网上有非常多电容暴浆的惨图,此时系统会变得非常不稳定,容易死机,显卡容易花屏,甚至无法点亮桌面。如果注意了散热,电容暴浆及其它半导体器件损坏的机率就会直线下线,各类电子元件也就可以达到应有的理论寿命。
对于机箱内部的散热,主要是建立合理的风道。比如前置风扇与后置风扇,形成一进一出的风道,同时尽量做到风扇数量少而精,口径大而转速低,避免在加强散热的同时却导致噪音大增。机箱内部宽敞的空间对于散热也是非常有帮助的。
你在攒机时,是否考虑过上述问题呢?如果你能,那么恭喜你,你的攒机技术已经比较成熟了 DIY[font=宋体]市场竞争剧烈,厂商们纷纷在概念上动足脑筋,比如超频、动态对比度、廉价。其中超频卖点非常吸引眼球。

装机流程第一步--如何找好信的过的装机店
买电脑的第一步肯定要先选卖场,北京最大的DIY卖场非中关村莫属了,再就是百脑汇,其它的像的宏图三包这样DIY电脑的相对比较少,推荐在太平洋、海龙或百脑汇这三家卖场,理由很能简单,这三家大卖场DIY市场做的大,容易货比货和价比价!消费者会有更多的选择余地。
进入卖场
选好了卖场,进去买就是了,其实不然,您肯定会遇到无数搭讪的推销员,大爷,阿姨的叫一通,你可一定别动心,这里推出IT卖场潜规则NO1: “带客提成” ,每个推销员只要成功的把您忽悠到自己公司的柜台前,业务做成,他必定有几十至百元的回扣,视这单业务的油水多少而定,这也就注定了你从他公司这里买东西不会便宜,而且费用中还有他今天的部分工资,要说这非法导购真是害人不浅,人人都说是厂家直销,要不就是厂家总代理;而且是除了飞机导弹没有您是要什么他们有什么,就没他们不卖的东西,您要是稍微有点犹豫就能从门口跟到您十楼。所以您还是抬头挺胸大步走进卖场吧!寻求自己的理想卖场吧!
挑选商家门店篇
在前几年的时候IT行业是一块大肥肉,油水多多赚钱多多,可是近几年做IT生意的人越来越多,已经处于饱和状态,价格也就越来越透明,赚钱也就越来越难,赚不到钱怎么办呢?那就只能骗了,也就是我们常说了JS。现在各个电脑城每天都有关门的,但是每天都有开业的。如果让你遇上了那种经营不善导制关门的装机店,那以后你的保修可就苦咯!
所以在选择商家的时候一定要选择规模大的有信誉而且正规装机商。选择好的商家其实只要你留心的话,很容易就看的出来。记得有句广告词葛尤是这么说的“吃饭,哪家人多我去哪家!”选商家也是一样的道理,哪个店面装机的人最多,哪个店面里空荡荡没什么生意,又是哪家门店的JS在和顾客吵架,很容易区别摊位的信誉,当然最好有朋友推荐口碑不错的商家最为放心。
装机店售后服务
一般的讲正规的装机店都是有合法的注册手续,有注册资金以及法人代表的。这样如果出了事故,当事人会受到法律责任的,这也就是一般大装机店很少出现坑蒙消费者事件原因。有法律约束,当人没人会去冒这个险。而小装机店大部分都没注册,草草租个柜台交个押金,东调西凑一些货,就开始经营了。没了法律约束,所以JS才敢大胆欺骗消费者,甚至欺骗同行。因为这样的骗子事件,IT卖场里已经屡见不鲜了,由于卖场里的货是相互调配的,而且当场不是现金结算的,每个周会有定的一天来对账结账。有些骗子商家就调配了场子里的很多配件,在快要到结账那一天的前一天晚上就收拾门店,卷起铺盖跑路了。
一家有实力的摊位无论售前服务还是售后服务都做的相当不错,没实力的只能纸上谈兵了。要想知道一家摊位是否有实力,大家也可以注意观察他们的“硬件设备”。比如摊位门市的大小,所在公司在几大卖场各有几家摊位,有实力有资金的公司,一般都会选择电梯旁或卖场的一二层档口做摊位。没实力的装机商都是在那些不起眼的小角落里租的柜台。而且大公司有不少的专用设施,如配送货车以及客户记录,小的摊位无论无何都做不到这一点的。
装机流程第二步--心知肚明比价格
了解自己需要的配件报价,这是装机前的必要工作,古人说得好:“知己知彼,百战不殆!”装机也是如此,价格不清楚,肯定成为案板上的鱼肉,先到网上比如说ZOL在线,pchome电脑之家这种网络媒体去查寻价格。心里有个大概了就可以选几家比较大的有规模的商家去列几个同样配置的报价,两三家下来基本心里也有个谱了。接下来就去找相关产品的总代理(现在卖场大的代理商一般都兼做DIY),那里的价格一般比较低,也就是出货价。
列单篇
列单,这是IT谈单工作人员的必修课,一张装机单列出来的方式不下百种,如何能让利润最大化,JS作足了功课,有几大技巧,一看,经验丰富的销售人员,一眼就能看出你是否有IT背景,是否对硬件一无所知。二聊天、可不是普通聊天,几句话就能知道你的配置中,你对什么部件价格在意,对什么不了解,他一般会在你不注意的配件上出假货,或虚高价格。 三接力:你和一个商家接洽后,走不了多远,马上就有另一个商家跟过来,用“我们那XX产品更便宜”
装机流程第三步--坚定信念拒绝"推荐"

机箱电源选购篇:
如果前期你“寻价”功课做得不够,比如你对cpu了如指掌,但显示器不熟,那就会在显示器上吃大亏,当然如果你功课做得很好,还有一个IT卖场的潜规则NO.2:“配件挣不到钱 ,机箱电源出”,一般的装机配置单上都没明确写出机箱电源型号,攒机商托词说自选,实际选择时JS开道直接去了JS商量好的机箱门店,机箱电源在IT卖场里为什么租的起大店面?因为利润惊人,有时候攒机商带来的业务,一笔能赚二三百,当然攒机商拿走了大头,但留给机箱店主的利润也不少,因为这一系列“营销”中他们也扮演了重要角色。
列单中,配件价格虚高的一定要据理力争,配件的规格细节也要写清楚,比如硬盘的大小缓存及转速,还有更重要得是硬盘的保修期,显卡的品牌显存及显存带宽,CPU的散盒之分,主频及型号,主板的型号,显示器的型号,内存的品牌和速度等.
拒绝“推荐”
谈单中途,JS心里都会盘算这台机器的整体利润,如果吃力不讨好,肯定会建议你换配件,换的配件有好有坏,宗旨只有一个:哪个赚钱卖哪个!即使你只是装一个普通的家用机,一般的CPU及内存,如果他卖Geforce 8系列赚钱,他也会说服你Geforce 8600性能如何好,完全不管你的机器能不能带起来,如果你CPU和其他配件都不错,显卡举棋不定,他为了利润也能将早就过时了的Geforce 4 MX440的山寨货推销给你,价格不高,但一块能把一台机器的利润赚出来。所以我建议大家一定要“防推荐”
装机流程第四步--取货验货
最关键的重头戏来了,装机单谈妥以后,JS开始取货了,取货一般都是打电话到别人家里去调货,除非是自己代理的产品不用调货。这时你可要擦亮眼睛了。有标签的一定不放!有免费电话的一定不要心痛那几个话费,几千块都花了,再花个几块钱话费买个放心也值得!
1 . CPU
cpu可以分为散装与盒装。散装要注意一下ES版的。盒装也分为原盒与二包,cpu如果选盒装的,自己拆盒,观察盒子的外封是否完好,风扇是否原配,买Intel的盒装CPU一定要注意:CPU和风扇是一起质保的,没有风扇,盒装CPU也不质保。
2:内存
内存条如果选择品牌,一定要看有没有完整包装,有防伪标签及800电话查询的,一定要查,别不好意思,而且要看内存的金手指,有没有划痕,如果有就是上过机器的内存,这种坚决不要!
3:硬盘
除了注意所拿硬盘是否跟自己要求的型号,转速,缓存,一致以外,还要注意是否为新包装,正品的代理硬盘都是有外盒包装的,要是没有盒子,那有可能是水货或是散装。
4:主板与显卡
同样在购买板卡时一定要注意型号规格,和别的产品一样主板、显卡等型号较长,特别会让人误倒,例如目前市场中的8800显卡,我们可以看到市场中8800有几个版本,包括8800Ultra,8800GTX,8800GTS,8800GT,8800GS等,又有不同的核心G80和G92之分,而之间的价格差异非常大,所以选够时候一定要注意了。另外就算是一个型号也有可能出现不同版本,比如说8600GT就有128M和256M,搭配GDDR2和GDDR3不同显存的差别,这点也不能忽略了,只要你看清楚包装合上的型号以及显卡背面的PCB标签就能避免不必要的损失。
而主板的检查主要查看主板包装盒内配件、光盘是否齐全,主板上处理器和显卡插槽处贴纸是否有被动过的痕迹,显卡要特别注意金手指的地方有没有上机过的划痕,以确保产品是否被用过或反修过的产品。最后还要用户注意妥善保持显卡、声卡和显示器附赠的附件。一定保证要拿新的,CPU插槽上如果有贴纸看看撕过没有,散热器支架有没有磨损的痕迹。
5:显示器
目前市场一般都是液晶显示器的天下了。当然纯平的CRT还是有一定的出货量的,CRT一般就是看看包装箱有没有被拆封过就行了。但是液晶显示器可马虎不得哦。
首先就是是否提供坏点亮点保证,亮点与坏点是在选择LCD显示器中最受人头痛的,有些产品在购买时并没有发现有亮点与坏点,但在使用不久会但出现些斑斑点点,非常令人恼火,还好一些品牌的产品都提供亮点与坏点这样的保证,因此建议尽量选择这类的产品。
其次就是质保的时间,显示器是长线产品,长时间的质保总是让人更放心的
再次就是实际检测一下效果,带上相关的检测工具,看看有无严重的漏光,偏色,失真等等。至于参数等到不必过于介意,尤其是那些被商家蓄意炒作的东西,如XXXXX:1的对比度,目前的LCD满足日常的使用是绰绰有余的,你是更相信自己的眼睛还是参数,郑人买履的故事我想大家都听过。
6:最关键的机箱电源
自己寻价,自己付钱先买,JS看你自带机箱肯定心里淌血。 一般的商家机箱报价都会在原价上加个150到200元左右,你就看着砍吧!如果你觉着单买机箱再去配机器太麻烦,在装机店一起选机箱,这样也可以,但最好是事先看好自己想要的机箱,并摸清机箱的底价,这样可以和经销商砍价了。他们也就没办法从机箱上赚你太多的钱!
装机流程第五步--贴保修贴后安装测试
1:质保签
在DIY卖场里卖的每一样东西上面都会贴着一个易碎贴,在调货的时候谁家的货就会贴上谁家的贴,这个是质保签,有了它才知道是哪一家出的货,如果没有这个质保签有些配件就很难找商家质保了。所以在装机之前一定要让商家每件都要贴上这个易碎贴质保。
2:安装测试篇
安装不用自己动手,但安装时一定要及时提醒工作人员不要“野蛮施工”,安装的全过程一定要目不转睛的监视,防止JS偷换配件,如果在测试中有发现安装OS死机,玩游戏死机花屏的现象,一定要上前询问情况,问问他解决的办法,没问题也不要着急走。如果时间充裕就跑跑测试软件试试,如果差别较大,看看cpu及显卡是否不对。
相关常用软件可以这里下载
http://www.pcpop.com/doc/0/279/279564.shtml
装机流程第六步--签字画押电脑抱回家
1:签字画押篇
一切OK,不代表万事大吉,每样东西都有个盒,可以不要,但保修卡,说明书,驱动,配件一样都不能少,而且部分产品还有赠品,一定不要忘了问JS要,不要有时他是不会给你的,一切打包OK,该点票子了,别忙三包合同,或者保修合同写一份,详细写下各配件的规格,有序列号的写下序列号(如cpu),各部分配件的保修年限,最后别忘了盖上JS的红印,最后交钱走人。
2:配件一定要点清楚
将各种配件点清,不要遗落。另外硬件的包装盒不要着急仍,保留一段时间,以备打官司时需要。如此装机,我虽然无法保证价钱最低,但可以保证3.15你可以舒舒服服在家看人家打假,不用为自己的权益到各部门奔走呼号!真出问题也不用怕,所有证据都在手,直接找经销商,该换的换新的,不能换的就保修!