CNDL0005: the wix element contains an unexpected child element 'component' - msbuild

I'm trying to implement web.config editing during my install as is done here, but I'm coming up against an error when I build:
CNDL0005: the wix element contains an unexpected child element 'component'
This seems to suggest that Component should not be placed inside a product element. Yet, I'm using the same schema he is. It seems like I have to place my component inside a directory, inside a fragment, in order for msbuild to accept it. Anyone know why this is?
I've googled the error above and it returns no exact results.
I'm running Wix 3.9.

WiX is an XML/XSD abstraction of the underlying Windows Installer database. This database is relational and has such tables as Feature, FetaureComponent, Component, Directory and File. Each of these tables has Primary Key and Foreign Key Columns. For Example
Feature<->FeatureComponent<->Component
Directory<->Component<->File
A directory can have more then one component but a component can only belong to one directory. Same thing can be said for components and files. Therefore in WiX the Directory element is a parent element of the Component element and the Component Element is the parent of a File element. WiX transforms these into PK FK relationships at build time. MSBuild merely calls the WiX compiler tools (candle (the source of your 'CNDL0005' error), light) and MSBuild doesn't know anything about this directly.
A feature can contain more then one component and a component can belong to more then one feature (many to many join). In this case WiX created the Feature <-> ComponentRef relationship.
The example you cite shows the elements in the correct relationship. You just aren't assembling it correctly. The Wix.chm in your startmenu has help topics for each element and lists which elements can be the parent, which can be a child and has a link to the related Windows Installer database table that the element represents.

Related

Is it a bad practice to have 2 components with the same name if they're in different folders?

Lets say I have a website where I sell some sort of products. I'd have a Product.vue component which will be used to render all the products the user would see on the home screen.
I'd also need a second component that will be used to render the products on the admin panel, where the admin will be able to edit/delete.
Obviously both components will render a product, but they'll look different, have different functionality so I assume I can't make an all-mighty universal component for that, so I'd need 2 different components.
Now I'm really confused how to name my components. Currently I have the following folder structure:
Components
---Admin
------Product.vue
---Product.vue
So in the components folder I have a Product.vue and the Admin folder, which contains the admin Product.vue. I'm wondering if having 2 components with the same name in different folder is a bad practice?
The second alternative is calling the admin product AdminProduct.vue
Two components with the same name in the single app is never a good idea from maintainer point of view.
Lot of useful info can be gained from Vue Style Guide
Multi-word component names
Component names should always be multi-word, except for root App components, and built-in components provided by Vue, such as <transition> or <component>.
This prevents conflicts with existing and future HTML elements, since all HTML elements are a single word.
Tightly coupled component names
Child components that are tightly coupled with their parent should include the parent component name as a prefix. If a component only makes sense in the context of a single parent component, that relationship should be evident in its name.
So AdminProduct.vue is fine...
And instead of Product.vue it should be a ProductView.vue
Vue has an object hierarchy and beyond that it can render even multiple instances of the same component. So object-wise you're good.
Regarding the file names, I don't see big risks in it because one of the Product.vue files is clearly in the Admin folder and the other is not. However if this is a project that will be worked on by multiple developers, it might be wiser to name them differently. Not because git can't deal with directory structures, but because humans can make errors. At least I do it all the time.

WiX/MSI dynamically add CopyFile elements

