经过两天时间的奋斗,一个php的简单网站终于做完了,可以重新回到这里,专心分析我的Qt源代码了,感觉很好。
今天主要的研究对象就是在Windows系统环境下的事件派送器(QEventDispatcherWin32),该类继承自QAbstractEventDispatcher,提供在Windows平台下的事件机制封装。
#elif defined(Q_OS_WIN)
eventDispatcher = new QEventDispatcherWin32(q);
这里通过new的方式创建了对象,其构造函数实现如下:
QEventDispatcherWin32::QEventDispatcherWin32(QObject *parent)
: QAbstractEventDispatcher(*new QEventDispatcherWin32Private, parent)
{
}
创建了QEventDispatcherWin32Private的引用,该类构造函数如下:
QEventDispatcherWin32Private::QEventDispatcherWin32Private()
: threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0)
{
resolveTimerAPI();
wakeUpNotifier.setHandle(CreateEvent(0, FALSE, FALSE, 0));
if (!wakeUpNotifier.handle())
qWarning("QEventDispatcher: Creating QEventDispatcherWin32Private wakeup event failed");
}
第一行的:threadId(GetCurrentThreadId()),调用了Windows系统的API函数,获取当前线程的ID,接下来是:resolveTimerAPI()根据函数名猜测是解析时间API函数,进入该函数看一下:
static void resolveTimerAPI()
{
static bool triedResolve = false;
if (!triedResolve) {
#ifndef QT_NO_THREAD
QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
if (triedResolve)
return;
#endif
triedResolve = true;
#if !defined(Q_OS_WINCE)
qtimeSetEvent = (ptimeSetEvent)QLibrary::resolve(QLatin1String("winmm"), "timeSetEvent");
qtimeKillEvent = (ptimeKillEvent)QLibrary::resolve(QLatin1String("winmm"), "timeKillEvent");
#else
qtimeSetEvent = (ptimeSetEvent)QLibrary::resolve(QLatin1String("Mmtimer"), "timeSetEvent");
qtimeKillEvent = (ptimeKillEvent)QLibrary::resolve(QLatin1String("Mmtimer"), "timeKillEvent");
#endif
}
}首先,该函数用局部的静态变量防止了该类被重复调用,接下来的部分,分为了多线程支持和非多线程支持情况下的不同实现方式。在多线程方式下,QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
通过互斥锁的形式,对互斥池的全局对象进行加锁,在获取全局实例的时候,我进行了跟踪,发现&triedResolve 的作用在这里:
int index = int((quintptr(address) >> (sizeof(address) >> 1)) % mutexes.count());
这样做的有什么用,不是很清楚,在这里,我猜测是通过这样的方式产生一个随机的访问索引位置,之后将该位置的互斥对象返回,如果该位置没有,则创建一个这样的对象。
接下来的ptimeSetEvent和qtimeKillEvent是两个WindowsAPI函数的函数指针,对QLibrary:: resolve()函数返回的指针进行转换。
对QLibrary跟踪之后,找到了,该类的主要实现部分:
bool QLibraryPrivate::load_sys();
该函数首先对文件名进行了处理:
QString path = fi.path();
QString name = fi.fileName();
if (path == QLatin1String(".") && !fileName.startsWith(path))
path.clear();
else
path += QLatin1Char('/');
接下来有两行注释:
// The first filename we want to attempt to load is the filename as the callee specified.
// Thus, the first attempt we do must be with an empty prefix and empty suffix.
之后是在Windows下的实现代码
if (!fullVersion.isEmpty()) {
suffixes << QString::fromLatin1(".so.%1").arg(fullVersion);
} else {
suffixes << QLatin1String(".so");
}
看样子应该是生成了需要加载的库文件,根据版本号的不同,加载的文件后缀不同。
#if defined(Q_OS_AIX) // Not sure if any other platform actually support this thing.
if (loadHints & QLibrary::LoadArchiveMemberHint) {
dlFlags |= RTLD_MEMBER;
}
#endif
看了上面的注释应该能看出来,原来Qt也有很多需要完善的地方哦,只是之前对它了解的太少了。
接下来的这段代码生成了需要加载的库文件名称,并尝试查看该库文件是否存在。
bool retry = true;
for(int prefix = 0; retry && !pHnd && prefix < prefixes.size(); prefix++) {
for(int suffix = 0; retry && !pHnd && suffix < suffixes.size(); suffix++) {
if (!prefixes.at(prefix).isEmpty() && name.startsWith(prefixes.at(prefix)))
continue;
if (!suffixes.at(suffix).isEmpty() && name.endsWith(suffixes.at(suffix)))
continue;
if (loadHints & QLibrary::LoadArchiveMemberHint) {
attempt = name;
int lparen = attempt.indexOf(QLatin1Char('('));
if (lparen == -1)
lparen = attempt.count();
attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix));
} else {
attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix);
}
#if defined(QT_HPUX_LD)
pHnd = (void*)shl_load(QFile::encodeName(attempt), dlFlags, 0);
#else
pHnd = dlopen(QFile::encodeName(attempt), dlFlags);
#endif
#if defined(Q_OS_SYMBIAN)
// Never try again in symbian, dlopen already handles the library search logic,
// and there is only one possible suffix.
retry = false;
#else
if (!pHnd && fileName.startsWith(QLatin1Char('/')) && QFile::exists(attempt)) {
// We only want to continue if dlopen failed due to that the shared library did not exist.
// However, we are only able to apply this check for absolute filenames (since they are
// not influenced by the content of LD_LIBRARY_PATH, /etc/ld.so.cache, DT_RPATH etc...)
// This is all because dlerror is flawed and cannot tell us the reason why it failed.
retry = false;
}
#endif
}
}
在这段代码中,有一个函数dlopen(),google了一下,发现了它的解释:
gain access to an executable object file,我的理解是增加了访问对可执行文件的访问。是一个C语言的函数,#include <dlfcn.h >,成功时返回一个句柄,随后可以调用dlsym ()函数(obtain the address of a symbol from a dlopen object),和dlclose函数对文件进行操作。
这样,Windows平台下的库文件加载过程已经完成了,接下来就是看QAbstractEventDispatcher类的构造函数了。这里调用了QAbstractEventDispatcherPrivate的init方法。
Q_Q(QAbstractEventDispatcher);
if (threadData->eventDispatcher != 0) {
qWarning("QAbstractEventDispatcher: An event dispatcher has already been created for this thread");
} else {
threadData->eventDispatcher = q;
}
这里很简单了,就是简单的做了下赋值操作。
到这里,QCoreApplication的构造过程就分析完成了。
好了,今天就到这里,明天继续了。
2009年10月16日星期五 23:58