小祥的blog

游戏开发
随笔 - 11, 文章 - 0, 评论 - 7, 引用 - 0
数据加载中……

2008年3月22日

转载: 译:理解 Win32 "OutputDebugString"

                                                               译:理解 Win32 "OutputDebugString"

坚定的 Win32 开发人员可能对 OutputDebugString() API 函数比较熟悉,它可以使你的程序和调试器进行交谈。它要比创建日志文件容易,而且所有“真正的”调试器都能使用它。应用程序和调试器交谈的机制相当简单,而本文将揭示整件事情是如何工作的。

本文首先是由以下事件促使的,我们观察到 OutputDebugString() 在管理员和非管理员用户试图一起工作或游戏时并不总是能可靠地工作(至少在 Win2000 上)。我们怀疑是一些相关的内核对象的权限问题,此间涉略了相当多不得不写下来的信息。

请注意,尽管我们使用了“调试器”这一术语,但不是从调试 API 的意义上来使用的:并没有“单步执行”、“断点”或者“附着到进程”等可以在 MS Visual C 或者一些真正的交互开发环境中找到的东西。从某种意义上来说,任何实现了协议的程序都是“调试器”。可能是一个非常小的命令行工具,或者像来自于 SysInternals 那帮聪明的家伙们的 DebugView 那样的高级货。

内容目录

应用程序用法

<windows.h> 文件声明了 OutputDebugString() 函数的两个版本 - 一个用于 ASCII,一个用于 Unicode - 不像绝大多数 Win32 API 一样,原始版本是 ASCII。而大多数的 Win32 API 的原始版本是 Unicode。

使用一个 NULL 结尾的字符串缓冲区简单调用 OutputDebugString() 将导致信息出现在调试器中,如果有调试器的话。构建一条信息并发送之的通常用法是:


sprintf(msgbuf, "Cannot open file %s [err=%ld]\n", fname, GetLastError());
OutputDebugString(msgbuf);

不过在实际环境中我们中的不少人会创建一个前端函数,以允许我们使用 printf 风格的格式化。下面的 odprintf() 函数格式化字符串,确保结尾有一个合适的回车换行(删除原来的行结尾),并且发送信息到调试器。


#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
void __cdecl odprintf(const char *format, ...)
{
char buf[4096], *p = buf;
va_list args;
va_start(args, format);
p += _vsnprintf(p, sizeof buf - 1, format, args);
va_end(args);
while ( p > buf  &&  isspace(p[-1]) )
*--p = '\0';
*p++ = '\r';
*p++ = '\n';
*p   = '\0';
OutputDebugString(buf);
}

于是在代码中使用它就很简单:


        ...
odprintf("Cannot open file %s [err=%ld]", fname, GetLastError());
...

我们已经这样使用多年了。

协议

在应用程序和调试器之间传递数据是通过一个 4KB 大小的共享内存块完成的,并有一个互斥量和两个事件对象用来保护对他的访问。下面就是相关的四个内核对象:

对象名称 对象类型
DBWinMutex Mutex
DBWIN_BUFFER Section (共享内存)
DBWIN_BUFFER_READY Event
DBWIN_DATA_READY Event

互斥量通常一直保留在系统中,其他三个对象仅当调试器要接收信息才出现。事实上 - 如果一个调试器发现后三个对象已经存在,它会拒绝运行。

当 DBWIN_BUFFER 出现时,会被组织成以下结构。进程 ID 显示信息的来源,字符串数据填充这 4K 的剩余部分。按照约定,信息的末尾总是包括一个 NULL 字节。


struct dbwin_buffer {
                DWORD   dwProcessId;
                char    data[4096-sizeof(DWORD)];
                };
                

