OPOS DirectIO event review
This BYTE* bTmp = new BYTE[4]; could just be put on the stack with BYTE bTmp[4]; (the proper term is actually automatic storage duration which in most cases means it ends up on the stack). This normally avoids a call to malloc and you don't have to delete it explicitly.
I'm not too versed in COM programming but if msr.SODirectIO(0, &amp;data, &amp;bstring); could throw an exception you might want to wrap it into a try {} finally {} in order to ensure the cleanup of the BSTR and COM resource is done regardless.
I know it's just a quick mockup but GetHexBytes could be reduced by using Linq:

string GetHexBytes(string str)
{
    var bytes = System.Text.UnicodeEncoding.Unicode.GetBytes(str);
    var hexBytes = bytes.Select(b =&gt; b.ToString("X2")).ToArray();
    return string.Join(":", hexBytes);
}