Just Let It Flow

December 16, 2010

7 Questionably Useful Things Visual Studio Can Do

Filed under: Windows — adeyblue @ 10:31 pm

Everybody likes to find out new stuff about tools they use. Fortunately, one of our frequently used tools has lots of undocumented and rarely used switches and behaviour that makes it a great candidate for fun discoveries of questionable value. So without further ado, here are seven things that the Visual Studio C++ compiler lets you do, presented in a rough order of potential usefulness.

Using keywords as identifiers

It’s possible to use reserved and key words (those that get coloured blue) as variable names and class names in your programs by wrapping them in __identifier(). The linked MSDN page says that the clr switch is required, but it isn’t, only their example requires it. It doesn’t really serve any purposes except for consuming third-party .net libraries that use C++ keywords in their variables or interfaces.

#include <cstdio>
 
int main()
{
    int __identifier(int) = 8; // int named int, only compiles under c++
    printf("%d\n", __identifier(int));
}

Knowing whether a symbol exist

Visual studio allows you to check the compiler’s internal symbol table for types, typedefs, macro, and functions and to do something if what you’re looking for is or isn’t found. It should be noted that even though the constructs use braces they do not introduce a new scope, anything done inside the braces exists at the previous scope level.

__if_exists(CreateWindow)
{
#pragma message("Included windows.h") // won't show
}
 
#include <windows.h>
 
__if_not_exists(ProcessBasicInformation) // these also require c++
{
    // didn't have definition for NtQueryInformationProcess info level
    // define it now
    static const UINT ProcessBasicInformation = 0;
    ULONG myGlobalLength = 0;
}
 
int main()
{
    PEB peb = {0};
    NtQueryInformationProcess(
        GetCurrentProcess(),
        ProcessBasicInformation, // can use this now knowing it will be defined
        &peb,
        sizeof(peb),
        &myGlobalLength
    );
}

Redefine basic operations

You know when you learned that when you define operator overloads in C++, at least one of the arguments has to be a user defined type? Well, not anymore. Visual Studio allows you to redefine any of the language’s basic operators, turning your super smart compiler into one that’ll fail first year maths.

// compile with the /d1nonUDToperators switch, you can add this to the additional options in the IDE
 
#include <iostream>
int operator+(int a, int b)
{
    return a - b;
}
 
/*
// beware of code like this, it will recurse indefinitely
// there's no way to undo this or get back to the proper behaviour
int operator-(int a, int b)
{
    return a - b;
}
*/
 
int main()
{
    int a = 7, b = 3;
    std::cout << a << '+' << b << '=' << a + b << '\n';
}

Print macro values

Sometimes it’s nice to know what a macro expands to, but not especially nice to pour over the entire output of the preprocessor, especially if your macro shares part of its name with something quite common. An easier alternative is to use the second form of pragma statement that VS allows, __pragma, instead of the more common #pragma. The advantage to the second one is that it can be used in macros.

#define STRINGIFY_INT(x) #x
#define STRINGIFY(x) STRINGIFY_INT(x)
#define PRINT_MACRO(x) \
    __pragma(message ("Value of macro " #x " is " STRINGIFY(x)))
 
#define MY_MACRO 45
PRINT_MACRO(MY_MACRO) // produces Value of macro MY_MACRO is 45
 
int main()
{
    return 0;
}

Naming threads

You may have when debugging programs using the threadpool or wininet that somethreads have custom names like “Main Thread” that give a little more as to their purpose rather than just the function they started executing from. You can apply these names to your own threads by causing a specific SEH exception in them.

#include <windows.h>
#include <process.h>
 
const DWORD MS_VC_EXCEPTION=0x406D1388;
 
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
   DWORD dwType; // Must be 0x1000.
   LPCSTR szName; // Pointer to name (in user addr space).
   DWORD dwThreadID; // Thread ID (-1=caller thread).
   DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
 
 
void SetThreadName(char* threadName, DWORD dwThreadID = (DWORD)-1)
{
   THREADNAME_INFO info;
   info.dwType = 0x1000;
   info.szName = threadName;
   info.dwThreadID = dwThreadID;
   info.dwFlags = 0;
 
   __try
   {
      RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
   }
   __except(EXCEPTION_CONTINUE_EXECUTION)
   {
   }
}
 
 
unsigned __stdcall ThreadFunc(void*)
{
   SetThreadName("Waiter");
   Sleep(20000);
}
 
int main()
{
   unsigned threadNum = 0;
   SetThreadName("Launcher");
   intptr_t hThread = _beginthreadex(NULL, 0, &ThreadFunc, NULL, 0, &threadNum);
   WaitForSingleObject((HANDLE)hThread, INFINITE);
   CloseHandle((HANDLE)hThread);
   return 0;
}

Timing builds

The IDE provides you a way to see how long the build took to complete. This is fine for casual curiosity but if your project is taking a long time to build and you want to see where the biggest slice of time is going, it’s not so great. Instead of using the IDE option, you can add the /Bt to the compiler command line and you’ll get a much finer breakdown of what’s taking so long. As an example, this can be used to measure the difference between defining and not defining WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <cstdio>
 
int main()
{
    printf("Console code page is %d\n", GetConsoleCP());
}

Compiler output:

cl /Ox /GL /Bt /nologo test.cpp /link /LTCG /nologo
test2.cpp
time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\c1xx.dll)=0.518s
time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\c2.dll)=0.003s
Generating code
Finished generating code
  OptRef: Total time = 0.000s
  OptIcf: Total time = 0.000s