OutputDebugString() 被应用调用时,它执行以下步骤。注意在任意位置的错误都将放弃整个事情,调试请求被认为是什么也不做(不会发送字符串)。

  1. 打开 DBWinMutex 并且等待,直到我们取得了独占访问。
  2. 映射 DBWIN_BUFFER 段到内存中:如果没有发现,则没有调试器在运行,将忽略整个请求。
  3. 打开 DBWIN_BUFFER_READYDBWIN_DATA_READY 事件对象。就像共享内存段一样,缺少对象意味着没有可用的调试器。
  4. 等待 DBWIN_BUFFER_READY 事件对象为有信号状态:表示内存缓冲区不再被占用。大部分时候,这一事件对象一被检查就处于有信号状态,但等待缓冲区就绪不会超过 10 秒(超时将放弃请求)。
  5. 复制数据直到内存缓冲区中接近 4KB,再保存当前进程 ID。总是放置一个 NULL 字节到字符串结尾。
  6. 通过设置 DBWIN_DATA_READY 事件对象告诉调试器缓冲区就绪。调试器从那儿取走它。
  7. 释放互斥量。
  8. 关闭事件对象和段对象,但保留互斥量的句柄以备后用。

在调试器端会简单一点。互斥量根本不需要,如果事件对象和/或共享内存对象已经存在,则假定其他调试器已经在运行。系统中任意时刻只能存在一个调试器。

  1. 创建共享内存段以及两个事件对象。如果失败,退出。
  2. 设置 DBWIN_BUFFER_READY 事件对象,由此应用程序得知缓冲区可用。
  3. 等待 DBWIN_DATA_READY 事件对象变为有信号状态。
  4. 从内存缓冲区中提取进程 ID 和 NULL 结尾的字符串。
  5. 转到步骤 2。

这使我们认为这决不是一种低消耗的发送信息的方法,应用程序的运行速度会受到调试器的左右。

权限问题

我们发现 OutputDebugString() 有时不可靠已经好几年了,而且我们十分不解为什么微软这么长时间也没把它搞好。奇怪的是,问题总是围绕着 DBWinMutex 对象出现,这就需要我们察看许可系统以找出为什么会这么麻烦。

互斥量对象会一直存活着直到使用它的最后一个程序关闭其句柄,故而它能在初始创建它的应用程序退出后保留相当长的时间。因为此对象被广泛地共享,所以它必须被赋予明确的许可以允许任何人使用它。事实上,“缺省”许可几乎从不适用,这一问题被计为在 NT 3.51 和 NT 4.0 中我们观察到的第一个问题

当时的修正方法是使用一个广泛开放的 DACL 创建互斥量,以此来允许任何人访问它,但是看样子在 Win2000 里这些许可被加强了。表面上它看起来是正确的,就像我们在下表中看到的:

SYSTEM MUTEX_ALL_ACCESS
Administrators MUTEX_ALL_ACCESS
Everybody SYNCHRONIZE | READ_CONTROL | MUTEX_QUERY_STATE

希望发送调试信息的应用只需要等待和获取该互斥量的能力,也即体现为拥有 SYNCHRONIZE 权限。上列的许可对于所有参与的用户都是完全正确的。

不过如果有人观察 CreateMutex() 在对象已经存在时的行为,就会发现奇怪的事情。在这种情况下,Win32 的表现就好像我们进行了如下调用:

OpenMutex(MUTEX_ALL_ACCESS, FALSE, "DBWinMutex");
                        

尽管我们确实只需要 SYNCHRONIZE 访问,但它还是假定调用者要做任何事情MUTEX_ALL_ACCESS)。因为非管理员没有这些权限 - 仅有上列的少许 - 互斥量不能被打开或者获取,于是 OutputDebugString() 不做任何事情就悄悄地返回了。

甚至将所有的软件开发都以管理员来执行也不是一个完整的修正方法:如果存在其他的用户(例如服务)以非管理员运行而许可配置不正确,它们的调试信息将会丢失。

我们感觉真正的修正需要微软为 CreateMutex() 添加一个参数 - 如果对象已经存在时用于隐含的 OpenMutex() 调用的访问掩码。也许某天我们会看到一个 CreateMutexEx(),但在此期间我们必须采用另外的方法。代之以,当对象已经存活于内存中时我们将硬性改变其上的许可配置。

这需要调用 SetKernelObjectSecurity(),下列程序片断展示一个程序如何才能打开互斥量并安装一个新的 DACL。此 DACL 即使在程序退出后也仍然保持着,只要任一其他程序还维护有它(译者注:应该是指互斥量)的句柄。

