Monthly Archives: February 2008

HowTo: Correctly read reparse data in Vista

In many newsgroups/forums you will see the following approach to read the reparse data (for example the original directory of a junction):

This works very well under XP (but only if you are an admin). It does not work under Vista if UAC is enabled (even if you are admin). It only works if the process was started evelated (as real admin)!

But if you go to the command prompt and type “dir c:\ /AL”, you will see that in the command prompt the display is always possible, even if you are not an admin:

C:\>dir /AL 
 Volume in drive C has no label. 
 Volume Serial Number is B424-9F82              

 Directory of C:\ 
11/02/2006  02:00 PM         Documents and Settings [C:\Users] 
               0 File(s)              0 bytes 
               1 Dir(s)  57,193,873,408 bytes free

So there must be a better way to read the reparse data.

First I tried to find an other function to get the data. But this had no success… then I remembered my research about “Shims” which are part of XP and later. These shims can be used to “fix” application compatibilities in news OSs. To activate a shim for an appliction you can use the “Application Compatibility Toolkit” from Microsoft. And also I remembered an Shim namaned “APILogger”. Ok, then I copied “cmd.exe” to a temp-dir and renamned it; created and installed the sdb-File with the APILogger for this new “testcmd.exe”; and executed the “dir c:\ /AL” command. Afterwards I could easily find the correct approach to get the reparse data (in a later blogpost I will explain how to use the APILogger-shim).

The key point is: You must open the directory only with “FILE_READ_EA” access!

Here is now the full working source-code for reading reparse-data:

#include "stdafx.h"
#include 

typedef struct _REPARSE_DATA_BUFFER {
  ULONG  ReparseTag;
  USHORT  ReparseDataLength;
  USHORT  Reserved;
  union {
    struct {
      USHORT  SubstituteNameOffset;
      USHORT  SubstituteNameLength;
      USHORT  PrintNameOffset;
      USHORT  PrintNameLength;
      ULONG   Flags; // it seems that the docu is missing this entry (at least 2008-03-07)
      WCHAR  PathBuffer[1];
      } SymbolicLinkReparseBuffer;
    struct {
      USHORT  SubstituteNameOffset;
      USHORT  SubstituteNameLength;
      USHORT  PrintNameOffset;
      USHORT  PrintNameLength;
      WCHAR  PathBuffer[1];
      } MountPointReparseBuffer;
    struct {
      UCHAR  DataBuffer[1];
    } GenericReparseBuffer;
  };
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;

#define REPARSE_DATA_BUFFER_HEADER_SIZE  FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)

#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 )

int _tmain(int argc, _TCHAR* argv[])
{
  HANDLE hFile;
  LPCTSTR szMyFile = _T("C:\\Documents and Settings");  // Mount-Point (JUNCTION)
  //LPCTSTR szMyFile = _T("C:\\Users\\All Users");  // Symbolic-Link (SYMLINKD)

  hFile = CreateFile(szMyFile, FILE_READ_EA, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    _tprintf(_T("Could not open dir '%s'; error: %d\n"), szMyFile, GetLastError());
    return 1;
  }

  // Allocate the reparse data structure
  DWORD dwBufSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
  REPARSE_DATA_BUFFER* rdata;
  rdata = (REPARSE_DATA_BUFFER*) malloc(dwBufSize);

  // Query the reparse data
  DWORD dwRetLen;
  BOOL bRet = DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, rdata, dwBufSize, &dwRetLen, NULL);
  if (bRet == FALSE)
  {
    _tprintf(_T("DeviceIoControl failed with error: %d\n"), GetLastError());
    CloseHandle(hFile);
    return 1;
  }
  CloseHandle(hFile);

  if (IsReparseTagMicrosoft(rdata->ReparseTag))
  {
    if (rdata->ReparseTag == IO_REPARSE_TAG_SYMLINK)
    {
      printf("Symbolic-Link\n");
      size_t slen = rdata->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
      WCHAR *szSubName = new WCHAR[slen+1];
      wcsncpy_s(szSubName, slen+1, &rdata->SymbolicLinkReparseBuffer.PathBuffer[rdata->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], slen);
      szSubName[slen] = 0;
      printf("SubstitutionName (len: %d): '%S'\n", rdata->SymbolicLinkReparseBuffer.SubstituteNameLength, szSubName);
      delete [] szSubName;

      size_t plen = rdata->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR);
      WCHAR *szPrintName = new WCHAR[plen+1];
      wcsncpy_s(szPrintName, plen+1, &rdata->SymbolicLinkReparseBuffer.PathBuffer[rdata->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], plen);
      szPrintName[plen] = 0;
      printf("PrintName (len: %d): '%S'\n", rdata->SymbolicLinkReparseBuffer.PrintNameLength, szPrintName);
      delete [] szPrintName;
    }
    else if (rdata->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
    {
      printf("Mount-Point\n");
      size_t slen = rdata->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
      WCHAR *szSubName = new WCHAR[slen+1];
      wcsncpy_s(szSubName, slen+1, &rdata->MountPointReparseBuffer.PathBuffer[rdata->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], slen);
      szSubName[slen] = 0;
      printf("SubstitutionName (len: %d): '%S'\n", rdata->MountPointReparseBuffer.SubstituteNameLength, szSubName);
      delete [] szSubName;

      size_t plen = rdata->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR);
      WCHAR *szPrintName = new WCHAR[plen+1];
      wcsncpy_s(szPrintName, plen+1, &rdata->MountPointReparseBuffer.PathBuffer[rdata->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR)], plen);
      szPrintName[plen] = 0;
      printf("PrintName (len: %d): '%S'\n", rdata->MountPointReparseBuffer.PrintNameLength, szPrintName);
      delete [] szPrintName;
    }
    else
    {
      printf("No Mount-Point or Symblic-Link...\n");
    }
  }
  else
  {
    _tprintf(_T("Not a Microsoft-reparse point - could not query data!\n"));
  }
  free(rdata);
  return 0;
}

ADD (2008-03-07): Thanks to the comment of Sergey, I now changed the sample code to correctly display JUNCTIONs (mount points) and SYMLINKD (symbolic-links). It seems that the documentation of REPARSE_DATA_BUFFER is incorrect, because the missed a field named “Flags”. I changed the structure to the correct definition which can be found in the DDK header file “ntifs.h”.

marshal_as library in VC2008

The VC++ team added a (simple) marshal_as library in VC2008. But this library only supports simple datatypes like

  • String^ to char* / wchar_t* / BSTR / bstr_t / CComBSTR / std::string / std::wstring / CString<char> / CString<wchar_t>  and vice versa
  • IntPtr to HANDLE  and vice/versa

So, the support only contains “Strings” and “Handle”. But this are the most commonly needed scenarios for marshaling.
The usage of some of the string marshaling is shown here:
http://www.c-plusplus.de/forum/viewtopic-var-p-is-1455833.html#1455833

An overview of the build-in marshaling can be found here: Overview of Marshaling in C++

There is a website (http://www.marshal-as.net/), in which you can look-up additional marshaling implementations like:

Hopefully many, more will follow…
If you have an implementation of some kind of “marshal_as”, just put a comment to Kate Gregory

Local user-mode dumps on Vista SP1 and W2k8

Starting with Vista, MS introduced the new Windows Error Reporting API (WER) and removed Dr. Watson from the system. The WER-System now directly sends by default the data to WinQual. They also changed the behaviour, that a minidump is only generated if the WinQual-Server requests this. So by default there is no way to get to a local minidump if you are not registered at WinQual (which costs 400$/year).

Several ISVs complained about this issue. And now MS added a new feature in VIsta SP1 and W2k8 to allow the creationg of local minidump regardless of the WER setting. FOr more info see:

Collecting User-Mode Dumps

Want to download the complete .NET source code?

In one of my last posts I said that the .NET source code is available. While this is still true, you might have seens some trouble in getting the source, because the source is only available on demand while debugging a component. Also you must setup VS to retrive the source/PDBs.

But if you just want to look at the source, there was no easy way to do this.

On CodePlex there is now a tool called NetMassDownloader, which allows to download the complete source (or only from indivudual DLLs) to a directory.

Have fun!

Smallest application size for win32 console application

Several times someone asked the question: “Why is a simple console application 52 KB (CRT statically linked) or 7 KB (CRT as DLL) in size?”

The answer is: Because it uses the CRT ;)

Just to show you, that you can have smaller applications, here is (from my point of view) the smallest “usefull” application (Hello world):

// Filename: smallest.cpp 
#include 
#pragma comment(linker, "/entry:entry") 

void entry() 
{ 
  TCHAR szText[] = TEXT("Hello world\n"); 
  DWORD dwWritten; 
  WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), szText, lstrlen(szText), &dwWritten, NULL); 
}

You must compile and link this file with:
cl /c /O1 /GS- smallest.cpp
link /subsystem:console smallest.obj kernel32.lib

Then you get an EXE file with 2048 Bytes.

You can even reduce it more if you merge the sections together and specify an alignment for the linker (but then program will then not run on Win98;) ):
cl /c /O1 /GS- smallest.cpp
link /subsystem:console /MERGE:.rdata=.text /ALIGN:16 smallest.obj kernel32.lib

Then you get an EXE file with 688 Bytes (VS2008) / 768 Bytes (VS2010).

So the question is: why does the CRT need so much space?

Most of the space is needed for “security reasons”. That is also the reason why I needed the specify the “/GS-” compiler switch to reduce the exe size. If you enable the buffer security checks, it triggers the compiler to use several functions and structures, which must be provided by the CRT.
And if you use the CRT, it also provides the default entry point and initializes all kind of stuff like calling constructors of static/global classes and others.
These are the main reasons why the CRT uses so much memory.

Another think is: if you want to use floating-point values, you also need the CRT. The reason is not very obvious for me… but the compiler is triggering the symbol “_fltused” to be linked with the image. If you provide your own “implementation” of this variable, it seems to work; but I don’t understand why the compiler is needing this variable. This variable must be set to a “magic” value (0×9875); I don’t know where this value comse from (expect it is defined in the CRT sources).

If you want to compile these samples from within the VS (2008) IDE, then you need to change the following setings from the default “Win32 Console Application”:

  • Switch to “Release” configuration
  • C/C++|Code Generation|Buffer Security Check: No (/GS-)
  • Linker|Debugging|Generate Debug Info: No 
  • Linker|Advanced|Entry Point: entry
  • Linker|Advanced|Randomize Base Address: Disable Image Randomization (/DYNAMICBASE:NO)
  • Linker|Advanced|Fixed Base Address: Image must be loaded at a fixed address (/FIXED)
  • Manifest Tool|Input and Output|Embed Manifest: No

And then you have your 2KB application from within the IDE ;)