I can't seem to find any useful documentation from Microsoft about how one would use the Delimiter and InheritsFromParent attributes in the UserMacro element when defining user Macros in .vsprops property sheet files for Visual Studio.
Here's sample usage:
<UserMacro Name="INCLUDEPATH" Value="$(VCROOT)\Inc"
InheritsFromParent="TRUE" Delimiter=";"/>
From the above example, I'm guessing that "inherit" really means "a) if definition is non-empty then append delimiter, and b) append new definition" where as the non-inherit behavior would be to simply replace any current macro definition. Does anyone know for sure? Even better, does anyone have any suggested source of alternative documentation for Visual Studio .vsprops files and macros?
NOTE: this is not the same as the InheritedPropertySheets attribute of the VisualStudioPropertySheet element, for example:
<VisualStudioPropertySheet ... InheritedPropertySheets=".\my.vsprops">
In this case "inherit" basically means "include".
[Answering my own question]
InheritsFromParent means prepend. To verify this, I did an experiment that reveals how User Macros work in Visual Studio 2008. Here's the setup:
Project p.vcproj includes the property sheet file d.vsprops ('d' for derived) using the InheritedPropertySheets tag.
d.vsprops includes the property sheet file b.vsprops ('b' for base.)
p.vcproj also defines a Pre-Build Event which dumps the environment.
Both .vsprops files contain User Macro definitions.
b.vsprops
...
<UserMacro Name="NOENV" Value="B"/>
<UserMacro Name="OVERRIDE" Value="B" PerformEnvironmentSet="true"/>
<UserMacro Name="PREPEND" Value="B" PerformEnvironmentSet="true"/>
...
d.vsprops
...
<VisualStudioPropertySheet ... InheritedPropertySheets=".\b.vsprops">
<UserMacro Name="ENV" Value="$(NOENV)" PerformEnvironmentSet="true"/>
<UserMacro Name="OVERRIDE" Value="D" PerformEnvironmentSet="true"/>
<UserMacro Name="PREPEND" Value="D" InheritsFromParent="true"
Delimiter="+" PerformEnvironmentSet="true"/>
...
p.vcproj
...
<Configuration ... InheritedPropertySheets=".\d.vsprops">
<Tool Name="VCPreBuildEventTool" CommandLine="set | sort"/>
...
build output
...
ENV=B
OVERRIDE=D
PREPEND=D+B
...
From these results we can conclude the following:
PerformEnvironmentSet="true" is necessary for User Macros to be defined in the environment used for build events. Proof: NOENV not shown in build output.
User Macros are always inherited from included property sheets regardless of PerformEnvironmentSet or InheritsFromParent. Proof: in b.vsprops, NOENV is not set in the environment and in d.vsprops it is used without need of InheritsFromParent.
Simple redefinition of a User Macro overrides any previous definition. Proof: OVERRIDE is set to D although it was earlier defined as B.
Redefinition of a User Macro with InheritsFromParent="true" prepends the new definition to any previous definition, separated by a specified Delimiter. Proof: PREPEND is set to D+B (not D or B+D.)
Here are some additional resources I found for explanation of Visual Studio .vsprops files and related topics, it's from a few years back but it is still helpful:
understanding the VC project system part I: files and tools
understanding the VC project system part II: configurations and the project property pages dialog
understanding the VC project system part III: macros, environment variables and sharing
understanding the VC project system part IV: properties and property inheritance
understanding the VC project system part V: building, tools and dependencies
understanding the VC project system part VI: custom build steps and build events
understanding the VC project system part VII: "makefile" projects and (re-)using environments
There's documentation on the UI version of this here.
A lot of the XML files seem somewhat undocumented, often just giving a schema file. Your guess as to how they function is pretty much right.
It is not the whole story.
Delimiters are not inherited. Only
the list of items they delimit are inherited: The same user macros can have different delimiters in different property sheets but only the last encountered delimiter is used. (I write "last encountered" because at project level, we cannot specify a delimiter and what gets used there is the last property sheet that specified inheritance for that macro)
Delimiters works only if made of a
single character. A delimiter longer
than one character may have its
first and/or last character stripped
in some cases, in a mistaken attempt
to "join" the list of values.
$(Inherit) appears to work inside
user macros. Like for aggregate
properties, it works as a placeholder for
the parent's values, and it can appear multiple times. When no $(Inherit) is found, it is implied at the beginning if the inheritance flag is set.
$(NoInherit) also appears to work in user's macros(makes VC behaves as if the checkbox was unticked).
User macros (and some built-ins) appears
to work when used for constructing a property sheet's path (VC's own project converter uses that feature). The value
taken by user's macros in this situation is not
always intuitive, though, especially if it gets redefined in other included property sheets.
In general, what gets "inherited" or concatenated are formulae and not values (ie. you cannot use a user macro to take a snapshot the local value of (say) $(IntDir) in a property sheet and hope to "bubble up" that value through inheritance, because what gets inherited is actually the formula "$(IntDir)", whose value will eventually be resolved at the project/config/file level).
A property sheet already loaded is ignored (seem to avoid that the same property sheet has its user macros aggregated twice)
Both "/" and "\" appear to work in
property sheet paths (and in most
places where VS expects a path).
A property sheet path starting with
"/" (after macros have been resolved) is assumed to be in "./", where '.' is the location of the
calling sheet/project). Same if the path does not start with "./", "../" or "drive:/" (dunno about UNC).
Related
Visual Studio has macros like $(TargetDirectory), $(OutputPath) etc.
In my source code, I want to specify a relative path for the loading of a file from a folder a few levels below the TargetDirectory.
Currently I'm doing this: mLayer = mEngine->AddLayer("D:\\Projects\\abc.osg"); and I want it to be something like mLayer = mEngine->AddLayer(($TargetDirectory)+"..\\..\\abc.osg");
It's just a temporary requirement, so that I can give my code to a person for a small demo, and his TargetDirectory is differently aligned wrt my directories. Is there any way to make use of the Visual Studio macros in source code? (at least I know that System environment variables can be accessed)
Go to Project Properties -> Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions and add the following:
TARGET_DIRECTORY=LR"($(TargetDir))"
This defines a wide string literal named TARGET_DIRECTORY that contains the contents of the $(TargetDir) macro. The important thing here is that this creates a C++ raw string that does not treat backslashes as escape characters. Paths contain backslashes. Using a regular string literal would be incorrect and would even give you compiler errors in some cases.
Important!
If you use a macro that may contain a closing parenthesis followed by double quotation marks )" you must use an additional delimiter, that cannot occur in the macro value, for example:
TARGET_DIRECTORY=LR"|($(TargetDir))|"
In the case of windows file system paths this is not necessary because paths cannot contain double quotation marks.
You cannot do this automatically, but you can pass specific MSBuild properties to the preprocessor:
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>TARGET_DIRECTORY="$(TargetDirectory)"</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
This can be configured in the IDE by going to the Project Property Pages dialog, browsing to Configuration Properties -> C/C++ -> Preprocessor Definitions, and adding
TARGET_DIRECTORY="$(TargetDirectory)"
Note that your use of + for string literal concatenation is incorrect: string literals (and C Strings in general) cannot be concatenated using +. Rather, string literals can be concatenated simply by placing them adjacent to each other. For example,
TARGET_DIRECTORY "..\\..\\abc.osg"
Inside Properties -> Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor you can add your directive.
PROJECT_DIR=R"($(ProjectDir))"
In code you can use it like.
std::string com = "powershell groovy "PROJECT_DIR"generate_report.groovy " + EXE_DIR;
EXE_DIR is a string in our case.
I'd suggest making these relative to the application's working directory, or something. Perhaps check out the GetCurrentDirectory function, at http://msdn.microsoft.com/en-us/library/aa364934%28v=vs.85%29.aspx.
Not that I know of but I have an alternative.
Deploy your file as a post build step. In this step you can consume the $(OutDir) macro which represents your binaries drop folder. This should help you place this file at a relative position from your app and use that relative position within your code.
This will also be a lasting solution rather than something done temporarily.
use TARGET_DIRECTORY=""$(TargetDir)"" instead of TARGET_DIRECTORY="$(TargetDir)" for string macro. (Note double quotes)
Worked for me in VS2005.
Scenario 1
I created an empty vc++ project added a c file to it and the #include. Now in my main() function if I hover my mouse over the MessageBox func., it resolves to MessageBoxA.
Scenario 2
I create a win32 windows project now here MessageBox resolves to MessageBoxW???
I checked the Project properties->c/c++->preprocessor property there I found WIN32 defined so I did that in my previous project but still the same result.
What should I do.Yeah of course I can use the latter type of project but think of me as one stubborn rookie who wants to learn the tid-bits.
thanks.
The default "Character Set" property for a new empty project is "Multi-Byte", which means that the preprocessor will not define the UNICODE preprocessor symbol and so MessageBox will be replaced by MessageBoxA.
For a Win32 project, the default "Character Set" property is "Unicode", which means the preprocessor will define UNICODE and thus MessageBox will be replaced by MessageBoxW.
See the MSDN article Working with Strings for an introduction.
This is determined by the presence or otherwise of the UNICODE conditional which is determined by the project configuration: either Unicode or Multi-byte character set.
Project + Properties, Configuration Properties, General. It is the Character Set property. Only create empty projects after you figured out how everything works. Albeit that the quickest way to learn is by making all the mistakes.
General recommendation: always use MessageBoxW(). There's very little reason to compile your program with MBCS setting, which literally means "textually broken".
Try as I might to keep [SuppressMessage]s to a minimum in the codebase, new ones continue to get introduced. I would like some way to say in the code "I've reviewed this [SuppressMessage] and it is 'acceptable'".
One thought was to create my own My.CodeAnalysis.SuppressMessageAttribute class which inherits from System.Diagnostics.CodeAnalysis.SuppressMessageAttribute, but that class is sealed.
I'm sure I could cobble together something with a comment near the attribute and a post-processing step, but I'd like to stick to what is available "in the box" with Visual Studio 2010.
Even if the attribute weren't sealed, a subclassing approach would not work since the code analysis engine screens for exact SuppressMessageAttribute instances. As current versions are written, the engine would completely ignore subclass instances.
Personally, I use the following approach for management of suppressions:
All "permanent" suppressions must
have a Justification property that
explains the reason for the
suppression.
Where possible, permanent
suppressions must be placed in the
target code file. Otherwise, they
must be placed at the top of the
GlobalSuppressions file.
Temporary suppressions must all be
placed below a "Temporary
suppressions" header comment in the
GlobalSuppressions file, and they
should not be assigned a
justification.
All permanent suppressions are
subject to code review, in exactly
the same manner as the code itself.
I use difference detection to flag
code for review, so I can see which
suppressions have been added or
changed just as easily as I can see
which code has been modified.
Temporary suppressions must all be removed by a pre-defined phase of the project. After this point has passed, a rule that detects missing Justification properties is enabled.
If this sort of thing wouldn't work for you, another approach would be to use stand-alone FxCop to annotate your suppressed violations. Unfortunately, these annotations would reside outside your code base, but maybe that would suit you better if you want to prevent developers from "blessing" their own suppressions.
When you right-click on the CA warning in the errors list, one of the options given is Suppress Message(s) -> In Project Suppression File. When you select that, Visual Studio will add a line like the following to a GlobalSuppressions.cs file in your project:
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Autofac")]
This keeps them grouped together and out of your code files. Is this what you are trying to get at?
Very simple question that is apparently impossible to find a decent answer to: How can I make Visual Basic 6 stop changing my ^##*ing variable casing!?!
I know that the general opinion of a great many VB users is that this "feature" is actually quite helpful, but I doubt that they use it much with any source control system. This is absolutely INFURIATING when you are trying to collaborate on a project of any significant size with several other developers. If ignored, you produce thousands of false-positive "changes" to your files (even ones with no actual code changes!) that pollute the revision history and make it near impossible in some cases to locate the actual change that took place.
If you don't ignore it (like my office, where we have been forced to implement a "no unneeded case change" policy), you spend 5x the time you would normally on each commit because you have to carefully revert out VB's "corrections" on every file, sometimes reverting hundreds of lines to put in a one line change.
Surely there must be a setting, plugin, hack, etc. out there that can remove this unwanted "feature"? I am willing to take any method I can get as long as it doesn't require me to pick through piles of phantom diffs. And to squash a couple of complaints up front: No, I can't turn off case detection in my diff tool, that's not the point. No, we can't just make the case changes globally. We're working with hundreds of thousands of LOC being worked on by multiple developers spanning many years of development. Synchronizing that is not feasible from a business standpoint. And, finally: No, we cannot upgrade to VB.net or port to another language (as much as I would love to).
(And yes, I am just a tiny bit peeved at the moment. Can you tell? My apologies, but this is costing me time and my company money, and I don't find that acceptable.)
Depending on your situation adding
#If False Then
Dim CorrectCase
#End If
might help.
Here is a real world scenario and how we solved it for our 350k LOC VB6 project.
We are using Janus Grid and at some point all the code lines which referenced DefaultValue property of JSColumn turned to defaultValue. This was an opportunity to debug the whole IDE nuisance.
What I found was that a reference to MSXML has just been added and now the IDE picks up ISchemaAttributes' defaultValue property before the Janus Grid typelib.
After some experiments I found out that the IDE collects "registered" identifiers in the following order:
Referenced Libraries/Projects from Project->References in the order they are listed
Controls from Project->Components (in unknown order)
Source Code
So the simple fix we did was to create a dummy class/interface with methods that hold our proper casing. Since we already had a project-wide typelib we referenced from every project before anything other typelib, this was painless to do.
Here is part of the IDL for our IUcsVbIntellisenseFix interface:
[
odl,
uuid(<<guid_here>>),
version(1.0),
dual,
nonextensible,
oleautomation
]
interface IUcsVbIntellisenseFix : IDispatch {
[id(1)] HRESULT DefaultValue();
[id(2)] HRESULT Selector();
[id(3)] HRESULT Standalone();
...
}
We added a lot of methods to IUcsVbIntellisenseFix, some of them named after enum items we used to misspell and whatever we wanted to fix. The same can be done with a simple VB class in a common library (ActiveX DLL) that's referenced from every project.
This way our source code at some point converged to proper casing because upon check-out the IDE actually fixed the casing as per IUcsVbIntellisenseFix casing. Now we can't misspell enums, methods or properties even if we try to.
SIMPLE WAY: Dim each variable in the case that you want. Otherwise, VBA will change it in a way that is not understandable.
Dim x, X1, X2, y, Yy as variant
in a subroutine will change ALL cases to those in the Dim statement
I can sympathise. Luckily we're allowed to turn off case sensitivity in our version control diff tool!
It seems the VB6 IDE automatic case-correction occasionally changes case in variable declarations and references, perhaps depending on the order in which modules are listed in the VBP file? But the IDE doesn't tell you that the file needs to be saved. So the problem only shows up when you saved the file because of another edit. We briefly tried to prevent this by checking out all the files in a project and setting the case carefully, but it didn't go away.
I suppose you could list the variable names that are affected - the usual suspects are one letter names like "I", "X" and "Y", perhaps because they are used in standard event handlers like MouseDown. Then write an add-in that'll search for all declarations " As" and force the case to upper. Run the add-in on your modules before you check them in. You might be able to trigger the add-in to run automatically when you save in VB6.
EDIT: Something I've just thought of: adapt Fred's answer. From now on, every time you check in a file, add a block at the top to establish canonical case for the usual suspects. If nothing else, it's easier than reverting hundreds of lines by hand. Eventually you will have this block in every file & maybe then the problem will stop happening.
#If False Then
Dim I, X, Y ' etc '
#End If
I standardised the case across the codebase, normally by using the examples above (Dim CorrectCase), and removing it again.
I then triggered VB to save EVERY file, by doing a case sensitive search/replace of "End" with "End" (no functional change, but enough to get VB to resave).
Once that was done, I could then do a single commit to standardise the case, making it MUCH easier to keep on top of it at a later date.
In this example VB6 was changing the case of the following line following a typo I made when referencing a library: -
Dim MyRecordset As ADODB.REcordset
Ugly, and now every other instance of an ADODB.REcordset thus acquired the new misspelling. I fixed this as follows: -
Type in a new declaration as follows
Dim VB6CasingSucks AS ADODB, Recordset
Note the comma and space after ADODB. Hit [ENTER] for VB6 to check the line.
At this point all instances of REcordset change back to Recordset.
Delete your new declaration.
I don't know if this fix will help with enums/other variable names.
Specifically for controlling the case of enum values, there is a VB6 IDE add-in which may be helpful. Enums seem to have a slightly unique version of this problem.
As described in the link below:
The VB6 IDE has an annoying quirk when it comes to the case of Enum
members. Unlike with other identifiers, the IDE doesn't enforce the
case of an Enum member as it was declared in the Enum block. That
occasionally causes an Enum member that was manually written to lose
its original case, unless a coder typed it carefully enough.
...
However, if a project contains a lot of Enums and/or a particular Enum
has a lot of members, redeclaring the members in each of them can get
quite tedious fast. ...
Ref: http://www.vbforums.com/showthread.php?778109-VB6-modLockEnumCase-bas-Enforce-Case-of-Enums
...load and unload the add-in as needed via the Add-In Manager
dialog box. Usage is as simple as selecting the entire Enum block,
right-clicking and then choosing the "Lock Enum Case" context menu
item.
I have a similar problem:
in a bas module there I wrote :
Private sub bla_bla()
Dim K as integer
End Sub
so in a class module the Dim k as integer will automatically be replaced by IDE become 'Dim K as integer' <-- it's not logical but then:
I correct the bas module become:
Private sub bla_bla()
Dim k as integer
End Sub
then magically the problem in the class module was solved (still be k and not automatically replaced by IDE become K). Sorry I'm poor in English
I don't think there's any to do it. The IDE will change the case of the variable name to whatever it is when it's declared. But, honestly, back in the day I worked on several large VB6 projects and never found this to be a problem. Why are people on your development team constantly changing variable declarations? It seems like you have not established a clear variable naming policy that you enforce. I know your upset, so no offense, but it might be your policies that are lacking in this regard.
Unfortunately, according to this SO thread, alternate VB6 IDEs are hard to come by. So, your best bet is to solve this problem via policy. Or move to VB.NET. :)
Wow. I've spent a lot of time programming in VB6 and I have no idea what you're on about. The only thing I can think you're referring to is that intellisense will change the capitalization of variable names to match their declarations. If you're complaining about that, I would have to wonder why the hell they've been entered any other way to begin with. And if that is your problem, no, there's no way to disable it that I'm aware of. I'd suggest you, in one go, check out every file, make sure the caps on the declarations and uses of variables all match and check back in.
I have a VS project with an IntermediateDirectory like this: "....\temp\$(SolutionName)\$(ProjectName)".
I can read this value using a macro or add in, however, I would need the actual directory to manipulate files there. Right now, I manually replace the "$(SolutionName)" and "$(ProjectName)" with the respective values, which works fine but might become complicated when different macros or even user macros from property sheets are used.
So my question is:
Does the Visual Studio API have a built in function to expand macros like these? Or is there some other elegant solution?
There is an elegant solution! But I only know the one that applies to C++ projects.
Assuming you're in a C# add-in:
// Get the main project from the first startup project
VCProject vcMainProject = (VCProject)(_applicationObject.Solution.SolutionBuild.StartupProjects as IVCCollection).Item(1);
Project mainProj = (Project)_vcMainProject .Object;
// Get the configuration we'll be using
IVCCollection cfgs = (IVCCollection)_vcMainProject .Configurations;
VCConfiguration vcCfg = (VCConfiguration) cfgs.Item(mainProj.ConfigurationManager.ActiveConfiguration.ConfigurationName + "|" + mainProj.ConfigurationManager.ActiveConfiguration.PlatformName);
string finalString = vcCfg.Evaluate("....\temp\$(SolutionName)\$(ProjectName)");
You can also check out this page:
http://msdn.microsoft.com/en-us/library/czt44k0x%28VS.71%29.aspx
If you're not using this for C++, there should be a similar interface for the Project, Configuration, and Solution classes provided for other languages (C# and VB).
As far as i know, there is no API available that will expand those macro values. Although it shouldn't be too hard to write a quick and dirty implementation that deals with only the values that you care about.
For instance, in this case you only care about 2 values (SolutionName and ProjectName). If these are the values you are primarily interested in use a simple search and replace with the best values.
Yes this is a sub-optimal solution. But it may help to unblock your progress.