本文记录了vs调试程序的一些thicks,持续更新。
参考链接:https://msdn.microsoft.com/zh-cn/library/9s7c9wdw.aspx
1. 在调试可执行程序或dll时,有时需要依赖于其他release版本的库,因此不能使用debug调试,因此使用如下步骤。
a) 工程项目上右键 -> 属性
b) c++ -> 常规 -〉调试信息格式 选 “程序数据库(/Zi)或(/ZI)”, 注意:如果是库的话,只能(Zi)
c) c++ -> 优化 -〉优化 选 “禁止(/Od)”,如果这项不这样选,有些变量值不能在调试中看到。
d) 连接器 -〉调试 -〉生成调试信息选 “是(/DEBUG)”,pdb文件生成。
Debug 和 Release 的真正秘密,在于一组编译选项。下面列出了分别针对二者的选项(当然除此之外还有其他一些,如/Fd /Fo,但区别并不重要,通常他们也不会引起 Release 版错误,在此不讨论)
Debug版本
参数 含义
/MDd /MLd 或 /MTd 使用 Debug runtime library (调试版本的运行时刻函数库)
/Od 关闭优化开关
/D "_DEBUG" 相当于 #define _DEBUG,打开编译调试代码开关 (主要针对assert函数)
/ZI 创建 Edit and continue(编辑继续)数据库,这样在调试过程中如果修改了源代码不需重新编译
/GZ 可以帮助捕获内存错误
初始化内存和变量。包括用 0xCC 初始化所有自动变量,0xCD ( Cleared Data ) 初始化堆中分配的内存(即动态分配的内存,例如 new ),0xDD ( Dead Data ) 填充已被释放的堆内存(例如 delete ),0xFD( deFencde Data ) 初始化受保护的内存(debug 版在动态分配内存的前后加入保护内存以防止越界访问),其中括号中的词是微软建议的助记词。这样做的好处是这些值都很大,作为指针是不可能的(而且 32 位系统中指针很少是奇数值,在有些系统中奇数的指针会产生运行时错误),作为数值也很少遇到,而且这些值也很容易辨认,因此这很有利于在 Debug 版中发现 Release 版才会遇到的错误。要特别注意的是,很多人认为编译器会用0来初始化变量,这是错误的(而且这样很不利于查找错误)。 |
通过函数指针调用函数时,会通过检查栈指针验证函数调用的匹配性。(防止原形不匹配) |
函数返回前检查栈指针,确认未被修改。(防止越界访问和原形不匹配,与第二项合在一起可大致模拟帧指针省略 FPO )通常 /GZ 选项会造成 Debug 版出错而 Release 版正常的现象,因为 Release 版中未初始化的变量是随机的,这有可能使指针指向一个有效地址而掩盖了非法访问。除此之外,/Gm/GF等选项造成错误的情况比较少,而且他们的效果显而易见,比较容易发现 |
/Gm 打开最小化重链接开关, 减少链接时间
Release版本
参数 含义
/MD /ML 或 /MT 使用发布版本的运行时刻函数库
/O1 或 /O2 优化开关,使程序最小或最快
/D "NDEBUG" 关闭条件编译调试代码开关 (即不编译assert函数)
/GF 合并重复的字符串, 并将字符串常量放到只读内存, 防止被修改
实际上,Debug 和 Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。
2. 工作路径。
在vs调试c++项目的时候,当前工作路径为.vcxproj所在路径,尽管exe文件默认不与.vcxproj在同一路径下。
3. 并行编译
a) 在“调试”中选取某项目的“属性”。
b) 在“属性”->“配置属性”->“C/C++”->“常规”中将“多处理器编译”打开。
c) 在“属性”->“配置属性”->“C/C++”->“代码生成”中将“启用最小重新生成”关闭。
d) 在“调试”中选取某项目的“选项”。
e) 在“选项”->“项目和解决方案”->“VC++项目设置”中设置最大并发C++编译数。
4. Debug版本的警告信息。
在 Debug 版中使用 /W4 警告级别,这样可以从编译器获得最大限度的错误信息,比如 if( i =0 )就会引起 /W4 警告。不要忽略这些警告,通常这是你程序中的 Bug 引起的。但有时 /W4 会带来很多冗余信息,如未使用的函数参数警告,而很多消息处理函数都会忽略某些参数。我们可以用:
#progmawarning(disable: 4702)
//禁止
#progmawarning(default: 4702)
//重新允许来暂时禁止某个警告,或使用
#progmawarning(push, 3)
//设置警告级别为 /W3
#progmawarning(pop)
//重设为 /W4
来暂时改变警告级别,有时你可以只在认为可疑的那一部分代码使用 /W4。
5. C#调用c++编写的dll。
a) 参数传递:
//c++:HANDLE(void *) —- c#:System.IntPtr //c++:Byte(unsigned char) —- c#:System.Byte //c++:SHORT(short) —- c#:System.Int16 //c++:WORD(unsigned short) —- c#:System.UInt16 //c++:INT(int) —- c#:System.Int16 //c++:INT(int) —- c#:System.Int32 //c++:UINT(unsigned int) —- c#:System.UInt16 //c++:UINT(unsigned int) —- c#:System.UInt32 //c++:LONG(long) —- c#:System.Int32 //c++:ULONG(unsigned long) —- c#:System.UInt32 //c++:DWORD(unsigned long) —- c#:System.UInt32 //c++:DECIMAL —- c#:System.Decimal //c++:BOOL(long) —- c#:System.Boolean //c++:CHAR(char) —- c#:System.Char //c++:LPSTR(char *) —- c#:System.String //c++:LPWSTR(wchar_t *) —- c#:System.String //c++:LPCSTR(const char *) —- c#:System.String //c++:LPCWSTR(const wchar_t *) —- c#:System.String //c++:PCAHR(char *) —- c#:System.String //c++:BSTR —- c#:System.String //c++:FLOAT(float) —- c#:System.Single //c++:DOUBLE(double) —- c#:System.Double //c++:VARIANT —- c#:System.Object //c++:PBYTE(byte *) —- c#:System.Byte[] //c++:BSTR —- c#:StringBuilder //c++:LPCTSTR —- c#:StringBuilder //c++:LPCTSTR —- c#:string //c++:LPTSTR —- c#:[MarshalAs(UnmanagedType.LPTStr)] string //c++:LPTSTR 输出变量名 —- c#:StringBuilder 输出变量名 //c++:LPCWSTR —- c#:IntPtr //c++:BOOL —- c#:bool //c++:HMODULE —- c#:IntPtr //c++:HINSTANCE —- c#:IntPtr //c++:结构体 —- c#:public struct 结构体{}; //c++:结构体 **变量名 —- c#:out 变量名 //C#中提前申明一个结构体实例化后的变量名 //c++:结构体 &变量名 —- c#:ref 结构体 变量名 //c++:WORD —- c#:ushort //c++:DWORD —- c#:uint //c++:DWORD —- c#:int //c++:UCHAR —- c#:int //c++:UCHAR —- c#:byte //c++:UCHAR* —- c#:string //c++:UCHAR* —- c#:IntPtr //c++:GUID —- c#:Guid //c++:Handle —- c#:IntPtr //c++:HWND —- c#:IntPtr //c++:DWORD —- c#:int //c++:COLORREF —- c#:uint
//c++:unsigned char —- c#:byte //c++:unsigned char * —- c#:ref byte //c++:unsigned char * —- c#:[MarshalAs(UnmanagedType.LPArray)] byte[] //c++:unsigned char * —- c#:[MarshalAs(UnmanagedType.LPArray)] Intptr
//c++:unsigned char & —- c#:ref byte //c++:unsigned char 变量名 —- c#:byte 变量名 //c++:unsigned short 变量名 —- c#:ushort 变量名 //c++:unsigned int 变量名 —- c#:uint 变量名 //c++:unsigned long 变量名 —- c#:ulong 变量名
//c++:char 变量名 —- c#:byte 变量名 //C++中一个字符用一个字节表示,C#中一个字符用两个字节表示 //c++:char 数组名[数组大小] —- c#:MarshalAs(UnmanagedType.ByValTStr, SizeConst = 数组大小)] public string 数组名; ushort
//c++:char * —- c#:string //传入参数 //c++:char * —- c#:StringBuilder//传出参数 //c++:char *变量名 —- c#:ref string 变量名 //c++:char *输入变量名 —- c#:string 输入变量名 //c++:char *输出变量名 —- c#:[MarshalAs(UnmanagedType.LPStr)] StringBuilder 输出变量名 //c++:char ** —- c#:string //c++:char **变量名 —- c#:ref string 变量名 //c++:const char * —- c#:string //c++:char[] —- c#:string //c++:char 变量名[数组大小]—- c#:[MarshalAs(UnmanagedType.ByValTStr,SizeConst=数组大小)] public string 变量名; //c++:char* 变量名[] —– c#:string[] 或者通过List<string>.ToArray()
//c++:struct 结构体名 *变量名 —- c#:ref 结构体名 变量名 //c++:委托 变量名 —- c#:委托 变量名
//c++:int —- c#:int //c++:int —- c#:ref int //c++:int & —- c#:ref int //c++:int * —- c#:ref int //C#中调用前需定义int 变量名 = 0; //c++:*int —- c#:IntPtr //c++:int32 PIPTR * —- c#:int32[] //c++:float PIPTR * —- c#:float[]
//c++:double** 数组名 —- c#:ref double 数组名 //c++:double*[] 数组名 —- c#:ref double 数组名 //c++:long —- c#:int //c++:ulong —- c#:int
//c++:UINT8 * —- c#:ref byte //C#中调用前需定义byte 变量名 = new byte();
//c++:handle —- c#:IntPtr //c++:hwnd —- c#:IntPtr
//c++:void * —- c#:IntPtr //c++:void * user_obj_param —- c#:IntPtr user_obj_param //c++:void * 对象名称 —- c#:([MarshalAs(UnmanagedType.AsAny)]Object 对象名称 //c++:char, INT8, SBYTE, CHAR —- c#:System.SByte //c++:short, short int, INT16, SHORT —- c#:System.Int16 //c++:int, long, long int, INT32, LONG32, BOOL , INT —- c#:System.Int32 //c++:__int64, INT64, LONGLONG —- c#:System.Int64 //c++:unsigned char, UINT8, UCHAR , BYTE —- c#:System.Byte //c++:unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t —- c#:System.UInt16 //c++:unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT —- c#:System.UInt32 //c++:unsigned __int64, UINT64, DWORDLONG, ULONGLONG —- c#:System.UInt64 //c++:float,FLOAT —- c#:System.Single //c++:double, long double, DOUBLE —- c#:System.Double //Struct需要在C#里重新定义一个Struct //CallBack回调函数需要封装在一个委托里,delegate static extern int FunCallBack(string str);
//unsigned char** ppImage替换成IntPtr ppImage //int& nWidth替换成ref int nWidth //int*, int&, 则都可用 ref int 对应 //双针指类型参数,可以用 ref IntPtr //函数指针使用c++: typedef double (*fun_type1)(double); 对应 c#:public delegate double fun_type1(double); //char* 的操作c++: char*; 对应 c#:StringBuilder; //c#中使用指针:在需要使用指针的地方 加 unsafe
//unsigned char对应public byte /* * typedef void (*CALLBACKFUN1W)(wchar_t*, void* pArg); * typedef void (*CALLBACKFUN1A)(char*, void* pArg); * bool BIOPRINT_SENSOR_API dllFun1(CALLBACKFUN1 pCallbackFun1, void* pArg); * 调用方式為 * [UnmanagedFunctionPointer(CallingConvention.Cdecl)] * public delegate void CallbackFunc1([MarshalAs(UnmanagedType.LPWStr)] StringBuilder strName, IntPtr pArg); |
b) 托管内存与非托管内存的转换
示例:
在C/C++下的结构数据如下:
typedefstruct
{
char sLibName[ 256 ];
char sPathToLibrary[ 256 ];
INT32 iEntries;
INT32 iUsed;
UINT16 iSort;
UINT16 iVersion;
BOOLEAN fContainsSubDirectories;
INT32 iReserved;
}LIBHEADER;
它们在C#下面长得样子就变成这样:
[StructLayout(LayoutKind.Sequential)]
public struct LIBHEADER
{
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 256)]
public char[] sLibName;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 256)]
public char[] sPathToLibrary;
public Int32 iEntries;
public Int32 iUsed;
public UInt16 iSort;
public UInt16 iVersion;
public Boolean fContainsSubDirectories;
public Int32 iReserved;
}
然后写一个函数负责转换。
publicStructType ConverBytesToStructure(byte[] bytesBuffer)
{
// 检查长度。
if (bytesBuffer.Length !=Marshal.SizeOf(typeof(StructType)))
{
throw newArgumentException("bytesBuffer参数和structObject参数字节长度不一致。");
}
IntPtr bufferHandler =Marshal.AllocHGlobal(bytesBuffer.Length);
for (int index = 0; index <bytesBuffer.Length; index++)
{
Marshal.WriteByte(bufferHandler, index,bytesBuffer[index]);
}
StructType structObject =(StructType)Marshal.PtrToStructure(bufferHandler, typeof(StructType));
Marshal.FreeHGlobal(bufferHandler);
return structObject;
}
然后我们的函数用例是这样:
FileStream file =File.OpenRead(@"D:/Jagged Alliance 2 Gold/INSTALL.LOG");
byte[] buffer = newbyte[Marshal.SizeOf(typeof(LIBHEADER))];
file.Read(buffer, 0, buffer.Length);
LIBHEADER testValue = CommonTools.ConverBytesToStructure(buffer);
string libName = newstring(testValue.sLibName);
stringpathToLibrary= new string(testValue.sPathToLibrary);
c) Sta问题。
6. MDA调试。
1) contextSwitchDeadlock
非托管dll执行时间过长,便会触发该异常,关闭即可。