How to pass parameters for a managed code dll in installshield? - c#

I have a dotnet script which is for encryption and decryption. I have to pass the parameters for the setvalues function in installshield. How can I achieve this? Dotnet code is as follows. I have the assembly (.dll) file.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Security;
using System.Xml;
using System.Collections.Specialized;
namespace EncryptionDecryption
{
public class EncrptionHelper
{
#region Member variables
static byte[] entropy = { 0, 8, 2, 3, 5 };
#endregion
#region Public Methods
public static void SetValue(string configFilePathName, string appSettingKey, string appSettingValue)
{
appSettingValue = EncryptString(ToSecureString(appSettingValue));
SetSetting(appSettingKey, appSettingValue, configFilePathName);
}
public static string GetValue(string configFilePathName, string appSettingKey)
{
string value = GetSetting(appSettingKey, configFilePathName);
value = ToInsecureString( DecryptString(value));
return value;
}
#endregion
#region Private Methods
private static bool SetSetting(string Key, string Value, string configFilePath)
{
bool result = false;
try
{
// System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(configFilePath);
// config.AppSettings.File = configFilePath;
// config.AppSettings.Settings[Key].Value = Value;
// config.Save(ConfigurationSaveMode.Modified);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(configFilePath);
xmlDoc.SelectSingleNode("//appSettings/add[#key='" + Key +"']").Attributes["value"].Value = Value;
xmlDoc.Save(configFilePath);
ConfigurationManager.RefreshSection("appSettings");
result = true;
}
finally
{ }
return result;
}
private static string GetSetting(string Key, string configFilePath)
{
string result = null;
try
{
XmlDocument appSettingsDoc = new XmlDocument();
appSettingsDoc.Load(configFilePath);
XmlNode node = appSettingsDoc.SelectSingleNode("//appSettings");
XmlElement value = (XmlElement)node.SelectSingleNode(string.Format("//add[#key='" + Key + "']"));
result = (value.GetAttribute("value").ToString());
}
finally
{ }
return result;
}
private static SecureString ToSecureString(string input)
{
SecureString secure = new SecureString();
foreach (char c in input)
{
secure.AppendChar(c);
}
secure.MakeReadOnly();
return secure;
}
private static string ToInsecureString(SecureString input)
{
string returnValue = string.Empty;
IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(input);
try
{
returnValue = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr);
}
finally
{
System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);
}
return returnValue;
}
private static string EncryptString(System.Security.SecureString input)
{
byte[] encryptedData = System.Security.Cryptography.ProtectedData.Protect(
System.Text.Encoding.Unicode.GetBytes(ToInsecureString(input)), entropy, System.Security.Cryptography.DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encryptedData);
}
private static SecureString DecryptString(string encryptedData)
{
try
{
byte[] decryptedData = System.Security.Cryptography.ProtectedData.Unprotect(
Convert.FromBase64String(encryptedData),
entropy,
System.Security.Cryptography.DataProtectionScope.CurrentUser);
return ToSecureString(System.Text.Encoding.Unicode.GetString(decryptedData));
}
catch
{
return new SecureString();
}
}
#endregion
}
}
Update: Action start 14:31:36: Encryption.
MSI (c) (84:40) [14:31:36:525]: Invoking remote custom action. DLL: C:\Users\<username>\AppData\Local\Temp\MSIE259.tmp, Entrypoint: m1
InstallShield: Attempting to load through CLR 4 APIs...
InstallShield: Getting meta host...
InstallShield: Enumerating available runtimes...
InstallShield: Highest available runtime: v4.0.30319
InstallShield: Trying to use highest runtime...
InstallShield: Using highest version runtime...
InstallShield: Loading assembly Security.Encryption from resource 4097
InstallShield: Calling method with parameters [(System.String)C:\Program Files (x86)\<Installdir>\<configfilename>.config, (System.String)VWFPassword, (System.String)]
InstallShield: Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Program Files (x86)\<Installdir>\<configfilename>.config'.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize)
at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials, IWebProxy proxy, RequestCachePolicy cachePolicy)
at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
at System.Xml.XmlTextReaderImpl.OpenUrlDelegate(Object xmlResolver)
at System.Threading.CompressedStack.runTryCode(Object userData)
at
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.CompressedStack.Run(CompressedStack compressedStack, ContextCallback callback, Object state)
at System.Xml.XmlTextReaderImpl.OpenUrl()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)
at System.Xml.XmlDocument.Load(XmlReader reader)
at System.Xml.XmlDocument.Load(String filename)
at Security.Encryption.EncrptionHelper.SetSetting(String appSettingKey, String appsettingValue, String configFilePathName)
at Security.Encryption.EncrptionHelper.SetValue(String configFilePathName, String appSettingKey, String appSettingValue)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at InstallShield.ClrHelper.CustomActionHelper.CallMethod(EntryPointInfo info)
at InstallShield.ClrHelper.CustomActionHelper.RunAction(UInt32 installHandle, Int32 entryNumber, Int64 instanceHandle)
InstallShield: Managed code threw an unhandled exception.
This is the error I receive after doing all that is mentioned in the screenshots below and doing some R&D. The directory mentioned "C:\Program Files (x86)\\.config" exists when the encryption custiom action is being called but it throws an exception.