...
                        // open the mutex that we're going to adjust
                        HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "DBWinMutex");
                        // create SECURITY_DESCRIPTOR with an explicit, empty DACL
                        // that allows full access to everybody
                        SECURITY_DESCRIPTOR     sd;
                        InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
                        SetSecurityDescriptorDacl(
                        &sd,            // addr of SD
                        TRUE,           // TRUE=DACL present
                        NULL,           // ... but it's empty (wide open)
                        FALSE);         // DACL explicitly set, not defaulted
                        // plug in the new DACL
                        SetKernelObjectSecurity(hMutex, DACL_SECURITY_INFORMATION, &sd);
                        ...
                        

这一方法明确地走向了正确的道路,但我们还需要找一个地方来放置此逻辑。把它放在一个一经请求即运行的小程序中是可以的,但是看起来它有可能被中断。我们的办法是写一个 Win32 服务来干这件事情。

我们的 dbmutex 工具完成的就是这一工作:它在系统引导时启动,打开或者创建互斥量,然后设置对象的安全性以允许广泛的访问。然后休眠直到系统关闭,在此过程中保持互斥量的打开状态。它不消耗任何 CPU 时间。

实现细节

我们花了很多时间使用 IDA Pro 深入到 Windows 2000 KERNEL32.DLL 的实现中,我们认为,对于它在更精确的基础上到底是如何工作的已经有了良好的掌握。在这儿我们给出 OutputDebugString() 函数的伪代码(我们没有编译过它),以及创建互斥量的函数。

我们故意略去了大多数的错误检查:如果事情变糟了,它将释放所有已分配的资源并退出,就像没有调试器存在一样。目的是展示一般行为而不是对代码的完整的逆向工程。

“setup” 函数 - 名字是我们起的 - 创建互斥量或者在已经存在时打开它。经过一些努力来设置互斥量对象的安全性以使任何人都能用它,尽管我们会看到其实并没有完全正确地得到它。

OutputDebugString.txt

胡思乱想

一些人可能会感到这是一个安全性问题,其实并不是。非管理员用户确实拥有适当使用 OutputDebugString() 的所有权限,不过由于“请求比所需更多权限”这一常见问题,一个合理的请求因形成了错误的形态而被拒绝了。

但并不像大部分的这种问题那样,这并非是有意的。大多数的错误是开发人员显式请求了更多(如“MUTEX_ALL_ACCESS”),而这次的掩码是由 CreateMutex() 的行为隐含的。这使得如果 Win32 API 不做改动的话更加难于避免。

---

当分析 KERNEL32.DLL 中的 OutputDebugStringA() 时,非管理员如何能够有可能去削弱系统变得明显起来。一旦得到互斥量,一个要发送调试信息的应用会等待 DBWIN_BUFFER_READY 事件对象就绪最多十秒钟,如果超时则放弃。这看起来是一个谨慎的防范措施,如果调试系统忙的话,用以避免被饿死。

但在更早的步骤里,等待互斥量,没有这样的超时设定。如果系统中的任何进程 - 包括非特权进程 - 可以以请求 SYNCHRONIZE 权限打开此互斥量,并且不释放它,所有其他试图获取此互斥量的进程将会无限停止完蛋。

我们的研究表明,所有类型的程序都会发送随意的调试信息(例如,MusicMatch Jukebox 就有一个唠唠叨叨的键盘钩子),这些线程通过很少的几行代码就能停止住。没有必要停止整个程序 - 可能还有其他的线程 - 但在实际中,开发人员不计划使用 OutputDebugString() 将会是一条拒绝服务之路(译者注:此句没有完全明白,请参看原文)。

---

最奇怪的是,我们发现 OutputDebugString() 并非一个天然的 Unicode 函数。大多数的 Win32 API 具有“真正的”使用了 Unicode 的函数(“W” 版本),如果调用“A”版本的函数则它们自动从 ASCII 转换到 UNICODE。

但是,因为 OutputDebugString 把在内存缓冲区中的数据最终是作为 ASCII 传递到调试器中的,它们具有相反于常规的 A/W 配对。这就暗示了如果要在 Unicode 程序里发送一个快捷信息到调试器,可以通过直接调用 “A” 版本来实现:

OutputDebugStringA("Got here to place X");
                        

本文转自: http://sluttery.spaces.live.com/default.aspx

版权声明:本篇为转载文章,可随意转载或收藏
小祥的BLOG http://xfxsworld.cnblogs.com

