Windows 下msvc2010编译 NSIS 2.46
按照 NSIS 帮助文档的说法,要构建 NSIS 首先安装 Python 和 SCons。
1、从NSIS 官方网站 http://sourceforge.net/projects/nsis/files/ 下载 NSIS 2.46 的源代码 zip 包,解压到 C:\nsis-2.46-src,此次我直接从NSIS的repository checkout源码包。
2、从Python 官方网站 http://www.python.org/getit/ 下载 Python 2.7.6 安装到 C:\dev\Python27,将此路径添加到系统 PATH 环境变量。
3、从SCons 官方网站 http://sourceforge.net/projects/scons/files/ 下载 scons-2.3.0-setup 安装(自动默认到 Python 的子目录),将路径 C:\dev\Python27\Scripts 添加到系统 PATH 环境变量。
4、由于 NSIS 的 Menu 采用 wxWidgets 编写,因此去 wxWidgets 官方网站 http://www.wxwidgets.org/downloads/ 下载 wxWidgets-2.9.5.zip解压到 C:\dev\wxWidgets。新建环境变量 WXWIN,值为该路径 C:\dev\wxWidgets
5、在进行下一步之前,确定你的系统有MSVC的编译环境,我此次编译使用的是msvc010。
6、进入 NSIS 的 Menu 目录,C:\nsis-2.46-src\Contrib\NSIS Menu\wx,Visual Studio Command Prompt (2010)命令行下启动 wxbuild.bat,先编译 wxWidgets程序,等待 10 分钟。
7、进入 NSIS 目录 C:\nsis-2.46-src\Contrib\NSIS Menu\,修改 SConscript文件,以下是这个文件的patch
— C:/SConscript 星期三 四月 10 17:13:20 2013
+++ C:/nsis-2.46-src/Contrib/NSIS Menu/SConscript 星期四 十一月 28 18:29:30 2013
@@ -13,17 +13,27 @@ resources = Split(“””
rc = ‘nsismenu/nsismenu.rc’
libs = Split(“””
– advapi32
– comctl32
+ kernel32
+ user32
gdi32
+ comdlg32
+ winspool
+ winmm
shell32
– user32
+ comctl32
+ ole32
+ oleaut32
+ uuid
+ rpcrt4
+ advapi32
+ wsock32
+ wininet
“””)
if env[‘UNICODE’]:
– libs.append([“wxbase29u”, “wxmsw29u_core”, “wxmsw29u_html”])
+ libs.append([“wxbase29u”, “wxmsw29u_core”, “wxmsw29u_html”, “wxpng”, “wxjpeg”, “wxtiff”, “wxzlib”, “wxexpat”])
else:
– libs.append([“wxbase29”, “wxmsw29_core”, “wxmsw29_html”])
+ libs.append([“wxbase29”, “wxmsw29_core”, “wxmsw29_html”, “wxpng”, “wxjpeg”, “wxtiff”, “wxzlib”, “wxexpat”])
html = Split(“””
#Menu/index.html
8、回到 NSIS 主目录 C:\nsis-2.46-src,撰写一个编译脚本build.bat:
set ZLIB_W32=c:\dev\zlib-1.2.8
set WXWIN=C:\devfolder\wxWidgets-2.9.5
set MSVS_VERSION = 10.0
scons NSIS_CONFIG_LOG=yes NSIS_MAX_STRLEN=8192 dist-zip
pause
Visual Studio Command Prompt (2010)命令行下启动build.bat:等待 5-10 分钟。在 NSIS主目录下会躺着一个 zip 包,就是它了。
SourceAFIS
SourceAFIS
Fingerprint recognition toolkit
Authenticate users with their fingerprints.
Search large databases of fingerprints quickly.
Embed fingerprint recognition in your application.
Study & research the science of fingerprints.
Chrome使用 http://www.google.com/ncr 做默认搜索大招
首先查到google.com的ip地址203.208.46.180。
然后,进入选项 -管理搜索引擎,添加一个新的Google搜索地址:http://203.208.46.180/search?hl=zh_cn&q=%s 如果想用英语界面,就用h ttp://203.208.46.180/search?q=%s
最后,把这个搜索设成默认搜索引擎。
Windows 添加右键菜单
1.对任意扩展名的文件添加资源管理器右键菜单的注册表方法
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\shell\notepad]
[HKEY_CLASSES_ROOT\*\shell\notepad\command]
@=”\”C:\\Windows\\System32\\notepad.exe\” %1″
2.对任意文件夹添加资源管理器右键菜单的注册表方法
仅需注意Folder表示文件夹, 有些为Directory, *表示所有文件.
3. IE的右键菜单
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt
vs2010编译MySQL 5.5.15并生成安装包
1.准备:
a. 下载mysql-5.5.15.zip http://dev.mysql.com/downloads/mysql/
b. 下载较新版本的CMake并安装,我使用的是CMake 2.85
c. 下载bison并安装,注意安装目录不能有空格.http://gnuwin32.sourceforge.net/packages/bison.htm
2. 解压mysql-5.5.15.zip,修改sql\sql_locale.cc,因该文件中有很多vs2010不能识别的多国语言的字符编码,实验发现及时改变成任何utf8类型的编码格式仍不行,后发现vs2010有对UTF8兼容的bug,故只好修改该文件,让它暂时只支持en_US一种语言。
3.将bison安装目录中bin\m4.exe拷贝到mysql-5.5.15\sql目录,使其与sql_yacc.cc在同一个目录下,此问题为bison调用m4.exe时无法找到其路径,应该是cmakelist.txt的问题。期间可能还会发生m4.exe找不到GNUWin32包的动态库的问题,去GNUWin32找相应的库下载并拷贝到mysql-5.5.15\sql下。
4.cmake gui 下配置生成工程文件MySQL.sln.
5.控制台下进到MySQL.sln所在目录执行:
"c:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" MySQL.sln /build RelWithDebInfo "c:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" MySQL.sln /build RelWithDebInfo /project initial_database "c:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" MySQL.sln /build RelWithDebInfo /project PACKAGE "c:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" MySQL.sln /clean
至此如果没有异议会在MySQL.sln所在目录会找到_CPack_Packages\win32\ZIP,会有一个mysql-5.5.15-win32.zip和完整的安装包mysql-5.5.15-win32,这就是最终生成的可以使用的安装包了。
********************************************************************
updated for mysql-5.5.16:
同样是m4.exe生成sql_yacc.cc的问题,在此版本中,需要copy m4.exe及其依赖的动态库与MySQL.sln保持在同一个目录下。–2011年09月22日
关于封装文件内存映射
1. c++/MFC 封装好的文件内存映射类 摘自http://hi.baidu.com/andywangcn/blog/item/90c16635e32b70bbd1a2d359.html
首先介绍内存映射文件操作——函数的用法以及先后执行顺序
// 第一步:创建文件
HANDLE hFile = CreateFileForMapping(_T(“MyMemFile.dat”), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(hFile != 0);
// 第二步:创建内存映射文件对象
HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024,
_T(“MyMapppingFile”));
ASSERT(hMapFile != 0);
// 第三步:获取内存映射文件对象视图
BYTE* pData = (BYTE*)MapViewOfFile(hMapFile, FILE_MAP_WRITE, 0, 0, 1024);
ASSERT(pData != NULL);
// 对 pData 指针进行读写操作
// ……
// 第四步:取消内存视图映射
UnmapViewOfFile(pData);
// 第五步:关闭内存映射对象句柄
CloseHandle(hMapFile);
// 第六步:关闭文件句柄
CloseHandle(hFile);
=================================header file=========================================
// MapFile.h : header file
#if !defined(AFX_MAPFILE_H__D067E5C8_C4D7_4569_A6BB_9D651FB3F396__INCLUDED_)
#define AFX_MAPFILE_H__D067E5C8_C4D7_4569_A6BB_9D651FB3F396__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// CMapFile class 使用说明:
// 1 一个对象,打开文件映射之后,立即处理,处理完之后才能再用它打开另一个文件映射
class CMapFile
{
public:
CMapFile(); // protected constructor used by dynamic creation
virtual ~CMapFile();
// Attributes
public:
PVOID m_pvFile;
BOOL m_bSmooth; // 为TRUE表示不阻塞,即不弹出任何对话框
private:
HANDLE hFileMap;
// Operations
public:
BOOL OpenMapFile(CString sFile);
// Implementation
public:
BOOL CloseMapFile();
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_MAPFILE_H__D067E5C8_C4D7_4569_A6BB_9D651FB3F396__INCLUDED_)
=========================================implementation file===========================
// MapFile.cpp : implementation file
#include “stdafx.h”
#include “MapFile.h”
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMapFile
CMapFile::CMapFile()
{
m_pvFile=NULL;
m_bSmooth=FALSE;
}
CMapFile::~CMapFile()
{
if(NULL!=m_pvFile)
{
UnmapViewOfFile(m_pvFile);
m_pvFile=NULL;
}
}
/////////////////////////////////////////////////////////////////////////////
// CMapFile operation handlers
// 空文件会弹出 map could not be opened,在里面敲一个回车,就好了
BOOL CMapFile::OpenMapFile(CString sFile)
{
if(NULL!=m_pvFile)
{
UnmapViewOfFile(m_pvFile);//在当前应用程序的内存地址空间解除对一个文件映射对象的映射
m_pvFile=NULL;
}
// 1:创建或打开一个文件内核对象: Open the file for reading and writing.
// 这里CreateFile函数的参数,路径无论是否有空格都不能有引号,有引号的时候会出错。
HANDLE hFile = CreateFile(sFile,GENERIC_READ|GENERIC_WRITE,
0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
// 由于hFile即使为INVALID_HANDLE_VALUE,下面的CreateFileMapping仍然可以正常运行,
// 所以这里一定要对hFile进行检查!
if (hFile == INVALID_HANDLE_VALUE)
{
if(!m_bSmooth) // 为TRUE表示不阻塞,即不弹出任何对话框
{
AfxMessageBox(_T(“File[“)+sFile+_T(“]could not be opened. Maybe it’s not exist.”));
}
return FALSE;
}
// 2:创建一个文件映射内核对象
DWORD dwFileSize = GetFileSize(hFile, NULL);
//获取文件大小=文件长度+(WCHAR)”(ssume the whole file can be mapped)
// Create the file-mapping object. The file-mapping object is 1 character
// bigger than the file size so that a zero character can be placed at the
// end of the file to terminate the string (file). Because I don’t yet know
// if the file contains ANSI or Unicode characters, I assume worst case
// and add the size of a WCHAR instead of CHAR.
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0,
dwFileSize+1, /*sizeof(WCHAR)==2,sizeof(char)==1*/
//因为我们要在文件的末尾加上一个字符串的结束符”, 当我们将这个文件映射到内存中时,
//我们就可以像操作字符串一样地来操作文件了。
//如果该文件小于设定的大小,本函数将扩展该文件的大小,使磁盘上的文件变大。这样当以后将该文件作为内存映射 文件使用时,物理存储器就已经存在了。
NULL // 这个文件映射对象的名字用于与其他进程共享该对象,这里我们还用不到。
);
if (hFileMap == NULL)
{
if(!m_bSmooth)
{
AfxMessageBox(_T(“File[“)+sFile+_T(“] map could not be opened. May be it’s empty.”));
}
return FALSE;
}
// 3:将文件数据映射到进程的地址空间:
// 当创建了一个文件映射对象之后,仍然必须让系统为文件的数据保留一个地址空间区域,
// 并将文件的数据作为映射到该区域的物理存储器进行提交。
m_pvFile = MapViewOfFile(hFileMap,/*FILE_MAP_WRITE*/FILE_MAP_READ,0,0,0);//获取映射文件在内存中的首地址
//CloseHandle(hFileMap);// 关闭内存映射对象句柄
if (m_pvFile == NULL)
{
if(!m_bSmooth)
{
AfxMessageBox(_T(“Could not map view of file[“)+sFile+_T(“].”));
}
return FALSE;
}
// 4:既然我们通过pvFile得到了映象视图的起始地址,那么可以对视图做一些操作了:
// ANSI版本:
// PSTR pchANSI = (PSTR) m_pvFile;
// UNICODE版本:
// PWSTR pchUnicode = (PWSTR) pvFile;如char* sz=(char*)m_pvFile;sz[dwFileSize/sizeof(char)]=”;
// 5:从进程的地址空间中撤销文件数据的映象:
// UnmapViewOfFile(m_pvFile); // 封装为类,映象要在其他地方使用所以这句话要去掉
// 6:关闭文件映射对象和文件对象:
// CloseHandle(hFileMap);
//我们改变了文件的长度,因此要重新设置文件的结束符以删除留在文件尾部的多余内容(比如删除我们先前加到文件末尾的”字符)
SetFilePointer(hFile,dwFileSize, NULL, FILE_BEGIN);
SetEndOfFile(hFile); //设定当前文件指针所在处为文件结束处.该处后面的内容将被删除
CloseHandle(hFile);
return TRUE;
}
BOOL CMapFile::CloseMapFile()
{
if(NULL!=m_pvFile)
{
//取消内存视图映射
if(UnmapViewOfFile(m_pvFile))
{
m_pvFile=NULL;
CloseHandle(hFileMap);// 关闭内存映射对象句柄
return TRUE;
}
return FALSE;
}
return TRUE;
}
2. ShareMem
#include <afxmt.h>
struct RWStr
{
int readIndex;
int writeIndex;
};
class AFX_EXT_CLASS ShareMem
{
public:
ShareMem(int bufNum, int bufSize, char* bufName,int* isSuc); //构造函数
~ShareMem();
public:
BOOL Read(LPVOID data); //读数据
BOOL Write(LPVOID data); //写数据
BOOL Release();
private:
char* name;
char* indexName;
int size;
int num;
struct RWStr rw;
CSemaphore* writeSemap;
CSemaphore* readSemap;
CMutex* rMutex;
CMutex* wMutex;
CMutex* pMutex;
HANDLE buffer;
HANDLE pointer;
LPVOID pBegin;
LPVOID pIndex;
};
#include "stdafx.h"
#include <iostream>
#include <string>
#include "ShareMem.h"
using namespace std;
/*
int bufNum //要申请缓存的数目
int bufSize //每个缓存的大小,在写入和读取数据时,以bufSize为单位
char* bufName //缓存的名称,每个缓存必须有唯一的名称
int* isSuc //输出参数,构造成功(*isSuc)为0,失败则为-1
*/
ShareMem::ShareMem(int bufNum, int bufSize, char* bufName,int* isSuc)
{
*isSuc=0;
if(num<1024*100)
num=1024*100;
else
num=bufNum;
size=bufSize;
name=bufName;
char temp[100];
strcpy(temp,name);
strcat(temp,"pointer");
indexName=temp;
//cout<<indexName<<endl;
//步骤一:调用CreateFile()函数,以适当的方式创建或打开一个文件核心对象;
//步骤二:把CreateFile()函数返回的文件句柄作为参数,传给CreateFileMapping()函数,
// 由 CreateFileMapping()函数创建一个文件映射核心对象的适当属性;
pointer=CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(rw),indexName);
if(pointer==NULL)
{
*isSuc=-1;
return;
}
//步骤三:创建了文件映射核心对象后,调用MapViewOfFile()函数,告诉系统把文件的哪一部分映射到进程的地址空间中,以何种方式映射;
if(!(GetLastError()==ERROR_ALREADY_EXISTS))
{
pIndex=MapViewOfFile(pointer, FILE_MAP_WRITE,0,0,0);
ZeroMemory(pIndex,sizeof(rw));
}
else
pIndex=MapViewOfFile(pointer, FILE_MAP_WRITE,0,0,0);
memcpy((void*)&rw,pIndex,sizeof(rw));
//read互斥体
char* tempName=NULL;
strcpy(temp,name);
strcat(temp,"rMutex");
tempName=temp;
rMutex=new CMutex(FALSE,tempName);
//writ互斥体
strcpy(temp,name);
strcat(temp,"wMutex");
tempName=temp;
wMutex=new CMutex(FALSE,tempName);
//
strcpy(temp,name);
strcat(temp,"pMutex");
tempName=temp;
pMutex=new CMutex(FALSE,tempName);
//writ信号量
strcpy(temp,name);
strcat(temp,"write");
tempName=temp;
writeSemap=new CSemaphore(num,num,tempName);
//read信号量
strcpy(temp,name);
strcat(temp,"read");
tempName=temp;
readSemap=new CSemaphore(0,num,tempName);
if(rMutex==NULL||wMutex==NULL||pMutex==NULL||writeSemap==NULL||readSemap==NULL)
{
*isSuc=-1;
return;
}
buffer = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,num*size,name);
if(buffer==NULL)
{
*isSuc=-1;
//cout<<"Create Mapping Faile!"<<endl;
return;
}
//步骤四:利用MapViewOfFile()函数返回的指针来使用文件数据;
pBegin=MapViewOfFile(buffer, FILE_MAP_WRITE,0,0,0);
}
ShareMem::~ShareMem()
{
//步骤五:操作完毕后,调用UnmapViewOfFile()函数,告诉系统 撤销对文件映射核心对象的映射;
BOOL unBuffer = UnmapViewOfFile( pBegin );
BOOL unIndex = UnmapViewOfFile( pIndex );
if(!(unIndex && unBuffer))
{
//cout<<"Unmap Faile!"<<endl;
}
}
/*
功能说明:释放资源
返回值:
操作成功则返回TRUE
操作失败返回FALSE
*/
BOOL ShareMem::Release()
{
HANDLE temp1 = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,num*size,name);
HANDLE temp2 = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(rw),indexName);
if(temp1!=NULL&&temp2!=NULL)
{
//步骤六:使用CloseHandle()函数关闭文件映射核心对象;
//步骤七:使用CloseHandle()函数关闭文件核心对象;
CloseHandle(temp1);
CloseHandle(temp2);
}
else
return FALSE;
if(rMutex!=NULL&&wMutex!=NULL&&writeSemap!=NULL&&readSemap!=NULL)
{
delete rMutex;
delete wMutex;
delete writeSemap;
delete readSemap;
}
return TRUE;
}
/*
功能说明:从缓存读取数据,以bufSize为单位
参数说明:
LPVOID data //所要得到数据的指针,读取具体数据结构时可先进行强制转换后再传入参数
返回值:
操作成功则返回TRUE,data保存所读取的数据
操作失败返回FALSE,data无定义
*/
BOOL ShareMem::Read(LPVOID data)
{
readSemap->Lock();
pMutex->Lock();
memcpy((void*)&rw,pIndex,sizeof(rw));
LPVOID read=(LPVOID)((char*)pBegin+rw.readIndex*size);
memcpy(data,read,size);
//cout<<"Geted id: "<<*(int*)data<<" Read index:"<<rw.readIndex<<endl;
rMutex->Lock();
rw.readIndex=(rw.readIndex+1)%num;
rMutex->Unlock();
memcpy(pIndex,(void*)&rw,sizeof(rw));
pMutex->Unlock();
writeSemap->Unlock();
if(read==NULL)
{
//cout<<"Failed to Map(read): "<<GetLastError()<<endl;
return FALSE;
}
return TRUE;
}
/*
功能说明:向缓存写入数据,以bufSize为单位
参数说明:
LPVOID data //所要写入数据的指针,写入具体数据结构时可先进行强制转换后再传入参数
返回值:
操作成功则返回TRUE,data为写入的数据
操作失败返回FALSE,data无定义
*/
BOOL ShareMem::Write(LPVOID data)
{
writeSemap->Lock();
pMutex->Lock();
memcpy((void*)&rw,pIndex,sizeof(rw));
LPVOID write=(LPVOID)((char*)pBegin+rw.writeIndex*size);
memcpy(write,data,size);
//cout<<"Thread id:"<<GetCurrentThreadId()<<" Write index:"<<rw.writeIndex<<endl;
wMutex->Lock();
rw.writeIndex=(rw.writeIndex+1)%num;
wMutex->Unlock();
memcpy(pIndex,(void*)&rw,sizeof(rw));
pMutex->Unlock();
readSemap->Unlock();
if(write==NULL)
{
//cout<<"Failed to Map(write): "<<GetLastError()<<endl;
return FALSE;
}
return TRUE;
}
3.CMemMapFile 1.53 http://www.naughter.com/memmap.html
4.Shared Memory--win32 programming http://comsci.liu.edu/~murali/win32/Main.htm
KEIL下增加STC 51单片机,不影响选择原有器件
1、在Keil/C51/INC下新建目录”STC”文件夹, 把所有STC单片机的头文件拷贝到”STC”目录下。
3、下载的用于keil下的STC器件数据库更名为STC.CDB并拷贝到Keil/UV2或者UV3或UV4目录下。
4、打开Keil文件夹下的TOOLS.ini文件。
5、在[UV2]下键入CDB0=”UV2\STC.CDB”(“STC”)或者CDB0=”UV4\STC.CDB”(“STC”)或者CDB0=”UV4\STC.CDB”(“STC”),需要单独一行。
6、保存TOOLS.ini文件,添加完毕。
7、启动Keil,打开Options for Target ‘…’的Device页,
选择Database中"STC",就可以选择STC单片机了,而且不会影响原来数据库的使用,这相当于另外增加了一个器件选型的数据库文件。
映射文件Pointer.map
值得注意的是,如果你只在VC Project Setting对话框中打开Generate mapfile,还是不够的。因为你一定还要输出程序代码地址和源代码行号!!这非常的重要!
要得到这些信息,请在Project Options对话框中键入"/mapinfo:lines /mapinfo:exports"。请你一定要养成这种习惯!因为这不是默认设置。
我们得到的map文件大致如下,我删节了大多数输出:
Pointer
(应用程序名)
Timestamp is 3d4407a7 (Sun Jul 28 23:03:03 2002)
(时间戳)
Preferred load address is 00400000
(最佳装载基地址。非常重要的一个数据。不过一般都是这个数。)
Address Publics by Value Rva+Base Lib:Object
0001:00000250 _main 00401250 f Pointer.obj
(_main的虚地址)
Line numbers for .\Debug\Pointer.obj(E:\ Pointer\Pointer.cpp) segment .text
12 0001:00000250 14 0001:00000268 15 0001:0000026f 16 0001:00000276
18 0001:0000027f 20 0001:00000291 23 0001:000002a4 24 0001:000002a6
(这就是我们的Pointer.cpp所对应的程序代码行号和相对虚拟地址的对应表)
我们可以从中看到,最佳装载基地址是0x00400000,_main的虚地址是0x00401250,而0001:00000250又是什么意思呢?
0x00000250就是_main的相对虚拟地址(RVA)。
0x00010000就是PE头文件的大小,一般都是这个数。
所以虚地址就是这么算出来的:
0x00401250 = 0x00400000 + 0x00010000 + 0x00000250
虚地址 = 最佳装载基地址 + PE头文件的大小 + 相对虚拟地址(RVA)
通过_main的RVA的计算,我们也就知道了怎么计算崩溃地址0x00401279的RVA,是0x00000279,对吧?
然后,在这个MAP映射文件的"Line numbers for .\Debug\Pointer.obj(E:\ Pointer\Pointer.cpp) segment .text"这个行号段中查找这个地址。如你所看到的,只有16行对应的00000276和18行对应的0000027F,没有00000279呀?
没有17行的对应关系,说明17行是空行。
那么00000279就一定是16行的了!这样你不用看那个程序员的代码,就可以通知他:崩溃发生在你的Pointer.cpp的第16行了!很酷吧!
利用pexports和MSVC,制作在MSVC下能链接的MingW编译的动态库
以下为BAT文件的内容
rem 必须要先安装MinGW
rem 通过下面的命令,来设置环境变量,并且只在本过程中有效
set path==%path%;C:\MinGW\bin\
pause;
rem 执行下面的命令来编译,参考:http://blog.csdn.net/shania_wang/archive/2010/10/26/5966492.aspx
mkdir dll_bin
gcc -O2 -shared -Wall -Wl,–export-all-symbols -mpreferred-stack-boundary=2 -march=i386 -falign-functions=0 -fno-strict-aliasing -DTCC_TARGET_PE -DLIBTCC -o dll_bin\libtcc.dll tcc.c
pause
rem 从dll导出 def文件, 参考:http://www.emmestech.com/software/pexports-0.43/download_pexports.html
pexports dll_bin\libtcc.dll > dll_bin\libtcc.def
pause
rem 调用VC的工程制造引出库,MinGW 的 dlltool制造出来的在release版本下无法引入
call “C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT”
LIB /def:dll_bin\libtcc.def /machine:i386 /out:dll_bin\libtcc_imp.lib
pause
摘自:http://www.cppblog.com/woaidongmao/archive/2011/06/30/149816.html
C函数中,可变长参数的传递?
C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为:
int printf( const char* format, …);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点“…”做参数占位符),实际调用时可以有以下的形式:
printf("%d",i);
printf("%s",s);
printf("the number is %d ,string is:%s", i, s);
一个简单的可变参数的C函数
先看例子程序。该函数至少有一个整数参数,其后占位符…,表示后面参数的个数不定。在这个例子里,所有的输入参数必须都是整数,函数的功能只是打印所有参数的值。函数代码如下:
//示例代码1:可变参数函数的使用
#include "stdio.h"
#include "stdarg.h"
void simple_va_fun(int start, …)
{
va_list arg_ptr;
int nArgValue =start;
int nArgCout="0"; //可变参数的数目
va_start(arg_ptr,start); //以固定参数的地址为起点确定变参的内存起始地址。
do
{
++nArgCout;
printf("the %d th arg: %d",nArgCout,nArgValue); //输出各参数的值
nArgValue = va_arg(arg_ptr,int); //得到下一个可变参数的值
} while(nArgValue != -1);
return;
}
int main(int argc, char* argv[])
{
simple_va_fun(100,-1);
simple_va_fun(100,200,-1);
return 0;
}
下面解释一下这些代码。从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:
(1)由于在程序中将用到以下这些宏:
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
va在这里是variable-argument(可变参数)的意思。
这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件。
(2)函数里首先定义一个va_list型的变量,这里是arg_ptr,这个变量是存储参数地址的指针.因为得到参数的地址之后,再结合参数的类型,才能得到参数的值。
(3)然后用va_start宏初始化(2)中定义的变量arg_ptr,这个宏的第二个参数是可变参数列表的前一个参数,即最后一个固定参数。
(4)然后依次用va_arg宏使arg_ptr返回可变参数的地址,得到这个地址之后,结合参数的类型,就可以得到参数的值。
(5)设定结束条件,这里的条件就是判断参数值是否为-1。注意被调的函数在调用时是不知道可变参数的正确数目的,程序员必须自己在代码中指明结束条件。至于为什么它不会知道参数的数目,在看完这几个宏的内部实现机制后,自然就会明白。
第二篇
C语言之可变参数问题
C语言中有一种长度不确定的参数,形如:"…",它主要用在参数个数不确定的函数中,我们最容易想到的例子是printf函数。
原型:
int printf( const char *format [, argument]… );
使用例:
printf("Enjoy yourself everyday!\n");
printf("The value is %d!\n", value);
这种可变参数可以说是C语言一个比较难理解的部分,这里会由几个问题引发一些对它的分析。
注意:在C++中有函数重载(overload)可以用来区别不同函数参数的调用,但它还是不能表示任意数量的函数参数。
问题:printf的实现
请问,如何自己实现printf函数,如何处理其中的可变参数问题? 答案与分析:
在标准C语言中定义了一个头文件专门用来对付可变参数列表,它包含了一组宏,和一个va_list的typedef声明。一个典型实现如下:
typedef char* va_list;
#define va_start(list) list = (char*)&va_alist
#define va_end(list)
#define va_arg(list, mode)\
((mode*) (list += sizeof(mode)))[-1]
自己实现printf:
#include
int printf(char* format, …)
{
va_list ap;
va_start(ap, format);
int n = vprintf(format, ap);
va_end(ap);
return n;
}
问题:运行时才确定的参数
有没有办法写一个函数,这个函数参数的具体形式可以在运行时才确定?
答案与分析:
目前没有"正规"的解决办法,不过独门偏方倒是有一个,因为有一个函数已经给我们做出了这方面的榜样,那就是main(),它的原型是:
int main(int argc,char *argv[]);
函数的参数是argc和argv。
深入想一下,"只能在运行时确定参数形式",也就是说你没办法从声明中看到所接受的参数,也即是参数根本就没有固定的形式。常用的办法是你可以通过定义一个void *类型的参数,用它来指向实际的参数区,然后在函数中根据根据需要任意解释它们的含义。这就是main函数中argv的含义,而argc,则用来表明实际的参数个数,这为我们使用提供了进一步的方便,当然,这个参数不是必需的。
虽然参数没有固定形式,但我们必然要在函数中解析参数的意义,因此,理所当然会有一个要求,就是调用者和被调者之间要对参数区内容的格式,大小,有效性等所有方面达成一致,否则南辕北辙各说各话就惨了。
问题:可变长参数的传递
有时候,需要编写一个函数,将它的可变长参数直接传递给另外的函数,请问,这个要求能否实现?
答案与分析:
目前,你尚无办法直接做到这一点,但是我们可以迂回前进,首先,我们定义被调用函数的参数为va_list类型,同时在调用函数中将可变长参数列表转换为va_list,这样就可以进行变长参数的传递了。看如下所示:
void subfunc (char *fmt, va_list argp)
{
…
arg = va_arg (fmt, argp); /* 从argp中逐一取出所要的参数 */
…
}
void mainfunc (char *fmt, …)
{
va_list argp;
va_start (argp, fmt); /* 将可变长参数转换为va_list */
subfunc (fmt, argp); /* 将va_list传递给子函数 */
va_end (argp);
…
}
问题:可变长参数中类型为函数指针
我想使用va_arg来提取出可变长参数中类型为函数指针的参数,结果却总是不正确,为什么?
答案与分析:
这个与va_arg的实现有关。一个简单的、演示版的va_arg实现如下:
#define va_arg(argp, type) \
(*(type *)(((argp) += sizeof(type)) – sizeof(type)))
其中,argp的类型是char *。
如果你想用va_arg从可变参数列表中提取出函数指针类型的参数,例如
int (*)(),则va_arg(argp, int (*)())被扩展为:
(*(int (*)() *)(((argp) += sizeof (int (*)())) -sizeof (int (*)())))
显然,(int (*)() *)是无意义的。
解决这个问题的办法是将函数指针用typedef定义成一个独立的数据类型,例如:
typedef int (*funcptr)();
这时候再调用va_arg(argp, funcptr)将被扩展为:
(* (funcptr *)(((argp) += sizeof (funcptr)) – sizeof (funcptr)))
这样就可以通过编译检查了。
问题:可变长参数的获取
有这样一个具有可变长参数的函数,其中有下列代码用来获取类型为float的实参:
va_arg (argp, float);
这样做可以吗?
答案与分析:
不可以。在可变长参数中,应用的是"加宽"原则。也就是float类型被扩展成double;char, short被扩展成int。因此,如果你要去可变长参数列表中原来为float类型的参数,需要用va_arg(argp, double)。对char和short类型的则用va_arg(argp, int)。
问题:定义可变长参数的一个限制
为什么我的编译器不允许我定义如下的函数,也就是可变长参数,但是没有任何的固定参数?
int f (…)
{
…
}
答案与分析:
不可以。这是ANSI C 所要求的,你至少得定义一个固定参数。
这个参数将被传递给va_start(),然后用va_arg()和va_end()来确定所有实际调用时可变长参数的类型和值。