(Visual Studio)Part 4: Setting up Code for the Debugger
In the fourth part of the series on debugging in Visual Studio, we will discuss how to set up your code for the debugger.
Debug break
A direct call to a debug break can be used to stop the execution of a program. Why is this different than a breakpoint and why would you use it? Generally you would use it if you are not going to launch the debug session from Visual Studio, you are going to launch it after the fact. So if we want to try this how is a debug break called in c
Calling __debugbreak() can be used to generate a software breakpoint. This function is designed to be portable between platforms. You could also use the Win32 version DebugBreak() but it is not portable. So once you call __debugbreak() what happens from there? An exception is thrown and your program stops execution. This gives the programmer the opportunity to use Visual Studio to 'attach' to the process that is running the executable.
To attach to a running process and use the Visual Studio debugger, go to Tools and then select 'Attach to Process'. If you're running locally there is no need to change any of the dialog settings. However, if you are trying to run a debugger session remotely, you will need to change the Transport to Remote, and then the Qualifier to the remote PC you are trying to reach. Either way, once you have the process list simply find the process that you want Visual Studio to attach to and click OK. Visual Studio will then attach to this process and your debug session will begin.
A practical example of using __debugbreak() would be to debug a service. You cannot launch a service directly from Visual Studio; it must be launched with the Service Control Manager. So there is no way to directly debug it from the IDE. You must 'attach' to the process in order to debug it. The problem with debugging a service using this method is that you cannot debug the service from launch; it will simply launch and begin its run state.
Assertions
An assertion in code is a test of a condition and if the condition fails the execution of the program halts. It is designed to only be when the program is complied as debug. When an assert() is called it is accompanied by a message indicating which expression failed, the line number and module where the assertion took place. In a Windows application the assertion will come in a message box. In a console application it will go to the console screen.
In the following example, you can see that the string is tested for NULL. If the char array being passed in is NULL, without the assert in the debug build the memory doesn't exist and an exception would be thrown. Instead, the assert would fail and tell the programmer that something went wrong with this function.
void TestFunc(char *pszName)
{
assert(pszName==NULL);
strcpy(pszName,"Tom Smith");
}
The assert macros _ASSERT, _ASSERTE are used during debugging and are only available if the _DEBUG macro is defined. _ASSERTE is used to properly print out any Unicode characters that are in the expression passed to the macro. When either of these assert, that amounts to the report that the assertion failed, it lists the condition that failed and the source code module and line number it failed on. By default, in a Windows application the assertion will come in a message box and in a console application it will go to the console screen.
#define _DEBUG
void TestFunc(char *pszName)
{
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);
_ASSERT(pszName==NULL);
strcpy(pszName,"Tom Smith");
}
It may appear on the surface that assert() and _ASSERT, _ASSERTE do the same thing. As far as behavior of halting execution on a failed Boolean condition they do. However the macros are only called when _DEBUG macro is defined. The other difference is that you can define the desired behavior of the assert through a series of calls to _CrtSetReportMode(). This is because the macros invoke a _CrtDbgReportW report message and this mechanism is designed to allow a programmer to track progress of a debug build. You can define the macro assert to report to a debug file, to only use the console window instead of a message box, etc.
Exception Handling
Exception handling is essentially a way for the programmer to do error handling on parts of code that may fail under certain conditions without halting the program. For example, if a character array is access without having its memory defined it would normally throw a first chance exception and the operating system would halt the program. With exception handling this is caught and can be handled by the programmer.
Here is an example of exception handling in code. Assume that the *pszName character array passed into this function is NULL. You can see from this example that the program doesn't actually halt, what happens is when the strcpy call is 'tried', the exception is thrown by the operating system but is 'caught' by the catch block. So instead of halting the program, the program reports the error and returns FALSE instead of TRUE from this function. The syntax catch(.) means that ANY exception will be caught and handled.
BOOL TestFunc(char *pszName)
{
try
{
strcpy(pszName,"Tom Smith");
}
catch(.)
{
printf("ERROR: Cannot copy string into pszName!
");
return FALSE;
}
return TRUE;
}
void main()
{
char *pszMyName = NULL;
if(TestFunc(pszMyName)==FALSE)
{
printf("ERROR: Forgot to instantiate array, aborting");
exit(-1);
}
exit(0);
}
Catching any exception is ok for very quick generic stuff but is not all that useful in tracking what the specific issue might be. How do we narrow down the exception handling to specific items? This is easily handled by using a throw and defining the specific exception in the catch block we need. Here the exception of char * is the only thing that will be checked for an exception. When the check for NULL is made on the variable and we force the exception with the throw call, we can tell the exception handler exactly what caused the problem.
BOOL TestFunc(char *pszName)
{
try
{
if(pszName == NULL)
{
throw "ERROR: pszName parameter was NULL!";
}
strcpy(pszName,"Tom Smith");
}
catch(char * error)
{
printf("ERROR: %s, pszName == NULL, cannot copy name into array
",error);
return FALSE;
}
return TRUE;
}
void main()
{
char *pszMyName = NULL;
if(TestFunc(pszMyName)==FALSE)
{
printf("ERROR: Forgot to instantiate array, aborting");
exit(-1);
}
exit(0);
}
The items that are put into the catch function are not limited to just variable types. A class can be passed that is used to hold exception data. Below is an example of this type of class exception. You can see that there are now two types of exceptions that are throw using the class, we pass the enumeration into the constructor in order for the class to track what the exception is.
enum ExceptionErrors
{
ArrayIsNull=1,
ArrayNotBigEnough
};
class CMyException
{
int m_iErrorCode;
public:
void ShowDescription()
{
if(m_iErrorCode & ArrayIsNull)
{
printf("ERROR: Array variable was NULL
");
}
if(m_iErrorCode & ArrayNotBigEnough)
{
printf("ERROR: Array size to small
");
}
}
CMyException(int iError){m_iErrorCode = iError;}
};
BOOL TestFunc(char *pszName, int iNameLength)
{
try
{
if(pszName == NULL)
{
throw CMyException(ArrayIsNull);
}
if(iNameLength
Miscellaneous Debugging Calls
There are other calls that can be made to perform some types of debugging. The reasons to use these types vary but generally these are all called to halt execution of a running program due to some error condition.
Original website: http://www.cprogramming.com/tutorial/visual_studio_debugging_code_setup.html
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Unity에서 Visual Studio가 열리지 않는 경우 VSCode로 대체하는 방법Unity에서는 Edit -> Preferences -> External Tools에서 사용할 편집기를 지정할 수 있습니다. 일반적으로 Visual Studio 2019 등이 기본 편집기로 설정되었지만 Unity 2...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.