A WinForms DataGridView control has the ability to automatically generate its columns and populate from a specified data source (which can be a DataSet, a simple list or something else). All you have to do is something like this:

var list = new List<Record>() {new Record() {Id = 1, Name = "item 1", Date = DateTime.Now}};

this.dataGridView.AutoGenerateColumns = true;
this.dataGridView.DataSource = list;

When you do the same in MFC, it doesn’t work (supposing that you followed all the steps for hosting a WinForms control in an MFC application as described in MSDN).

m_dgv->AutoGenerateColumns = true;
auto source = gcnew System::Collections::Generic::List<Record^>();
auto r1 = gcnew Record();
r1->Id = 1;
r1->Name = "item 1";
r1->Date = System::DateTime::Now;
source->Add(r1);

m_dgv->DataSource = source;

dgv1

After debugging through the .NET framework sources I realized the problem was that the BindingContext property of the DataGridView control was null. This property represents a manager of the list of bindings for the control. If this is null then the control will use the binding context of the parent, which is usually a WinForms form. However, in this MFC application there is no WinForms form and the parent of the DataGridView control is null, which means the control does not have a bindings manager, and no bindings can be set.

The solution is to explicitly set the BindingsContext property to an existing binding context (a new object) before setting the data source.

m_dgv->BindingContext = gcnew System::Windows::Forms::BindingContext();
m_dgv->DataSource = source;

dgv2

The lesson learned is that when you host a WinForms control in an MFC application some things won’t just work out of the box, if they rely on a parent form functionality. There is no such WinForms form and you might need to do additional manual initialization.

, , , , , , Hits for this post: 406 .

NuGet has recently added support for native projects. This simplifies a lot deployment of native libraries. Even though cpplinq is not a big library (in fact is just a header file) I have created a NuGet package so that you are able to automatically add it to your project.

Here is what you have to do.

  1. Make sure you have NuGet 2.5 or newer, otherwise the NuGet package manager won’t show up in your VC++ projects.
  2. In the context menu for your project choose Manage NuGet Packages…
    cpplinqdemo1
  3. Search for cpplinq and install the package.
    cpplinqdemo2
  4. Include the cpplinq.hpp header and start using the library. Here is a sample to test that everything is all right.
    #include "cpplinq.hpp"
    
    void computes_a_sum ()
    {
        using namespace cpplinq;
        int ints[] = {3,1,4,1,5,9,2,6,5,4};
    
        auto result =    from_array (ints)
                      >> where ([](int i) {return i%2 ==0;})  // Keep only even numbers
                      >> sum ()                               // Sum remaining numbers
                      ;
    }
    

Notice that all the settings for library (such as adding the proper entry for the include directories or defining NOMINMAX so that min and max macros will not be defined for the project) are automatically performed, so you can focus on coding.

, , , , , , , Hits for this post: 1078 .

Two funny things I have discovered today.

First is an easter-egg in a trace route, from people at BeagleNetworks.net that have inserted the plot of Star Wars Episode IV in their internal routes. Just traceroot 216.81.59.173 to see what happens. Here is the listing:

[...]>tracert -h 60 216.81.59.173