posted @ 2008-03-22 16:55 小祥 阅读(885) | 评论 (0)编辑

将Debug调试信息输出到控制台或文件

 将Debug调试信息输出到控制台或文件

        时常会碰到这种情况,策划们跑过来说程序出错了,然后呱呱啦啦的描述了一大堆也没把问题说清楚。若是能将程序的运行信息自动的记录起来,找bug就方便多了,也就是log功能。

        另外为调试程序,时常需要在程序里打入一些调试信息,这样可以更方便的发现BUG。这些调试信息只能在编辑器的调试模式下才能看到,或者借助第三方软件比如debugview等,但是会受到编译环境的限制,又不可能每次都带着debugview运行程序。。。若能将调试信息自动记录到log文件就方便多了。

        在调试模式下,调试器之所以能够得到信息,是因为捕捉了程序中的 OutputDebugString() 输出的信息。所以如果我们能够自己写程序捕捉OutputDebugString()函数输出的信息,就能解决上述的需求了。有关OutputDebugString()函数的原理请看这里:http://www.unixwiz.net/techtips/outputdebugstring.html,也可在本blog查看翻译版本。

        原理看明白后,实现起来就很容易了。下面给出具体代码:

 1const MAX_DebugBuffer = 4096;    // 应用程序和调试器之间传递数据是通过一个 4KB 大小的共享内存块完成的
 2
 3typedef struct dbwin_buffer {
 4    DWORD   dwProcessId;
 5    char    data[4096-sizeof(DWORD)];
 6}
DEBUGBUFFER,*PDEBUGBUFFER;
 7
 8// 线程函数
 9void WINAPI DebugTrackProc(PVOID pvParam)
10{   
11    HANDLE hMapping = NULL;  
12    HANDLE hAckEvent = NULL;
13    HANDLE hReadyEvent = NULL;
14    PDEBUGBUFFER pdbBuffer = NULL;  
15    TCHAR tzBuffer[MAX_DebugBuffer];  
16        
17    // 打开事件句柄    
18    hAckEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("DBWIN_BUFFER_READY"));    
19    if(hAckEvent == NULL)
20    {
21        CloseHandle(hAckEvent); 
22        return;
23    }

24
25    hReadyEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("DBWIN_DATA_READY"));  
26    if(hReadyEvent == NULL)
27    {
28        CloseHandle(hReadyEvent); 
29        return;
30    }
 
31
32    // 创建文件映射  
33    hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MAX_DebugBuffer, TEXT("DBWIN_BUFFER"));   
34    if(hMapping == NULL)
35    {
36        CloseHandle(hMapping); 
37        return;
38    }
 
39
40    // 映射调试缓冲区  
41    pdbBuffer = (PDEBUGBUFFER) MapViewOfFile(hMapping, FILE_MAP_READ, 000);  
42  
43    // 循环  
44    whiletrue )
45    {             
46        // 激活事件
47        SetEvent(hAckEvent);  
48        // 等待缓冲区数据
49        if ( WaitForSingleObject(m_hReadyEvent, INFINITE) == WAIT_OBJECT_0 )   
50        {              
51            // 保存信息,这就是我们想要的,有了这个信息,想打log或是输出到控制台就都可以啦      
52            tzBuffer = pdbBuffer->szString;              
53        }
   
54    }
 
55
56    // 释放   
57    if (pdbBuffer) 
58    {      
59        UnmapViewOfFile(pdbBuffer);   
60    }
   
61    CloseHandle(hMapping);   
62    CloseHandle(hReadyEvent);  
63    CloseHandle(hAckEvent);
64}


版权声明:本篇为原创文章,允许转载,但转载时请务必以超链接形式标明文章的原始出处和作者信息。请尊重本人的劳动成果,谢谢!
小祥的BLOG http://xfxsworld.cnblogs.com

posted @ 2008-03-22 16:49 小祥 阅读(415) | 评论 (0)编辑

2008年1月30日

常量指针与指针常量

 常量指针与指针常量

常量指针,就是指向常量的指针,关键字 const 出现在 * 左边,表示指针所指向的地址的内容是不可修改的,但指针自身可变。
指针常量,指针自身是一个常量,关键字 const 出现在 * 右边,表示指针自身不可变,但其指向的地址的内容是可以被修改的。