Yes, it can be done with following steps:
1- write your required functionality in c# installer class (make sure your are using installer class)
2- Compile and add your dll into installshield (recomended create a separate component for this dll)
3- Select component view -> select above component and go to .Net settings section, set the ".Net Installer class" to true. Set the ".net installer class parameters"
Parameters are passed as key/value pair e.g
/targetDirectory="[INSTALLDIR]\"
All steps are same, just added screenshots.
Create a dll with an installer class and your encrypt/decrypt class.
Add dll and config file to component(above mentioned), if config file is already added to some other component then its fine. no need to add again.
I have added and retrieved INSTALLDIR variable as argument which is predefined. if you want to receive some input from user (from some custom textboxes) then you will need to define your own variables to store and pass values as arguments.
Creating dll with installer class and your requred logic for other task
Creating component and adding files
Mark the dll as installer class and pass arguments
Here goes installer class:
using System;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.ComponentModel;
using System.Configuration.Install;
using System.Collections.Generic;
namespace EncryptionDecryption
{
[RunInstaller(true)]
public class InstallerClassDemo : Installer
{
private string installationDirectory=string.Empty;
private string testString=string.Empty ;
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
try
{
//For testing purpose only.. work only in debug mode when pdb files are deployed as well.
//Debugger.Break();
installationDirectory = Context.Parameters["INSTALLDIR"];
//I believe, the config file must be located in the installation directory if so, then use the following way to compute path
string configFilePath = Path.Combine(installationDirectory, "myConfigFile.config");
EncrptionHelper.SetValue(configFilePath, "testKey", "testValue");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
protected override void OnCommitted(System.Collections.IDictionary savedState)
{
base.OnCommitted(savedState);
}
public override void Uninstall(System.Collections.IDictionary savedState)
{
base.Uninstall(savedState);
}
}
}

I think installshield cannot (or need extra work) for read your assemblies, but you can run simple console application which consume your assemblies from installscript and passing parameter from installshield.
Create simple console application
Include console application on Support Files
Copy to installation folder or assemblies location, so console application can access your assemblies
Launch from installscript using launchappandwait
If consoleapp.exe not use anymore, just delete it.

Related

Access to folder Denied C#

I'm a fairly new C# Programmer, and I've run into an error beyond my ability to fix.
Currently I'm working on coding a discord bot, and upon trying to instantiate and Program object, it returns an "Access is denied error".
The issue is that the error is referring to a folder, not a file and I've tried a bunch of stuff to fix it.
Running Visual Studio as administrator
Ensuring my account has permissions to access the files and folder
Changing the location of the project files
Restarting visual Studio
Recoding the project off a clean sheet
Ensuring the folder and files are not "Read Only"
The error is thrown at this line: => new Program().MainAsync().GetAwaiter().GetResult();
I'm basically out of ideas at this point. The full details of the exception message are as follows:
System.UnauthorizedAccessException
HResult=0x80070005
Message=Access to the path 'C:\Users\XXX\source\repos\discordBot\discordBot\bin\Debug' is denied.
Source=mscorlib
StackTrace:
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access)
at train_bot.Program.d__3.MoveNext() in C:\Users\XXX\source\repos\discordBot\discordBot\Program.cs:line 46
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at train_bot.Program.Main(String[] args) in C:\Users\XXX\source\repos\discordBot\discordBot\Program.cs:line 21
The less detailed version
System.UnauthorizedAccessException: 'Access to the path 'C:\Users\SettingAdmin\source\repos\discordBot\discordBot\bin\Debug' is denied.'
using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace train_bot
{
class Program
{
private DiscordSocketClient Client;
private CommandService Commands;
static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();
private async Task MainAsync()
{
//configuring client
Client = new DiscordSocketClient(new DiscordSocketConfig
{
LogLevel = LogSeverity.Debug //changes detail in log
});
Commands = new CommandService(new CommandServiceConfig
{
CaseSensitiveCommands = true,
DefaultRunMode = RunMode.Async,
LogLevel = LogSeverity.Debug
});
Client.MessageReceived += Client_MessageReceived;
await Commands.AddModulesAsync(Assembly.GetEntryAssembly());
Client.Ready += Client_Ready;
Client.Log += Client_Log;
string Token = "";
using (var Steam = new FileStream(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location).Replace(#"bin\Debug\netcoreapp2.0", #"Token.txt"), FileMode.Open, FileAccess.Read))using (var ReadToken = new StreamReader(Steam))
{
Token = ReadToken.ReadToEnd();
}
await Client.LoginAsync(TokenType.Bot, Token);
await Client.StartAsync();
await Task.Delay(-1);
}
private async Task Client_Log(LogMessage Message)
{
Console.WriteLine($"{DateTime.Now} at {Message.Source}] {Message.Message}");
}
private async Task Client_Ready()
{
await Client.SetGameAsync("Hentai King 2018", "", StreamType.NotStreaming);
}
private async Task Client_MessageReceived(SocketMessage arg)
{
//Configure the commands
}
}
}
The problem may be that you try to open a directory as file. The path you construct is:
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location).Replace(#"bin\Debug\netcoreapp2.0", #"Token.txt")
This would only work if Assembly.GetEntryAssembly().Location indeed contains the string #"bin\Debug\netcoreapp2.0".
You probably intended something like
Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), #"Token.txt")
Like the error said, you don't have access to that folder. If you are running it on debug mode make sure you run visual studio as administrator so you get to ignore all that on dev environment. If its deployed, make sure the the account running your program has appropriate rights to the folder.

SSIS - Move Excel File with OLEDB Connection to Archive

I've created a connection using Microsoft Office 12.0 Access Database Engine OLE DB Provider as Excel Schema to loop through all the sheets in the Excel file as demonstrated in this question How to loop through Excel files and load them into a database using SSIS package?
And using Foreach ADO.NET Schema Rowset Enumerator to loop through the excel files.
Everything is working fine now, but after importing the data from Excel, I wanted to move that file to Archive folder. And tried using a File System Task, but I get the error as
[File System Task] Error: An error occurred with the following error message: "The process cannot access the file because it is being used by another process.".
And I also tried with script task from this link. But I was getting some error and couldn't solve the error as I've got zero knowledge on C#.
Below is the error I've got when I tried to move the files using a script task.
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
at Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTATaskScriptingEngine.ExecuteScript().
Update:
Here's my complete code with which I'm trying to move the files.
If I add a breakpoint at enum ScriptResults, I don't get that popup and the task gets completed successfully and the file is also been moved to archive, but if I don't add any breakpoint in the C# code, I get that pop and the file is not moved to archive.
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.IO;
#endregion
namespace ST_9fc6ad7db45c4a7bb49f303680f789ef
{
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
public void Main()
{
DirectoryInfo di = new DirectoryInfo(Dts.Variables["SplitSkill_FolderPath"].Value.ToString());
FileInfo[] fi = di.GetFiles("*.xlsx");
String filename = fi[0].Name;
string sourceFileName = filename;
string destinationFile = #"D:\Flipkart\Data\Split Skill\Archive\" + sourceFileName;
string sourceFile = #"D:\Flipkart\Data\Split Skill\" + sourceFileName;
if (File.Exists(destinationFile))
File.Delete(destinationFile);
// To move a file or folder to a new location:
System.IO.File.Move(sourceFile, destinationFile);
Dts.TaskResult = (int)ScriptResults.Success;
}
#region ScriptResults declaration
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
}
As far as I understand, I think you have a task for looping through sheets and another for looping through files. So you probably have 2 tasks inside a foreach loop. Try making a copy of the file inside the foreach loop with a system task
for the executable
c:\windows\system32\cmd.exe
and for the arguments soemthing like
C COPY "C:\xxx\abc\\Destination_template.accdb" "C:\xxx\abc\\Destination_template.accdb"Destination_template - Kopie.accdb"
Then create a file system task which moves that copy to your archive.
This should do the trick (Maybe not the best approach but should work)

C# Error About UnauthorizedAccessException

I want to copy file to networking drive.But it show error : C:\pdf.zip has denide (copytonetworkingdrive function).
So I want to change file ACL (copyfileto function).
Problem
1.) I can not access c:\pdf.zip
2.) I can not copy file c:\ to networking drive.
3.) I can not change file access control.
4.) I can not access \\networkingdrive\\blablabla$
( I has login by user via programming [look at logonuser function] )
I develop on windows 7 64bit, VS C# 2012, .Net Framework 4.0.
using Ionic.Zip;
using System.Security.Principal;
using System.Security.Permissions;
using System.Security.AccessControl;
using System.Management;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
My Code :
private static void copyfileto(string fin, string fout)
{
int i;
FileStream fsin, fsout;
*FileSecurity files = File.GetAccessControl(fin);*
WindowsIdentity self = System.Security.Principal.WindowsIdentity.GetCurrent();
FileSystemAccessRule rule = new FileSystemAccessRule( self.Name, FileSystemRights.FullControl,AccessControlType.Allow);
AuthorizationRuleCollection acl = files.GetAccessRules(true,true, typeof(System.Security.Principal.NTAccount));
files.AddAccessRule( rule );
fsin = new FileStream( fin ,FileMode.Open);
fsout = new FileStream( fout ,FileMode.Create);
do
{ i = fsin.ReadByte() ;
if ( i != -1 ) fsout.WriteByte((byte) i );
} while ( i != -1);
fsin.Close();
fsout.Close();
}
private void copytonetworkdrive()
{
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
IntPtr hTokenLocal;
if (LogonUser(frm_main.myapp.gl_login_user_for_attachment.ToString(),
frm_main.myapp.gl_folder_attachment_server.ToString(),
frm_main.myapp.gl_pass_user_for_attachment.ToString(), LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, out hTokenLocal))
{
//if (hToken != IntPtr.Zero) CloseHandle(hToken);
//if (hTokenDuplicate != IntPtr.Zero) CloseHandle(hTokenDuplicate);
WindowsIdentity identity = new WindowsIdentity(hTokenLocal);
WindowsImpersonationContext context = identity.Impersonate();
copyfileto("C:\\Pdf.zip",frm_main.myapp.gl_folder_attachment_server.ToString() +#"ZIP\quotation\Pdf.zip");
}
}
In FileSecurity files = File.GetAccessControl(fin);
Error :
System.UnauthorizedAccessException was unhandled
HResult=-2147024891
Message=Attempted to perform an unauthorized operation.
Source=mscorlib
StackTrace:
at System.Security.AccessControl.Win32.GetSecurityInfo(ResourceType resourceType, String name, SafeHandle handle, AccessControlSections accessControlSections, RawSecurityDescriptor& resultSd)
at System.Security.AccessControl.NativeObjectSecurity.CreateInternal(ResourceType resourceType, Boolean isContainer, String name, SafeHandle handle, AccessControlSections includeSections, Boolean createByName, ExceptionFromErrorCode exceptionFromErrorCode, Object exceptionContext)
at System.Security.AccessControl.FileSystemSecurity..ctor(Boolean isContainer, String name, AccessControlSections includeSections, Boolean isDirectory)
...
Please Help and Thank.

Roslyn: workspace loads in console application but not in msbuild task

I have a custom msbuild task with this command:
var workspace = Workspace.LoadStandAloneProject(csprojPath);
When I run it, it throws the following error:
System.InvalidCastException was unhandled by user code
Message=Unable to cast transparent proxy to type 'Roslyn.Utilities.SerializableDataStorage'.
Source=Roslyn.Services
StackTrace:
at Roslyn.Utilities.RemoteServices.CreateInstance[T]()
at Roslyn.Services.Host.TemporaryStorageServiceFactory.CreateService(IWorkspaceServiceProvider workspaceServices)
at Roslyn.Services.Host.WorkspaceServiceProviderFactory.Provider.c__DisplayClass7.b__4()
at Roslyn.Utilities.NonReentrantLazy`1.get_Value()
at Roslyn.Services.Host.WorkspaceServiceProviderFactory.Provider.GetService[TWorkspaceService]()
at Roslyn.Services.SolutionServices..ctor(IWorkspaceServiceProvider workspaceServices, ILanguageServiceProviderFactory languageServicesFactory)
at Roslyn.Services.Solution..ctor(SolutionId id, String filePath, VersionStamp version, VersionStamp latestProjectVersion, ILanguageServiceProviderFactory languageServiceProviderFactory, IWorkspaceServiceProvider workspaceServices)
at Roslyn.Services.Host.SolutionFactoryServiceFactory.SolutionFactoryService.CreateSolution(SolutionId id)
at Roslyn.Services.Host.TrackingWorkspace.CreateNewSolution(ISolutionFactoryService solutionFactory, SolutionId id)
at Roslyn.Services.Host.TrackingWorkspace..ctor(IWorkspaceServiceProvider workspaceServiceProvider, Boolean enableBackgroundCompilation, Boolean enableInProgressSolutions)
at Roslyn.Services.Host.HostWorkspace..ctor(IWorkspaceServiceProvider workspaceServiceProvider, Boolean enableBackgroundCompilation, Boolean enableInProgressSolutions, Boolean enableFileTracking)
at Roslyn.Services.Host.LoadedWorkspace..ctor(ILanguageServiceProviderFactory languageServiceProviderFactory, IWorkspaceServiceProvider workspaceServiceProvider, IProjectFileService projectFileFactsService, IDictionary`2 globalProperties, Boolean enableBackgroundCompilation, Boolean enableFileTracking)
at Roslyn.Services.Host.LoadedWorkspace..ctor(ExportProvider exportProvider, Boolean solutionLoadOnly, Boolean enableFileTracking)
at Roslyn.Services.Host.LoadedWorkspace..ctor(Boolean enableFileTracking)
at Roslyn.Services.Host.LoadedWorkspace.LoadStandAloneProject(String projectFileName, String configuration, String platform, String language, Boolean enableFileTracking)
at Roslyn.Services.Workspace.LoadStandAloneProject(String projectFileName, String configuration, String platform, String language, Boolean enableFileTracking)
...
The same code, when run in a console application, with the same project, runs fine.
Any ideas? Googling has not been helpful!
Here's a sample MsBuild task with Roslyn.
In order to reconstruct the command line needed by the Workspace.LoadProjectFromCommandLineArguments method, we have to pass some info from the msbuild file into our task.
The referenced assemblies: the
#(ReferencePath) item group.
The cs files to be compiled: the #(Compile) item group.
The base directory: the $(MSBuildProjectDirectory) built-in property.
That's all that Roslyn needs to parse your source files. (See the note at the end of this post.)
So create a C# class library project.
These are the project references that you'll need:
Microsoft.Build.Framework
Microsoft.Build.Utilities.v4.0
Roslyn.Compilers
Roslyn.Services
The code for the custom MsBuild task:
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Roslyn.Services;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RoslynMsBuildTask
{
public class RoslynTask : Task
{
[Required]
public ITaskItem[] ReferencePath { get; set; }
[Required]
public ITaskItem[] Compile { get; set; }
[Required]
public ITaskItem BaseDirectory { get; set; }
public override bool Execute()
{
Log.LogMessage(MessageImportance.High, "RoslynTask.Execute called...\n");
// Format the command line with the minimal info needed for Roslyn to create a workspace.
var commandLineForProject = string.Format("/reference:{0} {1}",
ReferencePath.Select(i => i.ItemSpec).ToSingleString(",", "\"", "\""),
Compile.Select(i => i.ItemSpec).ToSingleString(" ", "\"", "\""));
// Create the Roslyn workspace.
var workspace = Workspace.LoadProjectFromCommandLineArguments("MyProject", "C#", commandLineForProject, BaseDirectory.ItemSpec);
// Make sure that Roslyn actually parsed the project: dump the source from a syntax tree to the build log.
Log.LogMessage(MessageImportance.High, workspace.CurrentSolution.Projects.First()
.Documents.First(i => i.FilePath.EndsWith(".cs")).GetSyntaxRoot().GetText().ToString());
return true;
}
}
public static class IEnumerableExtension
{
public static string ToSingleString<T>(this IEnumerable<T> collection, string separator, string leftWrapper, string rightWrapper)
{
var stringBuilder = new StringBuilder();
foreach (var item in collection)
{
if (stringBuilder.Length > 0)
{
if (!string.IsNullOrEmpty(separator))
stringBuilder.Append(separator);
}
if (!string.IsNullOrEmpty(leftWrapper))
stringBuilder.Append(leftWrapper);
stringBuilder.Append(item.ToString());
if (!string.IsNullOrEmpty(rightWrapper))
stringBuilder.Append(rightWrapper);
}
return stringBuilder.ToString();
}
}
}
To demonstrate that it actually works, add the following lines at the end of your csproj file (just before the closing Project tag). But only if the project was already built successfully and it can find your task dll in the output folder.
<Target Name="AfterBuild" DependsOnTargets="RoslynTask"/>
<UsingTask AssemblyFile="$(OutputPath)\RoslynMsBuildTask.dll" TaskName="RoslynMsBuildTask.RoslynTask" />
<Target Name="RoslynTask">
<RoslynTask ReferencePath="#(ReferencePath)" Compile="#(Compile)" BaseDirectory="$(MSBuildProjectDirectory)" />
</Target>
It will dump the source of your first cs file to the build output.
Note that other csc.exe switches (like ConditionalDirectives, output type, etc) may also matter depending on the type of analysis you are trying to do. You can also pass them to your task using this pattern. See $(MSBuildToolsPath)\Microsoft.CSharp.targets file, CoreCompile target, Csc task for a complete list of properties that MsBuild passes to csc.exe.
This is a limitation of MSBuild. Roslyn can't invoke MSBuild recursively during a build to determine the project properties/files/references. In order to create a Roslyn IProject during in a build task, try using the LoadFromCommandLineArgs() method instead. You'll need to construct your task to take the same arguments as the CscTask ends up passing to the compiler.
Hope this helps!

drools.net Reading DRL file

I am trying to run a simple application that uses drool.net. Whenever I execute, the application crashed at builder.AddPackageFromDrl("DroolsApp.Rules.drl", stream); The Error stated that NullReferenceException was unhandled and Object reference not set an instance of an object. I tried to change drl file but the error was still present. Am I missing anything? Also is there any site that provides a detailed tutorial for using drools.net and do I need any specific program to create and edit drl files as I am using a simple notepad.
Thanks in Advance
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using org.drools.dotnet.compiler;
using org.drools.dotnet.rule;
using org.drools.dotnet;
using System.IO;
using System.Reflection;
namespace DroolsApp
{
class Program
{
static void Main(string[] args)
{
PackageBuilder builder = new PackageBuilder();
Stream stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("DroolsApp.Rules.drl");
builder.AddPackageFromDrl("DroolsApp.Rules.drl", stream);
Package pkg = builder.GetPackage();
RuleBase ruleBase = RuleBaseFactory.NewRuleBase();
ruleBase.AddPackage(pkg);
executeRules(ruleBase, 5);
executeRules(ruleBase, 6);
executeRules(ruleBase, 7);
}
public static void executeRules(RuleBase ruleBase, int amount)
{
WorkingMemory workingMemory = ruleBase.NewWorkingMemory();
Quest quest = new Quest();
quest.Value = amount;
workingMemory.assertObject(quest);
workingMemory.fireAllRules();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DroolsApp
{
class Quest
{
public int Value { set; get; }
}
}
DRL File:
package DroolsApp
rule "NumberFive"
when
quest : Quest( Value == 5 )
then
Console.WriteLine("FIVE");
end
rule "NumberSix"
when
quest : Quest( Value == 6 )
then
Console.WriteLine("SIX");
end
Stack Trace:
System.NullReferenceException was unhandled
Message=Object reference not set to an instance of an object.
Source=drools.dotnet
StackTrace:
at org.drools.dotnet.semantics.DotnetClassTypeResolver.resolveType(String className)
at org.drools.semantics.java.RuleBuilder.build(ColumnDescr columnDescr)
at org.drools.semantics.java.RuleBuilder.build(RuleDescr ruleDescr)
at org.drools.semantics.java.RuleBuilder.build(Package pkg, RuleDescr ruleDescr)
at org.drools.compiler.PackageBuilder.addRule(RuleDescr ruleDescr)
at org.drools.compiler.PackageBuilder.addPackage(PackageDescr packageDescr)
at org.drools.compiler.PackageBuilder.addPackageFromDrl(String fileName, Reader reader)
at org.drools.dotnet.compiler.PackageBuilder.AddPackageFromDrl(String fileName, Stream drlStream)
at MinimalDroolsForm.Program.Main(String[] args) in C:\Users\Carl\Desktop\DroolsApp\Program.cs:line 19
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
It's for sure an error in drl file. Try to remove all rules and imports, compile them, and then add single line and repeat (it must validate though). Drools.net reacts for compilation errors with NullReferenceException or an error with loading CompiledRules0.dll (because it wasn't generated). It is also a good thing to add a Pre-build Build Event deleting the previously generated CompiledRules0.dll (when Drools.net fails to generate the file it uses the previous one). Here's the code:
cd $(TargetDir)
del CompiledRules0.*
Two hints:
1) when calling GetManifestResourceStream method, be sure to pass namespace and filename as parameter.
2) In visual studio (if you are using it), rule file properites, set build action to embedded resource.

Categories