Tracing route to read.more.at.beaglenetworks.net [216.81.59.173] over a maximum of 60 hops:
 [...]
 10   129 ms   133 ms   129 ms  216.66.0.26
 11   169 ms   168 ms   171 ms  10.26.26.102
 12   166 ms   172 ms   172 ms  Episode.IV [206.214.251.1]
 13   168 ms   173 ms   167 ms  A.NEW.HOPE [206.214.251.6]
 14   172 ms   170 ms   172 ms  It.is.a.period.of.civil.war [206.214.251.9]
 15   169 ms   172 ms   171 ms  Rebel.spaceships [206.214.251.14]
 16   170 ms   166 ms   172 ms  striking.from.a.hidden.base [206.214.251.17]
 17   171 ms   172 ms   171 ms  have.won.their.first.victory [206.214.251.22]
 18   169 ms   173 ms   172 ms  against.the.evil.Galactic.Empire [206.214.251.25]
 19   173 ms   174 ms   169 ms  During.the.battle [206.214.251.30]
 20   172 ms   172 ms   172 ms  Rebel.spies.managed [206.214.251.33]
 21   171 ms   172 ms   172 ms  to.steal.secret.plans [206.214.251.38]
 22   170 ms   172 ms   172 ms  to.the.Empires.ultimate.weapon [206.214.251.41]
 23   169 ms   172 ms   173 ms  the.DEATH.STAR [206.214.251.46]
 24   170 ms   171 ms   171 ms  an.armored.space.station [206.214.251.49]
 25   169 ms   175 ms   172 ms  with.enough.power.to [206.214.251.54]
 26   169 ms   173 ms   171 ms  destroy.an.entire.planet [206.214.251.57]
 27   172 ms   175 ms   168 ms  Pursued.by.the.Empires [206.214.251.62]
 28   172 ms   172 ms   171 ms  sinister.agents [206.214.251.65]
 29   171 ms   171 ms   172 ms  Princess.Leia.races.home [206.214.251.70]
 30   171 ms   172 ms   172 ms  aboard.her.starship [206.214.251.73]
 31   170 ms   190 ms   175 ms  custodian.of.the.stolen.plans [206.214.251.78]
 32   169 ms   172 ms   173 ms  that.can.save.her [206.214.251.81]
 33   174 ms   172 ms   173 ms  people.and.restore [206.214.251.86]
 34   170 ms   173 ms   173 ms  freedom.to.the.galaxy [206.214.251.89]
 35   172 ms   172 ms   173 ms  0-----I-------I-----0 [206.214.251.94]
 36   172 ms   172 ms   171 ms  0------------------0 [206.214.251.97]
 37   171 ms   173 ms   173 ms  0-----------------0 [206.214.251.102]
 38   172 ms   172 ms   173 ms  0----------------0 [206.214.251.105]
 39   171 ms   173 ms   174 ms  0---------------0 [206.214.251.110]
 40   172 ms   171 ms   172 ms  0--------------0 [206.214.251.113]
 41   171 ms   172 ms   171 ms  0-------------0 [206.214.251.118]
 42   172 ms   172 ms   172 ms  0------------0 [206.214.251.121]
 43   171 ms   171 ms   172 ms  0-----------0 [206.214.251.126]
 44   169 ms   173 ms   172 ms  0----------0 [206.214.251.129]
 45   171 ms   172 ms   175 ms  0---------0 [206.214.251.134]
 46   173 ms   173 ms   173 ms  0--------0 [206.214.251.137]
 47   171 ms   174 ms   172 ms  0-------0 [206.214.251.142]
 48   171 ms   173 ms   174 ms  0------0 [206.214.251.145]
 49   174 ms   173 ms   173 ms  0-----0 [206.214.251.150]
 50   174 ms   173 ms   173 ms  0----0 [206.214.251.153]
 51   171 ms   173 ms   174 ms  0---0 [206.214.251.158]
 52   176 ms   181 ms   172 ms  0--0 [206.214.251.161]
 53   172 ms   173 ms   172 ms  0-0 [206.214.251.166]
 54   176 ms   228 ms   171 ms  00 [206.214.251.169]
 55   175 ms   171 ms   172 ms  I [206.214.251.174]
 56   172 ms   173 ms   175 ms  By.Ryan.Werber [206.214.251.177]
 57   175 ms   171 ms   172 ms  Blizzards.Breed.CCIE.Creativity [206.214.251.182]
 58   175 ms   177 ms   177 ms  Please.Try.Again.Tracerote.to.obiwan.scrye.net [206.214.251.185]
 59   173 ms   176 ms   176 ms  read.more.at.beaglenetworks.net [216.81.59.173]

The second is a funny error message when an error occurred while trying to unsubscribe from youtube. The message reads “A team of highly trained monkeys has been dispatched to deal with the situation.”
trainedmonkeys

, , , , Hits for this post: 2928 .

I was writing recently about an MFC bug in CDatabase class in Visual Studio 2012. In the meanwhile I have stumbled upon an even greater bug (check this report on Connect for the details): when the OpenEx function fails (wrong credentials, service not reachable, machine is shot down etc.) it corrupts memory. What happen next is chance. I don’t know when a fix will be available, but until that happens there is a workaround for this. Just like for the previous bug, you have to derive CDatabase and override OpenEx method, copying the original implementation from the MFC sources and adding one more line to null the pointer to already released memory. So, I’ll extend my previous CDatabaseEx class to the following implementation:

class CDatabaseEx : public CDatabase  
{  
public:  
   CString GetConnectEx()  
   {  
      CString strConnect = m_strConnect;  
  
      if (strConnect.GetLength() == 0)  
      {  
         DATA_BLOB connectBlob;  
         if (CryptUnprotectData(&m_blobConnect, NULL, NULL, NULL, NULL, 0, &connectBlob))  
         {  
            strConnect = (LPTSTR)connectBlob.pbData;  
            LocalFree(connectBlob.pbData);  
         }  
      }  
  
      return strConnect;  
   }