I am a noob in Windows programming, but I have to create a tricky MSI installer that installs a plugin via WiX toolset.
The installer should detect upon running all installed versions of specific software and their plug-ins directories via Windows Registry API. After that it should show all of them on a separate page (dialog) with appropriare checkboxes. A user should choose what versions they want the plugin install into.
I've created a custom action (in C++ and put it into a DLL that is in the MSI database) that interacts registry API then loops over the results and adds temporary records to the database tables:
adds path properites to persist the plugin paths;
adds records to the CheckBox table;
adds properties for them to hold their states;
adds conditions for them than checks their property states and enables/disables them;
adds events to reset path properties according to the state properies.
It runs after AppSearch
<InstallUISequence>
<Custom Action="PopulateVersions" After="AppSearch">Not Installed</Custom>
</InstallUISequence>
Then clicking Next button (I know that it's a wrong place to do such things) performs the custom action that filters off active property paths takes the first of them and performs the SetTargetPath action (it works fine). For the rest of them the action inserts appropriate temporary records into:
the DuplicateFile table, where DestFolder is the property name;
the Component table, copying all field values from the original component, setting the Component_Parent field value to the original;
the Directory table. One record per path property, Direcory_Parent is TARGETDIR;
It installed the plugin only to the first property path refers to (which was passed to the SetTargetPath action).
Fine... I added a few CopyFile elements that refer to my custom properties (I declared a few properies to prevent MSI build error due to unknown properties) to the WiX markup just for testing:
<Property Id="PathProperty0" Value="{}"/>
<Property Id="PathProperty1" Value="{}"/>
<Property Id="PathProperty2" Value="{}"/>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id="INSTALLLOCATION" Name="MyPluginDir">
<Component Id="PluginExt" Guid="C112184A-307C-5E15-994F-0DFDA9DD427E">
<File Id="MyPlugin" Name="MyPlugin.dll" Source="MyPlugin.dll" Vital="yes" />
<CopyFile Id="MyPlugin_Copy1" FileId="MyPlugin" DestinationProperty="PathProperty1"/>
<CopyFile Id="MyPlugin_Copy2" FileId="MyPlugin" DestinationProperty="PathProperty2"/>
</Component>
</Directory>
</Directory>
</Directory>
Now the ProgressDialog is saying that "{}" is invalid path, but all of the properties were set to valid paths (I've checked this in debug)! Seems like it forgets or ignores all temporary database changes/property changes after showing elevating UAC prompt that asks for access to the same MSI file. The prompt is raised after clicking the Install button (with a shield icon). Probably it reads the database again and does not find any changes as they were in memory or cache, I don't know.
What am I doing wrong or how to make that installer properly? IMHO my implementation is overcomplicated. I do need your assistance.
I see at least two mistakes:
The properties for paths that you want to change at run time should be PUBLIC properties (that is, their names must not include lower case letters. Furthermore they should probably be listed in the SecureCustomProperties property, which you can ensure by declaring them in your source with or without a value, and specifying the Secure attribute.
The value you are specifying for your directory-related property is literally {}. This is an unusual value, and is only rarely correct. Typically you want an empty string instead. The exception is when you are in a Formatted context and need to specify a non-empty string that results in an empty string value, such as a property-setting ControlEvent. The Property element of your wix source is not such a context.
I'm not certain about the full complexity of the approach you described (I haven't been able to fully think it through), but I do want to encourage the general approach you're taking of programmatically creating table data and letting Windows Installer do the rest. This approach is important enough to have been given a name of sorts: semi-custom action.
Per the guidance on semi-custom actions, you will likely want to schedule your PopulateVersions action in both the InstallUISequence and InstallExecuteSequence (especially once it uses RemoveFile to perform cleanup) in order to ensure that installs, maintenance, and silent removals all get the proper table data added.
I think you are making this over complicated. You could just create an AddIns feature with a bunch of AddInVersionXXXX sub features and use the results of the AppSearch in Feature Conditions then show the Custom Setup dialog.
In my IsWiX I do a similar thing except I always install to every version detected. The trick is CopyFile will gracefully skip over the copy if the directory property doesn't have a value. It's really simple and can be see here:
http://iswix.codeplex.com/SourceControl/latest#main/Source/Installer/IsWiXNewAddInMM/IsWiXNewAddInMM.wxs

Get target file path from custom action

How can I get a path to file being installed from a WiX custom action?
I'm creating a WIX Extension, which has a custom element which can be nested under <File> component like this:
<Component Id="FooComponent">
<File Id="filekey" Name="foo.txt">
<myextension:Stuff />
</File>
</Component>
The extension has its custom table which has a foreign key columns pointing to "Component" and "File" tables, and it is executed when a file's component is being installed/uninstalled (like the built-in IIS extension or SQL extension for example)
What I want to achieve is, in my deferred (sheduled) custom action, figure out the target path of the file the extension isbound to. I.e. basically in the differed custom action, I want to get value of [!filekey] (in terms of MSI formatted string). How can I go about that?
I have found a somewhat similar topic here
One of the solutions suggested was to use MsiFormatRecord from a custom action and pass that [#filekey] to that function. It resolves properly then.
I've found examples of using this approach in WiX sources, in gaming extension and NetFX extensions; they use code like this:
StrAllocFormatted(&pwzFormattedFile, L"[#%s]", pwzFileId);
WcaGetFormattedString(pwzFormattedFile, &pwzGamePath);
Here WcaGetFormattedString is basically a wrapper for MsiFormatRecord
Still unanswered, is this a right approach to the issue?
Basically you pass that [!filekey] to the deferred custom action in that format, using the CustomActionData indirect way of doing that. Then in your custom action you get the property value [CustomActionData] . This might help:
How to pass CustomActionData to a CustomAction using WiX?
To spell it out, if your type 51 custom action that is used to prepare CustomActionData has a Target of [!filekey] then when you retrieve the value of the CustomActionData property inside your custom action then it will contain the full path to the file, the entire path and filename. This demonstrably works, although it wouldn't be practical if you were doing this for many files.

WiX: Fragment not getting included when I replace repititous <ComponentRef> with Feature attribute

We're updating some old WiX scripts to take advantage of the Feature attribute of components, so that we don't have to update the file in two places whenever we add or remove a component (once to add the Component and again to add a ComponentRef to the ComponentGroup).
We separate out WiX project into separate files, one per fragment to make things more manageable. But my fragment defining the components for a feature is no longer getting included. It sounds identical to Tomas's issue in his response to the Feature attribute announcement. But while he is using Heat to auto-generate his Wix file, we're manually crafting them from scratch.
I tried creating a dummy property in the fragment's .wxs file and then referencing it in the main .wxs within the Product element. The fragment was still not included and I got an invalid property ID.
We initially tried using the strategy described here to reference directories in the component, but while this eliminates the need for a separate ComponentGroup full of ComponentRefs, it also separates my component definition from the directory tree definition, which kind of defeats the purpose of only needing to look in one place to do an update.
Will I need to pull all of my fragments into the main Product.wxs file simply to take advantage of the Feature attribute, and avoid having to update the file in two places per component? Or is there an easy way to get the fragment to include? Again using a dummy property reference didn't seem to work.
Edit: I think I found the issue in my build--I had defined a Directory under the Product Tag and then a DirectoryRef in the Fragment, rather than the other way around.
Take a look at how we authored the WiX setup itself: Author Components under ComponentGroups and use ComponentGroupRef to pull those into Features. That eliminates most duplication. You can author Directory and DirectoryRef elements in fragments in the same file as your Components/ComponentGroups.

WIX Feature Tree de-selecting sub-features doesn't change size of parent component

Under my parent component are three sub-components - a website and two window services. A hidden sub-feature also has the database scripts that are executed using SQLCMD.
When I deselect any of the sub-features, it doesn't change size of parent component i.e. the size of the parent component doesn't decrease, it remains the same as when all the features are selected.
Please advise.
The terminology here is confusing. Are "sub-components" in your lingo Features? If so, then how big are the File elements in the Features? The cost will only include the size of Files unless you add costs with the ReserveCost element.