NDepend 5.3

I have always considered software development as an art and a science at the same time, in which developers translate business requirements into computer instructions, therefore creativity, innovation and technology are amalgamated into one, but the road from an idea’s inception to design then development is long and amazingly not perfect, hence we developers sometimes rush things to meet deadlines or just build the functionality with the “promise of refactoring and/or improving it” later.

As a software developer and architect, I must conduct code review for new and existing codebases. One of the challenges I have always faced is the need to query codebases, in order to find dependencies and ensure that the code and/or changes being produced will not affect or break some OOP principles neither will introduce dependency issues.

There are a few tools out there I use to check source code quality, but none of them it’s as powerful or flexible enough as NDepend.  I have been a NDepend user for a long time now, and I even posted an article about it a few years back, and besides having a refreshed user interface, it now supports CQLINQ as well as CQL (Code Query Language) being the latter for legacy and compatibility reasons.

Once we start NDepend, the start page is displayed. From it, we can start analyzing our code as well as installing add-ins for Reflector and Visual Studio.

image

NDepend smoothly integrates into Visual Studio since version 2008, thus making it easier for developers.

image

There are many features in the product, but just to mention a few:

  • Multi VS solutions wide-analysis and collaboration
  • Rich Code Search in VS
  • Multi Query Edition in VS
  • Reflector disassembly’s comparison
  • Continuous comparison with a base line in VS
  • Code visualization in VS
    - Dependency Matrix
    - Dependency Graph and Metric View

image

The Dashboard allows developers to have a quick look at how their codebase is structured, but more importantly the incurred rule violations in code. Information is displayed in a succinct and clear way, not to mention the graphics that make it easy to interpret our code better.  

methods_that_could_have_a_lower_visibility

I have briefly described some of the features and benefits of NDepend, but before I forget to mention, it is available to download as a 14-day trial so you can use it and get a better idea about the capabilities of the product.

Regards,

Angel

Read XML config files with Visual C++

Hi Community,

I am currently working on an add-in for Visual Studio to expose some functionality available in the “Debugger Engine and Extension APIs”. The development is in the very early stages, but I have completed already one feature required by the Add-in, which is the  ability to read XML configuration files similarly to how .NET and the CLR do but in this case using Visual C++. This Add-in comprises native and managed code, where .NET helps me to interact with the IDE and the core functionality of the Add-in is encapsulated in a DLL. This blog entry describes the ConfigReader class.

A sample configuration file is shown below

<?xml version="1.0" encoding="utf-8"?>

<config>

    <sendOutputToVSWindow enabled="true" />

    <extensions>

        <extension name="ext" path="C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x86\winext\ext.dll" />

        <extension name="wow64exts" path="C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x86\WINXP\wow64exts.dll" />

        <extension name="exts" path="C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x86\WINXP\exts.dll" />

        <extension name="uext" path="C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x86\winext\uext.dll" />

        <extension name="ntsdexts" path="C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x86\WINXP\ntsdexts.dll" />

    </extensions>

</config>

The configuration file must reside in the same folder of the library, and it’s located in the constructor of the class

ConfigReader::ConfigReader() {

    ReadConfig();

}

 

void ConfigReader::ReadConfig() {

    if (!LocateConfigFile())

        throw std::exception("Config file not found. Unable to proceed.");

}

Since the DLL can be anywhere (unlike .NET) that it’s either in the GAC or the bin folder of the application, I needed to find the configuration file in the same path of the library, and this is done via enumerating the loaded modules.

BOOL ConfigReader::LocateConfigFile() {

    auto retval = FALSE;

    DWORD nModuleCount = 0;

    IXMLDOMDocumentPtr pDocPtr;

    MODULEINFO moduleDetails = {0};

    HANDLE hToken = NULL, hProcess = NULL;

    HMODULE hLoadedModules[Max_Loaded_Modules];

 

    CoInitialize(NULL);

 

    if ((hToken = GetThreadToken()) != NULL && SetPrivilege(hToken, SE_DEBUG_NAME, TRUE)) {

        if ((hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, GetCurrentProcessId())) != NULL) {

            if ((EnumProcessModulesEx(hProcess,  hLoadedModules, sizeof(hLoadedModules), &nModuleCount, LIST_MODULES_ALL)) != NULL) {

                auto modules = std::vector<HMODULE>(std::begin(hLoadedModules), std::end(hLoadedModules));

 

                 #ifdef _WIN64

                     nModuleCount = Item_Count(nModuleCount) / 2;

                 #else

                     nModuleCount = Item_Count(nModuleCount);

                 #endif

                 

                 auto config = DoesConfigFileExist(hProcess, modules, TargetImageName);

 

                 if (!config.empty())

                     retval = ParseConfigFile(config);

            }

            CloseHandle(hProcess);

        }

        SetPrivilege(hToken, SE_DEBUG_NAME, FALSE);

        CloseHandle(hToken);

 

        CoUninitialize();

    }

 

    return retval;

}

 

