Recursively find files of certain types and log their paths (C++)
The declarations for dir and file can be moved lower, where they are first assigned values.  However, since dir is only used in one place, it can be eliminated and the value used directly.

HANDLE file = FindFirstFileW(root_slash.c_str(), &amp;file_info);


When testing file attributes, the check for invalid attributes should be first, and you can combine several individual tests into one:

if (file_info.dwFileAttributes != INVALID_FILE_ATTRIBUTES &amp;&amp; 
    !(file_info.dwFileAttributes &amp; (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_SYSTEM) &amp;&amp;
    (name != L"." &amp;&amp; name != L".."))


Since there are three attributes you want to ignore, you can define a constant to hold them rather than list them out in your if statement.

Incidentally, since cFileName is a C array, you need to assign it to a string variable to be able to use the equality comparisons with it.  Or you could leave it in cFileName and use basic comparisons like strcmp.  Since there are two similar strings that are very short, you could also do direct character comparison but that makes the code larger and harder to understand and should only be done when absolutely necessary.

Since you decrement limit with each recursive call to get_files, you reduce the limit for the current directory as well.  If your initial directory has 16 subdirectories, your search will skip the 10th, may skip some of the subdirectories of the first 9, and will search all of the directories under the 11th and later subdirectories.  You should use

get_files(path, ext, log, limit - 1);


instead.  If any of the recursive calls fail in some way (return nonzero) you ignore the error and keep going.  This is reasonable in this instance, but does make the "recursion limit reached" error somewhat pointless since it will never be returned to the caller.  This one value should probably be handled differently, so that if any search reaches the recursion limit, this value is returned to the original caller to indicate that the results are incomplete.

Potentially more serious is that your extension comparison is case sensitive.  A file called "IMAGE.JPG" will not be listed, because the extension ins in uppercase and you're looking for a lowercase one.

Unless there's an absolute need for it, you should omit the std::flush from the log outputs.  This will reduce the performance as every filename will be written one at a time, instead of in larger chunks.