   virtual BOOL OpenEx(LPCTSTR lpszConnectString, DWORD dwOptions = 0)
   {
     ENSURE_VALID(this);
     ENSURE_ARG(lpszConnectString == NULL || AfxIsValidString(lpszConnectString));
     ENSURE_ARG(!(dwOptions & noOdbcDialog && dwOptions & forceOdbcDialog));

     // Exclusive access not supported.
     ASSERT(!(dwOptions & openExclusive));

     m_bUpdatable = !(dwOptions & openReadOnly);

     TRY
     {
        m_strConnect = lpszConnectString;

        DATA_BLOB connectBlob;
        connectBlob.pbData = (BYTE *)(LPCTSTR)m_strConnect;
        connectBlob.cbData = (DWORD)(AtlStrLen(m_strConnect) + 1) * sizeof(TCHAR);
        if (CryptProtectData(&connectBlob, NULL, NULL, NULL, NULL, 0, &m_blobConnect))
        {
           SecureZeroMemory((BYTE *)(LPCTSTR)m_strConnect, m_strConnect.GetLength() * sizeof(TCHAR));
           m_strConnect.Empty();
        }

        // Allocate the HDBC and make connection
        AllocConnect(dwOptions);
        if(!Connect(dwOptions))
        {
           m_blobConnect.pbData = NULL;
           return FALSE;
        }

        // Verify support for required functionality and cache info
        VerifyConnect();
        GetConnectInfo();
     }
     CATCH_ALL(e)
     {
        // HERE IT IS, the workaround (hopefully temporary) for MFC bug with OpenEx corrupting memory
        // http://connect.microsoft.com/VisualStudio/feedback/details/760371/localfree-called-twice-in-cdatabase-mfc-11
        m_blobConnect.pbData = NULL;
        Free();
        THROW_LAST();
     }
     END_CATCH_ALL

     return TRUE;
   }
};  
, , , , , Hits for this post: 3467 .

Problem

I have recently migrated a C# 2.0 project registered for COM interop to .NET 4.5 and when I imported the type library in a C++ project with no_registry, suddenly I got some errors because the type library could not be imported. Here are the steps to reproduce:

  • create a .NET Class Library project and set platform target to .NET framework 4.5
  • check Register for COM interop
  • build the project
  • import the type library in a C++ project:
    #import "DemoClassLibrary.tlb" no_dual_interfaces no_registry
    

The result is the following error:

1>[...]: error C3510: cannot locate dependent type library '' {bed7f4ea-1a96-11d2-8f08-00a0c9a6186d} v.2.4
1>[...]: fatal error C1083: Cannot open type library file: '[...]\democlasslibrary.tlb': Error loading type library/DLL.

Solution

Searching for the solution I found that this was a known issue when you have both CLR 2.0 and 4.0 installed on the same machine. See this KB article: VC++ 2010 #import with no_registry fails with error C1083 or C3510. Unfortunately I was unable to fix the problem with the solution indicated there.

There are two tools that can generate a type library from an assembly:

  • tlbexp.exe: generates a type library from a specified .NET assembly
  • regasm.exe: registers metadata from an assembly to the Windows Registry, but in addition can create a type library from for the input assembly when /tlb switch is used.

When a project specifies to register for COM interop what Visual Studio does is similar to calling regasm.exe with /codebase switch specified. Since I had before problems with interop assemblies automatically generated by Visual Studio (with tlbimp.exe) I though it would be the same (only the other way around). Therefore I unchecked “register for COM interop” and added as a custom build step registration with regasm.exe, like this:

c:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe DemoClassLibrary.dll /tlb:DemoClassLibrary.tlb

Not very surprisingly, the generated file was different and the #import command executed without problems.

Problem solved!

Cause

The question that arises is why are the two files, generated with Visual Studio and with regasm.exe, different? You can see they are different if you open them in an hex editor. But if you just use oleview.exe, the disassembled type library looks identical.

The obvious answer that occurred to me, but eventually proved wrong, was that Visual Studio is not actually using regasm.exe to register the assembly and generate the type library. It actually uses a MSBuild task for that.

When the RegisterForComInterop property is set in a .csproj an MSBuild task is executed.

1>Using "RegisterAssembly" task from assembly "Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
1>Task "RegisterAssembly" (TaskId:32)
1>  Task Parameter:Assemblies=[...]\DemoClassLibrary.dll (TaskId:32)
1>  Task Parameter:TypeLibFiles=[...]\DemoClassLibrary.tlb (TaskId:32)
1>  Task Parameter:AssemblyListFile=obj\DemoClassLibrary.csproj.UnmanagedRegistration.cache (TaskId:32)
1>  Task Parameter:CreateCodeBase=True (TaskId:32)
1>  Registering assembly "[...]\DemoClassLibrary.dll" for COM Interop. (TaskId:32)
1>  Exporting and registering type library "[...]\DemoClassLibrary.tlb". (TaskId:32)
1>  Type 'P' exported. (TaskId:32)
1>  Type 'P' exported. (TaskId:32)
1>Done executing task "RegisterAssembly". (TaskId:32)

The task can be found in Microsoft.Common.targets (in c:\Windows\Microsoft.NET\Framework\v4.0.30319\)

  <!--
    ============================================================
                                        UnmanagedRegistration

    Registers the main assembly for COM interop.
    ============================================================
    -->
  <PropertyGroup>
    <UnmanagedRegistrationDependsOn></UnmanagedRegistrationDependsOn>
  </PropertyGroup>
  <Target
      Name="UnmanagedRegistration"
      Condition="'$(RegisterForComInterop)'=='true' and '$(OutputType)'=='library'"
      DependsOnTargets="$(UnmanagedRegistrationDependsOn)"
        >

    <PropertyGroup>
      <RegisterAssemblyMSBuildArchitecture Condition="'$(RegisterAssemblyMSBuildArchitecture)' == ''">$(PlatformTargetAsMSBuildArchitecture)</RegisterAssemblyMSBuildArchitecture>
    </PropertyGroup>

    <PropertyGroup Condition="'$(TargetFrameworkAsMSBuildRuntime)' != '' and '$(RegisterAssemblyMSBuildArchitecture)' != ''">
      <!-- Falling back to the current runtime if we are targeting CLR2 and the task host doesn't exist will lead to 
           incorrect behavior in some cases, but it's the same incorrect behavior as Visual Studio 2010, and thus better
           than causing build breaks on upgrade to Win8 the way not doing so would.  For more details, see the 
           corresponding comment in GenerateResource. -->
      <RegisterAssemblyMSBuildRuntime 
          Condition="'$(RegisterAssemblyMSBuildRuntime)' == '' and 
                     $([MSBuild]::DoesTaskHostExist(`$(TargetFrameworkAsMSBuildRuntime)`, `$(RegisterAssemblyMSBuildArchitecture)`))">$(TargetFrameworkAsMSBuildRuntime)</RegisterAssemblyMSBuildRuntime>

      <!-- If the targeted runtime doesn't exist, fall back to current -->
      <RegisterAssemblyMSBuildRuntime Condition="'$(RegisterAssemblyMSBuildRuntime)' == ''">CurrentRuntime</RegisterAssemblyMSBuildRuntime>
    </PropertyGroup>

    <RegisterAssembly
        Assemblies="@(_OutputPathItem->'%(FullPath)$(TargetFileName)')"
        TypeLibFiles="@(_OutputPathItem->'%(FullPath)$(TargetName).tlb')"
        AssemblyListFile="@(_UnmanagedRegistrationCache)"
        CreateCodeBase="true"
        MSBuildRuntime="$(RegisterAssemblyMSBuildRuntime)"
        MSBuildArchitecture="$(RegisterAssemblyMSBuildArchitecture)"
        Condition="!Exists('@(_UnmanagedRegistrationCache)')"/>

    <ItemGroup>
      <FileWrites Include="@(_OutputPathItem->'%(FullPath)$(TargetName).tlb')"/>
    </ItemGroup>
  </Target>

