Update Feature Name in Feature Table (MSI) with Wix3 DTF - c#

I'm trying to update existing entry in Feature Table in MSI. More specifically, the Feature Identifier itself (first column - Feature).
String featureQuery = "SELECT * FROM `Feature`";
view = db.OpenView(featureQuery);
view.Execute();
rec = view.Fetch();
rec.SetString("Feature", "NewName"); <- error here "Function failed during execution"
view.Modify(ViewModifyMode.Update, rec);
However, when I do the same but only change "Feature" column to "Title" (in row where error occurs) for example, title column in MSI is changing to "NewName".
So, my question is - does this even possible or I'm making mistake somewhere? If later, please point me where, I would be very grateful. Anyway, any suggestions are very appreciated, thanks!

This example of mine isn't exactly the same because it's my dumb C# P/Invoke test, but it does work and change the Feature.Feature value, so it is allowed by the APIs, and perhaps there's something that the DTF wrapper classes have in the way of defaults etc that needs changing. The obvious difference is in the SetString/MsiRecordSetString code where the native API requires a field number. I apologize for not looking at the DTF, but SetString presumably maps the "Feature" string to an actual field number to update the first field of the record. If it has an overload that takes a field number, try that one. I'll add that you haven't actually selected a specific feature, so you're at risk of modifying the "first" feature, because select * will return them all.
IntPtr hDb = IntPtr.Zero;
int res = MsiInvoke.MsiOpenDatabase("C:\\Phil\\MyDD\\Samples Setup\\InsertRTF\\setup.msi", MsiInvoke.MSIDBOPEN_TRANSACT, out hDb);
string qinsert = "SELECT * FROM `Feature`";
IntPtr hView =IntPtr.Zero;
res = MsiInvoke.MsiDatabaseOpenView(hDb, qinsert, out hView);
res = MsiInvoke.MsiViewExecute(hView, 0);
IntPtr hRec= IntPtr.Zero;
res = MsiInvoke.MsiViewFetch(hView, out hRec);
res = MsiInvoke.MsiRecordSetString(hRec, 1, "Whatever");
res = MsiInvoke.MsiViewModify(hView, 4, hRec); // 4 = msimodify_replace 3 = modify_assign
res = MsiInvoke.MsiViewClose(hView);
res = MsiInvoke.MsiDatabaseCommit(hDb);
MsiInvoke is just a dumb P/invoke class I created, starting like this:
public class MsiInvoke
{
//Oops MSIHandles are not IntPtrs.
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiOpenDatabase(string filename, int persist, out IntPtr dbhandle);
public const int MSIDBOPEN_DIRECT = 2;
public const int MSIDBOPEN_TRANSACT = 1;

Related

Mapping Enum from WMI/Win32

I am writing an application to record some hardware information of computers in our environment and I am unclear on an enum mapping from the Win32/CIM/WMI(?) API. I am getting VideoOutputTechnology from the root/wmi/wmimonitorconnectionparams class using the Microsoft.Management.Infrastructure library. This value is UInt32 enum - the source is visible here.
I have successfully mapped most of this to a C# enum, but I am unclear on how I would write out the last five values (see below). Is it true that D3DKMDT_VOT_SVIDEO_4PIN = D3DKMDT_VOT_SVIDEO = 1?
D3DKMDT_VOT_SVIDEO = 1,
D3DKMDT_VOT_COMPOSITE_VIDEO = 2,
D3DKMDT_VOT_COMPONENT_VIDEO = 3,
// omitted for brevity
D3DKMDT_VOT_SVIDEO_4PIN = D3DKMDT_VOT_SVIDEO,
D3DKMDT_VOT_SVIDEO_7PIN = D3DKMDT_VOT_SVIDEO,
D3DKMDT_VOT_RF = D3DKMDT_VOT_COMPOSITE_VIDEO,
D3DKMDT_VOT_RCA_3COMPONENT = D3DKMDT_VOT_COMPONENT_VIDEO,
D3DKMDT_VOT_BNC = D3DKMDT_VOT_COMPONENT_VIDEO,
My version of this code looks similar to the following:
public enum WmiMonitorConnectionParamsVideoOutputTechnology : uint
{
SVideo = 1,
CompositeVideo = 2,
ComponentVideo = 3,
// Is this correct?
SVideo4Pin = WmiMonitorConnectionParamsVideoOutputTechnology.SVideo,
SVideo7Pin = WmiMonitorConnectionParamsVideoOutputTechnology.SVideo,
RF = WmiMonitorConnectionParamsVideoOutputTechnology.CompositeVideo,
RCA_3COMPONENT = WmiMonitorConnectionParamsVideoOutputTechnology.ComponentVideo,
BNC = WmiMonitorConnectionParamsVideoOutputTechnology.ComponentVideo,
}
I realize that this appears to be an Int32 type in this source code, but it's coming from CIM as UInt32. However, that does not change my question on mapping these enum values
I found this information here.
Frankly, I'm not really sure what I'm looking at so I'm having trouble phrasing a question on Google to get the right answer.

Need a workaround to access ReadOnlySpan<T> inside a function that returns an IEnumerable

while dealing with native code interop I decided it was the time to learn and try new Span features of the C# language. Although with many trials, everything was going perfectly fine until I come to the final stage of my very long function for which I insert below just a minimal reproducible sample:
[DllImport(dll, SetLastError = true)]
internal static extern void GetNativeData(out byte lpBuffer, int size, out bytesRead);
ReadOnlySpan<T> ReadArray<T>(ReadOnlySpan<byte> buf, int Length) where T : unmanaged
{
var size = Length * Unsafe.SizeOf<T>();
if (buf.Length < size)
buf = new byte[size];
GetNativeData(out MemoryMarshal.GetReference(buf), size, out int read));
Dh.CreateError(ReadMemoryErr);
return MemoryMarshal.Cast<byte, T>(buf.Slice(0, size));
}
static IEnumerable<MyClass> GetResult()
{
// Here I allocate a buffer
Span<byte> buf = new byte[1000];
// After a long serie of calls to unmanaged DLL functions I end up with something like this:
ReadOnlySpan<uint> uintRes = data.ReadArray<uint>(buf, 10);
ReadOnlySpan<ushort> shortRes = data.ReadArray<ushort>(buf, 10);
for (int i = 0; i < uintRes.Length; i++)
{
// Any access to spans inside this loop result in Error CS4013
string r = GetFunRes(uintRes[i]);
IntPtr r2 = GetFunRes2(shortRes[i]);
yield return new MyClass() { Prop1 = r, Prop2 = r2 };
}
}
The error I get is
Error CS4013: Instance of type 'Span' cannot be used inside a
nested function, query expression, iterator block or async method
Now, I have read that there are workarounds for this. The articles only shows usage for async methods, but it also states that applies to iterators. Unfortunately I have not been able to make this work. I only need to read specific elements of the span and then, yield a result that does not contains any element or reference to the span. It's just that whatever I try to, as soon as I try to access something the compiler fails.
I have read about Memory<T>, may be that this one could work, but I have some concerns because I have read that the performance is dramatically reduced. People also recommend Span at the first place. I hope I can find a solution because otherwise I will have to restart from scratch my project and rewrite everything because now is tightly tied to Span.
Thanks in advance for your help
#00110001:
var uintRes = data.ReadArray<uint>(buf, 10).ToArray();
I know that this will work, but I suppose that it will make Span usage useless along with the new generics unmanaged features I am trying to take advantage of (ReadArray<T>). If I am not wrong, calling ToArray() it's the same of the old Marshaling style where a new copy is created for each call:
internal static extern void GetNativeData(out uint[] lpBuffer, int size, out bytesRead);
#Ian Kemp
This is how I tried the workaround mentioned in the article:
int len = uintRes.Length;
for (int i = 0; i < len; i++)
{
var res = ParseData(i);
if (res == ExpectedResult())
yield return res;
}
MyClass ParseData(int index)
{
// CS8175: Cannot use ref local 'uintRes, shortRes' inside an anonymous method, lambda expression, or query expression
string r = GetFunRes(uintRes[index]);
IntPtr r2 = GetFunRes2(shortRes[index]);
return new MyClass() { Prop1 = r, Prop2 = r2 };
}
I am going to mark the question as closed because I found the answer myself, by reading this article. Basically, in order to use IEnumerable with yield, Memory<T> is needed because Span<T> is a ref struct that is allocated on the stack and can't be used across yield boundaries. On the other side, Memory<T> can reside on heap while still providing Span<T> access. The author of the article shows the maximum that can be done with pure Span and the sample code required in order to enable IEnumerable with LINQ along with the benchmarks of the different implementations. IEnumerable+memory<T> is very slow compared to the other solutions, but for anyone who still want LINQ, I recommend to look at NetFabric.Hyperlinq, an alternative version of LINQ developed from the same author of the article in order to fill that gap. I am not sure if even with this solution will still persist a big difference with for/foreach loops combined with pure Span, but I suppose it mainly depends from the number of calls, type of queries etc (see LinqBenchmarks to see how each query performs).
Thanks
P.s. I noticed that sometimes the server hosting the article is unreachable, therefore I include here a web archive capture to ensure that it will be always reachable in the future.

