Server.CreateObject + Server Context + COM :-) - c#

I am implementing a COM component using .net for a few outdated components for a server migration task.
The old component was used to receive an image path on the current server, convert it to a jpeg and save the converted image in the same path.
This is trivial to do in .net, however my main problem is obtaining the server context. I am not sure how to do either one of the following:
saving the image to the same path that the original image was retrieved from
or sending the image directly to the response stream (avoiding the need to save the converted image)
The component is being called within a class ASP page (yes, unfortunately!) using:
Server.CreateObject("Component.Class")
Is there anyway in the implementing .net code that I can obtain a reference to the Server object that created the component? I was hoping that if I could obtain a reference to the server, I could then obtain the appropriate context to write back to the response stream.

I have worked this out, obtaining a few clues for the internet and google. Thought I should post this in case anyone else has the same problem.
Obtaining the server context was a matter of including the following references:
COMSVCLib (should already be in your COM section when adding references to your project)
ASPTypeLibrary: Look for: "Microsoft Active Server Pages Object Library". If you don't see this immediately in your COM section, then you will need to add this from "windows programs and features". Add the following feature: Internet Information Services -> WWW Services -> ASP
Working out the above was the hardest thing :-) After that, its very simple to reference your server and other intrinsic ASP objects:
COMSVCSLib.AppServer aspServer = new COMSVCSLib.AppServer();
COMSVCSLib.ObjectContext oc = aspServer.GetObjectContext();
ASPTypeLibrary.Server server = (ASPTypeLibrary.Server)oc["Server"];
You can do the same with with other ASP objects such as Request and Response.

Related

C# Executing a query and getting data out of an Access Database (accdb) without using ACE.OLEDB