例:
        常量指针: const char* ptr = “hello”
        指针常量: char* const ptr = “hello”

另外常量指针有两种写法:const既可写在类型前,又可写在类型后。如上面的例子,常量指针:char const * ptr = “hello” 也是正确的。

最后再举个例子,与迭代器经常在一起用。
若希望迭代器所指向的东西不可变,则需要的是 const_iterator。例:

std::vector<int>::const_iterator Iter = vec.begin();
*Iter = 10;//错误,Iter是常量指针
Iter++;//正确,Iter本身可变

若希望迭代器本身不可变,指向的内容可变,则可以这样写:
const std::vector<int>::iterator Iter = vec.begin();
*Iter = 10//正确,指针常量
Iter++;     //错误,指针本身不可变

版权声明:本篇为原创文章,允许转载,但转载时请务必以超链接形式标明文章的原始出处和作者信息。请尊重本人的劳动成果,谢谢!
小祥的BLOG http://xfxsworld.cnblogs.com

posted @ 2008-01-30 23:33 小祥 阅读(285) | 评论 (0)编辑

2008年1月29日

wxWidgets helper classes

     摘要: wxWidgets helper classeswxWidgets库可以创建控制台( console )和界面( gui )程序,下面将在控制台模式下介绍一些helper class。Console这是一个简单得控制台程序,在控制台窗口里输出一些信息。#include<wx/string.h>intmain(intargc,char**argv){wxPuts(wxT("AwxWidg... 阅读全文

posted @ 2008-01-29 21:52 小祥 阅读(420) | 评论 (0)编辑

2008年1月26日

SplashScreen

 SplashScreen

     开始学习wxWidgets。准备把学到的都记下来,算是自己的学习笔记。
     今天写了一个SplashScreen的小例子。SplashScreen的意思通俗点来说就是在程序运行前先蹦出来的东东。举个例子,比如游侠网下载的游戏,每次运行前都会蹦出来一个小图片,停留几秒后,等它消失了才会进入游戏。
     大致流程是:1. 加载需要的image handler,下表列出的是wx可用的image handler(可以看出目前只有PNG格式支持alpha通道)
                          
wxBMPHandler For loading and saving, always installed.
wxPNGHandler For loading (including alpha support) and saving.
wxJPEGHandler For loading and saving.
wxGIFHandler Only for loading, due to legal issues.
wxPCXHandler For loading and saving (see below).
wxPNMHandler For loading and saving (see below).
wxTIFFHandler For loading and saving.
wxTGAHandler For loading only.
wxIFFHandler For loading only.
wxXPMHandler For loading and saving.
wxICOHandler For loading and saving.
wxCURHandler For loading and saving.
wxANIHandler For loading only.
    wx默认加载wxBMPHandler,如果要用其他格式,则可以通过调用wxImage::AddHandler()函数进行加载。也可以调用wxImage::wxInitAllImageHandlers()加载所有的,但通常没有这个必要。
                    2. Load图片。
                        通过wxBitmap::LoadFile( const wxString& name, wxBitmapType type )函数。
                        name是将要load进来得图片的名字,type是图片格式的类型,比如bmp格式的图片就是wxBITMAP_TYPE_BMP ,PNG格式的就是wxBITMAP_TYPE_PNG。。。
                    3. 可以创建SplashScreen了。
                        wxSplashScreen(const wxBitmap& bitmap, long splashStyle, int milliseconds, wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxSIMPLE_BORDER|wxFRAME_NO_TASKBAR|wxSTAY_ON_TOP)
                        具体参数一看就明白意思了。bitmap就是上面第2步创建的,splashStyle是风格,例:wxSPLASH_CENTRE_ON_SCREEN。后面几个参数分别是 持续时间,父窗口指针, id号, 位置, 大小,样式。。

 1wxImage::AddHandler(new wxPNGHandler);
 2
 3    wxBitmap bitmap;
 4
 5    bitmap.LoadFile(_T("splash.png"), wxBITMAP_TYPE_PNG);
 6
 7    wxSplashScreen* const splashScreen = new wxSplashScreen(bitmap,
 8            wxSPLASH_CENTRE_ON_SCREEN|wxSPLASH_TIMEOUT,
 9            -1, NULL, wxID_ANY, wxDefaultPosition, wxDefaultSize,
10            wxSIMPLE_BORDER|wxSTAY_ON_TOP);
11
12    Yield(); // 挂起消息
13
14         Sleep( 2000 );
15
16        delete splashScreen;