To check if I can reproduce, I have created MSBuild file (explicitreg.xml) with some hard-coded values that only runs that registration task.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="UnmanagedRegistration" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <UnmanagedRegistrationDependsOn></UnmanagedRegistrationDependsOn>
  </PropertyGroup>
  <Target
      Name="UnmanagedRegistration"
      DependsOnTargets="$(UnmanagedRegistrationDependsOn)"
        >

    <PropertyGroup>
      <RegisterAssemblyMSBuildArchitecture Condition="'$(RegisterAssemblyMSBuildArchitecture)' == ''">$(PlatformTargetAsMSBuildArchitecture)</RegisterAssemblyMSBuildArchitecture>
    </PropertyGroup>

    <PropertyGroup Condition="'$(TargetFrameworkAsMSBuildRuntime)' != '' and '$(RegisterAssemblyMSBuildArchitecture)' != ''">
      <RegisterAssemblyMSBuildRuntime 
          Condition="'$(RegisterAssemblyMSBuildRuntime)' == '' and 
                     $([MSBuild]::DoesTaskHostExist(`$(TargetFrameworkAsMSBuildRuntime)`, `$(RegisterAssemblyMSBuildArchitecture)`))">$(TargetFrameworkAsMSBuildRuntime)</RegisterAssemblyMSBuildRuntime>

      <!-- If the targeted runtime doesn't exist, fall back to current -->
      <RegisterAssemblyMSBuildRuntime Condition="'$(RegisterAssemblyMSBuildRuntime)' == ''">CurrentRuntime</RegisterAssemblyMSBuildRuntime>
    </PropertyGroup>

    <RegisterAssembly
        Assemblies="bin\Debug\DemoClassLibrary.dll"
        TypeLibFiles="bin\Debug\DemoClassLibrary.tlb"
        AssemblyListFile="obj\DemoClassLibrary.csproj.UnmanagedRegistration.cache"
        CreateCodeBase="true" />

    <ItemGroup>
      <FileWrites Include="bin\Debug\DemoClassLibrary.tlb"/>
    </ItemGroup>
  </Target>
</Project>
c:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe explicitreg.xml

But surprise: this produced the exact same output as the regasm.exe command. Comparing the diagnose logs from MSBuild (for the build of the .csproj and my custom file) I couldn’t spot any difference in the execution of the task. Also using the Process Monitor (procmon.exe from Sysinternals) to check access to the TLB file, I could clearly see the difference for the file writing, because different lengths were produced from Visual Studio build and explicit MSBuild run, though again I could not see any difference in the call stacks.

So, the actual cause for this behavior is still unknown to me and I would appreciate if anyone that knows the answer clarifies it.

, , , , , , , Hits for this post: 4330 .

After migrating an MFC application from Visual Studio 2008 to Visual Studio 2012 I run into an unexpected error: the application was having problem fetching data from the SQL Server database. After debugging it turned out that function CDatabase::GetConnect that I was using to retrieve the connection string after opening the database (for different purposes) was suddenly returning an empty string. It turned out this was a known MFC bug, reported on Microsoft Connect. The cause of the problem is that CDatabase encrypts the connection string, and then empties it. Here is a snippet of OpenEx:

