COM Exception for client-side object called by Classic ASP page - asp-classic

I have tricky problem, which I'm struggling quite a bit with.
The current solution, consists of a Classic ASP site hosted on some Win2K3 server, that calls some Client-side DLL's on XP machines coded in VB6.
These client-side DLL's then again can call some other COM objects, in this particular case it calls IBM Louts Notes (Lotus Domino Objects 1.2).
Now for different reasons these DLL's has to be converted to .NET (still x86), at this stage, this is the only change to be done. This works quite well except for one piece of code which throws an error.
COMException when calling the Lotus Notes COM object
ASP script calling the DLL
Set objLotus = CreateObject("OpenLotusNotes_FU_v2.clsMain")
sRet = objLotus.OpenLotus_mail()
Client-side DLL
Dim session As NotesSession = New NotesSession() 'works well
Dim objNotesWrkSp As Object
objNotesWrkSp = Activator.CreateInstance(Type.GetTypeFromProgID("Notes.NotesUIWorkspace")) 'crashes
Exception
Retrieving the COM class factory for component with CLSID {29131502-2EED-1069-BF5D-
00DD011186B7} failed due to the following error: 80080005
Server execution failed (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE)).
When I try to run this code in console application on the same computer, it works. So it has to be some permissions(?), I have tried changing basically everything I can think of.
Any help would be much appreciated!
Updated 01.09.2014
What I see is when I trigger the code from ASP, is that it creates a new process of Notes everytime, but only in the background, no UI what so ever. When I trigger the code from a console application, I get the Notes UI, which asks me for password, if I don't already have notes running.
I believe that I'm getting the Exception because it eventually times out.

Have a read of this article , your dealing with an Out Of Process COM component which may not initialize properly for some reason.
Another possibility is that the COM threading model is not supported in a free threaded component which .NET is by default compiled with. You can compile you DLL with a STA attribute, but as far am I'm aware that only effects console applications. You might find some additional information in this article from MS, if you have not already read it of course. Hope something there helps you solve your problem.

Consult with your admins first, but for this scenario you can set no password for the ID the Lotus Notes uses.

I did resolve this, so if anyone else would run in to this…
First of all configure the “Notes Link” Component Service to run as a “The interactive user” and that the process owner has permissions in "Launch and Activation Permissions" and "Access permissions".
Then this should be possible
Dim objNotesWrkSp As Object
Dim objWorkspace As Type = Type.GetTypeFromProgID("Notes.NotesUIWorkspace")
objNotesWrkSp = Activator.CreateInstance(objWorkspace)
As it turned out in this particular case I could only get it to work with late-binding, when I tried to this it just opened a conhost.exe process and then never responed:
Dim session as New NotesSession
session.Initialize()
Among other similar issues... So then I only used late-binding for all communication with Notes.
Dim mailServerPath, mailFile As String
objWorkspace.InvokeMember("OpenDatabase", Reflection.BindingFlags.InvokeMethod, Nothing, objNotesWrkSp, New Object() {mailServerPath, mailFile})
And so on...

Related

EntityException: The underlying provider failed on Open. Can one server closing a db connection, make another server fail on opening?

