I'm trying to obfuscate an .exe file obtained after compiling my .NET 6 project with the "Produce a single file" option, the problem is that no obfuscator works on it, I wanted to know if anyone knows why?
Thanks in advance for your answer
You have to obfuscate main application dll, which is located in the "obj\Release\net6.0-windows\win-x64" folder and copy obfuscated dll to the path.
Here is a working example using Obfuscar. These lines are located in the .csproj file.
<Target Name="Obfuscation" AfterTargets="AfterCompile" Condition="'$(PublishProtocol)'!=''">
<Exec Command=""$(Obfuscar)" obfuscar.xml" />
</Target>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(PublishProtocol)'!=''">
<Exec Command="COPY "C:\Users\Application\obj\Release\net6.0-windows\win-x64\Obfuscated\Application.dll" "C:\Users\Application\obj\Release\net6.0-windows\win-x64\Application.dll"" />
</Target>
After that, when you publish single file exe, your application code inside the archive will be obfuscated.
When you publish a .NET application as a single file, the actual code is located in the DLL file. The EXE file is just a launcher.
Returning to the obfuscation, the idea is to obfuscate the assembly right after it is placed to the intermediate directory by a compiler. E.g. if you use ArmDot you just write:
<Target Name="Protect" AfterTargets="AfterCompile" BeforeTargets="BeforePublish">
<ItemGroup>
<Assemblies Include="$(ProjectDir)$(IntermediateOutputPath)$(TargetFileName)" />
</ItemGroup>
<ArmDot.Engine.MSBuildTasks.ObfuscateTask
Inputs="#(Assemblies)"
ReferencePaths="#(_ResolveAssemblyReferenceResolvedFiles->'%(RootDir)%(Directory)')"
SkipAlreadyObfuscatedAssemblies="true"
/>
</Target>
The same approach can be used with any obfuscator. The key thing is to use the following path: $(ProjectDir)$(IntermediateOutputPath)$(TargetFileName).
After that, the obfuscated assembly (DLL) is published.
Related
according to this link
https://blogs.msdn.microsoft.com/kirillosenkov/2015/04/04/how-to-have-a-project-reference-without-referencing-the-actual-binary/
I referenced the assembly of ProjectA in the projectB with ReferenceOutputAssembly=false like below :
<ProjectReference Include="..\ProjectA\ProjectA.csproj">
<Project>{b402782f-de0a-41fa-b364-60612a786fb2}</Project>
<Name>ProjectA</Name>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Content</OutputItemType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</ProjectReference>
This will indicate a dependency between projects to MSBuild, but won’t pass the output assembly of ProjectA as a reference to the compiler when compiling ProjectB.
MSBuild just copies the assembly A dll into the output directory for assembly B ,not the Satellite Dll.
I need both ProjectA.dll and ProjectA.resources.dll in the output directory but just projectA.dll is copied , Satellite Dll of Project A is not copied..
MSBuild just copies the assembly A dll into the output directory for
assembly B ,not the Satellite Dll. I need both ProjectA.dll and
ProjectA.resources.dll in the output directory but just projectA.dll
is copied , Satellite Dll of Project A is not copied..
The issue also appears in my side and I think when you use the function( Methods in the link) above, it will replicate the dependency without referencing project A, but also ignores the DLLs or resources.dlls referenced by project A. This approach focuses on Project dependencies and ignores other third-party DLLs used by the Project.
This behavior is characteristic of that scenario you used.
However, if you still want to use this function to reference Project A, you can try to use build event or copy task to copy ProjectA.resources.dll into the output path of Project B when you build:
1) Right-click on Project B-->Properties-->Build Events-->Post-Build Event Command line and then input this:
xcopy /y "xxxxxxxxxxx\de\ProjectA.resources.dll" "$(ProjectDir)$(OutputPath)de"
xcopy /y path1 path2: path1 means the path of the original file and path2 means the destination address.
Update 1
generally your solution works but it doesnt work for me because I want
to publish with clickonce and Postbuildevent is not called during the
publish .. I added a before publish tag in csproj of the project B to
run the postbuildevent but it doesnt work
First, you do not have to run the custom target after the wrong target Publish,instead, you should use the PublishBuild Target. Then use copy task to copy the file into Puplish folder during you execute the clickonce operation.
After all, you can try my sample which l have tested successfully in the ProjectB.csproj file.
<Target Name="CopyToPublish" AfterTargets="PublishBuild">
<ItemGroup>
<ResourceFiles Include="xxxxxxx\ProjectA.resources.dll" /> // the path of the ProjectA.resources.dll
</ItemGroup>
<Copy SourceFiles="#(ResourceFiles)" DestinationFiles="#(ResourceFiles->'$(PublishUrl)\de\%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>
Update 2
After l do some research, I found that when VS put files into Clickonce Manifest, it runs the targets(GenerateApplicationManifest, GenerateDeploymentManifest,GenerateManifests and so on) sequentially.These are the targets that act as the concrete execution of the file into the manifest. So when you use use your BeforePublish target to put the extra file as an item, you should run before the target. So please try this:
<Target Name="BeforePublish" BeforeTargets="GenerateApplicationManifest">
<ItemGroup>
<AdditionalPublishFile Include="xxxxxxx\de\ProjectA.resources.dll">
<Visible>False</Visible>
</AdditionalPublishFile>
</ItemGroup>
<Touch Files="#(IntermediateAssembly)" />
<CreateItem Include="#(AdditionalPublishFile)" AdditionalMetadata="TargetPath=de\%(FileName)%(Extension);IsDataFile=false">
<Output TaskParameter="Include" ItemName="_DeploymentManifestFiles" />
</CreateItem>
</Target>
When you publish the project, the ProjectA.resources.dll will display in the ProjectB.manifest file successfully. But the extra file will not display in the Property UI Publish-->Application Files, l think this is a UI display problem that does not affect the output of this file.
Hope it could help you.
My .net web app project also includes some unmanaged dlls as additional files.
These are a couple of levels deep in subfolders.
When I publish this project I need these files to be copied to the bin folder alongside all the other binaries.
No matter what settings I try, the best I can get is for them to be published into their existing folder structure which is not where I need them to be.
I've created a PostBuild event to copy the files and this works when building locally but not when publishing to a server. I've not been able to get PostPublish events to work in the same way.
Is there another way to achieve this?
Note this is similar but not the same as a previous question:
Publish unmanaged DLL from referenced project
I have a similar setup. 2 projects in my solution, one .NET Core and the other C++. When I am going to publish the dotnetcoreapp2.2 I want to include the precompiled C++ DLL from the other project.
#JuanR's answer is not working for me, though it is already pretty close to my version. It looks like the <ItemGroup> needs to be in the <Target> tag.
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
<ItemGroup>
<DataModelFiles Include="$(ProjectDir)..\MyCppProject\bin\Release\MyCppProject.dll" />
</ItemGroup>
<Copy SourceFiles="#(DataModelFiles)" DestinationFolder="$(PublishDir)" SkipUnchangedFiles="false" />
</Target>
Try using an after-publish task.
You can create an item group for copy:
<ItemGroup>
<binFilesToCopy Include="$(OutDir)\somepath\to\yourexternalDLLFolder\*" />
<!-- Add more folders/files you want to copy here -->
</ItemGroup>
Then add a target for after publishing:
<Target Name="AfterPublish">
<Copy SourceFiles ="#(binFilesToCopy)" DestinationFolder ="$(OutDir)\bin" />
</Target>
I did this mostly from memory so double-check for syntax, but get you the idea.
In the properties of the file you can set Copy to output directoryto Copy always or you can edit the solution file, expand the xml tag of the file needed and add <CopyToOutputDirectory>Always</CopyToOutputDirectory> as sub-tag.
I'm trying to build upon this question:
Reading a single value from a file in MSBuild
My goal is to have a single place to put the version number that's used in several projects, and I also want a portion of the version number in the DLL file name for one of the projects.
Based on the question above, I already got the first part, but I'm having difficulty with the second part and would appreciate some guidance.
In my solution, I set up a plain text file called Version.txt containing my full version number only:
1.1.0.0
In both of my projects, I opened their AssemblyInfo.cs files and removed the AssemblyVersion and AssemblyFileVersion items, then modified both projects to generate them in a separate file as described in the question above.
<ItemGroup>
<VersionFile Include="..\Version.txt" />
</ItemGroup>
<Target Name="BeforeBuild">
<ReadLinesFromFile File="#(VersionFile)">
<Output TaskParameter="Lines" PropertyName="VersionNumber" />
</ReadLinesFromFile>
<Delete Files="Properties\Version.cs" />
<WriteLinesToFile File="Properties\Version.cs" Lines="using System.Reflection%3B
[assembly: AssemblyVersion("$(VersionNumber)")]
[assembly: AssemblyFileVersion("$(VersionNumber)")]" />
</Target>
Now when I build, I get a generated Properties\Version.cs file for each project, which is used to build the EXE/DLL and shows up as "1.1.0.0" in their file properties. This is exactly what I want.
For the DLL, I would like to name the assembly "filename.v1.1.dll", where the "1.1" comes from the first two components in Version.txt above. I'm flexible on the format of Version.txt as long as I can get the full "1.1.0.0" in the EXE/DLL properties and "1.1" in the DLL file name.
To try this out, I modified the DLL's csproj file to have:
<RootNamespace>dllfile</RootNamespace>
<AssemblyName>dllfile.v$(VersionNumber)</AssemblyName>
Of course, this will insert the full version number in the file name, which I don't want.
Does anyone have any tips on how to proceed?
Thanks.
EDIT: I have been able to extract the major/minor components of the version number by adding the following to my .csproj BeforeBuild target:
<ReadLinesFromFile File="#(VersionFile)">
<Output TaskParameter="Lines" PropertyName="VersionNumber" />
</ReadLinesFromFile>
<PropertyGroup>
<VersionNumberFirstDotIndex>$(VersionNumber.IndexOf('.'))</VersionNumberFirstDotIndex>
<VersionNumberMajorStart>0</VersionNumberMajorStart>
<VersionNumberMajorLen>$(VersionNumberFirstDotIndex)</VersionNumberMajorLen>
<VersionNumberMinorStart>$([MsBuild]::Add(1, $(VersionNumberFirstDotIndex)))</VersionNumberMinorStart>
<VersionNumberSecondDotIndex>$(VersionNumber.IndexOf('.', $(VersionNumberMinorStart)))</VersionNumberSecondDotIndex>
<VersionNumberMinorLen>$([MSBuild]::Subtract($([MSBuild]::Subtract($(VersionNumberSecondDotIndex), $(VersionNumberFirstDotIndex))), 1))</VersionNumberMinorLen>
<VersionNumberMajor>$(VersionNumber.Substring($(VersionNumberMajorStart), $(VersionNumberMajorLen)))</VersionNumberMajor>
<VersionNumberMinor>$(VersionNumber.Substring($(VersionNumberMinorStart), $(VersionNumberMinorLen)))</VersionNumberMinor>
<VersionNumberShort>$(VersionNumberMajor).$(VersionNumberMinor)</VersionNumberShort>
</PropertyGroup>
<Message Text="DEBUG1 VersionNumberFull=$(VersionNumber)" Importance="High" />
<Message Text="DEBUG2 VersionNumberAbbrev=$(VersionNumberShort)" Importance="High" />
<Delete Files="Properties\Version.cs" />
<WriteLinesToFile File="Properties\Version.cs" Lines="using System.Reflection%3B
[assembly: AssemblyVersion("$(VersionNumber)")]
[assembly: AssemblyFileVersion("$(VersionNumber)")]" />
The only piece I'm missing now is how to get this VersionNumberShort into the DLL file name. Unless someone has a better idea, I can take Peter's suggestion and use Move tasks:
<Target Name="AfterBuild">
<Move SourceFiles="$(OutputPath)$(AssemblyName).pdb" DestinationFiles="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).pdb" />
<Move SourceFiles="$(OutputPath)$(AssemblyName).dll" DestinationFiles="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).dll" />
</Target>
<Target Name="AfterClean" DependsOnTargets="Common">
<Delete Files="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).pdb" ContinueOnError="true" />
<Delete Files="$(OutputPath)$(AssemblyName).v$(VersionNumberShort).dll" ContinueOnError="true" />
</Target>
Since I needed the same property definitions as before, I moved the snippet above into a "Common" target and referenced it in both the build and clean tasks shown here.
Peter - If you want to move your comment as an answer, I'll accept it.
Thanks!
EDIT: Following jdlugosz's answer, I tried setting the AssemblyName inside my task. Unfortunately, this still didn't seem to have any effect based on the original example listed at the top:
<Target Name="BeforeBuild">
...
<WriteLinesToFile ... />
<PropertyGroup>
<AssemblyName>dllfile.v$(VersionNumber)</AssemblyName>
</PropertyGroup>
</Target>
I tried running this with MSBuild from a Visual Studio Developer Command Prompt:
msbuild /target:clean projfile.csproj
msbuild /verbosity:diag projfile.csproj > out.txt
Prior to this, I renamed the at the top of my csproj file and in the "redefinition" to something unique to make it easy to search (e.g. "dllfileoriginal" vs. "dllfilemodified").
Looking through the output log, I can't find any reference to the modified text; it's still dllfileoriginal everywhere in the output.
Following the WriteLinesToFile task, it looks like the following targets were built:
IncrementalClean (finished)
PostBuildEvent
CoreBuild
AfterBuild
Build
There's no reference to either DLL name inside these.
It looks like the is currently my best bet still.
The Target Name is is shown on the General page under the Configuration Properties tab in the IDE Property Page editor. I don't have one handy myself to look up the name for you, but you can do it by changing the blank in the IDE to something like XXXX and save. Then view the diff in the version control commit reviewer and see what the name of the Property is. In this case, then edit the line to change XXXX to $(OutputPath)$(AssemblyName).v$(VersionNumberShort)
Oh, check out the FormatVersion task, which might help. I think there are some premade tasks that manipulate a version assembly similar to what you show, too.
What I'm doing for versions is passing the pieces in via #defines as /D command line arguments. I guess you don't have that in C# though, IIRC.
This works for me, and it solves the seemingly simple problem of appending the version info string to a filename at build.
First, the post-build event:
(Right-Click Project -> Properties -> Build Events -> Edit Post-build...)
$(TargetPath) "version" > $(TargetDir)text.txt
set /p version= <$(TargetDir)text.txt
copy $(TargetPath) $(TargetDir)$(TargetName)_%version%.exe
del $(TargetDir)text.txt
Now, the trick:
Overload sub main to return the version info, and call it in a post-build event on the exe that was just built.
here is an example in F#:
[<EntryPoint>]
let main argv =
let version = argv.Length = 1 && argv.[0] = "version"
if version then
let version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()
do stdout.WriteLine(version)
do stdout.Flush()
else
try
//...
The post-build event above
1) calls the newly built exe with a "version" arg, and writes the output to a txt file
2) reads the text file contents into a local variable
3) renames the newly built exe by adding the version info
3) copies the newly built exe adding the version info to the name
4) cleans up the temp file
*changed "move" to "copy" so that Visual Studio can still F5 the project
I'm not only looking for a cleaner way to do this, but also find out why what I'm currently doing works...
I'm using MSBuild (not Visual Studio, just Notepad++ and calling MSBuild via a command line). MSBuild requires a project file (just like Visual Studio) to guide the build process. As part of the build, I'm making a call to a C# file using .NET's UsingTask in the project file to make the appropriate changes to the database. This way I only need to run MSBuild when I install this on a new computer and the build will update the database for me.
The C# code uses a third party DLL. In order to access that DLL I need to copy it from a static location (e.g. D:\ThirdParty\MyDLL.dll) to the current build directory. That part's fine. The strange part is that it also needs me to copy the DLL to C:\Windows\Microsoft.NET\Framework\v4.0.30319. If I copy the DLL to either the current build directory or C:\Windows\Microsoft.NET\Framework\v4.0.30319 and not the other, then it won't build. If I copy it to both directories, only then will it work.
Here are the important parts of my code:
Defining the UsingTask
<UsingTask
TaskName="UpdateDB"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup />
<Task>
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data.Linq" />
<Reference Include="MyDLL.dll" />
<Reference Include="System.Xml" />
<Using Namespace="MyDLL.Db.Foo" />
<Code Type="Class" Language="cs" Source="UpdateDB.cs" />
</Task>
</UsingTask>
Copy the DLL...
<Copy
SourceFiles="D:\ThirdParty\MyDLL.dll"
DestinationFolder="$(MSBuildToolsPath)"
/>
<Copy
SourceFiles="D:\ThirdParty\MyDLL.dll"
DestinationFolder="$(MSBuildProjectDirectory)"
/>
Call the UsingTask defined above...
<UpdateDB />
So I'm wondering why I need to copy the DLL to both locations and not just one or the other. I've tested this over and over again by removing the DLL from both directories and just copying it to one of them. It only works if I copy the DLL to both. Is there a cleaner way to do this, and if not, why does it work this way?
Would you please try this:-
Create one directory "libraries" under project directory
Copy third party dll to that folder
<Copy
SourceFiles="D:\ThirdParty\MyDLL.dll"
DestinationFolder="$(MSBuildProjectDirectory)\libraries"
/>
Do not copy to C:\Windows\Microsoft.NET\Framework\v4.0.30319
And in task
<reference include="libraries\MyDll.dll">
And try to execute your program and let me know what happens
I have MyLib library project along with several examples. The library and examples are in the same solution MySolution.
In MyLib library project I have included MSBuild code to zip the whole solution and copy to another directory for internet publishing.
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">
<PropertyGroup>
<ReleasePath>C:\Users\Administrator\Projects\CA\Libraries\Api-DotNet\</ReleasePath>
<ZipFile>C:\Users\Administrator\Projects\CA\WebProject\libraries\Api-DotNet.zip</ZipFile>
</PropertyGroup>
<ItemGroup>
<LibraryFiles Include="$(ReleasePath)\**\*.*" Exclude="$(ReleasePath)\**\*.user;$(ReleasePath)\**\*.suo;$(ReleasePath)\Api.*;$(ReleasePath)\**\packages\**;$(ReleasePath)\**\Lib.Test\**;$(ReleasePath)\**\*.nuspec;$(ReleasePath)\**\*.nupkg;$(ReleasePath)\**\*nuget*;$(ReleasePath)\**\*internal*;$(ReleasePath)\**\*ReSharper*\**;$(ReleasePath)\**\.svn\**;$(ReleasePath)\**\obj\**;$(ReleasePath)\lib\bin\Debug\**;$(ReleasePath)\lib\bin\Publish\**;$(ReleasePath)\Example\**\bin\**;" />
</ItemGroup>
<Zip Files="#(LibraryFiles)" WorkingDirectory="$(ReleasePath)" ZipFileName="$(ZipFile)" ZipLevel="9" />
</Target>
</Project>
The problem is that when user download library and run on another computer the compiler show error that import library not found MSBuild.Community.Tasks.Targets. I would like to exclude ZipAndCopy code from project file when building the solution. How to do that?
Add this Condition to both the Import and the Zip elements:
Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')"
For example:
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"
Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')" />
Similar to this: C# Checking if a Property 'Starts/Ends With' in a csproj
The above solution hides the project file load error, but Tomas appears to be trying to use a Task from the MSBuild.Community.Tasks extension.
This should be installable by using NuGet. Here's a link to the source site showing we can install it via NuGet's Package Command Line:
PM> Install-Package MSBuildTasks
Their documentation isn't great. You'll need to also define the path using:
<Import Project="..\Packages\MSBuildTasks.1.4.0.88\tools\MSBuild.Community.Tasks.Targets"/>
<UsingTask AssemblyFile="..\Packages\MSBuildTasks.1.4.0.88\tools\MSBuild.Community.Tasks.Targets.dll"
TaskName="MSBuild.Community.Tasks.Zip" />
...where you need to replace the Version with the Version you are using from NuGet. It's not perfect, but I managed to get mine working.
NuGet will install it into your 'Packages' folder under the root of your Solution/Project Trunk.
I ran into issues where Visual Studio may still be fighting to look for the files in a specific location. If this happens, copy the files from '.\Packages\MSBuildTasks.1.4.0.88\tools*' to 'C:\Program Files (x86)\MSBuild\MSBuildCommunityTasks\'.
This isn't the most elegant, but I was able to successfully get the new Tags to work. If I find a way to fix this last part, I'll update my posting.
Sounds like you want multiple build configurations. I would suggest setting up one specifically for building and zipping the artifacts and a separate for your users.
Release ZIP could be your build with the post-build-event to zip your files, and Release could be an ordinary build that doesn't do anything special using the community tasks.