I have a need to reference two different versions of the Sharepoint API dll. I have a webservice that needs to run under both Sharepoint 2 and Sharepoint 3, but also needs to work with new features provided by the Sharepoint 3 API (Checkout and Content Approval)
What is the best way to acheive this - I'm currently leaning towards having two projects, with the code in a single file shared between the two with various sections of the code compiled in using conditional compilation.
Is there a better way ?
Thanks
Matt
This is how I spit out .NET 1.1 versions compiled against WSSv2 API and .NET 2.0 compiled against WSSv3 assembly. It will work for VS 2005 and 2008.
You will need to use MSBEE http://www.codeplex.com/Wiki/View.aspx?ProjectName=MSBee
Working with .NET 1.1 with Visual Studio 2008
Some tips
Open up *.csproj and find out where the SharePoint dll is referenced and change to something like this which changes the referenced assembly depending upon your target (FX1_1 means you are targeting .NET1.1 and therefore WSSv2)
<Reference Include="Microsoft.SharePoint">
<HintPath Condition="'$(TargetFX1_1)'!='true'">pathto\WSS3\Microsoft.SharePoint.dll</HintPath>
<HintPath Condition="'$(TargetFX1_1)'=='true'">pathto\WSS2\Microsoft.SharePoint.dll</HintPath>
</Reference>
Use conditional compilation for differences where necessary
#if FX1_1
// WSSv2 specific code
#else
// WSSv3 specific code
#endif
If you get a compiler error but the code looks right it may be that the error is only for .NET1.1 / WSSv2 and compiles fine in .NET2/WSSv3. Check the output tab to see for which target the error occurred
You will also need to master some MSBUILD ninja moves to keep a 1 step build process and keep yourself sane http://brennan.offwhite.net/blog/2006/11/30/7-steps-to-msbuild/ using MSBUILD you can get VS to compile both versions at the same time without resorting to the command line.
This will run the .NET1.1 compilation after .NET has finished and output some messages to the Output window to help you work out where errors occurred.
<Target Name="BeforeBuild">
<Message Text="--- Building for .NET 1.1 ---" Importance="high" Condition="'$(TargetFX1_1)'=='true'" />
<Message Text="--- Building for .NET 2.0 ---" Importance="high" Condition="'$(TargetFX1_1)'!='true'" />
</Target>
<Target Name="AfterBuild" Condition="'$(TargetFX1_1)'!='true'">
<MSBuild Projects="$(MSBuildProjectFile)" Properties="TargetFX1_1=true;" />
</Target>
You could give an "extern alias" a go.
This is one of those times when the VB late binding (option strict off) approach works well. Roll on C# 4.0 and dynamic.
You might try writing an interface for the bits you need (in a base library), and write 2 dlls: one referencing each version of the sharepoint dll. For both projects, implement the interface (throwing NotSupportedException for the bits you can't do), and load the appropriate dll at runtime? (factory approach)
Just try it with a single method before you get too absorbed... don't do the whole thing until you know it works for the simplest of simple methods.
Related
I am following this guide to automatically generate an API client with NSwag. But this client needs to support multiple target frameworks:
<TargetFrameworks>netcoreapp2.2;net452;net462;net472;net48</TargetFrameworks>
When I try to build this client, I get multiple errors like this:
(CS2012) Cannot open 'MyApi.dll' for writing -- 'The process cannot access the file 'MyApi.dll' because it is being used by another process.'
I suspect this is because each framework is building asynchronously and the DLL produced from my API is trying to be read by each process. How can I fix this issue / make each target framework build synchronously?
Well, I found a solution. My lack of understanding around the guide I was reading and the build process meant I wasn't asking the right questions.
I had this build target specified in my .csproj (as directed in the guide):
<Target Name="NSwag" BeforeTargets="PrepareForBuild" Condition="'$(GenerateCode)'=='True' ">
<Exec Command="$(NSwagExe_Core22) run nswag.json /variables:Configuration=$(Configuration)" />
</Target>
This target was running for each target framework I had specified in my <TargetFrameworks> tag. This was the task that was running in parallel to itself and causing the error from my question.
After a LOT more googling, I found this question and (consequently this answer) which gave me the solution I needed:
On multi target frameworks I use BeforeTargets="DispatchToInnerBuilds" so my custom command is only exeuted once before every build
So my final build target was as simple as this:
<Target Name="NSwag" BeforeTargets="DispatchToInnerBuilds" Condition="'$(GenerateCode)'=='True' ">
<Exec Command="$(NSwagExe_Core22) run nswag.json /variables:Configuration=$(Configuration)" />
</Target>
It should also be noted that this solution might not work for all people looking for their build targets to run once per build. See this issue comment that provides a similar answer but with more detail about multi-project solutions.
I want to access a MSBuild variable inside an unit test, which is a .NET 4.5 class library project (classic csproj), but I failed to find any articles discussing a way to pass values from MSBuild into the execution context.
I thought about setting an environment variable during compilation and then reading that environment variable during execution, but that seems to require a custom task to set the environment variable value and I was a bit worried about the scope of the variable (ideally, I only wanted it to be available to the currently executing project, not globally).
Is there a known solution to reading an MSBuild property from inside a DLL project in runtime? Can MSBuild properties be "passed as parameters" during execution somehow?
I finally made it work by using the same code generation task that is used by default in .Net Core projects. The only difference is that I had to manually add the Target in the csproj file for it to work, as code creation is not standard for framework projects:
<Target Name="BeforeBuild">
<ItemGroup>
<AssemblyAttributes Include="MyProject.SolutionFileAttribute">
<_Parameter1>$(SolutionPath)</_Parameter1>
</AssemblyAttributes>
</ItemGroup>
<WriteCodeFragment AssemblyAttributes="#(AssemblyAttributes)" Language="C#" OutputDirectory="$(IntermediateOutputPath)" OutputFile="SolutionInfo.cs">
<Output TaskParameter="OutputFile" ItemName="Compile" />
<Output TaskParameter="OutputFile" ItemName="FileWrites" />
</WriteCodeFragment>
</Target>
The lines with Compile and FileWrites are there for it to play nicely with clean and such (see linked answers in my comments above). Everything else should be intuitive enough.
When the project compiles, a custom attribute is added to the assembly, that I can then retrieve using normal reflection:
Assembly
.GetExecutingAssembly()
.GetCustomAttribute<SolutionFileAttribute>()
.SolutionFile
This works really well and allows me to avoid any hardcoded searches for the solution file.
I think you have a couple of options:
Use environment variables, like you already suggested. A custom task maybe required to do that, but it is easy to do, without any extra assemblies on your part. The required global visibility might be an issue tough; consider parallel builds on a CI machine, for example.
Write a code fragment during build and include that into your resulting assembly (something akin to what you have already found under the link you suggested in your comments.
Write a file (even app.config) during build that contains settings reflecting the MSBuild properties you need to have; read those during test runs.
(BTW, what makes little sense, is to attempt to read the MSBuild project file again during runtime (using the Microsoft.Build framework). For once that is a whole lot of work to begin with, for little gain IMHO.
And even more important, you most likely - depending on the complexity and dependencies of your properties - need to make sure you invoke the MSBuild libraries with the same properties that where present during the actual build. Arguably, that might put you back were you started from.)
The last two options are best suited because they share equal traits: they are scoped only to the build/test run you currently have (i.e. you could have parallel running builds without interference).
I might go for the third, because that seems to be the easiest to realize.
In fact I have done so on a larger project I've been working on. Basically, we had different environments (database connection strings, etc.) and would select those
as a post build step by basically copying the specific myenv.config to default.config.
The tests would only ever look for a file named default.config and pick up whatever settings are set in there.
Another version, compiled from several internet sources, get environment variable when building, then use its value in code
file AssemblyAttribute.cs
namespace MyApp
{
[AttributeUsage(AttributeTargets.Assembly)]
public class MyCustomAttribute : Attribute
{
public string Value { get; set; }
public MyCustomAttribute(string value)
{
Value = value;
}
}
}
file MainForm.cs
var myvalue = Assembly.GetExecutingAssembly().GetCustomAttribute<MyCustomAttribute>().Value;
file MyApp.csproj, at the end (get %USERNAME% environment variable in build, generate SolutionInfo.cs file, automatically include it to build)
<Target Name="BeforeBuild">
<ItemGroup>
<AssemblyAttributes Include="MyApp.MyCustomAttribute">
<_Parameter1>$(USERNAME)</_Parameter1>
</AssemblyAttributes>
</ItemGroup>
<WriteCodeFragment AssemblyAttributes="#(AssemblyAttributes)" Language="C#" OutputFile="SolutionInfo.cs">
<Output TaskParameter="OutputFile" ItemName="Compile" />
<Output TaskParameter="OutputFile" ItemName="FileWrites" />
</WriteCodeFragment>
</Target>
i.e. in PHP, you can build a library of your methods in one file, and error is only given, if there are problems in execution (not in compiler). I wonder if something like that is possible in C#, for example, I could put dedicated methods for both .NET 3.5 and 4.5 in same file:
//myFile.cs
public void mymethod_for_v35(object profile)
public async void mymethod_for_v45(dynamic profile)
so, I could include myfile.cs in all projects (whether targeting 3.5 or 4.5) and when I am targeting 3.5, in application I will only call first method . However, as 2nd method is for 4.5 (and 3.5 net compilers dont understand that), we still get compilation errors in IDE.
Does there exist any workaround or Flag, to allow the existence of that method (even though it's unsupported in current .NET version of project) ?
The most convenient way to achieve this is to use a multi-targeted library via the new SDK project syntax (CSP); start by creating a .NET Standard library, then change the <TargetFramework> to <TargetFrameworks>, simply semi-colon delimiting the frameworks you need to support separately, for example:
<TargetFrameworks>net40;netstandard1.3;netstandard2.0</TargetFrameworks>
(note that other frameworks will be implicitly supported - so if I consume it from a net462 project, the net40 build will be used)
Now add #if checks where needed, for example:
#if NET40
...
#endif
or
#if NETSTANDARD1_3
...
#endif
(note that . is mapped to _)
This means that the appropriate API surface will be surfaced automatically.
If you need to use different references for different target frameworks, that can also be done in the project file via a condition on an item-group:
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="..."/> <!-- whatever you need here -->
</ItemGroup>
At build, each target-framework will be evaluated (including the conditions) and built separately, then bundled together when creating a nupkg.
Is there anything out there that does obfuscation? I have tried Crypto Obfuscator for Android and when I de-compiled using dex2jar, I see no difference between obfuscated and normal assembly. So far I have went through following links:
http://forums.xamarin.com/discussion/14962/light-obfuscation
Mono for Android, code obfuscation
You refered to a forums post on xamarin.com ("Light Obfuscation"). There, now I have added an explanation of how to obfuscate with Xamarin Studio and Babel for .NET.
I will repeat it here:
You don't need to have the full Visual Studio to get an easy and comfortable way of obfuscating. I now use Babel for .NET with Xamarin Studio (in Windows). I haven't tried to get Babel running on a Mac, maybe it's possible.
So, here I will explain how to obfuscate your Android app in Xamarin Studio:
The good thing is that Xamarin Studio uses the MSBuild mechanism and Babel can be integrated in a MSBuild process.
For me (except for installing Babel) there were only two steps necessary:
(Step 1)
Edit you .csproj file with a text editor. Xamarin Studio must not be running.
<Project>
[... All existing stuff ...]
<UsingTask TaskName="Babel" AssemblyName="Babel.Build, Version=6.4.0.0, Culture=neutral, PublicKeyToken=138d17b5bd621ab7" />
<Target Name="AfterBuild" Condition=" '$(Configuration)' != 'Debug' ">
<Babel InputFile="$(TargetPath)" OutputFile="$(TargetPath)" GenerateDebug="true"
[...]
RulesFiles="babel.xml"
SuppressIldasm="false" ObfuscateTypes="true" ObfuscateProperties="true" ObfuscateEvents="true" ObfuscateMethods="true"
ObfuscateFields="true" VirtualFunctions="true" FlattenNamespaces="false"
StringEncryption="true"
/>
</Target>
</Project>
Whenever you build your app and the build mode is not Debug (so it is Release), this Task is applied. You can specify an xml file where you can define fine-grained rules for the obfuscation process. (e.g. exclude certain classes etc.)
By the way: A rule of thumb is: Define every class, interface, delegate or enum as "internal", not as "public". By default, types that have to be visible outside the assembly (public types) will not be obfuscated. Internal types will be obfuscated by default. The only class I marked as "public" is "MainActivity".
(Step 2)
When I started the first try for my app, I got the following error message:
BABEL : error : Could not resolve assembly: 'Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065'
Which I could not understand first, because a Hello-World Android app was obfuscated without problems. After some hours of research, I found the reason for the error. My activity (my game has only one activity) had the following attribute:
[Activity(
Label = "The name of my game",
MainLauncher = true,
WindowSoftInputMode = SoftInput.AdjustPan,
ConfigurationChanges = ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.Orientation | ConfigChanges.ScreenSize
)]
"Label" and "MainLauncher" turned out not to be the problem. But "WindowSoftInputMode" and "ConfigurationChanges" were the problems.
To fix it, I fully removed the [Activity (...)] attribute from the .cs file and added the necessary information by hand to the AndroidManifest.xml. This way, the obfuscation worked without problems.
You may wonder why the Activity attribute caused a problem. I realized that ILSpy also had a problem when this attribute was applied to the C# Activity class with "WindowSOftInputMode" and "ConfigurationChanges". So I think it is not a problem of Babel, but a problem of Xamarin. The reason might be that, while "Label" and "MainLauncher" are fundamental types (string and bool), the other two are not. Their types are defined in Mono.Android.dll which seems to be refered to in a wrong way. The best thing would be if Xamarin removed the attribute for the compiled dll because it is only used for making the AndroidManifest.xml in the build step.
Dotfuscator CE (free in Visual Studio) or Dotfuscator PRO (paid license) obfuscates Xamarin applications:
See the Xamarin manual here: Protecting Xamarin Apps
Using Dotfuscator, the most consistent and secure method of obfuscating your Xamarin apps is to integrate it into the MSBuild pipeline. This allows you to obfuscate your project using standard build tools, and lets you test your obfuscation using Xamarin's built-in debugger workflow. In order for the obfuscated outputs to be processed properly by Xamarin, Dotfuscator's "Mono Compatible" global setting should be set to "Yes" and a project property of "controlflow.disabled_manglers" with a value of "ILSpyBreaker" should be added.
Xamarin's platform specific utilities use reflection heavily, so as a starting point it is recommended to simply exclude the input assemblies from renaming (while still allowing control-flow obfuscation). Once that is working, you can then enable renaming if desired, and take the time to determine the minimum amount of exclusions needed for your application.
In each Android or iOS csproj file you should then add a reference to the Dotfuscate task and a target for AfterBuild, like so:
<UsingTask TaskName="PreEmptive.Tasks.Dotfuscate" AssemblyFile="$(MSBuildExtensionsPath)\PreEmptive\Dotfuscator\4\PreEmptive.Dotfuscator.Tasks.dll" />
////SNIP////
<Target Name="AfterBuild">
<PropertyGroup>
<DotfuscatorProperties>
<OutDir>$(OutDir)</OutDir>
<OutputPath>$(OutputPath)</OutputPath>
</DotfuscatorProperties>
</PropertyGroup>
<Dotfuscate ConfigPath="Obfuscate.Android.xml" Properties="$(DotfuscatorProperties)"/>
</Target>
Notice that we are passing in certain properties from the build process to the Dotfuscator process. Specifically OutDir and OutPath. By using project properties with default values we can specify paths in the Dotfuscator project file to allow us to configure the project using the stand-alone Dotfuscator UI, while having the build process handle where they actually are at build time.
In order for the Xamarin platform specific build process to properly find the obfuscated assemblies, they have to be copied back into the original assembly locations after obfuscation. Android has an additional restriction in that the original obfuscated PCL must be copied back to its project specific location as well. The easiest way to accomplish this is to have a post-build event in the Dotfuscator project to do the copying:
Adding a "ObRelease" and/or "ObDebug" configuration can be useful to only obfuscate when explicitly needed and wanted. This can be accomplished by adding a Condition property to the Dotfuscate element in the csproj (for instance: Condition=" '$(Configuration)' == 'ObRelease' "). Note that when adding a new "Release" config for Android, one needs to disable "Use Shared Runtime" and "Enable developer instrumentation" in the Android Options under the Packaging tab.
Once these steps are complete, you should be able to see Dotfuscator's build output during your builds in either Visual Studio or Xamarin Studio.
I am new to MSBuild. Just started trying it two days ago, and now I am just testing it. I have run into a problem where I get this error:
"c:\Users\martinslot\Documents\Visual Studio 2010\Projects\MultifileAssembly\SpecializedBuild.xml" (BuildNumberUtil target) (1) ->
c:\Users\martinslot\Documents\Visual Studio 2010\Projects\MultifileAssembly\SpecializedBuild.xml(4,34): error MSB4006: There is a circular dependency in t
he target dependency graph involving target "BuildNumberUtil".
My MSBuild script look like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BuildNumberUtil" DependsOnTargets="BuildStringUtil" >
<Message Text="=============Building modules for NumberUtil============="/>
<Csc TargetType="Module" Sources="NumberUtil/DoubleUtil.cs; NumberUtil/IntegerUtil.cs" AddModules="/StringUtil/StringUtil"/>
<Copy SourceFiles="#(NetModules)" DestinationFolder="../Output/Specialized"/>
</Target>
<Target Name="BuildStringUtil" DependsOnTargets="BuildNumberUtil" >
<Message Text="=============Building modules for StringUtil============="/>
<Csc TargetType="Module" Sources="StringUtil/StringUtil.cs;" AddModules="/NumberUtil/IntegerUtil;/NumberUtil/DoubleUtil"/>
<Copy SourceFiles="#(NetModules)" DestinationFolder="/Output/Specialized"/>
</Target>
</Project>
I understand the problem, actually I created this small example to see if MSBuild understood and could somehow correct the problem. How do I solve this?
My problem is that the two targets compile modules that rely on eachother. Does someone here have a solution on how to handle this kind of problem with MSBuild? Maybe I am constructing this in the wrong way?
You simply cannot build projects with circular dependencies. How could you? Which do you build first? There may be some esoteric, convoluted, incorrect way of doing so, but why do it? Circular dependencies usually indicate a design flaw. Fix the design, and you no longer have a circular dependency issue.
It is possible to construct Circular Modules within the scope of MSBuild and Visual Studio; however, doing so has a very limited set of situations where it would be valid to do so.
One key way to do this, if you're planning on using Xaml within your code, is to remove the Sources aspect of the Csc tag and generate your own .response file which actually points to the code you wish to inject. Within the Csc tag attributes you'd specify this file yourself in the ResponseFiles attribute.
Within your .response file, you would then break your application down into its assembly and netmodule components, making sure to include the core assembly's files first at all times. Typically the Csc tag's attributes are directly translated into Csc.exe command line parameters. The parameter names do not always match up. For the sake of resolution it's best to use full, non-relative, paths when referring to files (example, partial, .response below):
"X:\Projects\Code\C#\Solution Name\InternalName\ProjectName - InternalName\SearchContexts\StringSearchType.cs"
"X:\Projects\Code\C#\Solution Name\InternalName\ProjectName - InternalName\UI\Themes\Themes.cs"
/target:module /out:bin\x86\Debug\InternalName.UI.dll
"X:\Projects\Code\C#\Solution Name\InternalName\ProjectName - InternalName\UI\EditDatabaseImageControl.xaml.cs"
"X:\Projects\Code\C#\Solution Name\InternalName\ProjectName - InternalName\obj\x86\Debug\UI\EditDatabaseImageControl.g.cs"
You'll notice that this will end up with merging your multiple sets of Targets into one, and that I've included the xaml generated code myself. This is partly why you remove the Sources aspect, as the Xaml Page generator part of the MSBuild task automatically injects information into the #(Compile) set. Since there's a Debug/Release configuration, in the area where you define the response file to use, I create two versions of the response (since I'm using a T4 template):
ResponseFiles="$(CompilerResponseFile);InternalName.$(Configuration).response"
If you intended to include more than one platform in your code you'd likely need C*P response files where C is the number of configurations (Debug|Release) and P is the number of platforms (x86, x64, AnyCpu). This kind of solution would likely only be a sane method by using a generator.
The short version of this: it is possible to create circular modules so long as you can guarantee that you'll compile it all in one step. To ensure that you maintain the build functionality that is afforded to you with the Xaml build step, your best bet is to start with a normal C# project, and create your own .Targets file from the $(MSBuildToolsPath)\Microsoft.CSharp.targets in the <Import ... tag near the bottom. You'll also likely need a secondary csproj for design purposes since a large portion of intellisense is lost by using this workaround (or use a csproj Condition attribute where the target is selected by some flag you set). You'll also notice certain Xaml editors don't seem to like the binding to netmodule namespaces, so if you bind to types in a netmodule you'll likely have to do them in codebehind (I haven't tested workarounds for this since there's usually ways around static namespace binding)
For some reason within all this, the .baml compiled .xaml files are implicitly understood by the Csc compiler, I haven't been able to figure out where it's deriving this from a command argument, or if it's just implicit by design. If I had to guess they're inferred by the g.cs files associated to what you include in your list of included files.
Observe that this is occurred for web application (either ASP.NET standard web application or ASP.NET MVC application) and fix for this problem is to be removed the below line in ".csproj" file.
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
Package
</BuildDependsOn>
</PropertyGroup>