Property Is Not Updating Registry Value Advanced Installer - advanced-installer

I want to do something simple. Update a Registry Value with Advanced Installer.
The steps I took
1. Create a Property "LWL_KEY_2"
2. Create a Dialog with an Edit Box and set the Property name to "LWL_KEY_2"
3. Bind the Property Name to The Registry Key
The log shows the property gets the user input
SI (c) (04:28) [14:48:11:280]: PROPERTY CHANGE: Modifying LWL_KEY_1 property. Its current value is 'nick1'. Its new value: 'abc123'.
MSI (c) (04:28) [14:48:23:748]: PROPERTY CHANGE: Modifying LWL_KEY_2 property. Its current value is 'nick2'. Its new value: 'xyz456'.
Action ended 14:48:23: NewSeqDialog. Return value 1.
MSI (c) (04:88) [14:48:23:865]: Doing action: ExitDialog
Action 14:48:23: ExitDialog.
Action start 14:48:23: ExitDialog.
The end result is the Registry Key is created but has no value.
(If I set a default value that value gets populated in the registry)
Screenshots attached.

Most likely the dialog you use to set the registry property is executed/displayed after the WriteRegistryValues action is executed. This way the registry value is written before the user can input the registry value on your dialog.
To correct this bad design you should add your dialog, in Dialogs view of your advanced installer setup project, anywhere before the ProgressDlg dialog.

Related

Firebase remote config - Conditional parameter cannot select "User propertier"

I have added custom properties to users, like anonymousId, favoriteX and so on, but in firebase remote config ui I cannot create a condition like
for user that has property anonymousId = qa then set value of property to somevalue.
I fixed the issue.
That menu is enabled only after specified custom definitions in firebase, where user is the scope

Outlook Rest API push notifications: Filter the notifications based on specific custom property set by outlook add-in