BOOL CDatabase::OpenEx(LPCTSTR lpszConnectString, DWORD dwOptions)
{
	ENSURE_VALID(this);
	ENSURE_ARG(lpszConnectString == NULL || AfxIsValidString(lpszConnectString));
	ENSURE_ARG(!(dwOptions & noOdbcDialog && dwOptions & forceOdbcDialog));

	// Exclusive access not supported.
	ASSERT(!(dwOptions & openExclusive));

	m_bUpdatable = !(dwOptions & openReadOnly);

	TRY
	{
		m_strConnect = lpszConnectString;

		DATA_BLOB connectBlob;
		connectBlob.pbData = (BYTE *)(LPCTSTR)m_strConnect;
		connectBlob.cbData = (DWORD)(AtlStrLen(m_strConnect) + 1) * sizeof(TCHAR);
		if (CryptProtectData(&connectBlob, NULL, NULL, NULL, NULL, 0, &m_blobConnect))
		{
			SecureZeroMemory((BYTE *)(LPCTSTR)m_strConnect, m_strConnect.GetLength() * sizeof(TCHAR));
			m_strConnect.Empty();
		}

Though Microsoft promised to be solved the bug in the next major version I needed a fix now. So here is my fix.

CDatabase has a protected member m_strConnect which is supposed to keep the connection string in plain text, and one called m_blobConnect that represents the encrypted connection string. However, there is no method in CDatabase to return this blob. Getting the blob would allow you to decrypt it and get the actual connection string. So the solution is to derive CDatabase, provide a method to return the connection string, and in your code replace CDatabase with this derived class and the call to GetConnect() to this new method.

class CDatabaseEx : public CDatabase
{
public:
   CString GetConnectEx()
   {
      CString strConnect = m_strConnect;

      if (strConnect.GetLength() == 0)
      {
         DATA_BLOB connectBlob;
         if (CryptUnprotectData(&m_blobConnect, NULL, NULL, NULL, NULL, 0, &connectBlob))
         {
            strConnect = (LPTSTR)connectBlob.pbData;
            LocalFree(connectBlob.pbData);
         }
      }

      return strConnect;
   }
};
, , , , Hits for this post: 2634 .

A recent question on stackoverflow raised the problem of a fast algorithm to count the unique colors in an image (Faster algorithm to check the colors in a image). My answer what that this kind of problems are suited for parallelization. However, parallelization may only help when used judiciously.

To demonstrate the point I have written a pretty simple implementation in VC++ (with C++11), that uses a std::set to store the unique colors. The size of the set is the number of the colors. The implementation simply iterates over the pixels of the image and adds them to the set.

There are probably better solutions than the one presented in this article. The code shown here is merely for the purpose of showing how parallelization can help, though not everything that runs in parallel is faster that a sequential code.

There are some things to note:

  • I used CImage class to load an image from disk
  • images can be 1,4,8,16,24 or 32 bit per pixel, but in the code presented here, for simplicity, I assume the image loaded from disk is 24 bits per pixel
  • Windows device-independent bitmaps can be stored in memory in two ways: top-down (as you would naturally expect) or bottom-up. The processing of the image varies based on the storage. You can find details here: Top-Down vs. Bottom-Up DIBs.
  • CImage::GetPitch method returns the pitch of the bitmap, which is the distance in bytes between the two memory addresses representing the beginning of two consecutive lines of the bitmap. This is important because the bitmap can be stored (align) with padding bytes at the end of a line, and the pitch and the width are used to determine the number of padding bytes. If the pitch is positive, then the bitmap is stored top-down. If the pitch is negative then the image is stored bottom-up.
  • the pointer returned by CImage::GetBits points to the beginning of the buffer if the image is top-down, and to the end of the buffer if the image is bottom-up.
  • since the image data is stored in contiguous array, the position of each pixel is given by the following algorithm:
    let ptr be the beginning of the buffer
    foreach row from 0 to height
     foreach column from 0 to width
       if top-down // ptr point to the beginning of the buffer
         pixel = ptr + row * pitch + column * bytesperpixel
       else bottom-up // ptr points to the end of the buffer
         pixel = ptr - row * pitch + column * bytesperpixel
    

    Since the pitch is positive when the bitmap is stored top-down and negative when then image is stored bottom-up, the above algorithm can be simplified to:

    let ptr be the beginning of the buffer
    foreach row from 0 to height
     foreach column from 0 to width
       pixel = ptr + row * pitch + column * bytesperpixel
    

My simple implementation of the sequential algorithm looks like this:

int CountColors24bpp(unsigned char* data, int width, int height, int pitch)
{
    int bytespp = 3;
    std::set<unsigned int> colors;

    int padding = abs(pitch) - width * bytespp;

    for(int i = 0; i < height; ++i)
    {
        for(int j = 0; j < width; ++j)
        {
            unsigned char* ptr = data + i*pitch + j * bytespp;
            unsigned int color = ((*ptr) << 16) | (*(ptr+1) << 8) | *(ptr+2);
            colors.insert(color);
        }
    }

    return colors.size();
}

Loading the image from disk, and timing the execution looks like this:

CImage image;
image.Load(_T("d:\\sample.bmp"));

int width = image.GetWidth();
int height = image.GetHeight();
int pitch = image.GetPitch();
int bpp = image.GetBPP();

unsigned char* data = reinterpret_cast<unsigned char*>(image.GetBits());

{
    std::chrono::time_point<std::chrono::high_resolution_clock> start = std::chrono::high_resolution_clock::now();

    int colors = CountColors(data, width, height, bpp, pitch);

    std::chrono::time_point<std::chrono::high_resolution_clock> end = std::chrono::high_resolution_clock::now();

    auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count();

    std::cout << "[seq] colors count: " << colors << std::endl;
    std::cout << "[seq] elapsed time: " << elapsed_time << "ms" << std::endl;
}

The result on my machine (Intel Core i7 2.67GHz, 6 GB RAM) on four random bitmaps with sizes 200×150, 1680×1050, 3360×1065 and 3360×2100 look like this (obviously with small variations):

[seq] colors count: 513
[seq] elapsed time: 1ms

[seq] colors count: 10544
[seq] elapsed time: 81ms

[seq] colors count: 33454
[seq] elapsed time: 172ms

[seq] colors count: 33454
[seq] elapsed time: 345ms

The simplest parallelization you can think of is using parallel_for from the Parallel Patterns Library. Especially, because the conversion from a sequential implementation to a parallel one is pretty trivial.

#include <mutex>
std::mutex g_mutex;

int CountColors24bpp_pfor(unsigned char* data, int width, int height, int pitch)
{
    int bytespp = 3;
    std::set<unsigned int> colors;

    int padding = abs(pitch) - width * bytespp;

    parallel_for(0, height, [&](int i) {
        for(int j = 0; j < width; ++j)
        {
            unsigned char* ptr = data + i*pitch + j * bytespp;
            unsigned int color = ((*ptr) << 16) | (*(ptr+1) << 8) | *(ptr+2);
            g_mutex.lock();
            colors.insert(color);
            g_mutex.unlock();
        }
    });

    return colors.size();
}

When you run this code you get some pretty surprising results.

[pfor] colors count: 513
[pfor] elapsed time: 106ms

[pfor] colors count: 10544
[pfor] elapsed time: 5803ms

[pfor] colors count: 33454
[pfor] elapsed time: 10714ms

[pfor] colors count: 33454
[pfor] elapsed time: 15854ms

It’s not that surprising after all, if you look at how the insertion is performed. The access to the std::set is guarded with std::mutex, allowing only one thread to insert a new element into the set. Because of this synchronization it takes much more, in the order of 50-100 times, than the sequential implementation. Of course, the bigger the image is, the smaller this performance lost.

Obviously, parallelization through the means of parallel_for is not a solution in this case. Using threads directly however, can help, if we can get rid of syncing. This is possible by using a separate std::set for each thread, and merging the results at the end.

#include <thread>

struct thread_data 
{
    unsigned char* data;
    int width;
    int h0;
    int h1;
    int pitch;
};

void CountColors24bpp_threadfunc(thread_data td, std::set<unsigned int>& colors)
{
    int bytespp = 3;
    int padding = abs(td.pitch) - td.width * bytespp;

    for(int i = td.h0; i < td.h1; ++i)
    {
        for(int j = 0; j < td.width; ++j)
        {
            unsigned char* ptr = td.data + i*td.pitch + j * bytespp;
            unsigned int color = ((*ptr) << 16) | (*(ptr+1) << 8) | *(ptr+2);
            colors.insert(color);
        }
    }
}

int CountColors24bpp_threads(unsigned char* data, int width, int height, int pitch, int threadscount)
{    
    std::vector<std::set<unsigned int>> colors(threadscount);
    std::vector<std::thread> threads(threadscount);

    int range = height / threadscount;

    for(int i = 0; i < threadscount; ++i)
    {
        thread_data td;
        td.data = data;
        td.h0 = range * i;
        td.h1 = i == (threadscount - 1) ? height : td.h0 + range;
        td.width = width;
        td.pitch = pitch;

        std::thread t(CountColors24bpp_threadfunc, td, std::ref(colors[i]));
        threads[i].swap(t);
    }
    
    for(int i = 0; i < threadscount; ++i)
        threads[i].join();

    std::set<unsigned int> result;

    for(int i = 0; i < threadscount; ++i)
        result.insert(colors[i].begin(), colors[i].end());

    return result.size();
}

A few considerations:

  • the std::thread constructor does not take (yet) any number of parameters, so I had to pack the arguments in a structure
  • the std::set with the colors is passed by reference, and therefore it has to be packed in a std::ref
  • I create N threads, call the thread function with the arguments and then join them all
  • when all threads have finished I aggregate the individual sets in a single one

Running this code with 4 threads yields results that look like this:

[threads] colors count: 513
[threads] elapsed time: 1ms

[threads] colors count: 10544
[threads] elapsed time: 28ms

[threads] colors count: 33454
[threads] elapsed time: 61ms

[threads] colors count: 33454
[threads] elapsed time: 110ms

As you can see the performance is better than the sequential code in each case (and implicitly far better than the parallel_for version).

As I said at the beginning, one can imagine better algorithms (both sequential and parallel), but the straight forward conclusion is that you have to beware of parallelization, as not every parallel version of an algorithm can run faster than the sequential version. The more syncing for shared-access takes place, the more performance is affected and the results can get much worse than the sequential version.

, , , , , Hits for this post: 2613 .

There are some new operators available in cpplinq: singleton, generate, pairwise and zip_with.

singleton
This operator create a range with a single element, specified as argument. Using the operator is straight forward.

auto result = singleton (42) >> to_vector (); // yields {42}

generate
This operator creates a range using a given predicate.

auto x = -1;
auto result = generate ([&](){return (++x < 3) ? to_opt (x) : to_opt<int> ();}) >> to_vector (); // yields {0,1,2}

pairwise
Generates a new range of pairs by grouping adjacent elements of an input range. On an empty range it yields an empty range.

int numbers[] = {1,2,3,4,5};
auto result = from_array(numbers) >> pairwise() >> to_vector(); // yields {(1,2),(2,3),(3,4),{4,5}}

zip_with
Generates a new range of pairs by grouping together elements from two different ranges. If the two input ranges have different sizes, the result has the size of the smallest range. If any of the input ranges is empty, the result is an empty range.

int a[] = {1, 2, 3};
int b[] = {"one", "two", "three", "four", "five"};
auto zipped = from_array(a) >> zip_with(from_array(b)) >> to_vector(); // yields {(1,"one"), (2,"two"), (3,"three")}
, , , , Hits for this post: 2534 .

Microsoft has announced that Visual Studio 2012 Update 2 will bring support in Visual Studio and TFS for git. They already used git on codeplex and this move shows how popular git has become. I don’t work much with git, but as I said codeplex uses git, and I used it for working on cpplinq. However, the experience with Git Source Control Provider extension for Visual Studio was not the most pleasant. Fortunately, Visual Studio Tools for Git is a different story, allowing you to work exclusively from Visual Studio, without the need of addition tools. After giving it a try, I must say that even if it’s in CTP and still has some issues, it’s a totally different story than the previous extension or external tools I used. However, support for git will not be added to the previous versions of Visual Studio.

Here is more about the support for git:

Before you can start working with git, you need two things:

  1. Visual Studio 2012 Update 2 CTP 2
  2. Visual Studio Tools for Git

Extensions manager

In this post I’ll show what it takes to clone a git repository, work on the project, commit and push the changes on the remote branch.

To clone a repository you have go to Connect to Team Projects in Team Explorer, and under Local Git Repositories use the Clone pane to enter the URL of the server repository and the local destination. After the repository has been cloned, it shows up in the list, as shown below.

Cone a git

You can change the settings, such as your credentials from the Settings pane.

Git settings

To view your changes you can use the Git Changes > Commits pane. You can see included and excluded changes, and also untracked files. To commit your changes, provide a description (mandatory) and hit the Commit button. The page updates after the commit finishes.

Committing changes

It is possible to view the changes on each file with the built-in tools:

source file diff

After you committed all your changes and you’re ready to publish them on the server you can push the ongoing commits. If there are changes on the server, you must first pull those changes, merge locally, commit again and then push.

push ongoing commits

You can view the history for an entire branch from Team Explorer > Git Changes > Branches

Branch history

or for a single file from Solution Explorer (it is also possible to compare two versions).

File history

, , , Hits for this post: 3392 .

The Problem
You install a .NET redistributable package and want to reference the assemblies in your project. However, though everything is correctly installed to GAC, the assemblies don’t show up in Visual Studio’s Add Reference dialog in the .NET tab (regardless of what version of Visual Studio you have).

The Cause
Redistributable assemblies are intended for runtime. They are assemblies that have to be deployed on a target machine, so that applications needing them run correctly on other machines than the development machines. They are installed in GAC, but that is not enough for Visual Studio to pick them up and display them in the Add Reference dialog, in the .NET assemblies tab. Actually Visual Studio scans a set of folders that is specified in Registry and that’s why your redistributables are not showing up.

The Solution
The solution is fairly simple. Basically you have to do two things:

  • first, you should make a copy of the assemblies to another folder
  • second, you have to add that folder in Registry where Visual Studio keeps its list of folders with references. The path differs on 32-bit and 64-bit machines, but the details are specified in this article How to: Add or Remove References in Visual Studio.

Example
I will demonstrate the fix using the ReportViewer redistributable assemblies. I have several versions of Visual Studio installed on my machine: 2008, 2010 and 2012. I also have three versions of the ReportViewer control 9.0 (2008), 10.0 (2010) and 11.0 (2012), but also installed the redistributables for version 8.0 (2005). However, only the first three appear in Add References dialog in Visual Studio.

Add References dialog

Yet, all versions are present in GAC:

ReportViewer in GAC

I noticed that all other versions are installed under C:\Program Files (x86)\Microsoft Visual Studio xx\ReportViewer (where xx is the version). Since I already had a folder with some files for Visual Studio 2005, I created another folder there called ReportViewer.

In Registry, I looked for the key where the other versions are specified.

I added a new key called Report Viewer 8, and set the default value to the path of the folder that I created.

After re-opening the Add References dialog (no need to restart Visual Studio), the new assemblies shown up.

, , , , Hits for this post: 3791 .