这样,程序在每次运行前,都会在先屏幕中央弹出splash.png这张图片,2秒终后,出现程序界面。。

版权声明:本篇为原创文章,允许转载,但转载时请务必以超链接形式标明文章的原始出处和作者信息。请尊重本人的劳动成果,谢谢!
小祥的BLOG http://xfxsworld.cnblogs.com

posted @ 2008-01-26 21:45 小祥 阅读(333) | 评论 (0)编辑

2007年11月17日

vs2005环境下CELayoutEditor源码编译详细步骤

                               vs2005环境下CELayoutEditor源码编译详细步骤

今天初试了一下CEGUI的官方编辑器——CELayoutEditor,其过程真可谓“饱受挫折”。现简要写下其源码的编译过程,已使可能会用到的朋友少走些弯路

1.    要编译CELayoutEditor,自然需要先编译CEGUI的东东,有2种选择:
        1.1 安装SDK,这是下载地址:http://jaist.dl.sourceforge.net/sourceforge/crayzedsgui/CEGUI-SDK-0.5.0b-vc8.exe,不过并不推荐这种方法,因为SDK里的debug版dll似乎有问题。。。
        1.2 编译其源代码。下载地址:http://jaist.dl.sourceforge.net/sourceforge/crayzedsgui/CEGUI-0.5.0b.zip,另外还需要下载依赖文件包dependencies package ,下载地址是:http://jaist.dl.sourceforge.net/sourceforge/crayzedsgui/CEGUI-DEPS-0.5.0b-vc8.zip,然后将依赖文件解压进源代码文件夹就OK了。
                  1.2.1   先别急着找solution,有个地方需要注意。CEGUI默认的是OPENGL渲染,如果是用D3D的话还得改一个地方。进..\makefiles\premake文件夹找到config.lua文件,记事本打开。改成“OPENGL_RENDERER = false,DIRECTX9_RENDERER = true”,保存就可以了。CELayoutEditor是用OPENGL的,所以这里不做任何改动。
                  1.2.2    运行build_vs2005.bat文件,执行完毕后会生成CEGUI.sln文件,打开就可直接编译了
         1.3   建立环境变量:我的电脑\属性\高级\环境变量,然后在上面的用户变量区新建一个,变量名是CEGUI,变量值是CEGUI的解压目录。注意这步不能省


2    由于CELayoutEditor使用了wxWidgets,所以在编译CELayoutEditor之前还需要下载wxWidgets。目前最新版本是2.86,进入官方下载网页http://www.wxwidgets.org/downloads/ 选择WxAll下载。
        2.1 建立环境变量:我的电脑\属性\高级\环境变量,然后在上面的用户变量区新建一个,变量名是WXWIDGETS,变量值是wxWidgets的解压目录。
        2.2  进wxWidgets的解压目录找到wx.dsw,打开、“Yes To All”转换。转换完毕后先别编译,还需要改下。因为wxWidgets默认并不是OPENGL
Open $(WXWIDGETS)\include\wx\msw\setup.h and replace 

Code: 
#define wxUSE_GLCANVAS 0  
WITH Code: 
#define wxUSE_GLCANVAS 1 


7.2 Open $(WXWIDGETS)\include\wx\univ\setup.h and replace 

Code: 
#if defined(__WIN32__) 
   
#define wxUSE_GLCANVAS 1 
else    
    
#define wxUSE_GLCANVAS 0 

WITH 
Code: 
#define wxUSE_GLCANVAS 1 


           2.3    现在就可以开始编译了。注意要选择“Unicode Debug”和“'Unicode Release”方式

3          下载CELayoutEditor。我是用SVN接的,地址是 https://crayzedsgui.svn.sourceforge.net/svnroot/crayzedsgui/CELayoutEditor/trunk
            3.1    解压后打开工程,将Include和Lib路径改成自己的,其他的不用改
            3.2    建立环境变量:我的电脑\属性\高级\环境变量,然后在上面的用户变量区新建一个,变量名是CE_LAYOUT_EDITOR ,变量值是CELayoutEditor的解压目录。注意这步不能省
            3.3    终于可以开始编译了。。。

