How to parse and read web.Config or app.Config in C++/on Windows0 Reacties

Just a tiny gem, which not often would be required but it can save you some time. It also demonstrates the power of the IXmlReader in unmanaged code. Because, as far I am aware of, the processing time a .config file measuring it with TickCount always is 0 ms (too small to measure). Microsoft has optimized the XML Reader implementation for fast forward reading, and it also does not allocate strings in memory, it just passes the pointer to the unicode strings (either key or value). In line with that, you might appreciate :) why I attach to the BSTR key to find as well.

What this class does, it reads the   section and puts the key value pairs in a ‘named value collection’ item.

Note 1: I am a big fan of CComBSTR when the final client still understands COM/automation. That is the reason I did not use CString in this class. In addition, the CComBSTR class has been boosted by me, to optimize reallocation of existing memory. But you can use the default MS implementation as well. So, you can change CSimpleMap to CSimpleMap if you wish.

Note 2: The .config file is cached but it is parsed again if the filewritetime of the .config file was changed.

config.h header.

#include
#include
#pragma once


using namespace ATL;

class ConfigurationManager
{
private:
    static const int DELAYTICKS = 1000;
    std::map _map;
    time_t _ftLastCheck;
    CComPtr _xmlReader;
    CComPtr _malloc;
    HRESULT CheckTimeOut();
    //ansi version!
    CComBSTR _szFilePath;
   
    void Init();

public:
    ConfigurationManager();
    ConfigurationManager(const BSTR configFile);
    std::wstring& AppSettings(const std::wstring key, PCWSTR defaultValue = NULL);
    BSTR AppSettings(const BSTR key, PCWSTR defaultValue = NULL);
    time_t GetFileTime();
    ~ConfigurationManager();
};

 

Implementation:


#include
#include
#include "config.h"
#pragma comment(lib, "xmllite.lib")