I followed same steps that are mentioned in this question, to filter the push notification events based on custom properties set by outlook add-in.
Below is the resource link that I used while subscribing to push notifications.
https://outlook.office.com/api/v2.0/me/events/?$filter=SingleValueExtendedProperties%2FAny(ep%3A%20ep%2FPropertyId%20eq%20'String%20{00020329-0000-0000-C000-000000000046}%20Name%20cecp-7e248e5e-204e-4e2b-aa0f-788af20fc21c'%20and%20ep%2FValue%20ne%20null)
It's filtering the calendar items that are having custom properties set by add-in, irrespective of whatever custom property it is.
By looking at this resource link, we could say that no where we have mentioned the custom property name. But my add-in sets more than one custom properties to calendar item. I want to filter all calendar items that are having specific custom property. For example, My add-in sets any one of the below custom property to calendar based on business login.
Custom property 1:
var item = Office.context.mailbox.item;
item.loadCustomPropertiesAsync((result) => {
const props = result.value;
props.set("my_prop_one", "test_value_one");
props.saveAsync((saveResult) => console.log("Successfull"));
});
Custom property 2:
var item = Office.context.mailbox.item;
item.loadCustomPropertiesAsync((result) => {
const props = result.value;
props.set("my_prop_two", "test_value_tw");
props.saveAsync((saveResult) => console.log("Successful"));
});
Now I want to filter all calendar items that are having custom property my_prop_one.
EDIT 1:
As suggested by #Jason Johnston in one of the comments, I cross verified the property name and it's GUID using MFCMapi. Both property name and it's GUID values are correct.
MFCMapi data of custom property meetingsetby.
Then I collected data from MFCMapi and prepared the below url to filter calendar items that are having custom property meetingsetby and it's value webex.
https://outlook.office.com/api/v2.0/Me/Events?$filter=SingleValueExtendedProperties%2FAny(ep%3A%20ep%2FPropertyId%20eq%20'String%20{00020329-0000-0000-C000-000000000046}%20Name%20meetingsetby'%20and%20ep%2FValue%20eq%20'webex')
And below is the response from postman when I make the get call using above url.
As you can see, response has empty list even though there is one calendar item with custom property meetingsetby and value webex.
Then I set the SingleValueExtendedProperty to calendar item using outlook Rest API as described in this post. Below is the sample request data,
MFCMapi data of SingleValueExtendedProperty
Then I collected data from MFCMapi and prepared the below url to filter calendar items that are having singleValueExtendedProperty set in above step.
https://outlook.office.com/api/v2.0/Me/Events?$filter=SingleValueExtendedProperties%2FAny(ep%3A%20ep%2FPropertyId%20eq%20'String%20{6666AA44-4659-4830-9070-00047EC6AC6E}%20Name%20RestApiSingleValueExtendedProperty'%20and%20ep%2FValue%20eq%20'Set this property using REST API')
And below is the response from postman when I make the get call using above url.
As you can see, I can successfully filter the calendar items using singleValueExtendedProperty. But my requirement is filter calendar items that are having specific custom property set by my outlook web add-in.
Any suggestion/answers would be more than welcome.
Custom properties set by an add-in (using the CustomProperties interface) are not equivalent to normal MAPI named properties. Essentially what the add-in APIs do is take all of your "custom properties", serialize them as a JSON payload, then save it in a single MAPI named property, which will have the name cecp-{some guid}, and the property set GUID PS_PUBLIC_STRINGS {00020329-0000-0000-C000-000000000046}. The {some-guid} part of the name is equal to the Id of your add-in. This is all specified in MS-OXCEXT section 2.2.5.
So the end result here is that you cannot use $filter on the values you set in the CustomProperties interface, because there is no SingleValueExtendedProperty with that name and value. Instead, there is a single SingleValueExtendedProperty with the name cecp-{some guid}, with a string value that's the JSON serialization of ALL the custom props you set via the CustomProperties interface.
So how can you do what you want? Well, going back to your original URL, you can get all messages that have ANY properties set by your add-in by doing
$filter=SingleValueExtendedProperties/Any
(ep: ep/PropertyId eq 'String {00020329-0000-0000-C000-000000000046}
Name cecp-7e248e5e-204e-4e2b-aa0f-788af20fc21c' and ep/Value ne null)
Replacing the GUID after the cecp- with the GUID ID for your add-in.
But of course you want to find just the ones that have a specific property (meetingsetby) set to a specific value (webex). Unfortunately our API won't support substring searches when filtering the SingleValueExtendedProperties. So what you'd need to do is get all messages with any properties set by your add-in, then do your own filtering logic to find just the ones you want. So basically you would load them in memory, then check the value of that property yourself to find the ones you want.
You can make sure that the value of the property is included in the response by using an $expand clause. Something like this:
?$filter=SingleValueExtendedProperties/Any
(ep: ep/PropertyId eq 'String {00020329-0000-0000-C000-000000000046}
Name cecp-7e248e5e-204e-4e2b-aa0f-788af20fc21c' and ep/Value ne null)
&$expand=SingleValueExtendedProperties($filter=PropertyId eq 'String
{00020329-0000-0000-C000-000000000046} Name cecp-7e248e5e-204e-4e2b-aa0f-788af20fc21c')

Umbraco - Automatically sync the Content Node Name with a doc type property

Am using Umbraco 4.11.10
Is there a standard way to create a document type property which when updated, automatically syncs with the content node name?
I know this can be done in the Properties section in the Name field but that field cannot be moved from the properties tab and it is a little out of the way - users get confused.
How is this usually done?
Wing
There are some special use umbraco field aliases. One is umbracoUrlName which will override the page url - just add it to your doctype and put it in whichever tab you want to change the url from.
EDIT
Another option would be to create a custom data type and use it to create a field that overwrites the node name. Add a text field as the UI of the custom data type; add an event that is fired whrn the textbox changes and update the name.
http://our.umbraco.org/wiki/reference/api-cheatsheet/modifying-document-properties
// Get the document by its ID
Document doc = new Document(node.Id);
// Get the properties you wish to modify by it's alias and set their value
// the value is saved in the database instantly!
doc.getProperty("name").Value = <input textbox value?;
// After modifying the document, prepare it for publishing
User author = User.GetUser(0);
doc.Publish(author);
// Tell umbraco to publish the document so the updated properties are visible on website
umbraco.library.UpdateDocumentCache(doc.Id);

Wix: ListBox value limitation

Value field in ListBox table has String[64] type. Is there posible to expand this 64-characters limitation? I need to store some directory pathes there.
It's probably (never tried) possible in WiX to override the default schema of he ListBox table. I know in InstallShield I just go to the direct editor and adjust it. WiX has a template schema that is used to build the MSI and you might be able to use the Table element to redefine it. Or it might just give you an error message saying you are defining a well known table.
However, I'm not sure if there would be any side effects in the ListBox control if you exceed 64 char. I don't see anything in the MSI SDK saying what's allowed so I guess your milage may vary.
Here's a trick that you might like though. It's called the evil twin dialog trick. See, in MSI there's a bug that UI elements don't refresh very well and this trick works around it. Consider this:
Dialog1 with ListBox associated to property TESTPROP and Items One value 1 and Two value 2.
Textlabel that displayes [TESTPROP].
When start the dialog the text label is empty after clicking a row in the listbox. Click back and next and suddenly it has the expected text of 1 and 2.
Now create a clone of this dialog ( Dialog2 ) and put a control event on the Listbox of dialog1 that says NewDialog Dialog2 condition=1 and put a control event on the Listbox of dialog2 that says NewDialog Dialog1 condition = 1
Now when you run it the screen refreshes ( although with a big of an ugly flicker ) See it looks like it's the same dialog only it's really the evil twin dialog that's being transitioned to so that the data refreshes correctly.
Now for extra credit use your custom actions to do something like this
ListBox Item 1 Text C:\Pro...Foo\Bob value LISTBOXDIRPROP1
ListBox Item 2 Text C:\Pro...Foo\Ed value LISTBOXDIRPROP2
Property LISTBOXDIRPROP1 = C:\Program Files\Foo\Bob
Property LISTBOXDIRPROP2 = C:\Program Files\Foo\Ed
Then set your TextLabel to display [[TESTPROP]]. This tells it to get deference the value of the value of the property. In other words, TESTPRO = LISTBOXDIRPROP1 = C:\Proggram Files\Foo\Bob
This trick would allow you to display a line that fits the 64 char constraint yet gives additional information when the user selects it.

Strange "A component named TFrm1 already exists" error

I want to let the user to create multiple instances of the same form (let's call it Form1 which is a MDI child form). So I have two procedures like this where I create the forms.
procedure MyProcedure1; // procedure 2 is similar. it also has a var called MyFrm
var MyFrm: TFrm1;
begin
...
MyFrm:= TFrm1.create(MainForm);
MyFrm.BringToFront;
MyFrm.LoadFromFile(someFile);
end;
As you can see MyFrm is local var. This is ok for me as I don't need to programatically access the form after I create it. There is no other global variable named Frm1. In the OnClose event of MyFrm I have Action:= caFree;
What could cause the error above?
A user sent that error. It happened only once and I cannot reproduce it.
Edit:
The error appears in the "MyFrm:= TFrm1.create" line.
Some people suggested that I need to programatically give unique names to my dynamically created forms. I also wondered myself what name a form takes when it is created so I stepped into the code while calling the MyProcedure1 procedure.
Delphi automatically gives unique names like
MyFrm.name= MyFrm, then
MyFrm.name= MyFrm_1,
MyFrm.name= MyFrm_2,
MyFrm.name= MyFrm_3, and so on.
The MyFrm.Name is not altered in LoadFromFile. I have checked (breakpoint) the value of 'MyFrm.Name' at the end of procedure MyProcedure1; after LoadFromFile. The name is unique.
As some people suggested, I override the SetName procedure and checked the name of TMyFrm. Indeed each form gets a unique name.
procedure TMyFrm.SetName(const Value: TComponentName);
begin
ShowMessage(Value);
inherited;
end;
I have many forms in this app but only the MainForm is auto-created.
I don't use threads. Anyway this will not be relevant since the forms are created by user (so multi-threading is irrelevant unless the user can create 2 forms at the same time).
Giving MainForm as the Owner in TFrm1.Create will include the newly created form in the components list of MainForm. A component ensures that this list doesn't contain any two components with the same non-empty name (otherwise FindComponent won't work). This mechanism also works when a component changes its name.
As long as you don't specify the name in TFrm1.Create it is most likely that it is set by the LoadFromFile method, which means that you don't have much influence on the name unless you change the file's content.
A valid workaround is to create the form with nil as Owner, load the form from the file, change the name to a unique value or to an empty string and finally call MainForm.InsertComponent.
procedure MyProcedure1;
var MyFrm: TFrm1;
begin
...
MyFrm:= TFrm1.create(nil);
MyFrm.BringToFront;
MyFrm.LoadFromFile(someFile);
MyFrm.Name := ''; // or some unique name
MainForm.InsertComponent(MyFrm);
end;
The message is caused because each form must be uniquely named.
When you create a form twice, you need to ensure each instance has a unique name, or set the Name to an empty string. The latter also is the trick when using multiple instances of a data module, so that the automatic linking of data-aware controls does not end up always using the first instance.
Add
MyFrm.Name := MyFrm.Name + <something unique>;
MyFrm.Name := '';
after the Create call and you should be fine
MyFrm.Name is the same for both instances...
Make sure than MyFrm.Name is unique...
As far as my exploration along this line, yes the problem of "already exists" stems from having intances of the editor with the same value for the Name property. As another work around do not visually create the editor(s). Create a new component based on TForm/TFrame/TPanel for the editor(s) you want the user to be able to create multiple instances of. You will have to hand code the creation & deletion of any sub-controls, Setting their properties within your code and assigning values - anything from V_Btn = new TBitBtn(this), V_Btn->Color = clTeal, to V_Btn->OnClick = Close_The_Window. BUT NEVER assign a value to the Name property of any component in the new class and do not set the Name property of the editor once you have created an instance of the editor. Treat the Name property for editor as if it did not exist. After you have created the class and added it to your project the following is valid :
TMyeditor* Editor_01 = new TMyeditor(Main_Form);
TMyeditor* Editor_02 = new TMyeditor(Main_Form);
Editor_01->Parent = Tab_Sheet_Addresses;
Editor_02->Parent = Tab_Sheet_Billing;
The more complex the design concept for your editor the more effort you will undergoe to code the class. However this approach will resolve the "already exists" error.
End of answer.
The following is tangental to the original question as it is an extension of what you may want to further do with your code & I write it to help you along should it be the case. The following allows you to efficiently store/retrieve the editor(s) and its published properties such as position on the user's screen, themes, etc. If you've gone the above route, add the following :
void RegisterClassesWithStreamingSystem(void)
{
// Make sure that as part of the startup
// code TMyEditor is registered
// with the streaming system.
#pragma startup RegisterClassesWithStreamingSystem
Classes::RegisterClass(__classid(TMyEditor));
}
You can now ComponentToString <---> StringToComponent[*1] the editor(s).
You can now create a simple database of each editor saving it [*2] and re-creating the editor(s) at runtime. Saving & Recreating is almost entirely done by the TReader/TWriter objects.
{It is worth while to read about TReader/TWriter which is included in the Delphi help file}
[ Presupposing you have an instances of TMyEditor you want to save called Editor_01 & Editor_02 and
you've created the dataset and assigned it to a TClientDataSet named "CDS" ]
//How to write the Editors
String_Version_Of_Editor = ComponentToString(Editor_01);
CDS->Insert();
CDS->FieldByName("Data")->AsString = String_Version_Of_Editor;
CDS->Post();
String_Version_Of_Editor = ComponentToString(Editor_02);
CDS->Insert();
CDS->FieldByName("Data")->AsString = String_Version_Of_Editor;
CDS->Post();
//How to read, create an instance of, set the Owner of
//(allowing for automatic destruction/deletion
// if desired, Vis-à-vis Let the compiler/runtime package handle that),
//& setting the form's Parent
AnsiString String_Version_Of_Editor;
TWinControl* New_Editor;
String_Version_Of_Editor = CDS->FieldByName("Data")->AsString;
//The next line creates/constructs the new editor
New_Editor = StringToComponent(String_Version_Of_Editor);
//The next line sets the new editor's Owner to Main_Form
//It also assigns Main_Form the responsibility of object cleanup
Main_Form->Insert(New_Editor);
//The next line sets the Editor's Parent causing it to be part of the
//displayed user interface (it has been invisble since creation)
New_Editor->Parent = Tab_Sheet_Addresses;
//Move on to the next editor;
CDS->Next();
String_Version_Of_Editor = CDS->FieldByName("Data")->AsString;
New_Editor = StringToComponent(String_Version_Of_Editor);
Main_Form->Insert(New_Editor);
New_Editor->Parent = Tab_Sheet_Billing;
People who read the above who are astute will have noted that in the above code the New_Editor is of type TWincontrol not TMyEditor - though it likely should have been. However I did this to draw attention to the fact that problematically the TReader object in Delphi which is really doing the work of converting a string to a component object instance creates/constructs any object which has been registered with the streaming class via RegisterClass. In this manner explicit creation of the editor via explicitedly naming it's type is avoided. If thought is given to the design of TMyEditor and its descendents the only change required to the code is to change TWinControl* to TMyEditor* - even that is not required if published properties beyond TWinControl* are not accessed outside the scope of TMyEditor - Example TMyEditor has access to the variables whose values it is editing and does not require this information to be supplied to the editor.(If working from a DataModule, #include the datamodule's header into TMyEditor).
Side Note:
You may have a utility to know what class was read from the database so that you may place the instance where it belongs. To do this #include <typeinfo> into your code.
Example : If you have instances of TMyEditor, TMyEditor_Generation_01, TMyEditor_Generation_02, etc writen to the database the following will allow you to examine the instances read at runtime for placement into the user interface :
if (typeid(New).name() == "TMyEditor *")
New_Editor->Parent = Tab_Sheet_Addresses;
else
if (typeid(New).name() == "TMyEditor_Generation_01 *")
New_Editor->Parent = Tab_Sheet_Billing;
else
if (typeid(New).name() == "TMyEditor_Generation_02 *")
New_Editor->Parent = Tab_Sheet_Other_Editor;
typeid(__).name() will return a string which is the name of the class, in this case will also inculde " *".
The above allows ANY object(s) to be stored in the database and recreated. The entries within the database are not required to be related. The TReader object buried in Delphi's code will decide at runtime what they are and use the correct constructor.
[*1] Note : The ComponentToString and StringToComponent are examples in the delpi/c++ help file.
[*2] Note : What is being saved are the published properties, therefore in your editor class any values you want stored and retrieved which are not already inherited and published should be declared in the __published section of your new class. Those items may also be custom objects, for those you will likely code custom specific methods/functions for the read/write access specifiers in defining the _property. I would suggest translating any complex object into a string value for ease of examining your code while under development.

Resources