I'm writing a WPF application.
Trying to use the normal method of getting a connection returns an error similar to: "The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine."
ACE.OLEDB has never been installed on this machine so this error makes sense.
I'm trying to create this application in a way so that our users won't need to contact IT to have the application installed. Getting IT involved is a no go situation and the project will be abandoned.
Another team has an Access database (accdb) that I want my application to extract information (only read, no insert or update). I talked to the team and they won't convert this database back to an earlier version (mdb).
After my research I assume that installing ACE.OLEDB without using Admin privileges is impossible. Because of this and my application requirement of not requiring admin privileges I need to start looking for "Mutant"/Dirty solutions that don't involve ACE.OLEDB.
I tried using power-shell but I'm getting the same problems as I had with C# (requires IT to install ACE.OLEDB).
I have two potential solutions. One write a VBA script that opens up the database and dumps a query result into a file. My C# application would call this VB script and then parse the created file.
The second option is to create a new Access process using Process.Start(fullFilePath) and somehow pass the command to execute a query and somehow pass the results back to the executing application (either via a method return or first to a file).
How would you get the data out?
Is there a way for C# to duplicate the DB file and convert it from (accdb -> mdb)?
This is the second question I ask that is very similar.
C# Connecting to Access DB with no install
The difference between the two (to prevent this is a duplicate question) is that in the previous question I was looking for ways to install ACE.OLEDB without admin privileges while here I'm just looking for any other work around.
Found a workaround. It uses Microsoft.Office.Interop.Access found in NuGet.
var accApp = new Microsoft.Office.Interop.Access.Application();
accApp.OpenCurrentDatabase(#tests.DatabasePath);
Microsoft.Office.Interop.Access.Dao.Database cdb = accApp.CurrentDb();
Microsoft.Office.Interop.Access.Dao.Recordset rst =
cdb.OpenRecordset(
"SELECT * FROM Users",
Microsoft.Office.Interop.Access.Dao.RecordsetTypeEnum.dbOpenSnapshot);
while (!rst.EOF)
{
Console.WriteLine(rst.Fields["username"].Value);
rst.MoveNext();
}
rst.Close();
accApp.CloseCurrentDatabase();
accApp.Quit();

Writing Variables via QuickOPC in C# (Visual Studio)

my project is about writing an OPC UA Client, to read and write variables on a Siemens PLC OPC UA Server. I'm using Visual Studio 2017 Enterprise and installed the Quick OPC Toolkit from OPClabs to get started and try to connect. To program the client, I'm using Windows Forms and C#.
Connecting with the server and reading variables is working just fine, but writing them gives me a headache:
1.) Before I started programming on my own, I downloaded the OPC UA Sample Client from the OPC Foundation (if someone needs the download-link just ask, the download is hard to find). I connected to the server and could browse through the variables, but the write function was greyed out/not available.
2.) I started programming a very simple client, but also failed to write variables. Reading via Live Binding (http://opclabs.doc-that.com/files/onlinedocs/QuickOpc/2018.2/User%27s%20Guide%20and%20Reference-QuickOPC/webframe.html#Making%20a%20first%20OPC%20UA%20application%20using%20Live%20Binding.html) is working, also reading them by using easyUAClient.Read() works. I tried to write a variable with this code:
namespace ErsteOPCUAVerbindung{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var easyUAClient = new EasyUAClient();
easyUAClient.WriteValue("opc.tcp://OpcUaClient:password#192.168.216.1:4840/", "nsu=SinumerikVarProvider;ns=2;s=/NC/_N_NC_TEA_ACX/$MN_PLC_CYCLIC_TIMEOUT", 1);
}
}}
but I keep getting an exception:
OpcLabs.EasyOpc.UA.OperationModel.UAException: "An OPC-UA operation failure with error code -2144010240 (0x80350000) occurred, originating from 'OpcLabs.EasyOpcUA'. The inner OPC-UA service exception with service result 'BadAttributeIdInvalid' contains details about the problem."
{"OPC-UA service result - An error specific to OPC-UA service occurred.\r\n---- SERVICE RESULT ----\r\nStatusCode: {BadAttributeIdInvalid} = 0x80350000 (2150957056)\r\n"}
I have no idea what is causing this. I suspected, that maybe some kind of access restriction is the reason, but I can't find any hints about it in the documentations and besides I'm logged in as administrator anyway.
Has anyone an Idea? Thank you.
I have had one more look at your code, and the way you pass in the user name and password (in the URL itself) is definitely not correct. The way it is given now it is essentially ignored. It may or may not be the cause for the problem with the Write, but it definitely needs to be changed. The proper way of specifying the user name and password would be:
var client = new EasyUAClient();
var endpointDescriptor = new UAEndpointDescriptor("opc.tcp://192.168.216.1:4840/");
endpointDescriptor.UserIdentity = UserIdentity.CreateUserNameIdentity("OpcUaClient", "password");
client.WriteValue(endpointDescriptor, "nsu=SinumerikVarProvider;ns=2;s=/NC/_N_NC_TEA_ACX/$MN_PLC_CYCLIC_TIMEOUT", 1);
Update: I found a documentation, which explained, that the administrator does not have write rights by default and how you can change that. You need to call the methode GiveUserAccess and pass two Arguments, the Username and "SinuWriteAll" (the second one is kind of hidden). I'll try it now with C# and post my solution if it works.

How to Call a Web Service in C#