ConfigurationManager::ConfigurationManager(const BSTR configFile) throw()
{
    _szFilePath = configFile;
    time(&_ftLastCheck);
    Init();
}
ConfigurationManager::ConfigurationManager() throw()
{   
    time(&_ftLastCheck);
    _szFilePath.Attach(GetModulePath());   
    if (!_szFilePath.IsEmpty())
    {       
        _szFilePath.Append(L".config");
       
        Init();
    }   
}
void ConfigurationManager::Init() throw()
{
    if (!_szFilePath.IsEmpty())
    {
        HRESULT hr = CoGetMalloc(1, &_malloc);
        hr = CreateXmlReader(IID_IXmlReader, (void**)&_xmlReader, _malloc);
        if (FAILED(hr))
        {
   
        }
    }
}
time_t ConfigurationManager::GetFileTime() throw()
{   
    struct stat stResult;
    CComBSTR ansi(_szFilePath);
    ansi.Attach(ansi.ToByteString());
    ::stat((char*)ansi.m_str, &stResult);        // get the attributes of afile.txt
   
    return stResult.st_mtime;
}
BSTR ConfigurationManager::AppSettings(const BSTR key, PCWSTR defaultValue) throw()
{
   
    HRESULT hr = CheckTimeOut();
    if (FAILED(hr))
    {
      
        return NULL;
    }
    CComBSTR find;
    find.Attach(key);
   
    auto found = _map.find(find);
    find.Detach();
       if (found != _map.end())
    {
        return found->second.Copy();
    }
    else if (defaultValue != NULL)
    {
        return ::SysAllocString(defaultValue);
    }
    return NULL;
   
}
ConfigurationManager::~ConfigurationManager() throw()
{
    _map.clear();
    _xmlReader.Release();
    _malloc.Release();
    _szFilePath.Empty();
}
HRESULT ConfigurationManager::CheckTimeOut() throw()
{
   
    auto curT = GetFileTime();
   
    PCWSTR pwzValue;
    auto memResult = ::difftime(curT, _ftLastCheck);
    if (memResult != 0.0F)
    {
        DWORD start = ::GetTickCount();
   
        HRESULT hr = S_OK;
       
        CComPtr pStream;
        CComPtr _readerInput;
       
        hr = ::SHCreateStreamOnFileEx(_szFilePath, STGM_READ | STGM_SHARE_DENY_NONE, FILE_ATTRIBUTE_NORMAL, FALSE,NULL, &pStream);

        if (SUCCEEDED(hr))
        {
            hr = ::CreateXmlReaderInputWithEncodingCodePage(pStream, _malloc, CP_UTF8, TRUE, NULL, &_readerInput);           
            hr = _xmlReader->SetProperty(XmlReaderProperty_DtdProcessing, DtdProcessing_Prohibit);   
            hr = _xmlReader->SetInput(_readerInput);
        }   
        else
        {
            return hr;
        }
       
        XmlNodeType nodeType = XmlNodeType::XmlNodeType_None;
        UINT lenValue;
        PCWSTR key;
        bool startCollecting  = false;
        while (S_OK == _xmlReader->Read(&nodeType) && hr == S_OK)
        {
            switch(nodeType) {
            case XmlNodeType::XmlNodeType_EndElement:
               
                //hr = pReader->GetDepth(&dept);
                hr = _xmlReader->GetLocalName(&pwzValue, NULL);
                if (startCollecting && lstrcmpW(pwzValue, L"appSettings") == 0)
                {
                    //break loop
                    hr = S_FALSE;
                }
                break;
            case XmlNodeType::XmlNodeType_Element:
                {
                    // get element name such as option in

                        hr = _xmlReader->MoveToAttributeByName(L"configSource", NULL);
                        if (hr == S_OK)
                        {
                            hr = _xmlReader->GetValue(&pwzValue, NULL);
                            
                                              
                            if (::PathIsRelativeW(pwzValue) == TRUE)
                            {
                                //TODO: call back to do a Server.MapPath
                                _szFilePath.Attach(FileStripFile(_szFilePath));                               
                                _szFilePath.Append(L'\\');
                                _szFilePath.Append(pwzValue);
                            }
                            else
                            {
                                _szFilePath = pwzValue;
                            }
                            _readerInput.Release();
                            pStream.Release();
                            return CheckTimeOut(); //recursion                           
                        }
                        hr = S_OK;//reset otherwise loop stops
                    }                   
                    else if (startCollecting && lstrcmpW(pwzValue, L"add") == 0)
                    {
                       
                        hr = _xmlReader->MoveToAttributeByName(L"key", NULL);
                        if (hr == S_OK)
                        {
                            hr = _xmlReader->GetValue(&pwzValue, &lenValue);
                            //key.Append( pwzValue, lenValue);
                            key = pwzValue;

                            //ATLTRACE(L"found key %s %d\r\n", pwzValue, lenValue);
                            hr = _xmlReader->MoveToAttributeByName(L"value", NULL);
                            if (hr == S_OK)
                            {
                                _xmlReader->GetValue(&pwzValue, NULL);
                                _map.insert(std::pair(key, pwzValue));
                            }
                        }
                    }                   
                }
                break;
            }
        }
        if (SUCCEEDED(hr)) _ftLastCheck = curT;
        if (_xmlReader != NULL)
        {
            _xmlReader->SetInput(NULL);
        }
    
        return S_FALSE;
       
    }

    return S_OK;
   
};

Howto: Check the case sensitive existence of a file0 Reacties

Sometimes, especially for files running on external FTP servers, where file names are case sensitive, a file named myFILE.html, is not the same file as myfile.html in the same path!

This function, can be used on an NTFS path for that purpose where File.Exists would fail, because it is case insensitive. (However, it does not enable you to have two files with just a different case in the same path)

///


/// Checks existance of file using a case sensitive compare
///

/// must be full filename
///
static bool FileExists(string file)

{

string pathCheck = Path.GetDirectoryName(file);

string filePart = Path.GetFileName(file);

      if (string.IsNullOrEmpty(pathCheck))

      {

      throw new ArgumentException("The file must include a full path", file);

      }

      string[] checkFiles = Directory.GetFiles(pathCheck, filePart, SearchOption.TopDirectoryOnly);

      if (checkFiles != null && checkFiles.Length > 0)

      {

            //must be a binary compare

            return Path.GetFileName(checkFiles[0]) == filePart;

       }

       return false;

}

 

Howto: Properly use the AccessCheck API for the current user0 Reacties

I've seen people pulling the hair out for not getting this API workign for them.

The API, even if impersonating the current user, returns error 1309. "An attempt has been made to operate on an impersonation token by a thread that is not currently impersonating a client."

The clue is that this API, (and this is not clearly documented on the MSDN) needs a duplicated handle.

