Вы правы насчет @{}
, это новая хеш-таблица. Я думаю, что для того, что вы хотите, вы можете просто создать список и каждый продукт, который проходит итерацию для сбора свойств, вы можете добавить его в этот список
Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer installerObj = (Installer)Activator.CreateInstance(installerType);
WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
var Products = installerObj.Products;
List<Hashtable> ProductCollection = new List<Hashtable>();
foreach (var product in Products)
{
Hashtable hash = new Hashtable();
hash.Add("ProductCode", product);
string[] Attributes = { "Language", "ProductName", "PackageCode", "Transforms", "AssignmentType", "PackageName", "InstalledProductName", "VersionString", "RegCompany", "RegOwner", "ProductID", "ProductIcon", "InstallLocation", "InstallSource", "InstallDate", "Publisher", "LocalPackage", "HelpLink", "HelpTelephone", "URLInfoAbout", "URLUpdateInfo" };
foreach (var attribute in Attributes)
{
try
{
var details = installer.ProductInfo[product.ToString(), attribute.ToString()];
hash.Add(attribute, details);
}
catch
{
}
}
ProductCollection.Add(hash);
}
Теперь просто обратитесь к ProductCollection, чтобы получить сведения о вашем продукте. Если вы хотите пойти дальше, вы можете создать класс для каждого MSI и попросить ваш процесс создать объект для каждого продукта.
public class MSIInfo
{
public string ProductCode { get; set; }
public string Language { get; set; }
public string ProductName { get; set; }
public string PackageCode { get; set; }
public string Transforms { get; set; }
public string AssignmentType { get; set; }
public string PackageName { get; set; }
public string InstalledProductName { get; set; }
public string VersionString { get; set; }
public string RegCompany { get; set; }
public string RegOwner { get; set; }
public string ProductID { get; set; }
public string ProductIcon { get; set; }
public string InstallLocation { get; set; }
public string InstallSource { get; set; }
public string InstallDate { get; set; }
public string Publisher { get; set; }
public string LocalPackage { get; set; }
public string HelpLink { get; set; }
public string HelpTelephone { get; set; }
public string URLInfoAbout { get; set; }
public string URLUpdateInfo { get; set; }
public override string ToString()
{
return $"{ProductName} - {ProductCode}";
}
public static IEnumerable<MSIInfo> GetProducts()
{
Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer installerObj = (Installer)Activator.CreateInstance(installerType);
WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
var Products = installerObj.Products;
List<MSIInfo> ProductCollection = new List<MSIInfo>();
foreach (var product in Products)
{
MSIInfo msi = new MSIInfo();
msi.ProductCode = product.ToString();
foreach (var property in msi.GetType()?.GetProperties())
{
try
{
if (property.Name != "ProductCode")
{
string val = installer.ProductInfo[product.ToString(), property.Name];
property.SetValue(msi, val);
}
}
catch (System.Runtime.InteropServices.COMException)
{
}
}
ProductCollection.Add(msi);
}
return ProductCollection;
}
}
Когда у вас есть этот класс, вы можете получить коллекцию из своего кода следующим образом
var Products = MSIInfo.GetProducts();
## связывает то, что перед ## с тем, что после него. Таким образом в Вашем примере, делающем ZEND_FN(foo)
, привел бы к zif_foo
Повторите ответ RvV.
знать, что при конкатенации литеральных строк можно найти некоторые несоответствия между pre-processors/compilers. Некоторые потребуют ##
#define STR_CAT(s1, s2) s1 ## s2
как в
const char s[] = STR_CAT("concat", "enation")
, тогда как другой откажется от него и вместо этого просто потребует, чтобы к этим двум литералам присоединился компилятор (в противоположность препроцессору), так потребует
#define STR_CAT(s1, s2) s1 s2
HTH