std::wstring ConfigReader::DoesConfigFileExist(const HANDLE& hProcess, std::vector<HMODULE>& hModules, const wchar_t* targetImage) {

    BOOL found = FALSE;

    std::wstring retval;

    wchar_t szDir[_MAX_DIR];

    wchar_t szExt[_MAX_EXT];

    wchar_t szBuffer[MAX_PATH];

    wchar_t szFName[_MAX_FNAME];

    wchar_t szDrive[_MAX_DRIVE];

 

    std::find_if(hModules.begin(), hModules.end(), [&, this](HMODULE hModule) {

        auto ret = FALSE;

 

        if (!found && hModule != nullptr  && (GetModuleFileNameEx(hProcess, hModule, szBuffer, Array_Size(szBuffer))) != NULL) {

            size_t cntConverted;

            char szAnsiPath[MAX_PATH];

            _wsplitpath_s(szBuffer, szDrive, Array_Size(szDrive), szDir, Array_Size(szDir), szFName, Array_Size(szFName), szExt, Array_Size(szExt));

            auto imageName = std::wstring(szFName).append(szExt);

            auto configPath = std::wstring(szDrive).append(szDir).append(ConfigFileName);

            wcstombs_s(&cntConverted, szAnsiPath, configPath.data(), configPath.size() );

            std::ifstream configFile(szAnsiPath);

 

            if (wcscmp(targetImage, imageName.data()) == 0  && configFile.good()) {

                configFile.close();

                retval.assign(configPath);

                found = TRUE;

            } 

        }

 

        return ret;

    });

 

    return retval;

}

 

HANDLE ConfigReader::GetThreadToken() {

    HANDLE retval;

    auto flags = TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY;

 

    if (!OpenThreadToken(GetCurrentThread(), flags, FALSE, &retval)) {

        if (GetLastError() == ERROR_NO_TOKEN) {

            if (ImpersonateSelf(SecurityImpersonation) &&

                !OpenThreadToken(GetCurrentThread(), flags, FALSE, &retval))

                retval = NULL;

        }

    }

    return retval;

}

 

BOOL ConfigReader::SetPrivilege(HANDLE& hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) {

    LUID luid;

    auto retval = FALSE;

    TOKEN_PRIVILEGES tp = {0};

    DWORD cb = sizeof(TOKEN_PRIVILEGES);

 

    if (LookupPrivilegeValue(NULL, Privilege, &luid)) {

        tp.PrivilegeCount = 1;

        tp.Privileges[0].Luid = luid;

        tp.Privileges[0].Attributes = bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0;

        AdjustTokenPrivileges(hToken, FALSE, &tp, cb, NULL, NULL);

 

        if (GetLastError() == ERROR_SUCCESS)

            retval = TRUE;

    }

    return retval;

}

A few things worth mentioning:

  • Always use STL containers and algorithms when possible (e.g.: std::vector instead of a C/C++ type array.
  • To query information about a process, this one has to be opened and most of the times (depending on what it’s required to do) a few flags must be set.  Also remember to close any handle after it’s been used.
  • Use std::wstring (UNICODE) instead of wchar_t*. They’re safer and better to use.
  • Use lambdas in conjunction with algorithms (e.g. s td::find_if)
  • Get to learn and know the functions to convert from Multibyte (ANSI) to wide characters (UNICODE) (e.g.: wcstombs_s)
  • Pass references (and use const whenever is possible)

Once the configuration file is found, it is parsed using the MSMXL parser which it’s COM based, therefore the use of smart pointers is strongly suggested.

BOOL ConfigReader::ParseConfigFile(const std::wstring& configFile) {

    auto retval = FALSE;

    VARIANT_BOOL success;

    IXMLDOMDocumentPtr pDocPtr;

    IXMLDOMNodePtr selectedNode; 

 

    CoInitialize(NULL);

 

    pDocPtr.CreateInstance("Msxml2.DOMDocument.6.0");

    

    if (SUCCEEDED(pDocPtr->load(_variant_t(configFile.c_str()), &success))) {

        if (SUCCEEDED(pDocPtr->selectSingleNode(_bstr_t(XmlRootNode), &selectedNode))) {

            ProcessElementRecursively(selectedNode);

            retval = TRUE;

        }

    }

 

    CoUninitialize();

 

    return retval;

}

 

void ConfigReader::ProcessElementRecursively(IXMLDOMNodePtr& node) {

    long childrenCount = 0;

    IXMLDOMNodePtr childNode;

    IXMLDOMNodeListPtr children;

 

    CoInitialize(NULL);

 

    if (SUCCEEDED(node->get_childNodes(&children)) && SUCCEEDED(children->get_length(&childrenCount)) && childrenCount > 0) {

        for (auto nCount = 0; nCount < childrenCount; nCount++) {

            if (SUCCEEDED(children->get_item(nCount, &childNode))) {

                ExtractInformationFromElement(childNode);

                ProcessElementRecursively(childNode);

            }

        }

    }

    

    CoUninitialize();

}

 

void ConfigReader::ExtractInformationFromElement(IXMLDOMNodePtr& node) {

    size_t nSize;

    VARIANT value;

    std::wstring key;

    BSTR nodeContent;

    DOMNodeType nodeType;

    WCHAR szNodeText[512] = {0};

    char szBuffer[MAX_PATH] = {0};

    

 

    CoInitialize(NULL);

 

    if (SUCCEEDED(node->get_nodeType(&nodeType)) && nodeType == DOMNodeType::NODE_ELEMENT) {

        nodeContent = SysAllocString(szNodeText);

        auto pElement = (IXMLDOMElementPtr)node;

        pElement->get_tagName(&nodeContent);

 

        if (wcscmp(nodeContent, L"sendOutputToVSWindow") == 0) {

            pElement->getAttribute(_bstr_t(L"enabled"), &value);

 

            if (value.vt != VT_NULL) 

                Properties.insert(std::make_pair(nodeContent, value.bstrVal));

 

        } else if (wcscmp(nodeContent, L"extension") == 0) {

            pElement->getAttribute(_bstr_t(L"name"), &value);

 

            if (value.vt != VT_NULL)

                key.assign(value.bstrVal);

 

            pElement->getAttribute(_bstr_t(L"path"), &value);

 

            if (value.vt != VT_NULL && !key.empty()) {

                Properties.insert(std::make_pair(key.c_str(), value.bstrVal));

                wcstombs_s(&nSize, szBuffer, key.c_str(), key.size());

                std::string name(szBuffer);

                wcstombs_s(&nSize, szBuffer, value.bstrVal, wcslen(value.bstrVal));

                std::string path(szBuffer);

                m_extensions.push_back(ExtInformation(name, path));

            }

                

        }

 

        SysFreeString(nodeContent);

    }

 

    CoUninitialize();

}

Our ConfigReader object has two main fields (Extensions and Properties) – Depicted below

image

and we can also retrieve any property from the configuration file, in a similar way we do it in .NET. This is accomplished via the GetSetting method

const std::wstring ConfigReader::GetSetting(const wchar_t* key) {

    std::wstring retval;

 

    if (Properties.size() > 0 && key != nullptr && wcslen(key) > 0) {

        typedef std::pair<const std::wstring, const std::wstring> item;

 

        std::find_if(Properties.begin(), Properties.end(), [&](item i) {

            auto ret = FALSE;

 

            if (retval.size() == 0) {

                if (wcscmp(i.first.data(), key) == 0) {

                    retval.assign(i.second);

                    ret = TRUE;

                }

            }

            return ret;

        });

    }

 

 

    return retval;

}

image

Regards,

Angel

Visual Studio “4″ CTP – General Availability

Hi Community,

Microsoft has announced the general availability for Visual Studio “14″ CTP Today. There are quite a few interesting features, in both native and managed languages.

Please find below some resources about this release:

Regards,

Angel