Pass 1: Interval #1, time = 0.062s
Pass 2: Interval #2, time = 0.016s
Final: Total time = 0.078s
time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\link.exe)=0.102s
 
cl /Ox /GL /Bt /nologo /DWIN32_LEAN_AND_MEAN test2.cpp /link /LTCG /nologo
test2.cpp
time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\c1xx.dll)=0.221s
time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\c2.dll)=0.002s
Generating code
Finished generating code
  OptRef: Total time = 0.000s
  OptIcf: Total time = 0.015s
Pass 1: Interval #1, time = 0.046s
Pass 2: Interval #2, time = 0.000s
Final: Total time = 0.046s
time(C:\test\Microsoft Visual Studio 10\VC\BIN\amd64\link.exe)=0.066s

Extra build info

gcc provides a way to list all the macros it pre-defines for you, unfortunaetly visual studio doesn’t have an option to do just that. You can get a list of all macros used along with all the compiler switches used by adding the /Bz switch to the compiler command line. Add /Bv for in-depth version information.

#include <windows.h>
#include <cstdio>
 
int main()
{
    printf("Console code page is %d\n", GetConsoleCP());
}

Compiler output

cl /Ox /GL /Bz /Bv /nologo /c test.cpp
Compiler Passes:
 C:\test\Microsoft Visual Studio 8\VC\BIN\cl.exe:        Version 14.00.50727.762
 
 C:\test\Microsoft Visual Studio 8\VC\BIN\c1.dll:        Version 14.00.50727.762
 
 C:\test\Microsoft Visual Studio 8\VC\BIN\c1xx.dll:      Version 14.00.50727.762
 
 C:\test\Microsoft Visual Studio 8\VC\BIN\c2.dll:        Version 14.00.50727.762
 
 C:\test\Microsoft Visual Studio 8\VC\BIN\link.exe:      Version  8.00.50727.762
 
 C:\test\Microsoft Visual Studio 8\VC\BIN\mspdb80.dll:   Version  8.00.50727.762
 
 C:\test\Microsoft Visual Studio 8\VC\BIN\1033\clui.dll: Version 14.00.50727.762
 
test.cpp
`C:\test\Microsoft Visual Studio 8\VC\BIN\c1xx.dll -zm0xFCDB0000 -il C:\Users\Adrian\AppData\Local\Temp\_CL_cc67db03 -f test.cpp -W 1 -Ze -D_MSC_EXTENSIONS -Zp8 -ZB64 -D_INTEGRAL_MAX_BITS=64 -Gs -Fotest.obj -pc \:/ -Fdvc80.pdb -D_MSC_VER=1400 -D_MSC_FULL_VER=140050727 -D_WIN32 -D_M_IX86=600 -D_M_IX86_FP=0 -GS -GR -D_CPPRTTI -Zc:forScope -Zc:wchar_t -Og -Oi -Ot -Oy -ltcg -Bd -nologo -D_MT -D_DLL -I C:\test\Microsoft Visual Studio 8\VC\ATLMFC\INCLUDE -I C:\test\Microsoft Visual Studio 8\VC\INCLUDE -I C:\test\Microsoft Visual Studio 8\VC\PlatformSDK\include'
 
`C:\test\Microsoft Visual Studio 8\VC\BIN\c2.dll -il C:\Users\Adrian\AppData\Local\Temp\_CL_cc67db03 -f test.cpp -W 1 -Gs4096 -dos -Fotest.obj -Fdvc80.idb -GS -Og -Ob2 -ltcg -Bd -MD'
?/out:test.exe
/ltcg
/nologo
test.obj
`"C:\test\Microsoft Visual Studio 8\VC\BIN\link.exe" /link /errorreport:queue @C:\Users\Adrian\AppData\Local\Temp\_CL_1a6fa7falk'
Generating code
Finished generating code

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress