Иногда в WEB-проектах на .NET бывает неудобно хардкодить версии ActiveX в CODEBASE. Данный код можно использовать для получения версии ActiveX напрямую из CAB-файла. Работает как на 32-х битных так и на 64-х битных Windows-платформах.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
/// <summary>
/// SetupApi Wrapper class
/// Platform SDK: Setup API
/// </summary>
public sealed class CabinetVersionProvider
{
/// <summary>
/// Получает версию из CAB-файла
/// </summary>
/// <param name="cabFilePath">Полный путь до CAB-файла</param>
/// <returns>значение FileVersion из inf-файла CAB-а</returns>
public static string GetVersion(string cabFilePath)
{
return getVersion(cabFilePath);
}
#region Тонкости реализации
/// <summary>
/// The FILE_IN_CABINET_INFO class provides information about a file found
/// in the cabinet.
/// Platform SDK: Setup API
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct FILE_IN_CABINET_INFO
{
public String NameInCabinet;
public uint FileSize;
public uint Win32Error;
public ushort DosDate;
public ushort DosTime;
public ushort DosAttribs;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public String FullTargetName;
}
/// <summary>
/// The FILEPATHS structure stores source and target path information.
/// Platform SDK: Setup API
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct FILEPATHS
{
public String Target;
public String Source;
public uint Win32Error;
public uint Flags;
}
/// <summary>
/// Platform SDK: Setup API
/// </summary>
private enum FILEOP : uint
{
FILEOP_ABORT = 0, // Abort cabinet processing.
FILEOP_DOIT, // Extract the current file.
FILEOP_SKIP // Skip the current file.
}
/// <summary>
/// The FileCallback callback function is used by a number of the setup
/// functions. The PSP_FILE_CALLBACK type defines a pointer to this callback
/// function. FileCallback is a placeholder for the application-defined
/// function name.
/// Platform SDK: Setup API
/// </summary>
private delegate uint PSP_FILE_CALLBACK(IntPtr context,
uint notification, IntPtr param1, IntPtr param2);
/// <summary>
/// The file has been extracted from the cabinet.
/// </summary>
private const uint SPFILENOTIFY_FILEINCABINET = 0x00000011;
/// <summary>
/// The current file is continued in the next cabinet.
/// </summary>
private const uint SPFILENOTIFY_FILEEXTRACTED = 0x00000013;
private const uint NO_ERROR = 0;
/// <summary>
/// The SetupIterateCabinet function iterates through all the files in a
/// cabinet and sends a notification to a callback function for each
/// file found.
/// Platform SDK: Setup API
/// </summary>
[DllImport("SetupApi.dll", CharSet = CharSet.Auto)]
private static extern bool SetupIterateCabinet(string cabinetFile,
uint reserved, PSP_FILE_CALLBACK callBack, IntPtr context);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
private static extern uint GetLastError();
private static readonly Dictionary<string,string> m_cache =
new Dictionary<string, string>();
private static string getVersion(string cabFilePath)
{
lock(m_cache)
{
string value;
if(!m_cache.TryGetValue(cabFilePath, out value))
{
value = new CabinetVersionProvider(cabFilePath).m_version;
value = string.IsNullOrEmpty(value) ? null : value;
m_cache.Add(cabFilePath, value);
}
return value;
}
}
private CabinetVersionProvider(string cabFileName)
{
m_version = null;
m_tempfile = null;
if (!SetupIterateCabinet(cabFileName, 0, callBack, IntPtr.Zero))
throw new Win32Exception((int)GetLastError());
if (m_tempfile != null)
{
string fileContent = File.ReadAllText(m_tempfile);
MatchCollection mc = m_re.Matches(fileContent);
if(mc.Count>0)
m_version = mc[0].Groups[1].Captures[0].Value.Trim();
try
{
File.Delete(m_tempfile);
}
catch(Exception err)
{
Trace.WriteLine(err);
}
}
}
private string m_version;
private string m_tempfile;
private static readonly Regex m_re =
new Regex(@"^\s*FileVersion\s*=\s*([^\s]+)\s*$",
RegexOptions.IgnoreCase
|RegexOptions.CultureInvariant
|RegexOptions.Multiline);
private uint callBack(IntPtr context, uint notification,IntPtr param1,
IntPtr param2)
{
switch(notification)
{
case SPFILENOTIFY_FILEINCABINET:
if (m_tempfile != null)
return (uint)FILEOP.FILEOP_SKIP;
FILE_IN_CABINET_INFO fileInCabinetInfo =
(FILE_IN_CABINET_INFO)Marshal.PtrToStructure(
param1,
typeof(FILE_IN_CABINET_INFO));
if (fileInCabinetInfo.NameInCabinet.ToLower().EndsWith(".inf"))
{
fileInCabinetInfo.FullTargetName = (m_tempfile = Path.GetTempFileName());
Marshal.StructureToPtr(fileInCabinetInfo, param1, true);
return (uint)FILEOP.FILEOP_DOIT;
}
else
return (uint)FILEOP.FILEOP_SKIP;
case SPFILENOTIFY_FILEEXTRACTED:
FILEPATHS filePaths =
(FILEPATHS)Marshal.PtrToStructure(param1, typeof(FILEPATHS));
return filePaths.Win32Error;
default:
return NO_ERROR;
}
}
#endregion
}