Anyway, spare your hair, have fun with the code. B.t.w. You can hire me for smart code and research on components etc..

http://www.adccure.nl for contact.

    [StructLayout(LayoutKind.Sequential)]

    internal struct GENERIC_MAPPING

    {

        internal uint GenericRead;

        internal uint GenericWrite;

        internal uint GenericExecute;

        internal uint GenericAll;

    }

    [DllImport("advapi32.dll", SetLastError = false)]

    static extern void MapGenericMask([In, MarshalAs(UnmanagedType.U4)] ref TokenAccessLevels AccessMask,

        [In] ref GENERIC_MAPPING map);

   

    [DllImport("advapi32.dll", SetLastError = true)]

    [return: MarshalAs(UnmanagedType.Bool)]

    public static extern bool DuplicateToken(IntPtr ExistingTokenHandle,

            [MarshalAs(UnmanagedType.U4)] TokenImpersonationLevel level,

            out int DuplicateTokenHandle);

    [DllImport("advapi32.dll", SetLastError = true)]       

     [return: MarshalAs(UnmanagedType.Bool)] static extern bool AccessCheck(

      [MarshalAs(UnmanagedType.LPArray)]

        byte[] pSecurityDescriptor, 

      IntPtr ClientToken,

      [MarshalAs(UnmanagedType.U4)]

        TokenAccessLevels accessmask,

      [In] ref GENERIC_MAPPING GenericMapping,

      IntPtr PrivilegeSet,

      ref int PrivilegeSetLength,

      out uint GrantedAccess,

      [MarshalAs(UnmanagedType.Bool)]

      out bool AccessStatus);

    [DllImport("kernel32")]

    static extern void CloseHandle(IntPtr ptr);

  internal static bool hasReadAccess(string path)

    {

        // Obtain the authenticated user's Identity

       

        WindowsIdentity winId = WindowsIdentity.GetCurrent(TokenAccessLevels.Duplicate | TokenAccessLevels.Query);

       

        WindowsImpersonationContext ctx = null;

        int statError = 0;

        IntPtr dupToken = IntPtr.Zero;

        try

        {

            // Start impersonating

            //ctx = winId.Impersonate(); works but AccessCheck does not like this

           

            int outPtr;

            //AccessCheck needs a duplicated token!

            DuplicateToken(winId.Token, TokenImpersonationLevel.Impersonation, out outPtr);

           

            dupToken = new IntPtr(outPtr);

            ctx = WindowsIdentity.Impersonate(dupToken);                

            Folder.GENERIC_MAPPING map = new Folder.GENERIC_MAPPING();

            map.GenericRead = 0x80000000;

            map.GenericWrite = 0x40000000;

            map.GenericExecute = 0x20000000;

            map.GenericAll = 0x10000000;

            TokenAccessLevels required = TokenAccessLevels.Query | TokenAccessLevels.Read | TokenAccessLevels.AssignPrimary | (TokenAccessLevels)0x00100000; // add synchronization

            MapGenericMask(ref required, ref map);

           

           

            uint status = 0;

            bool accesStatus = false;

            // dummy area the size should be 20 we don't do anything with it

            int sizeps = 20;

            IntPtr ps = Marshal.AllocCoTaskMem(sizeps);

           

            //AccessControlSections.Owner | AccessControlSections.Group MUST be included,

            //otherwise the descriptor would be seen with ERROR 1338

            var ACE = Directory.GetAccessControl(path,

                AccessControlSections.Access | AccessControlSections.Owner |

                    AccessControlSections.Group);

           

            bool success = AccessCheck(ACE.GetSecurityDescriptorBinaryForm(), dupToken, required, ref map,

                    ps, ref sizeps, out status, out accesStatus);

            Marshal.FreeCoTaskMem(ps);

            if (!success)

            {

                statError = Marshal.GetLastWin32Error();

            }

            else

            {

                return accesStatus;

            }

        }

        // Prevent exceptions from propagating

        catch (Exception ex)

        {

            Trace.Write(ex.Message);

        }

        finally

        {

            // Revert impersonation

           

            if (ctx != null)

                ctx.Undo();

            CloseHandle(dupToken);

        }

        if (statError != 0)

        {

            throw new Win32Exception(statError);

        }

       

        return false;

    }

This code is just a cut and paste. You can make it pretty.