I searched many examples of how to consume webservices methods with C#,
but all of them say to right click and add a service reference and type the address.
However I dont know why, but the webserver wich I am trying to connect does not work with this...
Here the address
https://bauru.sigiss.com.br/bauru/ws/sigiss_ws.php?wsdl
Can someone help me how could I interact with this specific webservice?
Using the WcfTestClient1 shows an error (that is probably the one you're running in to on an import):
Error: Cannot import wsdl:portTypeDetail: The ' ' character, hexadecimal value 0x20, cannot be included in a name.Parameter name: nameXPath to Error Source: //wsdl:definitions[#targetNamespace='urn:sigiss_ws']/wsdl:portType[#name='WebService SigISSPortType']Error: Cannot import wsdl:bindingDetail: There was an error importing a wsdl:portType that the wsdl:binding is dependent on.XPath to wsdl:portType: //wsdl:definitions[#targetNamespace='urn:sigiss_ws']/wsdl:portType[#name='WebService SigISSPortType']XPath to Error Source: //wsdl:definitions[#targetNamespace='urn:sigiss_ws']/wsdl:binding[#name='WebService SigISSBinding']Error: Cannot import wsdl:portDetail: There was an error importing a wsdl:binding that the wsdl:port is dependent on.XPath to wsdl:binding: //wsdl:definitions[#targetNamespace='urn:sigiss_ws']/wsdl:binding[#name='WebService SigISSBinding']XPath to Error Source: //wsdl:definitions[#targetNamespace='urn:sigiss_ws']/wsdl:service[#name='WebService SigISS']/wsdl:port[#name='WebService SigISSPort']Warning: No code was generated.If you were trying to generate a client, this could be because the metadata documents did not contain any valid contracts or servicesor because all contracts/services were discovered to exist in /reference assemblies. Verify that you passed all the metadata documents to the tool.Warning: If you would like to generate data contracts from schemas make sure to use the /dataContractOnly option.
I don't know enough about the service, but you may take a look at Can I create an element with forward slash as part of the name and find it's probably an attribute being used to decorate a property (which has a space in it).
1 WcftestClient can usually be found in C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\
If for whatever reason you can't get to it (e.g. a proxy server is used where you're working) then simply go to the WSDL in your browser, save the page as an XML file on disk, and when adding a service reference again don't put in the web URL, put the path to it on disk (e.g. C:\temp\wsdl.xml).
Finally I got someone who had the exaclty same problem as I did! He said to do the following:
For Windows Vista, Windows 7 e Server 2008:
Start>Run
Regedit
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/SecurityProviders/Schannel/Protocols
Right Click on Protocols -> New -> Key
Name: TLS 1.0
Right Click on the new key -> New -> Key
Name: Client
Select the created folder (Client), right click New -> Value DWORD
Name: Enabled
After that just add the reference to your project. To avoid happening the same in your app users, force the SSL use before instanciate the webservice:
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3;

Append to an online text log file from my offline C# app

I'm looking for customer feedback regarding one or more of my apps (optional, and they would be notified), so I'd like to send some text to be appended to an online log file from my offline C# Winforms app.
If it makes a difference, the server is Linux based, and I don't mind if the public can access it too, so no need to worry about encryption or anything (no personal details or anything like that would be stored in them).
What would be the C# code required to do such a thing? (Pretend the website is: http://www.website.com/logfile.txt). Would I have to read the file wholesale, and write it back wholesale, or is there a more efficient 'append' operation I could use?
EDIT: Looks to be harder than I imagined. If I have to make a simple PHP script to help with this task, so be it, though code for that would be appreciated as well if that's the case.
You have a couple options.
First, you could check out a service like loggly which is an online log file. You would have a personal API key to post data to from your application.
If you don't want to do that, you could write your own API that has a simple Post with a string parameter. It would then be responsible for opening the file, adding the text and saving it. The Winform could just fire and forget, knowing that the API can handle it.
In C#, you could use either RestSharp or the HttpClient to send the data to the API.
I think the problem you're going to run into, is having the Winform app save the file. If it was a local file, you can just append to a file (without reading the whole thin into memory.) But on a remote machine, you'd first have to download the entire file. The next problem would be making sure that the Winform app can save the file. Without something like an API call, you could run into a lot of issues.
This may not be robust enough for your needs, but this my solution.
The easiest way I can think of doing this is to have your C# application send the text to a web script. Since you said you didn't care if the data was encrypted I thought why not just pass the text as a get parameter to a PHP script.
This example is very simplistic; you may want to add other checks to meet your needs:
The C# code would look like:
string loggerUrl = "http://www.YourDomainExample.com/Logger.php?text=";
string textToLog = WebUtility.UrlEncode("This text came from my C# desktop application");
HttpWebRequest myWebRequest = (HttpWebRequest)HttpWebRequest.Create(loggerUrl + textToLog);
HttpWebResponse myWebResponse = (HttpWebResponse)myWebRequest.GetResponse();
myWebResponse.Close();
The PHP script residing on your web server would look like:
<?php
$text = htmlspecialchars($_GET["text"]);
$log = "log.txt";
$fh = fopen($log, 'a') or die("can't open file"); // Open log in append mode
$textToWrite = "$text\n"; //Write each comment on a line
fwrite($fh, $textToWrite);
fclose($fh);
?>
By doing it this way, basically anything that can call the url can append text to your log. So your logger could be part of a desktop application, run on a mobile phone or a web application etc.
To test that your PHP script is working correctly, you can use your Web Browser as a client and just go to http://www.YourDomainExample.com/Logger.php?text=Test from webbrowser and check for log.txt on your web server
If you want to log into a remote destination, I see two solutions. Both are using log4net:
Solution 1:
You can set up log4net to log into a database. You can see here for the configuration.
Solution 2:
You can derive your logging class from AppenderSkeleton and configure the behaviour to log into anything you want.
internal class MyAppender : AppenderSkeleton
{
/// <summary>
/// Subclasses of <see cref="T:log4net.Appender.AppenderSkeleton"/> should implement this method
/// to perform actual logging.
/// </summary>
/// <param name="loggingEvent">The event to append.</param>
protected override void Append(LoggingEvent loggingEvent)
{
/* Here you can do whatever you want with your loggingEvent */
}
}