MVC - Handling Common Key Values

I have a question that I'm sure has already been answered. Any help pointing me in the right direction would be appreciated.
I have a standard ASP MVC site. As with any site, I have the usual collection of key values for various aspects of the application.
Hair Color
10 = Green
20 = Brown
...
Status
10 = Active
20 = Paused
99 = Inactive
...
SubscriptionType
10 = 1 Week
20 = 1 Month
30 = 3 Month
...
Approval
0 = Pending Approval
10 = Approved
20 = Approved with Conditions
99 = Rejected
etc..etc..etc...
Normally, I have a bunch of DB tables associated with the various types but I find all the overall management of this method tedious (creating, adding values, SQL calls)
My question:
Is there a simple and straightforward way of defining and accessing these key values within the MVC code itself. I know I can use various means (Lists, Dictionary, Hashtables, Enums, etc..) but I'm looking for something that I can easily access across various Controllers AND Views (in some Common Helper class?). I want to be able to get the value from the key OR the reverse and get the key from the value.
I know there are probably 1000 ways to skin this cat but I'd be interested if anyone could point me to a simple and straightforward way to do it. Is this something that could (or should) be done in the Model layer? A Helper "CommonValues" class? Any suggestions would be welcome.
Apologies if this is "Programming 101" stuff. (I'm self taught)
For integers
You can use an enum, like this:
enum SubscriptionType
{
OneWeek = 10,
OneMonth = 20,
ThreeMonths = 30
}
You could certainly define your enums as members of a CommonValues class, if you have one. Or you could define them within the namespace and outside of any class, which would make them globally available.
For strings (or integers if you like this method better)
You can define constants as a member of a class. Constants are exposed as static fields so they are accessible anywhere.
public class CommonValues
{
public const string ColorRed = "RED";
public const string ColorBlue = "BLUE";
public const string WorkflowStateStopped = "STOPPED";
public const string WorkflowStateRunning = "RUNNING";
}
//Main program
var a = CommonValues.ColorRed;
var b = CommonValues.WorkflowStateRunning;
Notice the messy "ColorXXX" pattern. Seems a little 90s to me. If you want to restrict namespaces so your intellisense works a bit more usefully, you can use a nested class.
public class CommonValues
{
public class Colors
{
public const string Red = "RED";
public const string Blue = "BLUE";
}
public class WorkflowStates
{
public const string Running = "RUNNING";
public const string Stopped = "STOPPED";
}
}
//Main program
var b = CommonValues.Colors.Red;
var c = CommonValues.WorkflowStates.Running;
Display
When it comes to display the meaning of one of these codes, naturally your display elements should come from resources, which will automatically adapt to the current culture (after all, some day you will be an international giant). You can organize your resources with names like this:
var resources = new ResourceManager("CommonValues", myAssembly);
//Example for use with enum
SubscriptionType code = SubscriptionType.OneWeek;
var display = resources.GetString("SubscriptionType." + code.ToString()); //Resource ID = "SubscriptionType.OneWeek";
//Example for use with string constant
var colorCode = CommonValues.Colors.Red;
var display = resources.GetString("Colors." + colorCode); //Resource ID = "Colors.Red";
I wanted to put in some details of what I did in case this helps others down the line. Using John Wu's answer from above (thanks!!), here's what I did.
Create the enum. You need to put this in some class that you can reference in the View. I called mine EnumValues in the EnumHelper namespace.
namespace MyProject.EnumHelper
{
public static class EnumValues
{
public enum ProjectStatus
{
Active = 10,
Paused = 20,
Inactive = 99
}
...
So the beginning of my View looks like
#using MyProject.EnumHelper;
#model MyProject.Models.ProjectViewModel;
...
Now, if I have the integer value (say from the DB call) and I want the decoded string (ex. "Active"), I did this in the view.
var projectstatus = (EnumValues.ProjectStatus)Model.project_status;
If I need to get the enum integer value (ex. in a dropdown), I did this in the view.
var projectstatusid = (int)EnumValues.ProjectStatus.Active;
Thanks again to John Wu for pointing me in the right direction.

C# Split String and Assign Splitted Value To Another Variable

I am trying to split a string and assign the different values. The string it returns to me is:
0077|PCK|PRD|05025066840471|4|Can Opener|1|10|B|20.00|0|100|0|0.00|0|0|1|0|0
So I want to split the string on "|" and assign each of them to another variable. That is what I tried to do:
public static void LoadPRD(string sData)
{
string[] s = null;
prdType PRD = new prdType();
s = sData.Split("|");
PRD.bCode = s.Left(s[0], 14);
PRD.PCode = s.Left(s[1], 12);
PRD.Desc = s.Left(s[2], 40);
PRD.Pack = s.Val(s[3]);
PRD.Unit = s.Left(s[4], 12);
PRD.VATCode = s.Left(s[5], 1);
PRD.VATRate = Conversion.Val(s[6]);
PRD.Cost = Conversion.Val(s[7]);
PRD.Sell = Conversion.Val(s[8]);
PRD.Stock = Conversion.Val(s[9]);
PRD.AWS = Conversion.Val(s[10]);
PRD.OnOrder = Conversion.Val(s[11]);
PRD.OrderQty = Conversion.Val(s[12]);
PRD.LabelQty = Conversion.Val(s[13]);
PRD.Restriction = s.Left(s[14], 1);
PRD.MinStock = s.Val(s[15]);
PRD.PromoCode = s.Left(s[16], 3);
PRD.MnM = s.Left(s[17], 3);
}
The error message says that the Strings does not exist in the context, but it is not too of a helpful information, I do understand what it means but I am very confused on how to approach the solution.
Just so you know, I did create the variable before hand, I've posted them below:
public struct prdType
{
public string bCode;
public string PCode;
public string Desc;
public Int16 Pack;
public string Unit;
public string VATCode;
public float VATRate;
// Stored in pence
public long Cost;
public long Sell;
public long Stock;
public float AWS;
public long OnOrder;
public long OrderQty;
public long LabelQty;
public string Restriction;
public long MinStock;
public string PromoCode;
}
Your help will be much appreciated.
Thanks.
EDIT:
On
s = sData.Split("|");
it says: "The best overloaded method match for string.Split(params char[]) has some invalid arguments. It also says that arguments cannot be converted to char. Any ideas?
Rather than use legacy VB methods for this, I would suggest using C# methods all the way.
string[] s = sData.Split('|');
The use of Strings.Left is not readily apparent. Since you've already split the line, you'll have each element of the split in its entirety. If you want to take only the first n characters, you can do that, but there is no built-in equivalent for Strings.Left in C#.
For those elements that are a different type, you can use Convert.ToX:
PRD.Pack = Convert.ToInt16(s[3]));
PRD.VATRate = Convert.ToSingle(s[6]));
PRD.Cost = Convert.ToInt64(s[7]);
And so on. Note that float uses Convert.ToSingle, not Convert.ToFloat.
ADDED
Based on #Raphael's comment, Convert.ToX is not a direct replacement for Conversion.Val(), but as long as the string is strictly numeric you will be ok and will get the correct type.
These methods come from Microsoft.VisualBasic namespace.It should be only used if you know what you're doing (see Tim's comment on this answer).
I wouldn't advise you to use these methods.
They are equivalent methods in c# (or they're rather easy to implement).
Like String.Split, for example (so you could do var s = sData.Split('|'); )
A way to do something equivalent to String.Left
Wouldn't advise to do this, but anyway :
If you want absolutely use them, you should :
Add a reference to Microsoft.VisualBasic assembly (right click on project's references, you should find it in Framework libs)
Add the right using at the top of your code : using Microfost.VisualBasic;
You need to do s = sData.Split('|');

C# pointer reference

Is there a way to create c++ style pointer in c#? I need this to set an int in several places when I don't know which int it is.
Like in c++ I would do:
int *i;
if(cond0) i = &someint;
if(cond1) i = &otherint;
if(cond2) i = &thirdint;
if(cond3) *i = someval;
if(cond4) *i = otherval;
if(cond5) *i = thirdval;
If I do this in c# style I will need 9 ifs and my program has much more conditions so its not feasible.
I thought of make some sort of value like:
int val;
if(cond3) val = someval;
if(cond4) val = otherval;
if(cond5) val = thirdval;
if(cond0) someint = val;
if(cond1) otherint = val;
if(cond2) thirdint = val;
but it's not possible because cond3, 4 and 5 are scattered along the program.
It is, but you have to wrap any code that does it in an unsafe block.
Alternatively, if this is happening in a method then you might be able to use the 'ref' keyword to pass a parameter in by reference.
Both of these options really constrain the solution to method boundaries. If you're dealing with anything more scattered than that, in C# it's probably better to try and find ways to reorganize your code to use less global state instead.
Yes, there is a type called IntPtr which I use for Windows handles.
Here's an example of C# pointers that illustrates both their declaration and how to wrap them in an unsafe block
Also, see the C# Programming Guide - Pointer Types
I'm not sure if you provided enough information in your question to give a correct answer, but one possible solution is to set the values in a function using ref parameters.
class Program
{
static void Main(string[] args)
{
var i = 1;
var someint = 2;
var otherint = 3;
var thirdint = 4;
Console.WriteLine("i: {0}\nsomeint: {1}\notherint: {2}\nthirdint: {3}", i, someint, otherint, thirdint);
SetInts(true, false, false, false, false, false, ref i, ref someint, ref otherint, ref thirdint);
Console.WriteLine("i: {0}\nsomeint: {1}\notherint: {2}\nthirdint: {3}", i, someint, otherint, thirdint);
Console.ReadKey();
}
static void SetInts(bool cond0, bool cond1, bool cond2, bool cond3, bool cond4, bool cond5, ref int i, ref int someint, ref int otherint, ref int thirdint)
{
if (cond0) i = someint;
if (cond1) i = otherint;
if (cond2) i = thirdint;
if (cond3) i = someint;
if (cond4) i = otherint;
if (cond5) i = thirdint;
}
}

Categories

Resources