Introduction
For Sample Application refer: http://www.codeproject.com/threads/BlackSabbatch.asp
Why do you need an Application configuration file?
Application configuration file can be compared to a Windows INI file. But XML being a modern standard and that a schema can be used to effortlessly validate these file makes it very attractive to use. Also an XML file gives more flexibility to structure, author and validate an application configuration file. My sample here uses MSXML parser. The class is thread safe. What I mean by thread safe is that, when used in a multi threaded project, the application configuration file will work fine. The extra task I had to do here was to Marshall the MSXML DOM COM object across threads.
="1.0"="UTF-8"
<ApplicationConfig>
<Debug> <AppLogging> <BrokenLinksXMLFileName>
C:\MyProjects\BlackSabbath\BlackSabbath.xml</BrokenLinksXMLFileName>
<BrokenLinksXMLSchemaFileName>BlackSabbathSchema.xml
</BrokenLinksXMLSchemaFileName> <BrokenLinksXMLXSLTFileName>
BlackSabbath.xsl</BrokenLinksXMLXSLTFileName>
<LogFileName>C:\MyProjects\BlackSabbath\BlackSabbath.log</LogFileName>
<CurrentLoggingLevel>1</CurrentLoggingLevel> </AppLogging>
</Debug>
<Release> <AppLogging> <BrokenLinksXMLFileName>
C:\MyProjects\BlackSabbath\BlackSabbath.xml</BrokenLinksXMLFileName>
<BrokenLinksXMLSchemaFileName>BlackSabbathSchema.xml
</BrokenLinksXMLSchemaFileName>
<BrokenLinksXMLXSLTFileName>BlackSabbath.xsl
</BrokenLinksXMLXSLTFileName>
<LogFileName>C:\MyProjects\BlackSabbath\BlackSabbath.log</LogFileName>
<CurrentLoggingLevel>1</CurrentLoggingLevel> </AppLogging>
</Release>
</ApplicationConfig>
The above said file is BlackSabbathConfig.xml in the multithreaded sample provided at http://www.codeproject.com/threads/BlackSabbatch.asp. It has configuration settings for debug and release builds. Now the task is to write a reader class that is thread safe to access the configuration values. Since this is only a reader for a read only file, we don’t need a synchronization object to protect this class operation. The class name is CAppConfigReader
and it is a singleton class, which has to be created and released once per application.
Creation
Can be done using a call of GetInstance()
with the name of the configuration file.
CAppConfigReader* pConfig = CAppConfigReader::GetInstance(
"C:\\MyProjects\\BlackSabbath\\BlackSabbathConfig.xml");
ASSERT(NULL != pConfig);
VERIFY(NULL != pConfig);
Accessing from threads
Every subsequent call should not provide the file name, as it will result in reopening and loading the said file. The reason is because the implementation is singleton as there will be only one and only one application configuration file. If more configuration file reads needed, please change implementation from singleton to normal creation.
CAppConfigReader* pConfig = CAppConfigReader::GetInstance();
if(NULL != pConfig)
{
csLogLevel = pConfig->GetValue(("/AppLogging/CurrentLoggingLevel"));
logLevelM = (eLoggingLevel)atoi(csLogLevel);
}
Initialize COM
COM has to be initialized on the thread trying to use this class as the DOM object is a COM object. MSXML4 dll is marked both and will work in Apartment threads and Free threads. Hence you can use OleInitialize(),CoInitialize(),CoInitializeEx(COINIT_APARTMENTTHREADED)
for Apartment threading or CoInitializeEx (COINIT_MULTITHREADED) for Free threading. Free threaded initialization is faster and more efficient.
How to get a configuration Value
When you pass /AppLogging/LogFileName to pConfig->GetValue()
, the function internally prefix Debug or Release as the build and makes it //Debug/AppLogging/LogFileName or //Release/AppLogging/LogFileName. The usage is as follows
CAppConfigReader* pConfig = CAppConfigReader::GetInstance();
if(NULL != pConfig)
{
csLogFileName = pConfig->GetValue(CString("/AppLogging/LogFileName"));
}
Releasing
COM interfaces are thread affinitive. What does this statement mean? COM interfaces belong to the thread that created and lives and dies with them. What if other threads needed to access this interface. The parent has to Marshall this interface to the other thread. What will happen if we just used this interface from a thread safe global variable? Everything will be fine, but when we access a method or property of this interface, COM will throw a RPC_E_WRONG_THREAD
error. One of the standard functions available for marshalling purpose is CoMarshalThreadInterfaceInStream()
and CoGetinterfaceAndReleaseStream()
. These are old functions; we have an easy way to do this. IGlobalInterfaceTable
referred to as GIT. This interface internally implements marshalling between threads.
Important: Only the thread that Created/Registered the interface can revoke it. This implies that the thread that created has to stay alive till revoked. Yes that's true. When the thread that created it dies, the interface also becomes invalid. So please make sure you call the CAppConfigReader::GetInstance("C:\\MyProjects\\BlackSabbath\\BlackSabbathConfig.xml" )
and CAppConfigReader :: Release
from the same thread which will live till the end of the application. It should be preferably main thread. Single threaded application can use this without any worries. No need of using GIT in implementation either.
I am a software programmer with 17 years of experience working on Microsoft technologies using various tools. Using spare time I play Ping Pong and enjoy music and movies.I come from Trichur, a town in kerala, India. I love code forums and feel obligated to contribute every once in a while.
I think Internet and the information share it provides is mind boggling. Do we ever believe, tomorrow always brings to reality,above and beyond what dreams can glimpse.