Change SSRS data source of report programmatically in server side

Today, for each customer, we deploy same SSRS reports folder and data source folder.
The difference between these folders are the name of each folder and the connection string of the data source.
We are using Report Server 2008 R2.
Is it possible to maintain only one reports and data source folder and change programmatically its connection string on server-side before the report been rendered?
If not, Is it something that can be achieved by changing some logic in reports?
Today we use "shared data source" option.
This is something we've done in our environment - we maintain one set of reports that can be deployed at any client with their own configuration.
You've got a couple of options here. Since you're using a Shared Data Source this makes things easier as you won't need to define a Data Source for each report.
1. Use the rs.exe utility and a script file
rs.exe at Books Online
This program allows you to create script files (in VB.NET) that can interact with a Report Server Web Service. You create a script file (e.g. Deploy.rss) and call the rs.exe program with various parameters, including any custom ones you define:
rs.exe -i DeployReports.rss -s http://server/ReportServer -v DatabaseInstance="SQL" -v DatabaseName="ReportDB" -v ReportFolder="ClientReports"
So this would call a script DeployReports.rss, connect to http://server/ReportServer, with three user defined parameters which could be used to create a data source and the report folder.
In the scipt file you could have something like this:
Public Sub Main()
rs.Credentials = System.Net.CredentialCache.DefaultCredentials
CreateFolder(reportFolder, "Report folder")
CreateFolder(datasourceFolder, "Data source folder")
CreateDataSource()
End Sub
Which can then make Web Service calls like:
rs.CreateFolder(folderName, "/", Nothing)
'Define the data source definition.
Dim definition As New DataSourceDefinition()
definition.CredentialRetrieval = CredentialRetrievalEnum.Integrated
definition.ConnectString = "data source=" + DatabaseInstance + ";initial catalog=" + DatabaseName
definition.Enabled = True
definition.EnabledSpecified = True
definition.Extension = "SQL"
definition.ImpersonateUser = False
definition.ImpersonateUserSpecified = True
'Use the default prompt string.
definition.Prompt = Nothing
definition.WindowsCredentials = False
Try
rs.CreateDataSource(datasource, datasourcePath, False, definition, Nothing)
Console.WriteLine("Data source {0} created successfully", datasource)
Catch e As Exception
Console.WriteLine(e.Message)
End Try
You haven't specified what version of Reporting Services you're using, so I'm assuming 2008. Please note that there are multiple endpoints that can be used, depending on SQL Server version. The 2005/2008 end point is deprecated in 2008R2 and above but is still usable. Just something to bear in mind when writing your script.
2. Call the SSRS Web Service through an application
Report Server Web Service overview
The same calls that are made from the script above can be made in any other application, too. So you'd just need to add a reference to a Report Server Web Service through WSDL and you can connect to a remote service and call its methods to deploy reports, data sources, etc.
So ultimately you're connecting to the Report Server Web Service, it's just the medium used that you need to think about.
Using a script is easier to get running as it's just running a program from the command line, but writing your own deployment application will certainly give greater flexibility. I would recommend getting the script going, so you understand the process, then migrate this to a bespoke application if required. Good luck!
You can use an Expression Based Connection String to select the correct database. You can base this on a parameter your application passes in, or the UserId global variable. I do believe you need to configure the unattended execution account for this to work.
Note: be careful about the security implications. Realize that if you would pass sensitive data (e.g. passwords) into a parameter, that (a) it will go over the wire, and (b) will be stored in the execution log tables for reporting services.

Categories