I realize that this question has been asked before - but none of the answers apply to my specific case:
I have a solution containing a native C++ EXE project, which in turn depends on 40+ native C++ DLL projects (in the same solution).
The EXE project has the usual Debug, Release (and Profile) configuration, but since them same source code base is used to build three different applications (say A1, A2 and A3). In addition, it targets x32 and x64, so that's a total of 3 x 3 x 2 = 18 project configurations.
The DLL projects are shared by all three target applications, so there are only 3 (Debug, Release, Profile) times 2 (x32, x64) = 6 configurations.
Since the EXE project depends on the DLLs, it needs to copy the relevant output of the DLL projects to its target folder (or a subfolder thereof) whenever they are modified.
How would you handle updating of the DLLs?
A post-build action in each DLL project doesn't work (well), as it would have to copy the DLL to the relevant output folder of A1, A2 and A3. Any change in an output folder results in having to modify the post-build action in each of the 50 DLL projects.
I currently added the DLLs themselves as project items to the EXE project, and configured a custom build tool to copy it to the output folder. But that requires me to add each DLL 6 times, so I end up with 6 x 50 = 300 DLLs!
What I really need is something like a Copy Local option, which unfortunately only works assemblies. Do solution do you suggest to simplify this build setup?
What I'd do is to put all DLLs in a single folder (specific to Debug/Release etc), and copy them back to the appropriate output folder of your EXE.
This can be done in only two actions:
Set the output directory of all DLLs to a folder like $(ConfigurationName)_$(PlatformShortName)
select all DLL projects
right-click -> propoerties
select all configurations and all platform
set the output dir
Add a post-build action to the EXE project, common to all configurations that copies $(ConfigurationName)_$(PlatformShortName)\*.dll to $(TargetDir)
EDIT
There is a problem in the second step since the $(ConfigurationName) of your EXE is not the same as your DLL projects. Not sure but maybe you can have a way to deduce the corresponding configuration, in order to still have a single post-build event for all your 18 configs. It can be a new SO question:)
This works on VisualStudio 2019 (maybe previous versions also but not tried)
I tried several ways and this is the best compromise:
copied to the final target folder (the most important !)
copied in project bin folder
appears in the VS project view
can have different x64/x86 files
renamable (label Link)
Edit your *.vcxproj file and add this ItemGroup like any other
<ItemGroup>
<None Include="MyDllFolder\myDll.dll" CopyToOutputDirectory="PreserveNewest" Link="myDll.dll"/>
<!-- x86/x64 different dlls -->
<None Include="MyDllFolder\x86\myDll2_x86.dll" Link="myDll2_x86.dll">
<CopyToOutputDirectory Condition="$(PlatformTarget) == x86">PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="MyDllFolder\x64\myDll2_x64.dll" Link="myDll2_x64.dll">
<CopyToOutputDirectory Condition="$(PlatformTarget) == x64">PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Inspired from Automatic copy files to output during application building
Related
I've been trying for days to figure out how to get my native C++ DLL dependencies included in my ClickOnce deployment of a C# WinForms app built with VS2019. The native DLLs do not appear under Publish -> Application Files on the Properties page of the main .NET app (though the .NET DLLs do).
I've read this a hundred times:
Set the Build Action for the native DLLs to 'Content'.
... and I think I'm interpreting/doing that wrong.
Output from the native C++ projects is naturally not 'included' in any projects, (an apparent prerequisite for exposing the Build Action property,) and thus does not appear in Solution Explorer to allow me to set it to 'Content'. So I
[Solution Explorer] -> Project -> Add -> Existing Item -> [select native C++ DLL]
to add the native C++ DLLs to the Project to enable the Build Action property, which I then set to 'Content'. {Important Note: It has to be 'included' in a Project, rather than just the Solution to get a Build Action property.}
So I do that and it works, but of course I had to select a specific platform and configuration (e.g., x64 & release) of the native DLL, and this selection is fixed (not controlled by the selections in the VS2019 GUI when I build), and worse -- not even labeled as to which platform & configuration is 'included' in the project. {Side note: How did I not have to select which version (x86 vs x64) of the .NET DLLs to use under Publish -> Application Files ? It just automatically picks the right ones? How do I set up an x86 version and a x64 version that I can switch back & forth between & build each?} I cannot imagine this is the way it is supposed to be done. It is fragile and opaque. Surely there is a better way. I think I'm missing something that everyone else finds 'obvious'. Any other developer who tries to use or maintain this configuration will curse my name, and be right for doing so.
What is the 'right way' of making the ClickOnce deployment (via the VS2019 GUI) include my native DLLs (the projects for which are included in the same solution) in the ClickOnce deployment package?
Note, I found one promising setting in the Properties Page for the native C++ projects: Custom Build Setup -> General -> Treat Output as Content. But it does not seem to have any effect.
I'd be eternally grateful for any pointers.
After refining this for another day or so, I've found that the VS2019 GUI just comes up short for this purpose. The better answer is to just manually edit your .csproj file for the .NET project to Include the native DLLs as Content. Find the other elements in that project file and add the native DLLs like this:
<ItemGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<Content Include="..\bin\MyApp\x64\Release\native1.dll">
</Content>
<Content Include="..\bin\MyApp\x64\Release\native2.dll">
</Content>
</ItemGroup>
<ItemGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<Content Include="..\bin\MyApp\x86\Release\native1.dll">
</Content>
<Content Include="..\bin\MyApp\x86\Release\native2.dll">
</Content>
</ItemGroup>
(where *..\bin\MyApp{x86|x64}{Debug|Release}* is your output folder where your native DLLs, native1.dll and native2.dll have been copied, post-compile, to be used by your .NET app, MyApp.exe). Of course the x86 folder contains the 32-bit native DLLs, the x64 folder contains the 64-bit native DLLs, etc. - you know the drill.
No confusing extra links and whatnot to clutter up the solution/project, and the .dll files appear where they are supposed to, rather than in separate child sub-directories.
Of course you could add additional elements for the remaining conditions -- namely the debug builds, but who wants ClickOnce deployments for debug versions? You're probably running those directly from Visual Studio, right?
Adapted from here :
Compile your solution.
Right click on the managed project and select "Add/Existing Item". Do not use "Add Reference".
Navigate to your compiled native DLL and select it (adjust file types as needed to expose it).
Click on the down arrow in the "Add" split button and select "Add As Link" (this allows us to not keep multiple copies of the DLLs floating around).
Right click on that freshly added file and select "Properties".
Make sure "Build Action" is "Content" and "Copy To Output Directory" is set to "Do not copy" (since you already automated that task elsewhere, long before getting to the deployment stage of development).
Note that you'll get a 'File Not Found' error upon initial build after a proper cleaning of all output folders, but the native DLL will be created anyway, preempting that error on the next full build.
On the Properties page for the .NET app, do Publish -> Application Files -> Reset All to populate the native DLLs into the list. Adjust 'Publish Status' et. al. as needed.
Publish.
I found that I can maintain a 'virtual' folder structure in the project to store the links to the various versions (e.g., x86 vs. x64 and debug vs. release) of the native DLLs without cluttering up the file system, (and to ensure the DLLs aren't stale): Add the folder structure (e.g., ".\NativeDLLs\x64\Release", etc.) via the Solution Explorer, and add Links only (no real files) to the folders. Then delete the folders from the file system using Windows Explorer or shell commands. They will remain in Solution Explorer after deletion from the file system because they still contain the links.
The hierarchy of those virtual folders now exists only in the project file ([appname.csproj]) as Content elements (containing the Links) within Item Group elements (which seems to be the key to getting them to appear in the ClickOnce deployment world).
One pointer (for myself) that would have helped: Don't be afraid to hit the Reset All button on the Publish -> Application Files dialog.
I have a Visual Studio solution with the project I am working on (main project). The solution references a different project that main project requires as a dependency (dependency project).
The solution and the main project are located in one directory:
/mycode/main_project/main_project.sln
/mycode/main_project/main_project.csproj
The dependency project is located in a different directory:
/mycode/dependency_project/dependency_project.csproj
Both of these projects are under source control. I want to create some kind of configuration file for main project that is not under source control, so that another developer could clone both projects wherever they want and simply edit a non-source-controlled configuration file to allow the main project's solution to locate dependency project.
Currently, main solution locates dependency project using a relative path:
../dependency_project/dependency_project.csproj
If I enforce that all developers should clone these two projects to the same directory, the main solution will successfully link to the dependency project and everything will be happy. However, I would prefer that another developer can place the dependency project wherever they want.
Does Visual Studio 2019 Community support any kind of solution configuration file which could be kept ignored by version control and used to resolve the path to the dependency project?
Solution files are very primitive and do not offer such a dynamic functionality. However, if you don't need dependency_project to show up in the IDE, you can still reference it from main_project.csproj and that does give you more options. It should still build fine even if the project doesn't show up in Solution Explorer.
For example, you could reference it through an environment variable, with a default expected path if that variable isn't set:
<PropertyGroup>
<DependencyProjectPath Condition=" '$(DependencyProjectPath)' == ''>../dependency_project/dependency_project.csproj</DependencyProjectPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(DependencyProjectPath)" />
</ItemGroup>
If you set the DependencyProjectPath environment variable before opening the solution (because VS inherits the environment variables from where it was launched), it will override the default setting here (based on the Condition attribute).
I work on the project that I have inherited from another developer. It is mix of VB.NET & C#.
Solution is made of multiple projects: Config(start-up project),Common,Agent + project StdPlugin under Plugin folder. (Plugins are .dlls that are loaded during runtime).
For non-plugin projects, output path is set to bin\Debug\. For Plugins projects output path is set to Config\bin\Debug\Plugins.
After the project is compiled, Config\bin\Debug\ has all .dlls +.pdbs and .exe for all my projects. However, Config\bin\Debug\Plugins also contains copies of all project .dlls + .pdbs and .ddl + .pdb for StdPlugin project. In other words, I am getting duplicates. On top of that, when I run Build->Clean Solution, Config\bin\Debug\ is being cleaned out, but Config\bin\Debug\Plugins is not.
Some clarifications: 1. Project is build for debugging. 2. I noticed above issues while investigating The source file is different from the module was build issue that affects the Reader project. It would not stop on breakpoins in Reader project.
I'm new in the world of Visual Studio. Can somebody please explain what these two files contain? I know that one of them contains info about project, but what about the other one?
A project file .vcproj / .vcxproj contains settings on how to compile your code into a DLL or a binary file, or something else that the linker can assemble into one unit. A project file is just an xml file that contains compiler settings, linker settings, and describes which files you want to compile.
A solution file *.slnis a text file that groups together multiple project files.
So if you think of it like a tree, then you have got a good mental picture of it like this:
.sln
.vcproj
.h
.h
.cpp
.cpp
.vcxproj
.h
.h
.cpp
.cpp
.csproj
.cs
Solution files and project files are in an XML format and describe the parts of your projects and their relations, configurations and so on. In fact, both of these files are simply MSBuild scripts (which are run through MSBuild when, you guessed it, building your project.)
This means they are easy to manipulate by hand if needs be (though this should be a rare case) and also allows to add custom parts to the build script, create custom build scripts for MSBuild that can include the solution file, among other things, or just simple auto-build scripts that pass the solution file (or project) to MSBuild, say, on version control check-in.
The difference between solution files and project files is that a project file holds information specific to that project, unaware of its solution (though, Visual Studio will look up the hierarchy to an extent in an attempt find the relevant solution when opening a project, if one exists); the solution file is aware of all projects that are part of that solution and references each of them (such as a directory of files, if you like, but with projects), it also contains solution-wide information / configuration, that can be applicable to all projects within the solution.
As pointed out by Hans Passant, there is an exception: files for C++ projects pre-VS2010 are not XML MSBuild files, but are instead a format documented by Microsoft on MSDN.
A .vcproj file contains information about HOW to compile source to a target (mostly, an executable). In many cases, it is crucial to have the project file for successful compilation, so do not delete it. It is compareable to a .dsp file (Visual Studio 6), a .prj file (Borland compilers), or a Makefile (Unix, GNU compilers) and contains paths and compiler/linker command-line options.
A .sln file is merely a collection of multiple .vcproj files. As Visual Studio can automatically create one if not present, there is no need to keep it for distribution or archiving. It's the successor of a .dsw file (Visual Studio 6). Its name "Solution file" is IMHO misleading.
In short: one is for solution, and the other is for project, and a solution can contain multiple projects.
Visual Studio allows multiple projects in a solution. The data what projects are in a solution is in the sln (solution) file.
How can I set up a project in Visual Studio to copy the third-party DLLs that one of the project's references depends on?
I have a main application project and a class library DLL. The main application references the class library DLL, and the DLL itself references some third-party DLLs. When I compile the main application, it automatically copies the class library DLL to its output directory, but it does not copy the third-party DLLs.
I do not want to add references to the third-party DLLs from the main application project because the main application does not use them, they're only used by the class library.
You can achieve this with the project properties window. Visual Studio allows you to define events to occur, before, or after building. To get to the project properties window simply right-click on your project in the solution explorer window and click on 'properties'. From the left hand side go to the 'build events' tab.
In the post-build box type in a few copy commands. For example:
copy "$(SolutionDir)mydll.dll" "$(TargetDir)"
Where $(SolutionDir) and $(TargetDir) are both predefined variables. The standard syntax is as follows:
copy "source directory and file name" "destination directory"
If you click on the 'edit post build...' button it will bring up a box which has a listing of these predefined variables that you can insert (like $(SolutionDir) and $(TargetDir))
As a side note, this is a useful process for copying other files, such as custom configuration files, images, or any other dependencies your project may have.
The following fragment works for me:
<Project>
...
<ItemGroup>
<Content Include="Path\to\dll\dllname.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
...
</Project>
This works for C#.
For native C++ it still copy dll to output folder, but this dependency is not visible in Visual Studio, it should be edited in project file directly.
To test on non-trivial example
I tried to run C# project A which depends on native C++ project B. B projects depends on thirdparty native dll C - this dependency is implemented via fragment above in project file.
When I build A, C is copied to binary folder.
I tried it in Visual Studio 2010.
Take a look at this solution provided by Alex Yakunin
http://blog.alexyakunin.com/2009/09/making-msbuild-visual-studio-to.html
It worked for me really nicely - the scenario being DevExpress libraries expressly used had other dependencies which caused problems when deployed)
Note 1: Visual studio 2010 seems add referenced dlls automatically, however msbuild didn't. So Alex's solution worked since the release scripts used msbuild.
Note 2: Also had to make sure that for the referenced libraries (those which were referenced in code) copy-local was actually set to True in the csproj, even though the Solution Explorer said it was. The best way is to set copy-local = False, Save, set copy-local = True, Save.
These two steps - copy-local=true for referenced libraries and adding msbuild targets for the indirect references automated the build setup for me.
I would not recommend doing this. You end up with an N^2 explosion in the number of assemblies being copied around (and potentially, being rebuilt). If possible, you should have all of your projects place their assemblies in the same $(OutDir). If you're using TFS, Team Build does this for you.
I don't like to have my dependency files located in the project root folder but in a subfolder. But the files has to be placed in the root folder in the build folder.
My build events look like this:
Command: call xcopy /S /Y "$(SolutionDir)Dependencies\*.*" "$(TargetDir)"
In case "Dependencies" also contains subfolders, as mine does.
/S means to copy subfolder also
/Y means to not prompt for overwrite confirmation
Other xcopy parameters can be found at: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/xcopy
Go to the main application, references, to your class-library reference.
Set "Copy Local" to True.
It will now copy the bin directory of your class-library into the main application bin directory. Including any sub-dependency third-party dlls.
If you want to copy new file only in post-build, you can also use xcopy with flags /i /d /y
xcopy "$(ProjectDir)SubDir\*.dll" "$(TargetDir)" /i /d /y
100% sure this will work.
Just replace dll with your personal ref. file
<Reference Include="Xceed.Wpf.Toolkit">
<HintPath>..\..\..\3rdParty\Extended WPF Toolkit-2.2.1\Xceed.Wpf.Toolkit.dll</HintPath>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Content Include="..\..\..\3rdParty\Extended WPF Toolkit-2.2.1\Xceed.Wpf.Toolkit.dll">
<Link>Xceed.Wpf.Toolkit.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<SpecificVersion>False</SpecificVersion>
</Content>
I'm not really aware of any way to do this other than adding a reference to said .dll in the project itself. We've run across this in some of our own projects here, and the only solution we've found is to add the reference. I want to say that one of our developers did some research and found this to be the only solution, but don't quote me on that.