4          
            4.1    编译会出现一个错误,暂时我还没找到解决方法,仅仅是给错误的地方注释掉了。。。然后就编译成功了
            4.2    拷一些东西过来

TO $(CE_LAYOUT_EDITOR)\bin\debug (Debug configuration) 
- CEGUIBase_d.dll 
- CEGUIExpatParser_d.dll 
- CEGUIFalagardWRBase_d.dll 
- CEGUISILLYImageCodec_d.dll 
- CEGUITGAImageCodec_d.dll 
- OpenGLGUIRenderer_d.dll 
- SILLY_d.dll

TO $(CE_LAYOUT_EDITOR)\bin\release (Release configuration) 
- CEGUIBase.dll 
- CEGUIExpatParser.dll 
- CEGUIFalagardWRBase.dll 
- CEGUISILLYImageCodec.dll 
- CEGUITGAImageCodec.dll 
- OpenGLGUIRenderer.dll
- SILLY.dll

           4.3 SILLY.dll和SILLY_d.dll在依赖文件夹里

OK。。。到这里就完了,虽然这个工具的bug很多,不过不怕,编译它就是为了要改造它,呵呵。希望此文对你有所帮助

开启画面




运行界面


版权声明
:本篇为原创文章,允许转载,但转载时请务必以超链接形式标明文章的原始出处和作者信息。请尊重本人的劳动成果,谢谢!
小祥的BLOG http://xfxsworld.cnblogs.com

 

posted @ 2007-11-17 14:59 小祥 阅读(789) | 评论 (6)编辑

恢复CEGUI渲染状态

                                              恢复CEGUI渲染状态

刚刚接触CEGUI,在原D3D程序上画了一个小窗口后,发现渲染效果于以前的大相径庭,查看了源代码后发现,原来CEGUI在渲染后并没有恢复到默认的渲染状态,于是改之,再编译、运行、一切正常。。。

