Sergei Meleshchuk - blog

  • Unzip file via java.util.zip package from .NET code

    Unzip file via java.util.zip package from .NET code

    This week I had to modify a legacy java application that does quite a bit of unpacking of various zip files. Actually the file extensions are .EAR, .WAR, .JAR; those are all zip files. The target application will run under JRE 1.6.0_12.

    I had to find a way to code the logic in .NET, but still I need a number of files unzipped (then modify various manifests and descriptors and zip into .EARs again).

    Unfortunately, .NET/C# libraries do not have access to a direct equivalent of java.util.zip.* package. I considered few options, including new deflation namespace in .NET 3.5, a number of zlib-based options, use of J# namespace, and using interop to native code which in turn will JNDI to java.util.zip, purchasing a zip command line license, etc.

    Finally, I ended up with the simplest solution that works great for my needs; the needs/constraints being:

    -          Performance hit of 100 milliseconds per unzip operation is fine

    -          Need 100% same behavior as the one that Sun’s java has

    The solution is:

    -          Write small console java application, which will implement the needed unzip operations. My small java application takes

    o   <zip file name> <output directory name> [<zip entry name>]

    -          Somehow build this application

    -          Use this application from the .NET code. I set a small function for that.

    The following 1) briefly describes the small java application; this one is actually doing the zip/unzip operations, 2) describes how to build it and 3) shows the .NET part of the code.

    The java console application which actually unzips

    First, we define needed imports, with the zip-related ones:

    import java.io.File;

    import java.io.FileOutputStream;

    import java.io.InputStream;

    import java.io.IOException;

    import java.util.Enumeration;

    import java.util.zip.ZipEntry;

    import java.util.zip.ZipFile;

    import java.util.zip.ZipInputStream;

     

    Next, need few variables:

            File fileToUnpack = null;

            ZipFile zip = null;

            String targetDirName = null;

            String entryName = null;        // will stay null if unzip whole file

     

    Then we retrieve command-line arguments, validate them (like the zip/ear/war/jar file exists), and open the zip container. We are still in Sun’s java world.

            try

            {

                zip = new ZipFile(fileToUnpack);

            }

            catch (java.util.zip.ZipException e)

            {

                log("Cannot open zip file: " + e.toString());

                return;

            }

            catch (java.io.IOException e)

            {

                log("Cannot open zip file: " + e.toString());

                return;

            }

     

    In case the command line requested unzipping the whole file, the code path is below. Note few things like normalizing the output path…; if you miss this, you end up with the directory which is only partially functional. Also note the handling of the isDirectory case. The “streamToFile” code will follow.

            if (entryName == null) // unpack whole file

            {

                Enumeration en = zip.entries();

                while(en.hasMoreElements())

                {

                    ZipEntry entry = (ZipEntry)en.nextElement();

                    if (entry.isDirectory())

                    {

                        log("directory ignored: " +

                            entry.toString());

                        continue;

                    }

                    InputStream stm;

                    try {stm = zip.getInputStream(entry);}

                    catch (java.io.IOException e)

                    {

                        log ("Cannot get input stream for '" + entry.toString() + "' in zip file '" + fileToUnpack.getAbsolutePath());

                        return;

                    }

                    String targetFilename = targetDirName + "/" + entry.toString();

     

                    // create dir if needed

                   

                    int lastSlash = targetFilename.lastIndexOf("/");

                    if (lastSlash > 0)

                    {

                        String dirName = targetFilename.substring(0, lastSlash);

                        new File(dirName).mkdirs();

                    }

     

                    // Copy zip entry to output directory

                   

                    targetFilename = targetFilename.replace('/','\\');

                    streamToFile(stm, targetFilename);

                }

            }

     

    Now the function that actually copied a zip entry to the file.

        private static void streamToFile(InputStream stm, String targetFilename)

        {

            FileOutputStream stmOut = null;

            byte[] buffer = new byte[copyBufferSize];

            int bytecount;

           

            try {stmOut = new FileOutputStream(new File(targetFilename));}

            catch (java.io.IOException e)

            {

                log("Error opening output file '" + targetFilename + "': " + e.toString());

                System.exit(101);

            }

           

            try

            {

                while ((bytecount = stm.read(buffer)) > 0)

                {

                    stmOut.write(buffer, 0, bytecount);

                }

                stmOut.close();

            }

            catch (java.io.IOException e)

            {

                log("Error writing output file '" + targetFilename + "': " + e.toString());

                System.exit(101);

            }

        }

    We are almost done with java part, but not completely. The by far the most difficult part remains, at least for me: building the executable. I chose to package the result into the file that I named “main.jar”.

    There is a build tool which is similar to MSBUILD. While I did not find many MSBUILD features in ant (like automatic resolution of dependencies between projects), it’s very likely I don’t know enough about ant. Anyhow, the ant works great for me.

    Building java application

    <?xml version="1.0"?>

     

    <project name="ops-tool" default="all">

     

      <target name="set">

        <property environment="env"/>

        <property name="flavor" value="retail"/>

      </target>

     

     

      <target name="init" depends="set">

        <path id="classpath">

        </path>

      </target>

     

      <target name="compile" depends="init">

        <mkdir dir="classes"/>

        <javac srcdir="." destdir="classes"

               classpathref="classpath">

          <classpath>

            <pathelement location="${classpath}"/>

          </classpath>

        </javac>

      </target>

     

      <target name="jar" depends="compile">

        <delete dir="lib" quiet="yes"/>

        <mkdir  dir="lib"/>

        <jar

          destfile="lib/main.jar"

          basedir="classes"

          manifest="MANIFEST.MF"

          filesonly="yes">

        </jar>

      </target>

     

      <target name="all" depends="jar"/>

     

      <target name="clean" depends="init">

        <delete includeEmptyDirs="yes" quiet="yes">

          <fileset dir="${lib}"/>

          <fileset dir="${classes}"/>

        </delete>

      </target>

     

    </project>

    Testing java application

    I grabbed few different ZIP, JAR, EAR files and run the program from the command line test script, which I named run.cmd:

    m:\jdk1.6.0_12\bin\java -jar lib\main.jar utils.zip.Main %*

     

    For example, I run like:

    run unittest3\lib\EXML.jar unittest5

    Now .NET part

    This is by far the easiest of course. The following is simple testing code, with the procedure to actually call the java unzip as a part of it (what I am saying, this is not the final code; this works great).

    Declaration is:

    namespace J2eePack

    {

        using System;

        using System.Collections.Generic;

        using System.Diagnostics;

        using System.IO;

        using System.Linq;

        using System.Reflection;

        using System.Text;

     

        class Program

        {

            private const string jzip = @"…";  // this is the location of lib\main.jar

            . . .

    Now, let’s call couple of un-zipping functions. Only the bold part is interesting:

                private const string testEar = @"I:\lm\service\se\lm8\private\service-next\private\ops\test\JZIP\unittest\deployable.ear";

                private const string exmlJar = @"I:\lm\service\se\lm8\private\service-next\private\ops\test\J2eePack\J2eePack\unpack2\lib\EXML.jar";

                private const string exmlOut = @"I:\lm\service\se\lm8\private\service-next\private\ops\test\J2eePack\J2eePack\unpack2\lib\EXML";

                private const string targetSubDir = "unpack2";

     

                // Target location

     

                string current = Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName;

                current = Path.GetDirectoryName(current);

                current = Path.GetDirectoryName(current);

                current = Path.GetDirectoryName(current);

                string targetDir = Path.Combine(current, targetSubDir);

               

                // Unpack

     

                Unzip(testEar, targetDir);

                Unzip(exmlJar, exmlOut);

                // test result

     

                string[] files = Directory.GetFiles(targetDir, "*", SearchOption.AllDirectories);

                foreach (string file in files)

                {

                    Console.WriteLine(file);

                }

     

    The actual code that is doing all the work (by calling the java console program) is:

            /// <summary>

            /// Build and execute %java_home%\bin\java.exe -jar [loc]\lib\main.jar utils.zip.Main  [file to unzip] [targetDir];

            /// </summary>

            /// <param name="jar">Full or relative path to the file to unzip</param>

            /// <param name="targetDir">The directory to which the zip file will be unzipped to. The directory will be created if missing.</param>

            /// <returns>true if no erros detected.</returns>

            /// <remarks>Note that the example code below allows only 5 seconds for the callee to complete. Modify this to meet the

            /// production requirements. Also, note that the below example does not check the exit code of the java proccess.

            /// Please add a line when building the production code.

            /// </remarks>

            private static bool Unzip(string jar, string targetDir)

            {

                // java exec

     

                string javaHome = Environment.GetEnvironmentVariable("java_home");

                if (javaHome == null)

                {

                    Console.WriteLine("cannot obtain java_home");

                    // javaHome = @"M:\jdk1.6.0_12";

                    Environment.ExitCode = 101;

                    return false;

                }

                string java = Path.Combine(javaHome, @"bin\java.exe");

     

                // arguments

     

                string args = " -jar " + jzip + @"\lib\main.jar utils.zip.Main " + jar + " " + targetDir;

                Console.WriteLine(args);

     

                // shell out

     

                Process shell = new Process();

                shell.StartInfo.FileName = java;

                shell.StartInfo.Arguments = args;

                shell.StartInfo.UseShellExecute = false;

                shell.Start();

                bool ok = shell.WaitForExit(5000);

                if (!ok)

                {

                    Console.WriteLine("Error");

                    return false;

                }

     

                return true;

            }

     

  • Azure storage viewer

    Azure storage viewer

    (see http://blogs.msdn.com/sergeim for updates)

    Contents

    Introduction. 1

    System requirements. 1

    Sample screenshots: 1

    Installation. 3

    Step 1. Install and start the Viewer. 3

    Step 2. Modify configuration file. 4

    Step 3. Start browsing your storage. 5

     

     

    Introduction

    If you have registered for Azure Services at http://www.microsoft.com/azure/register.mspx, you may want to explore what is in your storage – your queues, blobs and tables. You can use the “Azure storage viewer” to browse your storage. To a limited extent, you can also add and delete objects in Azure storage (under your account, of course).

    The two screen shots below show messages in one of the queues, and contents of a small table.

    Azure storage viewer supports both mouse and keyboard navigation. It can be used as a learning tool, or as a content browser for small experimental projects.

    System requirements

    I tested on Windows 2003 SP2, Vista x32 and x64, Windows 2008 x64 (Standard and DC). I was admin on all machines. Viewer doesn’t need to run elevated.

    Sample screenshots

     

     

     

     

    Installation

     

    Step 1. Install and start the Viewer.

    The installation is ClickOnce-based. Type

    start http://meleshchuk.members.winisp.net/quince/default.htm

    in the command window (or click the URL), then click install.

    Note: if you already have .NET FX 3.5 SP1 installed, you can just start the Azure storage viewer by typing http://meleshchuk.members.winisp.net/quince/quince.application - the IE understands the “.application” file type.

    If you don’t have FX 3.5 SP1, then you will be prompted to install it. Installation of the FX will start automatically. It takes quite a while.

    The viewer will start and display the “credentials dialog”. Next, modify configuration file. See next step.

     

    Step 2. Modify configuration file

    When the viewer detects connection problem (with the storage), it brings up the “credentials dialog”. Either click “Edit credentials” or “Edit in notepad”. The first option will read configuration file and fillin the two text boxes with current values; copy-paste your account and shared key. The second option opens the configuration file, which looks like below. You can also open configuration file at any time by clicking “Edit Configuration” button.

    Change yellow entries to you account and shared key values that you received when signing up to Azure services. Save configuration file (CTRL+S) in case you selected the option to edit the configuration file.

    Click OK. You data should be displayed now.

    Disclaimer: It is the ClickOnce install generator that placed the viewer under Microsoft group, and I did not figure out yet how to change the group name to something more modest.

    . Disclaimer: It is the ClickOnce install generator that placed the viewer under Microsoft group, and I did not figure out yet how to change the group name to something more modest.

    <?xml version="1.0" encoding="utf-8" ?>

    <configuration>

      <appSettings>

       

        <!-- change 2 lines below -->

     

        <add key = "AccountName" value="sergeim"/>

        <add key = "AccountSharedKey"

             value="3SV+JU1S+wDiHPrNEIeOOk2b4Z…u5zPBGbB8CV+t6RfQ=="/>

       

        <!-- no need to change -->

       

        <add key="BlobStorageEndpoint" value="http://blob.core.windows.net"/>

        <add key="QueueStorageEndpoint" value="http://queue.core.windows.net"/>

        <add key="TableStorageEndpoint" value="http://table.core.windows.net"/>

       

        </appSettings>

    . . .

     

    Step 3. Start browsing your storage.

     

    Most of the tree view entries present a context menu when right-clicked.  Like below.

     

    Pure viewing, however, can be done by just using keyboard arrows.

    Typical screenshot of the just started viewer is shown below.

     

     

     

     

     

    Left pane updates as the selection of the tree items changes:

     

     

  • Accessing Azure storage with Raw REST API

    Accessing Azure storage with Raw REST API

    Sergei Meleshchuk - blog 

    Contents

    Introduction. 1

    Example. 2

    Client code: read a message from a queue. 2

    Missing parts. 3

    QueueBase. 3

    ServiceEndpoint. 3

    BuildSignature. 3

    BuildUri 4

    BuildRequest. 4

    ExecuteRequest. 4

    DecodeMessages. 5

    CurrentMessageList. 6

    MessageBase. 6

     

     

    Introduction

    Azure SDK defines [Raw] REST API, and supplies a client library which isolates a developer from REST API. The client library is a good thing.

    Sometimes, however, you may want much thinner layer (than the client library) on top of REST API. The reasons include:

    -          You want to understand the Azure REST API, without tearing your way thru the code of the client library,

    -          You want to really understand the client library code, and need to start with a much simpler version of it.

    I describe here what is probably the thinnest possible layer on top of the Azure REST API. This “Mini storage library” is easy to learn and understand, and it can be used on its own to access the Azure storage. I am totally ignoring (in this post) the needs of table storage. I chose to concentrate on queue and blob storage operations instead.

    The “mini library” a very small number of short functions, which you can completely inline if you want. The class structure is close to absent as well.

    Example

    I will show how to read a message from the queue storage. The code is, sure, more lengthy then in case of using the client library. However, this code if very close “to the metal”, and you can factor out the boilerplate part very easily. I did not do this intentionally.

    The code is followed by the description of the missing parts – the small set of small functions.

    Client code: read a message from a queue

     

            private void LibReadOne(object sender, RoutedEventArgs e)

            {

                TimeSpan elapsed;

     

                // Read configuration data: service URI, account name, access key

     

                string queueName = "registration2";

                QueueBase queue = new QueueBase(queueName);

     

                string date = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);

     

                string method = "GET";

                string suffix = queueName + "/" + "messages";

                string signature = BuildSignature(method, date, queue.Account,

                    queue.Key, suffix);

                suffix += "?numofmessages=1&visibilitytimeout=5";

                Uri uri = BuildUri(method, queue, suffix);

                HttpWebRequest request = BuildRequest(method, uri, queue.Account,

                    signature, date);

                DateTime starting = DateTime.Now;

                XmlDocument doc = ExecuteRequest(request, out elapsed);

                Trace("ExeReq time {0}\n", DateTime.Now - starting);

                lock (CurrentMessageListLock)

                {

                    /*List<MessageBase>*/

                    CurrentMessageList = DecodeMessages(doc);

                }

                //Trace("Decode time {0}\n", DateTime.Now - starting);

                if (CurrentMessageList != null)

                    foreach (MessageBase mm in CurrentMessageList)

                        Trace("[MessageText] {0}\n", mm.MessageText);

                else

                    Trace("[No messages]\n");

            }

     

    Missing parts

     

    Below are all the class definition, and low-level functions, needed for the above code to work.

    QueueBase

            class QueueBase : ServiceEndpoint

            {

                public string QueueName { get; set; }

                public QueueBase(string queueName)

                {

                    this.QueueName = queueName;

                    base.BaseUri = new Uri(ConfigurationSettings.AppSettings["QueueStorageEndpoint"]);

                    base.Account = ConfigurationSettings.AppSettings["AccountName"];

                    base.Base64Key = ConfigurationSettings.AppSettings["AccountSharedKey"];

                }

            }

     

    ServiceEndpoint

        public class ServiceEndpoint

        {

            public Uri BaseUri { get; set; }

            public string Account { get; set; }

            public string Base64Key { private get; set; }

            public byte[] Key

            {

                get { return Convert.FromBase64String(Base64Key); }

            }

        }

     

    BuildSignature

     

            string BuildSignature(

                string method,

                string date,

                string accountName,

                byte[] sharedKey,

                string suffix)

            {

                string signPart1 = string.Format(CultureInfo.InvariantCulture,

                    "{0}\n{1}\n{2}\n{3}\n{4}\n/",

                    method,                 // 0 - HTTP Method, like GET

                    string.Empty,           // 1 - "Content-MD5" header value. Leave blank.

                    string.Empty,           // 2 - Content type, like MIME. Leave blank.

                    string.Empty,           // 3 - Legacy "date" header. Leave blank.

                    "x-ms-date:" + date);   // 4 - Azure form of date header.

     

                string signPart2 = accountName + "/" + suffix;

                string signatureString = signPart1 + signPart2;

     

                byte[] signBytes = System.Text.Encoding.UTF8.GetBytes(signatureString);

                byte[] hash = new HMACSHA256(sharedKey).ComputeHash(signBytes);

                string signature = System.Convert.ToBase64String(hash);

                return signature;

            }

     

    BuildUri

     

            Uri BuildUri(string method, ServiceEndpoint svc, string suffix)

            {

                string accountUriString = string.Format(

                    CultureInfo.InvariantCulture,

                    "{0}{1}{2}.{3}",

                    svc.BaseUri.Scheme,

                    Uri.SchemeDelimiter,

                    svc.Account,

                    svc.BaseUri.Host);

     

                string divisorToken = suffix.Contains('?') ? "&" : "?";

                string uriString = string.Format(CultureInfo.InvariantCulture,

                    "{0}/{1}{2}{3}",

                    accountUriString,

                    suffix,

                    divisorToken,

                    "timeout=30");

                Uri uri = new Uri(uriString);

                return uri;

            }

     

    BuildRequest

     

            static HttpWebRequest BuildRequest(

                string method,

                Uri uri,

                string accountName,

                string signature,

                string date)

            {

                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);

                request.Timeout = 30 * 1000;

                request.ReadWriteTimeout = 30 * 1000;

                request.Method = method;

                request.ContentLength = 0;

                request.Headers.Add("x-ms-date", date);

                string AuthHeaderValue = string.Format(CultureInfo.InvariantCulture,

                                              "SharedKey {0}:{1}",

                                              accountName,

                                              signature);

                request.Headers.Add("Authorization", AuthHeaderValue);

                return request;

            }

     

    ExecuteRequest

     

            XmlDocument ExecuteRequest(

                HttpWebRequest request,

                out TimeSpan elapsed)

            {

                DateTime startTime = DateTime.Now;

                elapsed = TimeSpan.MinValue;

                try

                {

                    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())

                    {

                        if (response.StatusCode == HttpStatusCode.OK)

                        {

                            using (Stream stream = response.GetResponseStream())

                            {

                                elapsed = DateTime.Now - startTime;

                                XmlDocument doc = new XmlDocument();

                                try

                                {

    #if (DIRECTPARSE)

                                    byte[] data = new byte[10000];

                                    int length = stream.Read(data, 0, 10000);

                                    string dataString = Encoding.UTF8.GetString(data, 0, length);

    #endif

                                    doc.Load(stream);   // doc = output param

                                    stream.Close();

                                    response.Close();

                                    return doc;        // indicate success

                                }

                                catch (XmlException xe)

                                {

                                    stream.Close();

                                    response.Close();

                                    throw xe;

                                }

                            }

                        }

                        else

                        {

                            string errorMessage = response.ToString();

                            throw new InvalidOperationException("Retcode wrong " + errorMessage);

                        }

                    }

                }

                catch (WebException we)

                {

                    if (we.Response != null)

                    {

                        HttpWebResponse resp = (HttpWebResponse)we.Response;

                        string description = resp.StatusDescription;

                        if (description != null)

                        {

                            throw new InvalidOperationException(description);

                        }

                    }

                    throw new InvalidOperationException(we.ToString());

                }

            }

     

    DecodeMessages

     

            List<MessageBase> DecodeMessages(XmlDocument doc)

            {

                List<MessageBase> readMessages = null;

                MessageBase msg;

                XmlNodeList messagesNodes = doc.SelectNodes("//QueueMessage");

                if (messagesNodes.Count == 0)

                {

                    return null;

                }

                readMessages = new List<MessageBase>();

                foreach (XmlNode messageNode in messagesNodes)

                {

                    msg = new MessageBase();

                    msg.MessageId = messageNode.SelectSingleNode("MessageId").FirstChild.Value.Trim();

                    Debug.Assert(msg.MessageId != null);

                    if (messageNode.SelectSingleNode("PopReceipt") != null)

                    {

                        msg.MessagePopReceipt = messageNode.SelectSingleNode("PopReceipt").FirstChild.Value.Trim();

                        Debug.Assert(msg.MessagePopReceipt != null);

                    }

                    string base64 = messageNode.SelectSingleNode("MessageText").FirstChild.Value.Trim();

                    byte[] messageBytes = Convert.FromBase64String(base64);

                    msg.MessageText = Encoding.UTF8.GetString(messageBytes, 0, messageBytes.Length);

                    readMessages.Add(msg);

                }

                return readMessages;

            }

     

    CurrentMessageList

     

            List<MessageBase> CurrentMessageList;

     

    MessageBase

     

            class MessageBase

            {

                public string MessageId { get; set; }

                public string MessageText { get; set; }

                public string MessagePopReceipt { get; set; }

            }

     

     

  • How to do ETW logging from .NET application

    How to do ETW logging from .NET application

    Sergei Meleshchuk - blog 

    Contents

    Introduction. 1

    Sessions and providers. 1

    Sample tracing application. 1

    Starting tracing session. 2

    Stopping and decoding tracing session. 3

     

     

    Introduction

    ETW, Event tracing for Windows, provides strategic benefits like:

    -          You can log to same file from different threads and processes,

    -          Extremely fast,

    -          Tailing log records never lost, even your application crashes without warning,

    -          You can turn tracing on and off, change level or set of tracing components, without restarting the application

    -          And so on.

    There is a small penalty – namely, you have to decode trace files.

    .NET 3.5 provides tracing facilities for ETW. This post is a short reference of how to use the ETW in .NET.

    Sessions and providers

    You application will be the ‘trace provider’ (see example code about how to advertize the provider). Exposing provider is not enough to start tracing: you have to use one of the ETW command line utilities to actually enable tracing. Of course, this can be done from code; in the latter case you need some interop-ing and I am not writing about this here.

    Now, to set up ETW tracing, you have to:

    -          Author application appropriately:

    o   Register the listener, all as usual

    o   Create trace session via e.g. logman utility

    o   Start session

    To decode the trace file, you have to:

    -          Close tracing session via logman utility

    -           Decode the log file via another utility, tracerpt.

    The remaining part of this post provides details.

    Sample tracing application

     

    using System;

    using System.Diagnostics;

    using System.Diagnostics.Eventing;

     

    namespace EtwSample

    {

        class Program

        {

            private static Guid providerId =

                new Guid("{A11a0820-c24b-428c-83e2-26b41091702e}");

            static void Main(string[] args)

            {

     

                EventProviderTraceListener listener =

                    new EventProviderTraceListener(providerId.ToString());

                TraceSource source =

                    new TraceSource("MyProvider", SourceLevels.All);

                source.Listeners.Add(listener);

                source.TraceData(

                    TraceEventType.Warning | TraceEventType.Start, 2,

                    new object[] { "abc", "def", true, 123 });

     

                /* Uncommen below if you want more fancy logging ...

                source.TraceEvent(

                    TraceEventType.Warning, 12, "Provider guid: {0}",

                    new object[] { providerId });

                source.TraceInformation(

                   "string {0}, bool {1}, int {2}, ushort {3}",

                   new object[] { "abc", false, 123, (UInt32)5 });

                 * */

                for (int i = 0; i < 100; i++)

                {

                    source.TraceInformation(

                        "This is sample trace record " + i);

                }

                source.Close();

            }

        }

    }

     

    Starting tracing session

    1.       Pre-create logging directory, such as c:\logs

    2.       On Vista or Server 2008, open elevated command window and type:

    logman create trace mysession -o c:\logs\mylog -p "{A11a0820-c24b-428c-83e2-26b41091702e}"
    where the GUID is the same you have in the code line
    private static Guid providerId.
    Other (red) parts of the command are arbitrary names.

    3.       Start tracing session using the command (for example):
    logman start mysession

    Stopping and decoding tracing session

    1.       Stop session via command
    logman stop mysession

    2.       Decode log file
    tracerpt c:\logs
    \mylog_000001.etl

    As a result of the decoding, you have, in the current directory, 2 files:


    dumpfile.xml – contains your decoded trace file

    Summary.txt – some statistical information

  • Request authorization in Azure SDK

    Request authorization in Azure SDK

    Sergei Meleshchuk - blog

    Introduction

     

    Azure storage requires all client REST requests be “signed”. The storage server validates the signature. The server will reject the request if it is not properly signed.

    The goal of this post is to explain how the client-side signing works. That can help when troubleshooting 403 error codes. In addition, you can sign your requests entirely bypassing the client library of the Azure SDK.

    Shared key authentication (authorization) in Azure

     

    Client (that is, you) receives a “key”, in a form of Base64 string. The key is sent to you when you sign for Azure services.

    The client code takes certain parts of the request in a specific order, and builds (from those parts) a string. I refer to this string as to “signature string”. This string will be the argument to the signing algorithm, the latter computes the “signature”. This computed signature is sent, along with the request, to the server. Needless to say, the shared key itself is not sent.

    Thus, the client-side signing procedure is:

    -          Build a “signature string”, from which the actual signature will be computed,

    -          Compute signature, using the “signature string” as an argument, and using the shared key

    -          Send the resulting signature in a form of additional request header.

    Building the signature string

    By far the most interesting part of the whole signing exercise are rules of building the signature string. I start by listing parts of the request, which go into signature string.

    There are two parts.

    The first part is the “suffix” of the request URL, which is everything that follows the hostname part of the URL, without the query params starting with “?”.

    For example, if the URL is

    http://accountname.queue.core.windows.net/queuename/messages?numofmessages=1&timeout=30

    Then the suffix part for signature is:

    “queuename/messages”.

    Part 2 is composed of HTTP request header values, but not all of them. There are two groups of headers that do participate: 1) legacy headers like date, MD5, content type, and 2) Azure-specific headers, which are headers with names starting with “x-ms-”. For example, “x-ms-date”.

    For queue and blob storage, the first group can be ignored.

    The second group is, in general case, sorted by ascending header name, and concatenated. However, because in all practical cases, we only need one header (which is “x-ms-date”), I ignore the multi-header case here. The gain is extreeme simplicity of the resulting code.

    The signature string needs the value of current time, in UTC, and in standard format. The time  is to be retrieved by the code:

    string date = DateTime.UtcNow.ToString(

        "R",

        CultureInfo.InvariantCulture);

     

    Now, here is the code that builds the signature string:

    string signPart1 = string.Format(CultureInfo.InvariantCulture,

        "{0}\n{1}\n{2}\n{3}\n{4}\n/",

        method,                 // 0 - HTTP Method, like GET

        string.Empty,           // 1 - "Content-MD5" header value. Leave blank.

        string.Empty,           // 2 - Content type, like MIME. Leave blank.

        string.Empty,           // 3 - Legacy "date" header. Leave blank.

        "x-ms-date:" + date);   // 4 - Azure form of date header.

     

    string signPart2 = accountName + "/" + suffix;

    string signatureString = signPart1 + signPart2;

     

    The resulting signature string looks like below.  

    "GET\n\n\n\nx-ms-date:Mon, 01 Dec 2008 05:17:57 GMT\n/accountname/queuename/messages"

    Building the signature from signature string

     

    This is easy part. All the needed code is:

    byte[] signBytes = System.Text.Encoding.UTF8.GetBytes(signatureString);

    byte[] hash = new HMACSHA256(sharedKey).ComputeHash(signBytes);

    string signature = System.Convert.ToBase64String(hash);

    return signature;

     

    The first line converts the signature string to byte array; this is needed because the actual hash-computing code works on byte arrays.

    The second line computes the hash.

    And the third and the last line just convert the signature to Base64.

    Note: the “sharedKey” used in line 2 is computed, once, from the Base64 shared key your received by the code like below, also straightforward:

    public byte[] Key

    {

        get { return Convert.FromBase64String(Base64Key); }

    }

    How do we actually sign the HTTP request

     

    Azure SDK uses an Azure-specific HTTP header, with the name “Authorization”. Don’t be confused by “authorization” vs. “authentication” – the actual use as per now is for authentication of the client. And authentication/authorization header value is computed as below:

    string AuthHeaderValue = string.Format(CultureInfo.InvariantCulture,

        "SharedKey {0}:{1}",

        accountName,

        signature);

    Now we all we need to build needed headers for signed HTTP REST request:

    request.Headers.Add("x-ms-date", date);

    request.Headers.Add("Authorization", AuthHeaderValue);

     

    The End

  • Prepare VM: Add ISO Programmatically . Hyper-V, C# version

    Prepare VM: Programmatically Add ISO. Hyper-V, C# version

    [Sergei Meleshchuk. http://blogs.msdn.com/sergeim/] 

    Imagine I want prepare VM fully automatically (programmatically, via code). Say I need a fully functional VM. That is, OS and applications loaded, ready for use. Why adding (mounting) ISO is important here?

    Here is why. To set up a VM, I need 3 or 4 steps:

    -          Create VM

    -          Add (mount) installation disk. That is, add ISO

    -          Boot from this ISO image

    -          Next depends on ISO. If this is normal OS image, you will need to finish installation with some manual actions. However, ISO may contain PXE which will just do un-attended installation from say network share or via WDS (this is for future post).

    About this document

    I outline steps (of adding ISO), briefly comment on implementation of each step, than provide complete code.

    Credits

    Inspired by James O’Neill’s PowerShell code. See his site.

    Steps outline

    Step 1 will be grabbing an empty “template” of the ISO image. This template is pre-existent in Hyper-V WMI-based management infrastructure.

    Step 2 is to obtain management object representing our VM; the VM is likely to be just created. We only need this VM object to get access to the disk controller. To cut down number of command line parameters, we will hard-code the controller ID: it will be 1 (naturally).

    Step 3 will be obtaining the controller object. That is, the WMI object representing controller 1 on specific VM.

    Step 4, somewhat lengthy to explain. There is a pre-existing disk object for each disk controller 1 on each VM. This disk object has parent field, which is the controller’s identifier in WMI meaning of the word “identifier”. That is, the parent field is the WMI object path, usually pretty long string. Thus we will search for the disk object by scanning controllers, and comparing their Path with the parent field in each disk object. Little clumsy, yet I don’t know of another way.

    Step 5. Here important work starts. We set values on template (from step 1). There will be 2 values to set: a) path to ISO image, which we originally obtain from the command line and b) the disk object from step 4.

    Step 6 is to ask the management service to add the template (from step 4) to the VM (from step 2).

    Step 0. Preparation

    Use abbreviations and get data from command line. This is simple.

        using MO = ManagementObject;

        using MBO = ManagementBaseObject;

        using MOS = ManagementObjectCollection;

     

    . . .

                const string CONTROLLER_ID_1 = "1";

                string vmDisplayName;

                string iso;

           // e.g. @"H:\ISO\Virtual Server 2008\amd64fre.iso";

     

                // get arguments

     

                if (args.Length < 2)

                {

                    Console.WriteLine("usage: addiso <vmElementName> <isoPath>");

                    return;

                }

                vmDisplayName = args[0];

                iso = args[1];

     

    Step 1. Get ISO template object

    One line:

    MO moTemplateRasd = GetISOTemplate();

    -          where the GetISOTemplate we have to write ourselves:

    private MO GetISOTemplate()

    {

        string where;

     

        MO capability = GetWmiObject(

            "MsVM_AllocationCapabilities",

            "ResourceType = 21 and ResourceSubType = 'Microsoft Virtual CD/DVD Disk'");

        AssertWmi(capability);

     

        // -- MsVM_SettingsDefineCapabilities

     

        where = string.Format(

            "valuerange=0 and Groupcomponent = \"{0}\"",

            FixPath(capability.Path.Path));

        Log(capability.Path.Path);

        Log(where);

        MO define = GetWmiObject("MsVM_SettingsDefineCapabilities", where);

        string partComponent = define["partcomponent"] as string;

        Log("*{0}", partComponent);

     

        // -- MsVM_ResourceAllocationSettingData for IDE drive (RASD/IDE)

     

        MO component = new MO(partComponent);

        Logc("COMP", "#{0}", component.Path.Path);

        // this ws the one we want to set parent of

        DumpMO("COMP", component);

        return component;

    }

     

    Step 2. Get VM object.

    This is straight:

    MO moVmComputerSystem = GetVM(vmDisplayName);

    string vmGuidName = moVmComputerSystem["name"] as string;

     

    with the GetVM function being:

    MO GetVM(string vmElementName)

    {

        string where;

        where = string.Format(

            CultureInfo.InvariantCulture,

            "elementname='{0}'",

            vmElementName);

        MO theVm = GetWmiObject("msvm_computersystem", where);

        return theVm;

    }

     

    Step 3. Get controller.

    This is little difficult. We look for controller, having a) VM object and b) controller ID, which is traditionally (at the time of writing) 1.

     

    MO moControllerRasd = GetController(moVmComputerSystem, CONTROLLER_ID_1);

    GetController is our function that we have to write. The code is below.

    MO GetController(MO theVm, string controllerId)

    {

        string vmName = theVm["name"] as string;

        string where = string.Format(

            CultureInfo.InvariantCulture,

            "resourceSubtype='{0}' and instanceId Like 'Microsoft:{1}%' and address='{2}'",

            "Microsoft Emulated IDE Controller",

            vmName,

            controllerId);

        MO moControllerRasd = GetWmiObject("MsVM_ResourceAllocationSettingData", where);

        return moControllerRasd;

    }

     

    Step 4. Get disk object – child of the controller

    MO moDiskRasd = GetControllerChild(

        vmGuidName,

        moControllerRasd.Path.Path);

     

    Again, the GetControllerChild is what we have to write:

    MO GetControllerChild(string vmName, string controllerPath)

    {

        string where = string.Format(

            CultureInfo.InvariantCulture,

            "parent Like '%Microsoft:{0}%'",

            vmName);

        MOS childrenDisks = GetWmiObjects(Constants.RASD_CLASS, where);

        foreach (MO disk in childrenDisks)

        {

            string parentPath = disk["parent"] as string;

            if (parentPath != null)

            {

                if (parentPath.Equals(controllerPath, StringComparison.OrdinalIgnoreCase))

                {

                    DumpMO("child", disk);

                    return disk;

                }

            }

        }

        throw new InvalidOperationException("Cant locate child drive for controller " + controllerPath);

    }

     

    Step 5. Update template

    // Step 5. Now actual work: 

    //     - update template with disk resource data (by setting template's parent), and

    //     - set ISO location

     

    moTemplateRasd["parent"] = moDiskRasd.Path.Path;

    moTemplateRasd["connection"] = new string[] { iso };

    moTemplateRasd.Put();

     

    Step 6. Ask the management service to apply filled-in template

     

    MO moManagementService = GetMsVM_virtualSystemManagementService();

     MBO mboCallInParams = moManagementService.GetMethodParameters("AddVirtualSystemResources");

     mboCallInParams["TargetSystem"] = moVmComputerSystem;

     string componentText = moTemplateRasd.GetText(TextFormat.WmiDtd20);

     mboCallInParams["ResourceSettingData"] = new string[] { componentText };

     ManagementBaseObject mboCallOutParams = moManagementService.InvokeMethod(

         "AddVirtualSystemResources",

         mboCallInParams,

         null);

     

     

    Full code

    // auth: Sergei Meleshchuk, June 2008.

    // Based in part on powershell code by James O'Naill

    using System;

    using System.Management;

    using System.Globalization;

    namespace Hyperv.Misc

    {

        using MO = ManagementObject;

        using MBO = ManagementBaseObject;

        using MOS = ManagementObjectCollection;

     

        class MainAddISO3

        {

            static void Main(string[] args)

            {

                AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(Oops);

                MainAddISO3 main = new MainAddISO3();

                main.AddISO(args);

            }

            private void AddISO(string[] args)

            {

                const string CONTROLLER_ID_1 = "1";

                string vmDisplayName;

                string iso;              // e.g. @"H:\ISO\Virtual Server 2008\6001.18000.080118-1840_amd64fre_Server_en-us_VL-KRMSXVOL_EN_DVD.iso";

     

                // get arguments

     

                if (args.Length < 2)

                {

                    Console.WriteLine("usage: addiso <vmElementName> <isoPath>");

                    return;

                }

                vmDisplayName = args[0];

                iso = args[1];

     

                // Step 1. Get template (of type: MsVM_ResourceAllocationSettingData)

     

                MO moTemplateRasd = GetISOTemplate();

     

                // Step 2. Get VM (needed when locating controller, of type msvm_ComputerSystem)

     

                MO moVmComputerSystem = GetVM(vmDisplayName);

                string vmGuidName = moVmComputerSystem["name"] as string;

     

                // Step 3. Get disk controller (of type: MsVM_ResourceAllocationSettingData)

     

                MO moControllerRasd = GetController(moVmComputerSystem, CONTROLLER_ID_1);

     

                // Step 4. Get Disk - the RASD object to which the controller is a parent

     

                MO moDiskRasd = GetControllerChild(

                    vmGuidName,

                    moControllerRasd.Path.Path);

     

                // Step 5. Now actual work: 

                //     - update template with disk resource data (by setting template's parent), and

                //     - set ISO location

     

                moTemplateRasd["parent"] = moDiskRasd.Path.Path;

                moTemplateRasd["connection"] = new string[] { iso };

                moTemplateRasd.Put();

     

                // Step 6. Now really actual work: Add actual resource from info in the template

     

                MO moManagementService = GetMsVM_virtualSystemManagementService();

                MBO mboCallInParams = moManagementService.GetMethodParameters("AddVirtualSystemResources");

                mboCallInParams["TargetSystem"] = moVmComputerSystem;

                string componentText = moTemplateRasd.GetText(TextFormat.WmiDtd20);

                mboCallInParams["ResourceSettingData"] = new string[] { componentText };

                ManagementBaseObject mboCallOutParams = moManagementService.InvokeMethod(

                    "AddVirtualSystemResources",

                    mboCallInParams,

                    null);

                DumpMBO("AddVirtualSystemResources:", mboCallOutParams);

                // Check result here

            }

     

            // Main helpers

    #region helpers

            MO GetVM(string vmElementName)

            {

                string where;

                where = string.Format(

                    CultureInfo.InvariantCulture,

                    "elementname='{0}'",

                    vmElementName);

                MO theVm = GetWmiObject("msvm_computersystem", where);

                return theVm;

            }

            MO GetController(MO theVm, string controllerId)

            {

                string vmName = theVm["name"] as string;

                string where = string.Format(

                    CultureInfo.InvariantCulture,

                    "resourceSubtype='{0}' and instanceId Like 'Microsoft:{1}%' and address='{2}'",

                    "Microsoft Emulated IDE Controller",

                    vmName,

                    controllerId);

                MO moControllerRasd = GetWmiObject("MsVM_ResourceAllocationSettingData", where);

                return moControllerRasd;

            }

            MO GetControllerChild(string vmName, string controllerPath)

            {

                string where = string.Format(

                    CultureInfo.InvariantCulture,

                    "parent Like '%Microsoft:{0}%'",

                    vmName);

                MOS childrenDisks = GetWmiObjects(Constants.RASD_CLASS, where);

                foreach (MO disk in childrenDisks)

                {

                    string parentPath = disk["parent"] as string;

                    if (parentPath != null)

                    {

                        if (parentPath.Equals(controllerPath, StringComparison.OrdinalIgnoreCase))

                        {

                            DumpMO("child", disk);

                            return disk;

                        }

                    }

                }

                throw new InvalidOperationException("Cant locate child drive for controller " + controllerPath);

            }

            private MO GetMsVM_virtualSystemManagementService()

            {

                return GetWmiObject("MsVM_virtualSystemManagementService", null);

            }

            private MO GetISOTemplate()

            {

                string where;

     

                MO capability = GetWmiObject(

                    "MsVM_AllocationCapabilities",

                    "ResourceType = 21 and ResourceSubType = 'Microsoft Virtual CD/DVD Disk'");

                AssertWmi(capability);

     

                // -- MsVM_SettingsDefineCapabilities

     

                where = string.Format(

                    "valuerange=0 and Groupcomponent = \"{0}\"",

                    FixPath(capability.Path.Path));

                Log(capability.Path.Path);

                Log(where);

                MO define = GetWmiObject("MsVM_SettingsDefineCapabilities", where);

                string partComponent = define["partcomponent"] as string;

                Log("*{0}", partComponent);

     

                // -- MsVM_ResourceAllocationSettingData for IDE drive (RASD/IDE)

     

                MO component = new MO(partComponent);

                Logc("COMP", "#{0}", component.Path.Path);

                // this ws the one we want to set parent of

                DumpMO("COMP", component);

                return component;

            }

            #endregion helpers

     

            #region generic wmi helpers

            static string FixPath(string path)

            {

                string result = path.Replace("\\", "\\\\").Replace("\"", "'");

                Console.ForegroundColor = ConsoleColor.DarkYellow;

                Log("FixPath returns\n" + result);

                Console.ResetColor();

                return result;

            }

            private MO GetWmiObject(string classname, string where)

            {

                MOS resultset = GetWmiObjects(classname, where);

                if (resultset.Count != 1)

                    throw new InvalidOperationException(

                        string.Format(

                            "Cannot locate {0} where {1}",

                            classname,

                            where));

                MOS.ManagementObjectEnumerator en = resultset.GetEnumerator();

                en.MoveNext();

                MO result = en.Current as MO;

                if (result == null)

                    throw new InvalidOperationException("Failure retrieving " + classname + " where " + where);

                return result;

            }

     

            private MOS GetWmiObjects(string classname, string where)

            {

                string query;

                ManagementScope scope = new ManagementScope(@"root\virtualization", null);

                if (where != null)

                {

                    query = string.Format(

                       "select * from {0} where {1}",

                       classname,

                       where);

                }

                else

                {

                    query = string.Format(

                        CultureInfo.InvariantCulture,

                        "select * from {0}",

                        classname);

                }

                ManagementObjectSearcher searcher = new ManagementObjectSearcher(

                    scope,

                    new ObjectQuery(query));

     

                ManagementObjectCollection resultset = searcher.Get();

                return resultset;

            }

     

            void AssertWmi(MO mo)

            {

                if (mo == null)

                    throw new NullReferenceException("dfasd");

            }

            void AssertNotNull(string tag, object o)

            {

                if (o == null)

                    throw new NullReferenceException("AssertFailed with tag " + tag);

            }

            #endregion generic wmi helpers

            // -- end of main helpers

     

            // ---- Logging functions -----

            #region logging

            private static void Log(string message, params object[] data)

            {

                Console.WriteLine(message, data);

            }

            private static void Logc(string title, string message, params object[] data)

            {

                Console.ForegroundColor = ConsoleColor.Green;

                Console.Write(title);

                Console.ResetColor();

                Console.WriteLine(message, data);

            }

           private static void DumpMO(

               string message,

               MO mo,

               params object[] data)

           {

               Console.ForegroundColor = ConsoleColor.White;

               Console.WriteLine(message, data);

               Console.ResetColor();

               PropertyDataCollection props = mo.Properties;

               foreach (PropertyData prop in props)

               {

                   Log("{0:20} {1}", prop.Name, prop.Value);

               }

           }

           private static void DumpMBO(

               string message,

               ManagementBaseObject mo,

               params object[] data)

           {

               Console.ForegroundColor = ConsoleColor.White;

               Console.WriteLine(message, data);

               Console.ResetColor();

               PropertyDataCollection props = mo.Properties;

               foreach (PropertyData prop in props)

               {

                   Log("{0:20} {1}", prop.Name, prop.Value);

               }

           }

            #endregion logging

           // expection handler

            private static void Oops(object sender, UnhandledExceptionEventArgs e)

            {

                Console.BackgroundColor = ConsoleColor.White;

                Console.ForegroundColor = ConsoleColor.Black;

                Exception ex = e.ExceptionObject as Exception;

                Log(ex.Message);

                Console.ResetColor();

                Log(ex.ToString());

            }

        } // class MainCreateVm

     

        class Constants

        {

            internal const string DefineVirtualSystem = "DefineVirtualSystem";

            internal const string ModifyVirtualSystem = "ModifyVirtualSystem";

            internal const uint ERROR_SUCCESS = 0;

            internal const uint ERROR_INV_ARGUMENTS = 87;

            internal const string RASD_CLASS = "MsVM_ResourceAllocationSettingData";

        }

    }

     

     

     

  • Prepare VM: Create VM programmatically, Hyper-V API, C# version

    Create VM (Hyper-V) via code - .NET version Create VM programmatically

    [Sergei Meleshchuk. http://blogs.msdn.com/sergeim/] 

    From code, you can create many VMs per second. Those will be “bare-metal” VMs of course – you still will need to load image or just install OS on them. My example does not work on remote hosts, but you can modify the code in few minutes to do same on remote boxes.

    PowerShell vs. C#

    It looks everyone likes PowerShell, and for Hyper-V management in particular.

    Sometimes I feel more traditional languages (like C#) give more control/error handling. Another C# benefit is what I think is unsurpassed tracing and debugging facilities of Visual Studio (especially if to forget WinDbg for a second). Sometimes you can learn API by just stepping thru the code.

    Credits

    Here below is the PS1 à C# port of some code written originally by James O’Neill. I might have changed something a little, no offence taken.

    About this document

    I briefly 1) outline needed steps, 2) comment code fragments and 3) provide full code. Add System.Management.dll to references, and start the VS elevated (that is, right-click, select ‘run as administrator).

    Steps needed

    The steps are:

    -          Get parameters from command line

    -          Obtain the “MsVM_VirtualSystemManagementService” object, which will do all the work for us. I will call it VSMS for brevity.

    -          Step 1.
    Ask VSMS to create a sort of empty VM definition. I think about this thing as of a template.

    -          Step 2.
    Obtain set of VM settings; fill in those settings

    -          Step 3.
    Modify the newly created ComputerSystem (sort of template) with the settings we just prepared.

    Comments on steps

    First, some abbreviations I use:

        using MO = ManagementObject;

        using MBO = ManagementBaseObject;

        using MOS = ManagementObjectCollection;

     

    Step 1. Create VM definition.

    Trick here is a late-bound call on VSMS; see how managed WMI calls are coded (search for WMI InvokeMethod). The code is:

    // Create VM with empty settings

     

    MBO definition = sysMan.InvokeMethod(

        Constants.DefineVirtualSystem,

        sysMan.GetMethodParameters(Constants.DefineVirtualSystem),  // empty set

        null);

    uint retCode = (uint)definition["returnvalue"];

    if (retCode != Constants.ERROR_SUCCESS)

        throw new InvalidOperationException("DefineVirtualSystem failed");

     

    Next we get the WMI’s ManagementObject, which represents the instance of MSVM_ComputerSystem WMI class:

    string vmPath = definition["DefinedSystem"] as string;

    MO computerSystemTemplate = new MO(vmPath);

     

    At this point, we are half-done.

    Step 2. Fill in the VM settings

    First, get the generated VM name, which is actually a GUID (the display name is called ‘elementname’ in Hyper-V API world). Then we locate the ‘settings’ object:

    string vmName = (string)computerSystemTemplate["name"];

    // this is GUID; will need to locate settings for this VM

     

    MO settings = GetMsvm_VirtualSystemSettingData(vmName);

     

    Now fill in the settings object:

    // Now, set settings of this MSVM_ComputerSystem as we like

     

    settings["elementname"] = displayName;

    settings["notes"] = notes;

    settings["BIOSGUID"] = new Guid();

    settings["BIOSSerialNumber"] = "1234567890";

    settings["BIOSNumLock"] = "true";

    // settings["..."] = ...;

    // ... set whatever you like; see list at

    //     http://msdn.microsoft.com/en-us/library/cc136944(VS.85).aspx

    settings.Put();

     

    Step 3. Finalize.

    Use VSMS (management service) again to propagate settings to VM object.

    // Now, set the settings which were build above to newly created ComputerSystem

     

    MBO inParams = sysMan.GetMethodParameters(Constants.ModifyVirtualSystem);

    string settingsText = settings.GetText(TextFormat.WmiDtd20);

    inParams["ComputerSystem"] = computerSystemTemplate;

    inParams["SystemSettingData"] = settingsText;

    MBO resultToCheck = sysMan.InvokeMethod(

        Constants.ModifyVirtualSystem,

        inParams,

        null);

     

    // Almost done - now apply the settings to newly created ComputerSystem

     

    MO settingsAsSet = (MO)resultToCheck["ModifiedSettingData"];

     

    // Optionally print settingsAsSet here

     

    Log("Created: VM with name '{0}' and GUID name '{1}'", displayName, vmName);

     CreateVm

     

     

    Helper functions

    Useful functions (to be coded once) are:

    #region Wmi Helpers

    private MO GetWmiObject(string classname, string where)

    {

        MOS resultset = GetWmiObjects(classname, where);

        if (resultset.Count != 1)

            throw new InvalidOperationException(

                string.Format(

                    "Cannot locate {0} where {1}",

                    classname,

                    where));

        MOS.ManagementObjectEnumerator en = resultset.GetEnumerator();

        en.MoveNext();

        MO result = en.Current as MO;

        if (result == null)

            throw new InvalidOperationException("Failure retrieving " + classname +

        return result;

    }

     

    private MOS GetWmiObjects(string classname, string where)

    {

        string query;

        ManagementScope scope = new ManagementScope(@"root\virtualization", null);

        if (where != null)

        {

            query = string.Format(

               "select * from {0} where {1}",

               classname,

               where);

        }

        else

        {

            query = string.Format(

                CultureInfo.InvariantCulture,

                "select * from {0}",

                classname);

        }

        ManagementObjectSearcher searcher = new ManagementObjectSearcher(

            scope,

            new ObjectQuery(query));

     

        ManagementObjectCollection resultset = searcher.Get();

        return resultset;

    }

     

    #endregion Wmi helpers

     

    Full code

    // This is port to C# from a Powershell script written by James O'Neill

    using System;

    using System.Globalization;

    using System.Management;

    namespace Hyperv.Misc

    {

        using MO = ManagementObject;

        using MBO = ManagementBaseObject;

        using MOS = ManagementObjectCollection;

        class MainCreateVm

        {

            static void Main(string[] args)

            {

                AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(Oops);

                new MainCreateVm().CreateVm(args);

            }

            void CreateVm(string[] args)

            {

                if (args.Length < 1)

                {

                    Console.ForegroundColor = ConsoleColor.Red;

                    Log("Usage:   createvm <vmname> [<notes>]");

                    Console.ForegroundColor = ConsoleColor.White;

                    Log("Example: createvm vm1");

                    Console.ResetColor();

                    Environment.Exit((int)Constants.ERROR_INV_ARGUMENTS);

                }

                string displayName = args[0];

                string notes;

                if (args.Length > 1)

                    notes = args[1];

                else

                    notes = "Created " + DateTime.Now;

                MO sysMan = GetMsVM_VirtualSystemManagementService();

     

                // Create VM with empty settings

     

                MBO definition = sysMan.InvokeMethod(

                    Constants.DefineVirtualSystem,

                    sysMan.GetMethodParameters(Constants.DefineVirtualSystem),  // empty set

                    null);

                uint retCode = (uint)definition["returnvalue"];

                if (retCode != Constants.ERROR_SUCCESS)

                    throw new InvalidOperationException("DefineVirtualSystem failed");

     

                // Obtain WMI root\virtualization:ComputerSystem object.

                // we will need "Name" of it, which is GUID

     

                string vmPath = definition["DefinedSystem"] as string;

                MO computerSystemTemplate = new MO(vmPath);

                string vmName = (string)computerSystemTemplate["name"];

                // this is GUID; will need to locate settings for this VM

     

                MO settings = GetMsvm_VirtualSystemSettingData(vmName);

     

                // Now, set settings of this MSVM_ComputerSystem as we like

     

                settings["elementname"] = displayName;

                settings["notes"] = notes;

                settings["BIOSGUID"] = new Guid();

                settings["BIOSSerialNumber"] = "1234567890";

                settings["BIOSNumLock"] = "true";

                // settings["..."] = ...;

                // ... set whatever you like; see list at

                //     http://msdn.microsoft.com/en-us/library/cc136944(VS.85).aspx

                settings.Put();

     

                // Now, set the settings which were build above to newly created ComputerSystem

     

                MBO inParams = sysMan.GetMethodParameters(Constants.ModifyVirtualSystem);

                string settingsText = settings.GetText(TextFormat.WmiDtd20);

                inParams["ComputerSystem"] = computerSystemTemplate;

                inParams["SystemSettingData"] = settingsText;

                MBO resultToCheck = sysMan.InvokeMethod(

                    Constants.ModifyVirtualSystem,

                    inParams,

                    null);

     

                // Almost done - now apply the settings to newly created ComputerSystem

     

                MO settingsAsSet = (MO)resultToCheck["ModifiedSettingData"];

     

                // Optionally print settingsAsSet here

     

                Log("Created: VM with name '{0}' and GUID name '{1}'", displayName, vmName);

            } // CreateVm

            private MO GetMsVM_VirtualSystemManagementService()

            {

                return GetWmiObject("MsVM_VirtualSystemManagementService", null);

            }

            private MO GetMsvm_VirtualSystemSettingData(string vmName)

            {

                return GetWmiObject(

                    "Msvm_VirtualSystemSettingData",

                    string.Format("systemname='{0}'", vmName));

            }

            #region Wmi Helpers

            private MO GetWmiObject(string classname, string where)

            {

                MOS resultset = GetWmiObjects(classname, where);

                if (resultset.Count != 1)

                    throw new InvalidOperationException(

                        string.Format(

                            "Cannot locate {0} where {1}",

                            classname,

                            where));

                MOS.ManagementObjectEnumerator en = resultset.GetEnumerator();

                en.MoveNext();

                MO result = en.Current as MO;

                if (result == null)

                    throw new InvalidOperationException("Failure retrieving " + classname + " where " + where);

                return result;

            }

     

            private MOS GetWmiObjects(string classname, string where)

            {

                string query;

                ManagementScope scope = new ManagementScope(@"root\virtualization", null);

                if (where != null)

                {

                    query = string.Format(

                       "select * from {0} where {1}",

                       classname,

                       where);

                }

                else

                {

                    query = string.Format(

                        CultureInfo.InvariantCulture,

                        "select * from {0}",

                        classname);

                }

                ManagementObjectSearcher searcher = new ManagementObjectSearcher(

                    scope,

                    new ObjectQuery(query));

     

                ManagementObjectCollection resultset = searcher.Get();

                return resultset;

            }

     

            #endregion Wmi helpers

            private static void Log(string message, params object[] data)

            {

                Console.WriteLine(message, data);

            }

            private static void Oops(object sender, UnhandledExceptionEventArgs e)

            {

                Console.BackgroundColor = ConsoleColor.White;

                Console.ForegroundColor = ConsoleColor.Black;

                Exception ex = e.ExceptionObject as Exception;

                Log(ex.Message);

                Console.ResetColor();

                Log(ex.ToString());

            }

        } // class MainCreateVm

        class Constants

        {

            internal const string DefineVirtualSystem = "DefineVirtualSystem";

            internal const string ModifyVirtualSystem = "ModifyVirtualSystem";

            internal const uint ERROR_SUCCESS = 0;

            internal const uint ERROR_INV_ARGUMENTS = 87;

        }

    }

     

     

     

     

     

  • How to implement WPF TreeView with icons

    [Sergei Meleshchuk. http://blogs.msdn.com/sergeim/] 

    The trick is to use StackPanel (example below). The StackPanel contains image and text. 

    One of many ways to implement this is:

    - Create folder with icons and deploy the folder to the same directory where the .exe file lives.
      Note: If using IDE, copy this folder from project directory to target with the post-build step:
      xcopy /sy "$(ProjectDir)icons\*" "$(TargetDir)icons\*"

    - When initializing your app, compute location of the 'icons' folder:
        Settings.IconsFolder = GetIconFolder();  // IconsFolder is string

        where  the 'GetIconsFolder' is like below:

    private string GetIconFolder()

    {

         string execName =

            Assembly.GetExecutingAssembly().

               GetModules()[0].FullyQualifiedName;

         string currentFolder = System.IO.

               Path.GetDirectoryName(execName);

         string icons = System.IO.Path.Combine(

               currentFolder, "icons");

         return icons;

    }

     - Add TreeViewItem with the code like below:

    TreeViewItem tvi = CreateTreeViewItem(

        "samplename",

        Settings.IconsFolder,

        "sample.ico");

    treeView1.Items.Add(tvi);

    where the 'CreateTreeViewItem' is your little wrapper:

    private TreeViewItem CreateTreeViewItem(

    string header,

    string iconFolder,

    string iconName)

    {

        TreeViewItem child = new TreeViewItem();

        StackPanel pan = new StackPanel();

        if (iconName != null)

        {

            pan.Orientation = Orientation.Horizontal;

            IconBitmapDecoder icon = new IconBitmapDecoder(

                new Uri(System.IO.Path.Combine(

    iconFolder,

    iconName),

    UriKind.RelativeOrAbsolute),

                BitmapCreateOptions.None,

                BitmapCacheOption.OnLoad);

            Image image = new Image();

            image.Height = 16;

            image.Source = icon.Frames[0];

            pan.Children.Add(image);

        }

        pan.Children.Add(new TextBlock(new Run("  " + header)));

        child.Header = pan;

        return child;

    }

    That's it.

     


© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker