Compiling exe or dll by webserver - c#

I'd like to compile my source (or a part of it) by one of my webservers (like some websites offer nightly builds of their program). As I want my program to be customized by a third party and they have their own standalone application with say their logo and some custom strings in it. My preferable solution would be a dll file which would be loaded into my application, so I can still update the main application while retaining the customization by the third party.
So, the third party goes to my website, enters some fields and a dll file is generated (or do you have any other better way to do this?) and the dll file will be included by the application, which will grab the logo resource and some strings from it, to show in the application.
How can this be done? I'd rather to use Linux to build it, but if Windows is easier then that's not a problem either.

You can use the CSharpCodeProvider API for that, here is an example:
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = true;
CompilerResults results = csc.CompileAssemblyFromSource(parameters,
#"using System.Linq;
class Program {
public static void Main(string[] args) {
var q = from i in Enumerable.Rnge(1,100)
where i % 2 == 0
select i;
}
}");
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
If you want to use linux take a look at Mono MCS C# Compiler

It's easy on either platform. Just accept the data into a template C# file (String.Replace will work fine). And then just shell out to the compiler.

If you trust your third party vendor try to use Team City by JetBrains, commiting some changes in svn repo folder, will cause recompilation of project and you will get precompiled project.

How about this
Build a web interface to capture the third party's customisations into, say, a database.
Set up a Continuous Integration server to manage automated builds (for example Jenkins).
Then implement custom build steps in your CI solution to grab the customisations, drop them into copies of the source code, and have your CI do a build for each client - publishing the build artefacts somewhere where the client can see them (say, somewhere within the web interface)
You could set up custom triggers in your CI server to watch the database for new customisations. Or to be triggered by some operation in the web UI.

Related

ASP.NET C# external SDK not thread-safe

I have a Web API in ASP.NET/C#.
It uses an external 32-bit ActiveX SDK to communicate with a third-party application.
From my test, that SDK has problems when we connect two differents users at the same time. The second connection overwrites the first one.
If I call my API in two cURL loops, one connecting with userA and the other with userB, in some case, the call on userA will have the results of userB.
I don't have any static variables in my code, none that refer to the SDK for sure.
The only solution I can think of would be to "lock" the API while it is getting the response for the user. Is there any other solution ? If not, any pointer on how to do this in C# ?
The API has multiple controllers (think customer/invoice/payment/vendor), all of which are using the same SDK. Thus, a call to a method of CustomerController must lock calls to the other controllers too.
The lock only needs to be active while I using the SDK (which is probably 99% of the request time).
Edit 1:
The SDK is named "Interop.AcoSDK.dll", it is 32-bit. Visual Studio describe the file as "AcoSDK Library". It is an SDK for Acomba, an accounting program. The program itself has a very old structure, the origins dating back to the 80' in DOS (The program was named Fortune1000 back in those days). The interaction with the SDK is really not modern.
I've added the DLL to my project, and to use it, I call two parts.
AcoSDKX AcoSDK = new AcoSDKX();
int version = AcoSDK.VaVersionSDK;
if (AcoSDK.Start(version) != 0)
{
throw new Exception("Failed to start SDK");
}
cie = new AcombaX();
if (cie.CompanyExists(config.ciePath) == 0)
{
throw new Exception("Company not found");
}
int error = cie.OpenCompany(config.appPath, config.ciePath);
if (error != 0)
{
throw new Exception("Failed to open company: " + cie.GetErrorMessage(error));
}
AcoSDK.User User = new AcoSDK.User
{
PKey_UsNumber = config.user
};
try
{
error = User.FindKey(1, false);
}
catch
{
throw new Exception("Failed to find user");
}
if (error != 0)
{
throw new Exception("Failed to find user");
}
error = cie.LogCurrentUser(User.Key_UsCardPos, config.pass);
if (error != 0)
{
throw new Exception("Failed to login in Acomba: " + cie.GetErrorMessage(error));
}
The cie attribute above is a private AcombaX cie in the class.
That class is called from my other class to handle the connection to the SDK.
My other class declare it as a standard object (non-static).
The config above refers to an object with attributes for the company/user the API request is for. Calls for multiple companies can be made.
In the moment, my problem is that calling for different companies, data ends-up being mixed up. So values from Company-B will show in my query of Company-A, for example, when I loop 100 calls to the API in cURL to both companies at the same time. It doesn't do it each time, just some time, for some queries. Probably when a call open the SDK for company-B while the call for company-A has already connected to the SDK but haven't started requesting data.
You need to share some more information about the ActiveX SDK (there is no such thing really). There are three types of ActiveX
(great explanation here)
ActiveX EXE: Unlike a stand-alone EXE file, an ActiveX EXE file is designed to work as an OLE server, which is nothing more than a program designed to share information with another program. It has an .EXE file extension.
ActiveX DLL: ActiveX DLL files are not meant to be used by themselves. Instead, these types of files contain subprograms designed to function as building blocks when creating a stand-alone program. It has a .DLL file extension.
ActiveX Control: Unlike an ActiveX DLL or ActiveX EXE file, an ActiveX Control file usually provides both subprograms and a user interface that you can reuse in other programs. It has an .OCX file extension.
Based on the format of the SDK and the way it's being used, there might be solutions to make the calls parallel.
Updating the question with some code, example etc, might enable me to shed some more light.
This could be starting multiple applications instead of one and using them as a pool, creating multiple objects from the same library and more.
I've posted a comment asking for clarification; I'm hoping that you're at a development stage where you can redesign this feature.
ASP.NET is, generally, really poor with non-trivial synchronous processes. It's easy to exhaust the thread pool, and concurrency issues like what you describe are not uncommon when moving from "desktop" or RPC architecture.
I would strongly suggest changing your WebAPI / ASP.NET architecture for these kinds of operations to a queue/task based approach. The client submits a task and then polls/subscribes for a completion result. This will allow you to design the backend to operate in any manner necessary to prevent data corruption problems due to shared libraries. Perhaps one long-lived backend process per Company which processes requests - I don't know enough about your needs to make intelligent suggestions, but you have a lot of options here.

How to update your exe from remote host - c# 4.0 - wpf application

I want to update my exe from remote server. So when the button clicked on my wpf application it will download the remote and also a remote txt file and replace the current ones in the same folder that exe running. So it will overwrite the current txt and and exe file while my exe is running. How can i achive this ?
Remote host is url like www.mydomain.com/MyAPP.exe
wpf application , c# 4.0
The way that we resolved this issue was to create a shell exe that as the one that was installed and deployed initially to the client machines.
The "real" executable program is stored in a subdirectory of this initial app. When the shell app is launched, after it has downloaded and installed any updates for the real app, it launches the real app's executable in a separate AppDomain.
Here is the core of the "real" app launching from within the shell app:
System.AppDomainSetup oSetup = new System.AppDomainSetup();
string sApplicationFile = null;
// Use this to ensure that if the application is running when the user performs the update, that we don't run into file locking issues.
oSetup.ShadowCopyFiles = "true";
oSetup.ApplicationName = sAppName;
// Generate the name of the DLL we are going to launch
sApplicationFile = System.IO.Path.Combine(sApplicationDirectory, sAppName + ".exe");
oSetup.ApplicationBase = sApplicationDirectory;
oSetup.ConfigurationFile = sApplicationFile + ".config";
oSetup.LoaderOptimization = LoaderOptimization.MultiDomain;
// Launch the application
System.AppDomain oAppDomain = AppDomain.CreateDomain(sAppName, AppDomain.CurrentDomain.Evidence, oSetup);
oAppDomain.SetData("App", sAppName);
oAppDomain.SetData("User", sUserName);
oAppDomain.SetData("Pwd", sUserPassword);
oAppDomain.ExecuteAssembly(sApplicationFile);
// When the launched application closes, close this application as well
Application.Exit();
Note that in our version, the shell app collects the user name and password from the user in order to access the update web site correctly. This data is then passed to the "real" app through the SetData method on the AppDomain.
The solution depends on your particular case. But there's no straight solution, because you can't update assemblies while they are loaded into memory and being used. I can propose 2 solutions: using shadow copying and using some sort of helper executable. I've used both of them.
Shadow copying.
The obvious way is to make your main executable to be shadow copied, replace it while your app is running and then restart the app. But you can't make your default app domain to be shadow copied, only secondary app domains can be. But you still can move all your code into another assembly (say, MainAppLib.dll) and rewrite your main app executable (MainApp.exe) so that it contains only "loader code". This loader code has to create another app domain, set it to be shadow copied and then run your program logic in the secondary app domain. Beware not to have any direct references from your main app domain into MainAppLib.dll because then this assembly will be loaded into your main app domain which is not shadow copied and the assembly file will get locked. In most cases you can go with AppDomain.ExecuteAssembly() methods.
Helper executable
The idea is to use some sort of update finisher. Your main app remains unchanged, you only add a little amount of code into it, so that your app will download update, put it into temporary folder, and then your main app starts update finisher (in separate process) and exits. Update finisher waits till your app closes and then copies new files from temporary folder into your app folder replacing all files. Update finisher can't replace it's own executable but it can be done by main application before it starts the update finisher. After copying files update finisher runs your application.
p.s. Personally I prefer the former solution because it involves some sort of voodoo magic using app domains, reflection, assemblies e.t.c. And it can be evolved into using plugins if you need (e.g. via MEF framework). But the latter is easier to understand especially if you have never worked with app domains and manual assemblies loading, it's quite straightforward.
You could probably use ClickOnce (based on your comment above that you would be prepared to have another assembly get the exe....as the other poster mentioned you can't replace a running assembly). You can configure it to check at various times (e.g. on startup) for new versions and it automatically downloads them. Its a very robust solution and you can do a lot with the deployment assemblies.

Do you know an Inno Setup wrapper that I can use in C#?

I commonly use Inno Setup manually to build setups for my programs. Now I would like to write a program that can easily build various distributions of an other program, and in order to do that I'd liked to use Inno Setup by .NET code.
Do you know a way to use Inno Setup as an external library, or use a .NET wrapper or at least communicate with Inno Setup through command line?
Thanks for you help.
Yes, a .NET Wrapper for Inno Setup is available as a FREE and Open Source Project from SpiceLogic Inc. Here is the link
https://www.spicelogic.com/Blog/Inno-Setup-Net-Wrapper-8
It is an easy, ready to use Open Source C# class library.
Sample usage code snippet is provided below.
private void btnBuildInstaller_Click(object sender, RoutedEventArgs e)
{
Guid productGuid = Guid.NewGuid();
SetupSettings innoSetupSetting = new SetupSettings
{
CompanyName = "Your Company Name",
// A folder with this company name will be created in the Start Menu / Program Files folder
//and your Product short cut will be placed within this Company Name folder.
ProductName = "your Product Name",
ProductId = productGuid,
// You should keep your product Guid Unique so that you can publish update easily with same Guid.
ProductVersion = new Version("1.0.0.0"), // Your product version
DeployableFolderPath = "C:\\My Project\\Deployable",
// This folder contains all of the exe, dll etc whatever you want to publish to your user's computer.
// You do not need to specify each file name. Whatever file placed in this deployable folder will be
// published to your user's computer.
StartUpApplictionFileName = "My Exciting Calculator.exe",
// This is the main executive file name which will be placed in the Start Menu
ShortCutName = "Exciting Calculator", // Well, this shortcut name will be shown in the start menu,
//if your product name is very big, it is better to use a short cut name to identify your product
// quickly and this property holds that short cut name.
CompanyUrl = "http://your-company-website.com",
SupportUrl = "http://your-company-website.com/ContactUs",
IconFilePath = "C:\\My Project\\Resources\\my-icon.ico", // This icon will be shown in Desktop and Start Menu.
EULAFilePath = "C:\\My Project\\Resources\\End-User-License-Agreements.txt",
// If you set this property, only then a End User License Agreement Screen will be shown, otherwise no EULA screen will be shown.
Platform = PlatformNames.AnyCPU_Prefer32Bit,
// This property is self explanatory, you can set PlatformNames.AnyCPU, PlatformNames.x64, PlatformNames.x32 etc.
// If you set value PlatformNames.x64 then your application will be installed in "C:\Program Files" folder.
// If you set value PlatformNames.x32, then your application will be installed in "C:\Program Files (x86)" folder.
// If you set value PlatformNames.AnyCPU, then, if your user's OS is Windows 64 bit, then your application will be
// installed in "C:\Program Files" folder otherwise it will be installed in "C:\Program Files (x86)" folder.
UninstallerFeedbackUrl = "http://your-company-website.com/uninstall-feedback",
// If you set this property, then after uninstall, this url will be opened where you can ask your user to provide
// additional feedback.
GeneratedSetupScriptFolderPath = "C:\\My Project\\Temporary Scripts",
// This folder is the place where InnoSetup Intermediate scripts will be generated.
// As you understand that, your C# code will generate Pascal Scripts for InnoSetup and
// finally that Pascal script will be executed to build your installer file. So, this
// folder will be used to store the auto-generated Pascal Scripts. You will never need to
// look inside this folder. Every time you call the BuildSetupFile(), old scripts will be
// deleted and new scripts will be written.
OutPutSetupFilePath = "C:\\My Project\\Publish\\Setup.exe",
// This is the path where your final Installer file will be created.
// If a file already exists in this path then the existing file will be replaced.
Prerequisites = new List<Prerequisite> // This is a collection of prerequisites.
// Prerequisites can be .NET Framework, SQL Express Local Db etc.
// Every prerequisite can be configured to either install directly from Installer
// or Download from Web. IF you prefer to download from Web, then you can super configure
// to define if the required prerequisite file should be downloaded automatically from a
// remote URL or it will simply navigate to a Download page and user can download the necessary
// prerequisite from that page. For example, if you want to define .NET Framework 4.5 is a
// prerequisite for your application, then, you can navigate your user to the download page of
// Microsoft .net framework 4.5.
{
new DotNetFrameworkPrerequisite(DotNetVersions.V4_5)
// This example is a very basic common usage. Here we did not set any extra property of the DotNetFrameworkPrerequisite.
// Therefore, by default, the installer will check if the .NET framework version 4.5 is installed in user machine or not.
// If not, then, the installer will take the user to the official Microsoft .NET framework 4.5 download page.
// You can set many properties of this class to customize the behavior. For example, you can host the .NET Framework in your
// own server and silently download and install to user's machine so that if the user wont be aware about what is .NET framework,
// you wont risk missing the user's installation. Please check the documentation for details.
},
FileExtensionAssociation = ".abc",
// Yes, if you want to associate any file that has extension *.abc with the start up application,
// then, you set this property. That means if a file with extension *.abc is double clicked in user's PC,
// then the exe file defined in the property StartUpApplictionFileName = "My Exciting Calculator.exe", will be
// invoked and the full file path of the double clicked file will be passed to this exe file as command argument.
// So, you can program your application to receive this file path as command Argument.
SignToolInfo = new SignToolInfo("C:\\Program Files (x86)\\Windows Kits\\8.0\\bin\\x64\\signtool.exe")
// The constructor takes the path to your SignTool.exe file.
// Yes, you can program to Sign All Signable files (like exe and dll files) including the Uninstaller.
// You read right. Yes, your uninstaller will be signed too, which is really an exciting and rare feature
// in an installer software. If you do not want to digitally sign your files, then, do not set this SignToolInfo property.
{
PfxFilePath = "C:\\your-pfx-file-path.pfx",
TimeStampServerUrl = "http://digicert.timestampserver.com", // a TimeStamp Server Url
CertificateSubject = "Certificate Subject"
},
ShellContextMenuItem = new WindowsShellContextMenuItem
// Another exciting feature is Windows Context Menu Shell Integration.
// That means, you can right click on a file or folder and invoke your application.
// If you do not want the Shell Integration, then do not set this property.
{
DisplayName = "Resize Image", // This text will be shown in the Context Menu
// Say for example, your application is an Image Resizer application, then, you may want to
// allow the user to right click on an image type file to invoke the application.
// If the user right click on an Image file and click the menu item 'Resize Image',
// then your startup exe application defined in the property StartUpApplictionFileName = "My Exciting Calculator.exe", will be
// invoked and the full file path of the double clicked file will be passed to this exe file as a command argument.
// So, you can program your application to receive this file path as command Argument.
TargetType = WindowsShellContextMenuItem.TargetTypes.FileWithExtensionConstraint,
// This Target type can be WindowsShellContextMenuItem.TargetTypes.FileWithExtensionConstraint,
// WindowsShellContextMenuItem.TargetTypes.File or WindowsShellContextMenuItem.TargetTypes.Folder.
// If you set WindowsShellContextMenuItem.TargetTypes.FileWithExtensionConstraint, that means,
// The context menu will be shown on File but if the file has extensions defined in the array type
// property named ExtensionConstraints. IF you set WindowsShellContextMenuItem.TargetTypes.Folder
// for this TargetType, then the ContextMenu will be shown on Folders only. If you set TargetType
// = WindowsShellContextMenuItem.TargetTypes.File then the Context Menu will be shown on Any File type.
ExtensionConstraints = new[] { ".jpg", ".png"}
},
};
// Ok, once we have setup various properties for the object SetupSettings innoSetupSetting,
// we can instantiate the InnoSetupService and pass this Settings object to the constructor.
InnoSetupService generator = new InnoSetupService(innoSetupSetting, innoSetupCompilerExePath: "C:\\Program Files (x86)\\Inno Setup 5\\iscc.exe");
// Yes, you need to pass your InnoSetup Compiler File Path. At the time of this writing, This wrapper library is working fine on Inno Setup 5.
// And it should work on future versions too. If this library stops to work on future version, then, as this wrapper library source code is open,
// hopefully someone will come up with an upgrade which will work on the future version.
// finally call the BuildSetupFile() method of InnoSetupService Class. This method will return a log string which
// is captured from the Console output of InnoSetup.
string result = generator.BuildSetupFile();
// If all goes good, then, your Setup exe file is built by this time and stored in the path you defined in the property
// named OutPutSetupFilePath.
}
Don't know if there is already some ready to run InnoSetup wrapper, but you can do easily (as long as you familiar with InnoSetup), just by using command line call technique mantioned by you in question. To do this just create an instance of Process class, more or less like here:
Process innoProc = Process.Start(InnoSetupExePath, Arguments);
This is basically what you need, using one of the overloads of Process.Start
Basically, your C# application would create the installer script, and then launch the command-line compiler to create the setup executable. This is explained in the documentation, specifically in the booklet Other Information, topic Command Line Compiler Execution.
I have a c# class for the Inno compiler which uses the compiler API and supports getting progress and status information:
https://earlsoft.kilnhg.com/Repo/BuilderPro/Extensions/Inno/File/InnoCompiler.cs?rev=tip
It also allows the script to be "streamed in" savign haveing to write to disk.
Let me know if you find any bugs and I'll take a look.

Load SSRS Report from file in C# in a service

We are looking into replacing Crystal with SSRS.
I would like to know if it is at all possible to load the .rdl or .rdl.data file in C# and generate the report to a memory stream.
I have seen some examples using the LocalReport, but this seems to be part of either the WinForms or WebForms assemblies.
So what I would like to know is:
Is it possible to load the report from file as part of our service.
Can we then generate the report to a Stream (lets say a memory
stream)
Can I do this without using the WebForms/WinForms assemblies in my
service
Can we achieve this without using the webservice for SSRS
I'm not sure that this is an answer, but lets call it supporting material.
I have this code example from msdn that shows how you can do this by creating a service client and calling a given report as a PDF and saves it to file stream.
http://msdn.microsoft.com/en-us/library/reportexecution2005.reportexecutionservice.render.aspx
The problem I'm currently having is finding the correct client object to interact with after pointing VS to the SSRS service. The object I'm using to interact with the service is:
ReportExecutionServiceSoapClient rs = new ReportExecutionServiceSoapClient();
However, the interface doesn't match my code example. So this is a little closer, but not an answer.
UPDATE: The correct Proxy class generator
Here's the link to how to generate the proxy correctly. You'll need the windows sdk installed (current is 7.1). You can find that on microsoft.com, like I did. Execute the command line and it'll generate a file for you. Include in project:
http://msdn.microsoft.com/en-us/library/ms155134%28v=SQL.110%29.aspx
UPDATE: Got the thing workin
I just had to generate the correct proxy. Ok, so for SSRS 2010, apparently they split report execution and management out into two services. The only one I needed to generate my report from a C# console app was the execution service. Maybe that's totally obvious to everyone but me :) ?
Ok so open up a Windows SDK command shell and put this stuff in it for the execution service:
wsdl /language:CS /n:"Microsoft.SqlServer.ReportExecution" http://<Server Name>/reportserver/reportexecution2010.asmx?wsdl /o:"ReportExecution.cs"
Go to C:\Program Files\Microsoft SDKs\Windows\v7.1 and pick up your ReportExecution.cs file. Copy and paste it in your project.
Then you just have to say this:
Microsoft.SqlServer.ReportExecution.ReportExecutionService rsExec =
new Microsoft.SqlServer.ReportExecution.ReportExecutionService();
rsExec.Credentials = System.Net.CredentialCache.DefaultCredentials;
rsExec.Url = #"http://<ServerName>/reportserver/ReportExecution2005.asmx";
Follow along the example from this link, which is the same one as above, and you should be generating some pretty awesome reports from your handy C# app.

Best way to run a tool from ASP.Net page

I have a developer tool that I want to run from an internal site. It scans source code of a project and stores the information in a DB. I want user to be able to go to the site, chose their project, and hit run.
I don't want the code to be uploaded to the site because the projects can be large. I want to be able to run my assembly locally on their machine. Is there an easy way to do this?
EDIT: I should note, for the time being, this needs to be accomplished in VS2005.
EDIT 2: I am looking for similar functionality to TrendMicro's Housecall. I want the scan to run locally, but the result to be displayed in the web page
You could use a ClickOnce project (winform/wpf) - essentially a regular client app, deployed via a web-server. At the client, it can do whatever it needs. VS2005/VS2008 have this (for winform/wpf) as "Publish" - and results in a ".application" file that is recognised by the browser (or at least, some browsers ;-p).
You might be able to do the same with Silverlight, but that has a stricter sandbox, etc. It would also need to ask the web-server to do all the db work on its behalf.
I want to be able to run my assembly
locally on their machine
Sounds like you want them to download the tool and run it from their local machine, does that work for you?
Any code can scan files given the location and permissions. For a website to open an exe on a different machine and permit that to run and get access to the files contained on the web server would require a horrifically low level of security that would mean the entire system is practically completely open to attack. If your system is completely behind a firewall and hence protected from outside intererance then you want to look more at the permissions and less at the code.
To run an exe on a machine try following notepad example, though you may have to use a specified directory as well
ProcessStartInfo psi = new ProcessStartInfo("notepad.exe");
psi.WindowStyle = ProcessWindowStyle.Hidden;
Process p = new Process();
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(ExitHandlerToKillProcess);
p.StartInfo = psi;
p.Start();
and when done dont forget to kill the Process. Alternately use javascript. Either way watch the security permissions and remember the risks of doing this.
I would probably write some sort of command line tool or service that does the processing and extraction of project data. Then I would use a page to update/register projects that the web server and the command line tool both have common access to. then at specified times either manually or via cron or similar mechanisms extract the data to your database. once you have this, you just use the website to display last extraction times and the extracted data.
if the projects/end users are on a different subnet etc, then you will need the end users to run the tool and then have it post the data into the database.

Categories

Resources