下面是恢复其渲染状态的代码,与初始化渲染状态函数initPerFrameStates()对应

 1 void  DirectX9Renderer::UnitFrameStates()
 2 {
 3      //  setup vertex stream
 4
 5     d_device -> SetFVF(NULL);
 6
 7      //  set device states
 8     d_device -> SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
 9      d_device -> SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
10     d_device -> SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
11     d_device -> SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
12     d_device -> SetRenderState(D3DRS_FOGENABLE, FALSE);
13     d_device -> SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
14
15
16      //  setup texture addressing settings/*
17     d_device -> SetSamplerState(  0 , D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
18      d_device -> SetSamplerState(  0 , D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
19
20      //  setup colour calculations
21     d_device -> SetTextureStageState( 0 , D3DTSS_COLORARG1, D3DTA_TEXTURE);
22      d_device -> SetTextureStageState( 0 , D3DTSS_COLORARG2, D3DTA_CURRENT);
23      d_device -> SetTextureStageState( 0 , D3DTSS_COLOROP, D3DTOP_SELECTARG1);
24
25      //  setup alpha calculations
26     d_device -> SetTextureStageState( 0 , D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
27      d_device -> SetTextureStageState( 0 , D3DTSS_ALPHAARG2, D3DTA_CURRENT);
28      d_device -> SetTextureStageState( 0 , D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
29
30      //  setup filtering
31     d_device -> SetSamplerState( 0 , D3DSAMP_MINFILTER, D3DTEXF_POINT);
32      d_device -> SetSamplerState( 0 , D3DSAMP_MAGFILTER, D3DTEXF_POINT);
33
34      //  disable texture stages we do not need.
35     d_device -> SetTextureStageState( 1 , D3DTSS_COLOROP, D3DTOP_DISABLE);
36
37      //  setup scene alpha blending
38     d_device -> SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
39      d_device -> SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
40      d_device -> SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
42 }

另外CEGUI有2种渲染模式:立即渲染和缓冲渲染。所以在这2种渲染模式结束后都需要调用上面的代码以恢复渲染状态

版权声明:本篇为原创文章,允许转载,但转载时请务必以超链接形式标明文章的原始出处和作者信息。请尊重本人的劳动成果,谢谢!
小祥的BLOG http://xfxsworld.cnblogs.com

posted @ 2007-11-17 14:56 小祥 阅读(242) | 评论 (0)编辑

D3D Alpha混合

                                                                                     D3D Alpha混合

Direct3D计算Alpha混合的颜色公式:
Color = ( SrcRGB * SrcK ) + ( DestRGB * DestK )
 
SrcRGB表示源颜色值,即将要绘制的颜色值。SrcK表示源混合系数,通常赋值为D3DBLEND_SRCALPHA,即当前绘制像素的Alpha值
DestRGB表示目标颜色值,即当前缓冲区中的颜色值。DestK表示目标系数,通常赋值为D3DBLEND_INVSRCALPHA,即1减去当前绘制像素的Alpha值
 
则Direct3D计算Alpha混合的颜色公式可表示为
Color = ( SrcRGB * SrcAlpha ) + ( DestRGB * ( 1 - SrcAlpha ) )
 
混合步骤
 
Alpha混合默认关闭,所以首先需要开启它
Device -> SetRenderState( D3DRS_ALPHABLENDENABLE, true );
 
然后设置Alpha混合系数
Device -> SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
Device -> SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
 
还要指定资源(材质或Alpha通道)
Alpha通道可用DirectX自带的纹理工具创建,为DDS文件
 
DEMO演示图片:
alphablend.jpg
 

版权声明:本篇为原创文章,允许转载,但转载时请务必以超链接形式标明文章的原始出处和作者信息。请尊重本人的劳动成果,谢谢!
小祥的BLOG http://xfxsworld.cnblogs.com

posted @ 2007-11-17 14:54 小祥 阅读(342) | 评论 (0)编辑

地形高度算法小结

                                               地形高度算法小结

地形一般是用网格再从高度图里读取每个顶点的高度来生成。若进一步,想实现摄像机在地形上行走的效果,就需要算出地形上任意一点的高度。总结了3个方法:
 
一:
《Introduction to 3D Game Programming With Directx 9.0》这本书里介绍的,利用向量来算。
11.jpg
 

 1 float  Terrain::GetHeight( float  x,  float  z)
 2 {
 3    //  Translate on xz-plane by the transformation that takes
 4   //  the terrain START point to the origin.
 5  x  =  (( float )_width  /   2.0f +  x;
 6  z  =  (( float )_depth  /   2.0f -  z;
 7   //  Scale down by the transformation that makes the 
 8   //  cellspacing equal to one.  This is given by 
 9   //  1 / cellspacing since; cellspacing * 1 / cellspacing = 1.
10  x  /=  ( float )_CellSpacing;
11  z  /=  ( float )_CellSpacing;
12   //  From now on, we will interpret our positive z-axis as
13   //  going in the 'down' direction, rather than the 'up' direction.
14   //  This allows to extract the row and column simply by 'flooring'
15   //  x and z:
16   float  col  =  ::floorf(x);
17   float  row  =  ::floorf(z);
18   //  get the heights of the quad we're in:
19   //  
20      //   A   B
21      //   *---*
22      //     | / |
23      //   *---*  
24      //   C   D
25   float  A  =  GetHeightMapEntry(row,   col);
26   float  B  =  GetHeightMapEntry(row,   col + 1 );
27   float  C  =  GetHeightMapEntry(row + 1 , col);
28   float  D  =  GetHeightMapEntry(row + 1 , col + 1 );
29   //
30   //  Find the triangle we are in:
31   //
32   //  Translate by the transformation that takes the upper-left
33   //  corner of the cell we are in to the origin.  Recall that our 
34   //  cellspacing was nomalized to 1.  Thus we have a unit square
35   //  at the origin of our +x -> 'right' and +z -> 'down' system.
36   float  dx  =  x  -  col;
37   float  dz  =  z  -  row;
38   //  Note the below compuations of u and v are unneccessary, we really
39   //  only need the height, but we compute the entire vector to emphasis
40   //  the books discussion.
41   float  height  =   0.0f ;
42   if (dz  <   1.0f   -  dx)   //  upper triangle ABC
43   {
44    float  uy  =  B  -  A;  //  A->B
45    float  vy  =  C  -  A;  //  A->C<