I am experiencing database connection errors with an ASP.NET application written in VB, running on three IIS servers. The underlying database is MS Access, which is on a shared network device. It uses Entity Framework, code first implementation and JetEntityFrameworkProvider.
The application is running stable. But, approximately 1 out of 1000 attempts to open the database connection fails with either one of the following two errors:
06:33:50 DbContext "Failed to open connection at 2/12/2020 6:33:50 AM +00:00 with error:
Cannot open database ''. It may not be a database that your application recognizes, or the file may be corrupt.
Or
14:04:39 DbContext "Failed to open connection at 2/13/2020 2:04:39 PM +00:00 with error:
Could not use ''; file already in use.
One second later, with refreshing (F5), the error is gone and it works again.
Details about the environment and used code.
Connection String
<add name="DbContext" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=x:\thedatabase.mdb;Jet OLEDB:Database Password=xx;OLE DB Services=-4;" providerName="JetEntityFrameworkProvider" />
DbContext management
The application uses public property to access DbContext. DbContext is kept in the HttpContext.Current.Items collection for the lifetime of the request, and is disposed at it’s end.
Public Shared ReadOnly Property Instance() As DbContext
Get
SyncLock obj
If Not HttpContext.Current.Items.Contains("DbContext") Then
HttpContext.Current.Items.Item("DbContext") = New DbContext()
End If
Return HttpContext.Current.Items.Item("DbContext")
End SyncLock
End Get
End Property
BasePage inits and disposes the DbContext.
Protected Overrides Sub OnInit(e As EventArgs)
MyBase.OnInit(e)
DbContext = Data.DbContext.Instance
...
End Sub
Protected Overrides Sub OnUnload(e As EventArgs)
MyBase.OnUnload(e)
If DbContext IsNot Nothing Then DbContext.Dispose()
End Sub
What I have tried
Many of the questions on SO which address above error messages, deal with generally not being able to establish a connection to the database – they can’t connect at all. That’s different with this case. Connection works 99,99% of the time.
Besides that, I have checked:
Permissions: Full access is granted for share where .mdb (database) and .ldb (locking file) resides.
Network connection: there are no connection issues to the shared device; it’s a Gigabit LAN connection
Maximum number of 255 concurrent connections is not reached
Maximum size of database not exceeded (db has only 5 MB)
Changed the compile option from “Any CPU” to “x86” as suggested in this MS Dev-Net post
Quote: I was getting the same "Cannot open database ''" error, but completely randomly (it seemed). The MDB file was less than 1Mb, so no issue with a 2Gb limit as mentioned a lot with this error.
It worked 100% on 32 bit versions of windows, but I discovered that the issues were on 64 bit installations.
The app was being compiled as "Any CPU".
I changed the compile option from "Any CPU" to "x86" and the problem has disappeared.
Nothing helped so far.
To gather more information, I attached an Nlog logger to the DbContext which writes all database actions and queries to a log file.
Shared Log As Logger = LogManager.GetLogger("DbContext")
Me.Database.Log = Sub(s) Log.Debug(s)
Investigating the logs I figured out that when one of the above errors occured on one server, another one of the servers (3 in total) has closed the db connection at exactly the same time.
Here two examples which correspond to the above errors:
06:33:50 DbContext "Closed connection at 2/12/2020 6:33:50 AM +00:00
14:04:39 DbContext "Closed connection at 2/13/2020 2:04:39 PM +00:00
Assumption
When all connections of a DbContext have been closed, the according record is removed from the .ldb lock file. When a connection to the db is being opened, a record will be added to the lock file. When these two events occur at the exact same time, from two different servers, there is a write conflict to the .ldb lock file, which results in on of the errors from above.
Question
Can anyone confirm or prove this wrong? Has anyone experienced this behaviour? Maybe I am missing something else. I’d appreciate your input and experience on this.
If my assumption is true, a solution could be to use a helper class for accessing db, which catches and handles this error, waiting for a minimal time period and trying again.
But this feels kind of wrong. So I am also open to suggestions for a “proper” solution.
EDIT: The "proper" solution would be using a DBMS Server (as stated in the comments below). I'm aware of this. For now, I have to deal with this design mistake without being responsible for it. Also, I can't change it in the short run.
I write this as an aswer because of space but this is not really an answer.
It's for sure an OleDb provider issue.
I think that is a sharing issue.
You could do some tries:
use a newer OleDb provider instead of Microsoft.Jet.OLEDB.4.0. (if you have try 64 bits you could already have try another provider because Jet.OLEDB.4.0 is 32 bits only)
Implement a retry mechanism on the new DbContext()
Reading your tests this is probaly not your case. I THINK that Dispose does not always work properly on Jet.OLEDB.4.0 connections. I noted it on tests and I solved it using a different testing engine. Before giving up I used this piece of code
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
As you can understand reading this code, they are tries and the latest solution was changing the testing engine.
If your app is not too busy you could try to lock the db using a different mechanism (for example using a lock file). This is not really different from new DbContext() retries.
In late '90s I remember I had an issue related to disk sharing OS (I were using Novel Netware). Actually I have not experience in using mdb files on a network share. You could try to move the mdb on a folder shared with Windows
Actually I use Access databases only for tests. If you really need to use a single file database you could try other solutions: SQL Lite (you need a library, also this written by me, to apply code first https://www.nuget.org/packages/System.Data.SQLite.EF6.Migrations/ ) or SQL Server CE
Use a DBMS Server. This is for sure the best solution. As the writer of JetEntityFrameworkProvider I think that single file databases are great for single user apps (for this apps I suggest SQL Lite), for tests (I think that for tests JetEntityFrameworkProvider is great), for transfering data or, also, for readonly applications. In other cases use a DBMS Server. As you know, with EF, you can change from JetEntityFrameworkProvider to SQL Server or to MySql without effort.
You went wrong at the design stage: The MS Access database engine is unfit for ASP.Net sites, and this is explicitly stated on multiple places, e.g. the official download page under details.
The Access Database Engine 2016 Redistributable is not intended .... To be used by ... a program called from server-side web application such as ASP.NET
If you really have to work with an Access database, you can run a helper class that retries in case of common errors. But I don't recommend it.
The proper solution here is using a different RDBMS which exhibits stateless behavior. I recommend SQL Server Express, which has limitations, but if you exceed those you will be far beyond what Access supports, and wont cause errors like this.

Server.MapPath and running a command line utility from an ASP.NET application

I need to run an executable from an ASP.NET app using Process. The .exe file is located inside the ASP.NET project - 'ProjectRoot/Utilities/utility.exe'
Why does this code fail to run it:
string path = Server.MapPath("/Utilities/");
string args = " etc etc";
Process p = Process.Start(new ProcessStartInfo(path + "utility.exe", args));
p.WaitForExit(3000);
I've tried "\\utility.exe" too.
The answer depends on the type of the occuring exception. By the way, if the identity of application pool (which is the equal position to the process in a windows app) is set to a limited user (as it is by default) you can not execute a process on the server because of security issues.
If you know about the consequences and the server is your own, you can change the identity of the application pool to an authorized user, then your application can do such a thing without any hesitation.
Cheers
There's a few things to check here.
The first thing is to ensure that string path = Server.MapPath("/Utilities/"); is a valid path and that utility.exe is in the correct location.
You said that it does not throw an exception, so the invocation of utility.exe should at least be valid. However, utility.exe may swallow any exceptions it in turn encounters (depends on how it was coded), which may be why it does not appear to be working.
On top of that, your args variable may be specifying a file or some other resource that utility.exe accesses. If it does swallow exceptions, it could be masking a permissions error if the application pool identity does not have access to the resouce.

Utilize a 3rd Party API in VB.NET

I know this may be a simple answer or at least to most people but I'm not getting anywhere when attempting this on my own or through the various resources I have looked up. I'm pretty sure I'm just physically searching the wrong terminology or phrases to get my answers.
Here is my issue:
I am using ASP/VB.NET to build this integration. Visual Studio 2008. I previously had asked a question on API Integration on here and utilized that information to successfully perform the next steps. As this API is written differently I may not be grasping the appropriate functions correctly.
A company has given me several addresses for .ASMX portals. I add these into my service references. I can successfully see objects in my object browser and see all the procedures, etc., but further from here I'm unable to correctly use any objects. I attempted to replicate the integration based on my last API question and advice: Connecting to an API offered by a Company. When I'm attempting to use an object or simply relate a field to something on my script I continually get an error.
For example:
Dim A as New API.AddFunction
A.AccountNo = "123"
When running the page:
Object reference not set to an instance of an object.
Here is only one .ASMX I was given. I cannot release any more, until I receive permission to do so from the provider, I apologize.
http://cert1.tpayment.com:14935/Terminal.asmx
Anyhow, the root of my question is really how do I correctly interface with this API? What information do responders require so I can clarify this question more? Understandably you cannot guess at the problems until more information is shared. Is there a primer I can read through online?
The word you are looking for is XML web service or SOAP web service. This will help you with getting further with finding information on how to use such API. Specifically, your task is to "consume an XML web service using ASP.NET".
By having a quick look at the sample web service you provided it looks more than manageable. However, one thing you have to always remember that a web service operates on a "request - response" basis even though it is somewhat concealed when you add it as service reference to your project and it starts to look like just ordinary classes with methods and properties.
Here's a quick example to get you started. Let's assume you have a button "Button1" and a label "Label1" on your aspx page and that you added your example service as service reference with the namespace "Terminal". Here's a little server-side code for the click event of the button:
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
Dim SoapClient As New Terminal.TerminalSoapClient
Dim Credentials As New Terminal.OnBoardCredentials
Dim DataStructure As New Terminal.TerminalDataStructure
Dim ErrorMessages() As Terminal.OBAErrorMessage
Dim result As Boolean
Credentials.UserName = "username"
Credentials.PassWord = "password"
DataStructure.AccountNo = "123"
DataStructure.TerminalID = "123"
result = SoapClient.Add(Credentials, DataStructure, ErrorMessages)
Label1.Text = result.ToString & "; " & ErrorMessages(0).ErrorCode & " : " & ErrorMessages(0).ErrorMessage
End Sub
As you can see to start working with the web service you need a SOAP client. The client will expose the methods of the service such as "AddTerminal" in the example service.
Then you need to put together the arguments for that operation, which in the case of AddTerminal are "OnBoardCredentials", "terminal" (represented by the generic TerminalDataStructure class) and "ErrorMessage). Also note that this particular operation requires an array of ErrorMessages since the web service can return more than one in a single response.
Once you have specified the properties of each argument you are ready to call the operation (i.e. send the request) and use the returned value(s) (i.e. the response).
Technically, the response of this particular operation contains an overall result of the Add operation as boolean as well as error messages if any. However, only the overall result is returned by the TerminalSoapClient.Add operation whilst the error messages are added back to your array of OBAErrorMessage.
Lastly, for demo purposes I present the results back to label on the page. When I try it I get
False; 13000 : Error during Login
since I don't have valid credentials to log in to the service.
Though very crude I hope this example helps to get you started.

Return values from exe in javascript

i have to call an executable in the client machine in asp.net and get the return parameters, i been looking for an example but i couldn't find it.
it this possible to recover the output parameters from one exe in JavaScript?
i know that can i write:
var WshShell = new ActiveXObject("WScript.Shell");
var oExec = WshShell.Exec("My.exe");
but the clients executable returns 0 or 1 that values are the ones i need to collect
Thanks in advance
Browser-based JavaScript can't call executable files on client machines; to do so would be a catastrophic security problem. If you have to run an executable on the client machine, consider asking the user to install a .NET application, an ActiveX control, or something like Java if you want to be platform-independent.
Depending on what you're trying to do, you may not need to run an EXE on the client machine; you can do a LOT with standard cloud-type scenarios (JS or SilverLight on the client, Web services or WCF on the server). Without more information about your situation, however, it's impossible to tell.
EDIT: Based on your comments that you're using the ActiveXObject.Exec method, you can use the StdOut property of the WshScriptExec object that method returns. From MSDN's article on the StdOut property:
if (!oExec.StdOut.AtEndOfStream)
{
input += oExec.StdOut.Read(1);
//...
}

Problem with Unit testing of ASP.NET project (NullReferenceException when running the test)

I'm trying to create a bunch of MS visual studio unit tests for my n-tiered web app but for some reason I can't run those tests and I get the following error -
"Object reference not set to an
instance of an object"
What I'm trying to do is testing of my data access layer where I use LINQ data context class to execute a certain function and return a result,however during the debugging process I found out that all the tests fail as soon as they get to the LINQ data context class and it has something to do with the connection string but I cant figure out what is the problem.
The debugging of tests fails here(the second line):
public EICDataClassesDataContext() :
base(global::System.Configuration.ConfigurationManager.ConnectionStrings["EICDatabaseConnectionString"].ConnectionString, mappingSource)
{
OnCreated();
}
And my test is as follows:
TestMethod()]
public void OnGetCustomerIDTest()
{
FrontLineStaffDataAccess target = new FrontLineStaffDataAccess(); // TODO: Initialize to an appropriate value
string regNo = "jonh"; // TODO: Initialize to an appropriate value
int expected = 10; // TODO: Initialize to an appropriate value
int actual;
actual = target.OnGetCustomerID(regNo);
Assert.AreEqual(expected, actual);
}
The method which I call from DAL is:
public int OnGetCustomerID(string regNo)
{
using (LINQDataAccess.EICDataClassesDataContext dataContext = new LINQDataAccess.EICDataClassesDataContext())
{
IEnumerable<LINQDataAccess.GetCustomerIDResult> sProcCustomerIDResult = dataContext.GetCustomerID(regNo);
int customerID = sProcCustomerIDResult.First().CustomerID;
return customerID;
}
}
So basically everything fails after it reaches the 1st line of DA layer method and when it tries to instantiate the LINQ data access class...
I've spent around 10 hours trying to troubleshoot the problem but no result...I would really appreciate any help!
UPDATE:
Finally I've fixed this!!!!:) I dont know why but for some reasons in the app.config file the connection to my database was as follows:
AttachDbFilename=|DataDirectory|\EICDatabase.MDF
So what I did is I just changed the path and instead of |DataDirectory| I put the actual path where my MDF file sits,i.e
C:\Users\1\Documents\Visual Studio 2008\Projects\EICWebSystem\EICWebSystem\App_Data\EICDatabase.mdf
After I had done that it worked out!But still it's a bit not clear what was the problem...probably incorrect path to the database?My web.config of ASP.NET project contains the |DataDirectory|\EICDatabase.MDF path though..
Is LINQDataAccess.EICDataClassesDataContext looking to the web.config or some other outside source of data for its setup?
I can tell you for a fact that you must jump thru hoops to get web.config accessible to your test code.
Update
Ah, yes. I see that you're using ConfigurationManager on the line where your test fails... ConfigurationManager looks to web.config for configuration. This has been a sticking point for me when I write my tests.
You need to either change the code so that the class can be instantiated without web.config, or you need to make it so that your tests can access web.config.
Does your test project have it's own configuration file? This type of behavior usually means the app can't find the connection string. Test projects require their own file since they are not running in the context of the client app.
UPDATE The error you describe after adding an app.config is common when testing web applications built on SQLExpress and attaching an .mdf. SQLExpress cannot be run in more than one process at a time. So if you have previously run your web application it may still be active and will conflict with the attempt to attach the database.
You can use SQL Management Studio to attach the database permanently and then use a more traditional connection string like:
Server=myServer;Database=EICDatabase;Trusted_Connection=True;
For me it seems like your problem is the connection string, which is not set.
I assume your unit test is in a different project than the DAL.
You call the 'new' command on the datacontext constructor without a connection string. So it should usually use its default, when set. But since this setting normally is stored in the web.config of the other project there is no connection string set and you get the error.
If its right, what i assume, you have to get the settings from the DAL project into the unit-testing project. Simplest solution should be to copy web.config connection string to app.config of unit test project.
There are other possibilities for sure, but i think its not easy to get the web.config configuration into your unit-testing project.

Resources