在Windows下,Flex可以通过ActiveX方式通过ExterneralInterface接口与Qt通信,但是在Linux下,ExternalInterface方法就无效了,只能通过Socket方式与Qt通信。进行通信的两端Flex与Qt,需要绑定指定的端口,监听端口发送的数据(字符串),双方做出相应的响应。下面展示一个Qt与Flex进行文字交互的实例,如下图所示。
在QT端,首先在主窗口中调用其成员函数initSocket()初始化QTcpSocket连接,调用openSwf()函数初始化窗口部件QWebView,并将Flash嵌入到webView中。
void Widget::initSocket() { process= new QProcess(this); m_server = new QTcpServer(this); if(true == (m_server->listen(QHostAddress::LocalHost,5150))) { qDebug() << "bandsuccess!"; } connect(m_server, SIGNAL(newConnection()),this, SLOT(FlexConnected())); }
在initSocket()函数中,调用QTcpServer::listen()方法绑定IP和端口地址,并且将newConnection()信号与flash连接完毕FlexConnected()槽相关联。在FlexConnected()槽中,将readyRead()信号与处理TCP数据流接收和处理readPendingDatagrams()槽相关联。在Flex端,首先构造一个QTSocket对象,然后在其构造函数中初始化IP地址、端口和Flex提供的Socket对象,最后调用InitConnect()函数初始化连接。
public function InitConnect():void { socket.connect(server,port); //监听套接字连接是否关闭 socket.addEventListener(Event.CLOSE,closeHandler); //监听是否连接上服务器 socket.addEventListener(Event.CONNECT,connectHandler); socket.addEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler); socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR,securityErrorHandler); }下面代码是整个工程的代码,包含QT部分和Flex部分。
QT部分代码:
// widget.h class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); void openSwf(); void processTheDatagram(QByteArray array); private slots: void on_pushButton_clicked(); void readPendingDatagrams(); void FlexConnected(); private: void initSocket(); private: Ui::Widget *ui; QTcpServer *m_server; QTcpSocket *m_socket; QProcess* process; QString tempDir; QString tempFilename; }; //widget.cpp #define TEMP_DIR "/temp/" #define TEMP_FILE "QTFLEX/QTFLEX.html" #define REPLACE_STRING "{#AAA}" Widget::Widget(QWidget *parent) : QWidget(parent),ui(new Ui::Widget) { ui->setupUi(this); tempDir = qApp->applicationDirPath() + TEMP_DIR; qDebug()<< "tempDir: " << tempDir; tempFilename = tempDir + TEMP_FILE; qDebug() << "tempFilename" << tempFilename; initSocket(); openSwf(); } Widget::~Widget() { m_server->close(); delete m_server; delete process; } void Widget::initSocket() { process = new QProcess(this); m_server = new QTcpServer(this); if(true == (m_server->listen(QHostAddress::LocalHost,5150))) { qDebug() << "band success!"; } connect(m_server, SIGNAL(newConnection()),this, SLOT(FlexConnected())); } //Flex连接上事件 void Widget::FlexConnected() { qDebug() << "flash连接成功!"; m_socket = m_server->nextPendingConnection(); connect(m_socket,SIGNAL(readyRead()),this,SLOT(readPendingDatagrams())); } QByteArray CreatePolicy() { char szPolicy[1024] = ""; memset(szPolicy,'\0',1024); strcat(szPolicy,"<?xml version='1.0'?>"); strcat(szPolicy,"<cross-domain-policy>"); strcat(szPolicy,"<site-control permitted-cross-domain-policies='all'/>"); strcat(szPolicy,"<allow-access-from domain='*'to-ports='5150'/>"); strcat(szPolicy,"</cross-domain-policy>"); return QByteArray(szPolicy); } void Widget::readPendingDatagrams() { qDebug() << "准备接受!"; QByteArray datagram; datagram = m_socket->readAll(); m_socket->seek(0); if(0 ==(strcmp("<policy-file-request/>",datagram.constData() ))) { //首先发送安全协商报文,然后关闭socket,等待Flex重新连接 qDebug() << "安全认证完毕!"; QByteArray strPolicy = CreatePolicy(); m_socket->write(strPolicy); m_socket->close(); } else { processTheDatagram(datagram); } } void Widget::openSwf() { //设置webkit参数 QWebSettings *set = QWebSettings::globalSettings(); set->setAttribute(QWebSettings::JavascriptEnabled,true); set->setAttribute(QWebSettings::PluginsEnabled,true); set->setAttribute(QWebSettings::JavascriptCanOpenWindows,true); set->setAttribute(QWebSettings::DeveloperExtrasEnabled,true); QString dirPath = qApp->applicationDirPath(); dirPath = dirPath.left(dirPath.lastIndexOf('/')); qDebug() << dirPath; ui->webView->load(QUrl(/blog_article/tempFilename/index.html)); ui->webView->show(); } void Widget::processTheDatagram(QByteArrayarr) { qDebug()<<"comming"; QString s=""; s.append(arr); ui->textEdit->append(s); } void Widget::on_pushButton_clicked() { QString str=ui->lineEdit->text(); if(str==NULL || str=="") { return; } else { str="QT:"+str; ui->textEdit->append(str); m_socket->write(str.toAscii()); m_socket->close(); } }
Flex部分代码,在Flex3下编译通过:
//QTFLEX.mxml <mx:Script> <![CDATA[ import mx.controls.Alert; public var socket:QTSocket; public function initApp():void { socket=new QTSocket(); socket.InitConnect(); } public function sendMsg():void { var str:String=msg.text; if(str==null ||str=="") { Alert.show("发送内容不能为空!"); return; } str="FLEX:"+str; AddMsgToArea(str); socket.MsgSendHandler(str); } public functionAddMsgToArea(str:String):void { varstr1:String=ta.text; ta.text=str1+"\n"+str; } ]]> </mx:Script> <mx:Button x="188" y="264"label="发送" height="26" width="62"click="sendMsg();"/> <mx:TextInput x="10"y="264" width="170" height="26"id="msg"/> <mx:TextArea x="4"y="6" height="250" width="247"id="ta"/> </mx:Application> // QTSocket.as package { importflash.net.Socket; importflash.events.*; importflash.utils.ByteArray; importmx.controls.Alert; publicclass QTSocket { publicvar server:String; publicvar port:int=5150; publicvar socket:Socket=null; publicfunction QTSocket() { server="localhost"; port=5150; socket=newSocket(); } //Socket初始化 publicfunction InitConnect():void { socket.connect(server,port); //监听套接字连接是否关闭 socket.addEventListener(Event.CLOSE,closeHandler); //监听是否连接上服务器 socket.addEventListener(Event.CONNECT,connectHandler); socket.addEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler); socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); } //发送消息 publicfunction MsgSendHandler(msg:String):void { if(socket != null) { varbytes:ByteArray=new ByteArray(); //新建一个ByteArray存放数据 bytes.writeUTFBytes(msg); //写入数据,以utf-8格式传数据避免中文乱码 socket.writeBytes(bytes); socket.flush(); } } //Socket接到消息 pu
1.msdn 在debug模式下的内存结构
(曾今在gaia引擎里看过类似的自己模仿实现的内存管理结构)
typedef struct _CrtMemBlockHeader
{
// Pointer to the block allocated just before this one:
struct _CrtMemBlockHeader *pBlockHeaderNext;
// Pointer to the block allocated just after this one:
struct _CrtMemBlockHeader *pBlockHeaderPrev;
char *szFileName; // File name
int nLine; // Line number
size_t nDataSize; // Size of user block
int nBlockUse; // Type of block
long lRequest; // Allocation number
// Buffer just before (lower than) the user's memory:
unsigned char gap[nNoMansLandSize];
} _CrtMemBlockHeader;
/* In an actual memory block in the debug heap,
* this structure is followed by:
* unsigned char data[nDataSize];
* unsigned char anotherGap[nNoMansLandSize];//防止内存写越界
*/
The “NoMansLand” buffers on either side of the user data area of the block are currently 4 bytes in size, and are filled with a known byte value used by the debug heap routines to verify that the limits of the user’s memory block have not been overwritten. The debug heap also fills new memory blocks with a known value. If you elect to keep freed blocks in the heap’s linked list as explained below, these freed blocks are also filled with a known value. Currently, the actual byte values used are as follows:
NoMansLand (0xFD)
The “NoMansLand” buffers on either side of the memory used by an application are currently filled with 0xFD.
Freed blocks (0xDD)
The freed blocks kept unused in the debug heap’s linked list when the _CRTDBG_DELAY_FREE_MEM_DF flag is set are currently filled with 0xDD.
New objects (0xCD)
New objects are filled with 0xCD when they are allocated.
2._CrtDumpMemLeaks()
msdn说明
The _CrtDumpMemoryLeaks function determines whether a memory leak has occurred since the start of program execution. When a leak is found, the debug header information for all of the objects in the heap is dumped in a user-readable form. When _DEBUG is not defined, calls to _CrtDumpMemoryLeaks are removed during preprocessing.
_CrtDumpMemoryLeaks is frequently called at the end of program execution to verify that all memory allocated by the application has been freed. The function can be called automatically at program termination by turning on the _CRTDBG_LEAK_CHECK_DF bit field of the _crtDbgFlag flag using the _CrtSetDbgFlag function.
_CrtDumpMemoryLeaks calls _CrtMemCheckpoint to obtain the current state of the heap and then scans the state for blocks that have not been freed. When an unfreed block is encountered, _CrtDumpMemoryLeaks calls _CrtMemDumpAllObjectsSince to dump information for all of the objects allocated in the heap from the start of program execution.
By default, internal C run-time blocks (_CRT_BLOCK ) are not included in memory dump operations. The _CrtSetDbgFlag function can be used to turn on the _CRTDBG_CHECK_CRT_DF bit of _crtDbgFlag to include these blocks in the leak detection process.
For more information about heap state functions and the _CrtMemState structure, see the Heap State Reporting Functions . For information about how memory blocks are allocated, initialized, and managed in the debug version of the base heap, see Memory Management and the Debug Heap .
应用:
#include "stdafx.h"
#include <assert.h>
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif //此部分用于使_CrtDumpMemoryLeaks输出内存泄漏文件名和行号信息默认不会输出相关信息
void Exit
{
int i = _CrtDumpMemoryLeaks;
assert( i == 0);
}
int _tmain(int argc, _TCHAR* argv[])
{
atexit(Exit);
int* p = new int;
return 0;
}
不含红色部分输出:
Detected memory leaks!
Dumping objects ->
{112} normal block at 0x003AA770, 4 bytes long.
Data: < > 00 00 00 00
Object dump complete.
含红色部分输出:
Detected memory leaks!
Dumping objects ->
d:\code\consoletest\consoletest.cpp(21) : {112} client block at 0x003A38B0, subtype 0, 4 bytes long.
Data: < > 00 00 00 00
Object dump complete.
-------------------------------------------------------------------------------------------
1._CrtDumpMemoryLeaks
确定自程序开始执行以来是否发生过内存泄漏,如果发生过,则转储所有已分配对象。如果已使用 _CrtSetDumpClient 安装了挂钩函数,那么,_CrtDumpMemoryLeaks每次转储 _CLIENT_BLOCK 块时,都会调用应用程序所提供的挂钩函数。
CrtDumpMemoryLeaks()就是显示当前的内存泄漏。 注意是“当前”,也就是说当它执行时,所有未销毁的对象均会报内存泄漏。因此尽量让这条语句在程序的最后执行。它所反映的是检测到泄漏的地方。
一般用在MFC中比较准确,在InitInstance里面调用_CrtDumpMemoryLeaks
2.信息输出
Detected memory leaks!
Dumping objects ->
{52} normal block at 0x006D2498, 512 bytes long.
?Data: <??????????????? > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
{51} normal block at 0x006D2440, 24 bytes long.
?Data: < 4????????????? > 10 34 14 00 FF FF FF FF 00 00 00 00 00 00 00 00
Object dump complete.
3._CrtSetBreakAlloc
知道某个错误分配块的分配请求编号后,可以将该编号传递给 _CrtSetBreakAlloc 以创建一个断点
_CrtSetBreakAlloc(51);这样可以快速在{51}次内存泄漏处设上断点。
/*****************************************************************************************************/
最快速度找到内存泄漏
许式伟
2006年11月某日
内存管理是C++程序员的痛。我的《内存管理变革 》系列就是试图讨论更为有效的内存管理方式,以杜绝(或减少)内存泄漏,减轻C++程序员的负担。由于工作忙的缘故,这个系列目前未完,暂停。
这篇短文我想换个方式,讨论一下如何以最快的速度找到内存泄漏。
确认是否存在内存泄漏
我们知道,MFC程序如果检测到存在内存泄漏,退出程序的时候会在调试窗口提醒内存泄漏。例如:
class CMyApp : public CWinApp
{
public :
BOOL InitApplication()
{
int * leak = new int [ 10 ];
return TRUE;
}
};
产生的内存泄漏报告大体如下:
Detected memory leaks !
Dumping objects ->
c:\work\test.cpp( 186 ) : { 52 } normal block at 0x003C4410 , 40 bytes long .
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
这挺好。问题是,如果我们不喜欢MFC,那么难道就没有办法?或者自己做?
呵呵,这不需要。其实,MFC也没有自己做。内存泄漏检测的工作是VC++的C运行库做的。也就是说,只要你是VC++程序员,都可以很方便地检测内存泄漏。我们还是给个样例:
#include < crtdbg.h >
inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}
void main()
{
EnableMemLeakCheck();
int * leak = new int [ 10 ];
}
运行(提醒:不要按Ctrl+F5,按F5),你将发现,产生的内存泄漏报告与MFC类似,但有细节不同,如下:
Detected memory leaks !
Dumping objects ->
{ 52 } normal block at 0x003C4410 , 40 bytes long .
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
为什么呢?看下面。
定位内存泄漏由于哪一句话引起的
你已经发现程序存在内存泄漏。现在的问题是,我们要找泄漏的根源。
一般我们首先确定内存泄漏是由于哪一句引起。在MFC中,这一点很容易。你双击内存泄漏报告的文字,或者在Debug窗口中按F4,IDE就帮你定位到申请该内存块的地方。对于上例,也就是这一句:
int* leak = new int[10];
这多多少少对你分析内存泄漏有点帮助。特别地,如果这个new仅对应一条delete(或者你把delete漏写),这将很快可以确认
原文:http://www.debuginfo.com/articles/easywindbg.html
译者:arhat
时间:2006年4月13日
关键词:CDB WinDbg
导言
你 钟情什么样的调试器?如果你问我这个问题,我会回答是“Visual Studio + WinDbg”。我比较喜欢Visual Studio那朴实无 华且易操作的接口,更喜欢它能迅速把我需要的信息以可视的形式展示出来。但遗憾的是,Visual Studio调试器无法获取某些信息。例如,假设我想 知道哪个线程正在占用特殊的临界区?或者是哪个函数占用了大部分的栈空间?不用担心,有WinDbg呢。它的命令能回答这些问题,以及调试过程中出现的其 它有趣的问题。甚至不退出Visual Studio,WinDbg就可以附上目标应用程序??谢谢WinDbg支持入侵模式的调试(本文后面会详细讨
论),我们可以把Visual Studio GUI和WinDbg的命令行结合起来使用。
唯一的问题是WinDbg不太好用。需要花些时间适应 它的用户界面,而掌握它的命令则要花更多的时间。但是假设你现在就需要它,马上用它调试紧急的问题?有什么快速简便的方法吗?当然。WinDbg的小弟 CDB,功能和WinDbg差不多;因为它是基于命令行的,所以用起来更简单一些。在这篇文章里,我将把CDB作为Visual Studio调试器的补 充,介绍怎样使用CDB。在这篇文章里,你将会看到怎样配置CDB,怎样用它解决实际的问题。另外,我还会提供一些批处理文件,它们可以隐藏CDB命令行 接口的大部分复杂性,也让你少打几个字。
安装与配置
安装
当然,在使用CDB前,必须先安装并配置它。WinDbg和CDB是Debugging Tools for Windows 的一部分,可以从这里 下
载。安装很简单,你可以用默认设置安装,除非你准备用WinDbg SDK开发应用程序。(如果你准备用SDK,需要选择定制安装,并启用SDK安装;推 荐你把它安装在不包含空格的目录名的目录中)。安装完成后,安装目录里将包含所有必需的文件,包括WinDbg(windbg.exe)和 CDB(cdb.exe)。
调试工具也支持“xcopy”类型的安装。也就是说,在一台机器上安装后,如果你想在其它的机器上使用,不用再安装,直接把已经安装的目录直接拷过去就行了。
符号文件服务器路径
如 果不能访问操作系统DLL的最新的符号文件,有些重要的WinDbg命令将不能正常工作。在以往,我们可以从微软的FTP服务器上下载巨大的符号文件包, 然后从中找出需要的符号文件。这非常浪费时间,而且在操作系统更新或升级后,符号文件就过时了(因此也就变得毫无用处)。幸运的是,现在有更简便的方法来 获得符号文件??符号文件服务器。WinDbg和Visual Studio都支持这个方法,在需要时直接从微软维护的服务上下载最新的符号文件。有了符 号文件服务器,我们再也不用下载整个符号文件包了(那实在是太大了),因为调试器知道需要用到哪个DLLs,所以直接下载单个符号文件就行了。如果符号文
件在操作系统更新或升级以后过时了,调试器会注意到这种情形,并再次下载必需的符号文件。
为了使符号文件服务器起作用,我们应该让调试器知道符号文件服务器的路径。最简单的方法是在_NT_SYMBOL_PATH环境变量里指定符号文件服务器的路径。可以用如下的路径:
"srv*c:\symbolcache*http://msdl.microsoft.com/download/symbols"
(c:\symbolcache目录将被用来保存从符号文件服务器下载下来的符号文件;当然,你可以用任何有效的本地或网络路径)。例如:
set _NT_SYMBOL_PATH=srv*c:\symbols*http://msdl.microsoft.com/download/symbols
在你设置_NT_SYMBOL_PATH环境变量之后,就可以使用符号文件服务器了。关于符号文件服务器的更多信息,相关设置,以及可能会用到的排除故障的小技巧,可以从WinDbg的文档中找到(Debuggers | Symbols section)。
如果你需要从一台需登录的代理服务器后访问符号文件服务器。参见本篇文章中CDB and proxy servers部分,以了解更多信息。
CDB 命令行基础介绍
启动调试会话
当我们使用新的调试器时,第一个问题通常是:怎样开始调试会话呢?像大多数调试器一样,CDB允许我们调试应用程序的新实例,或者附上一个已经运行的过程。启动新实例就象下面一样简单:
cdb c:\myapp.exe
如果我们想附上已经运行的过程,可能会用上下列某个选项:
----------------------------------------------------------------------------------------------------------------------
选项 描述 例子
----------------------------------------------------------------------------------------------------------------------
-p Pid 这个选项允许CDB附上指定进程ID的进程。可以用任务管理器或类似的工具得到进程ID。 cdb -p 1034
----------------------------------------------------------------------------------------------------------------------
-pn ExeName 这个选项允许CDB用指定的可执行文件名(.exe)附上进程。这个选项比“-p Pid”更
方便,因为我们通常知道执行的程序名,不必在任务管理器中寻找进程的ID。但是如果
多个进程使用同一个名字(CDB将报错),就不能用这个选项了。 cdb -pn myapp.exe
----------------------------------------------------------------------------------------------------------------------
-psn ServiceName 这个选项允许CDB附上指定服务的进程。例如,假如你想附上Windows Management
Instrumentation服务,应该用WinMgmt作为服务名。 cdb -psn MyService
----------------------------------------------------------------------------------------------------------------------
CDB也可以分析故障转储。用-z选项打开故障转储:
cdb -z DumpFile
例如:
cdb -z c:\myapp.dmp
结束调试会话
启动新的调试会话后,CDB会显示它自己的命令行提示符。你可以在这个提示符下执行CDB支持的任何命令。
'q'命令结束调试会话并退出CDB:
0:000> q
quit:
>
警告:当你结束调试会话,退出CDB时,操作系统也将终止被调试的程序。如果你想退出CDB并保持被调试程序,可以用.detach命令(Windows XP或更新的操作系统才支持),或者用非入侵的模式(下面讨论)。
运行命令