C# Crystal Reports Asks Connection Parameter or Crashes Randomly - c#

I have a C# WinForm application which opens a Report based on Crystal Reports.
It has a DataSet connection, filled by the application.
DataSetRicerca_produzione ds = new DataSetRicerca_produzione();
Ricerca_produzioneTableAdapters.Ricerca_ProduzioneTableAdapter ta = new Ricerca_produzioneTableAdapters.Ricerca_ProduzioneTableAdapter();
ta.Connection.ConnectionString = CnnStrBld.ConnectionString;
ta.Fill(ds.Ricerca_Produzione, Inizio, Fine, OrdAcc, Cicli, var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, IdLav.ToString(), Idris.ToString(), IdRep.ToString(), IdOp.ToString(), IdArt.ToString(), IdMst.ToString(), IdCliente.ToString(), IdCodRagg.ToString(), IdLotto.ToString(), IdCausale.ToString(), EffMin, EffMax, Sgn, Cnf);
...
ReportDocument rd = new ReportDocument();
rd.Load(RPT PATH FILE);
rd.SetDataSource(ds);
foreach (CrystalDecisions.Shared.IConnectionInfo c in rd.DataSourceConnections)
{
c.SetLogon(pCnnStrBld.UserID, pCnnStrBld.Password);
c.SetConnection(pCnnStrBld.DataSource, pCnnStrBld.InitialCatalog, pCnnStrBld.IntegratedSecurity);
}
...
//crViewer is a CrystalDecisions.Windows.Forms.CrystalReportViewer
crViewer.ReportSource = rd;
It works properly until, it seems, the application reaches at least 2 GB of ram or the process has more than 2000 USER Objects.
The more those 2 values (Ram and USER Objects) increase, the more often this behavior can happen and, when it starts doing this, lowering the Ram occupation or the USER Objects of the application doesn't solve the issue.
The application, build in Release|x64, has no problem but, if you try to open the report in the conditions above (Ram & USER Object), it starts to have a strange behavior.
It can randomly do one of those things:
Open the report
Ask for SQL connection parameters
CrysalReports asks login
Crash without any specific error (this are the only 2 found in Event Viewer)
EventViewer Error1
EventViewer Error2
I also noticed that in %Temp% Folder it can happen:
When the report works: Are created 4 files
CRTemp Files
When the reports dowsn't work (parameter requested or crashed): Are created 3 files instead of 4 (the ~cpe is missing)
I expect the Report to work every time.
I tried installing on the PC even the CRRuntime (x64 bit) or the CR for VS with no different result.
Has someone faced this problem? How can I solve this?
Ask me more details if needed
Thanks in advance

Related

Parameter value in Access report dialog

so i'm trying to automate some reporting in Access 2013. When I run a report I get a dialog asking for a parameter (Enter Plant:), something like this.
What I want is to run this code without asking the query for a plant name. The code works but if I run it, it pops a dialog asking for a Plant name, and if I type the Plant Name it runs and saves the pdf file just like I want to. The report in Access works by giving a different Plant name and it outputs a different report depending on the given Plant. My idea is to put this code on a loop and in each iteration pass a different plant name and save a different new file. But it always pop a dialog asks for a plant name to be added manually.
Microsoft.Office.Interop.Access.Application oAccess = null;
// Start a new instance of Access for Automation:
oAccess = new Microsoft.Office.Interop.Access.Application();
// Open a database in exclusive mode:
oAccess.OpenCurrentDatabase(
"route DB", //filepath
true //Exclusive
);
//This doesnt work
// oAccess.DoCmd.SetParameter("[Enter Plant:]", "Arlington");
oAccess.DoCmd.OpenReport(
"06 - Security Report - Plants",
AcView.acViewReport,
"qry Security Report - Plant",
//This doesnt work either, still asks me for a plant name
"[Enter Plant:] ='Arlington'",
AcWindowMode.acWindowNormal
);
//If I give the plant name to the dialog it works correctly en saves a pdf file wit the report
oAccess.DoCmd.OutputTo(
AcOutputObjectType.acOutputReport,
System.Reflection.Missing.Value,
"PDF Format (*.pdf)",
"route to save file",
false,
System.Reflection.Missing.Value,
System.Reflection.Missing.Value,
AcExportQuality.acExportQualityPrint
);
oAccess.Quit();
I can access the query but unfortunately i cant modify it, also its pretty long thats why I will not be able to show it(looks like it was created by a the Access Wizard, so its preeety long) though here its an example where the asked parameter is being used:
AND ((Signers.Location)=[Enter Plant:])
This parameters is in the query like 40+ times.
Any ideas? Thanks in advance!
I have faced this many times at work, and the solution I used most often is to have a TextBox in a form somewhere that can be referenced directly.
AND ((Signers.Location)=[Forms]![frmMyForm]![txtMyPlantTextBox])
NOTE: You would need to have the form open and data entered into the TextBox before you run the query.

Using Redemption RDOContactItem cannot save more than a number of contacts MAPI_E_TOO_BIG

found I'm trying to create contacts into a user's Mailbox programmatically (using Redemption), based on values from a database.
RDOContactItem rci = (RDOContactItem)session.GetDefaultFolder(rdoDefaultFolders.olFolderContacts).Folders["Contacts Subfolder"].Items.Add("IPM.Contact");
...
rci.Save();
As soon as I reach the limit 250, I get the error:
Error in IMsgStore::OpenEntry(Inbox or Root): MAPI_E_TOO_BIG
ulVersion: 0
Error: Your server administrator has limited the number of items you can open simultaneously. Try closing messages you have opened or removing attachments and images from unsent messages you are composing.
Component: Microsoft Exchange Information Store
Read Dmitry Streblechenko's comments on "This is an indication that you have too many open objects. Do you open each and every message in a folder?" suggestions on http://www.microsoft-questions.com/microsoft/Plaform-SDK-Mapi/32731171/mapietoobig.aspx and even tried his suggestion "Do you release all Exchange objects as soon as you are done with them?"
if (rci != null) Marshal.ReleaseComObject(rci);
even casting to IDisposable to able to dispose it, but it didn't work.
I haven't find a way to close a contact item after being saved.
Increasing the number of items that can be opened simultaneously on the server side is not a happy option either.
How to solve this?
You are using multiple dot notation (5 if I am counting correctly), and that causes the compiler to create implicit variables that you cannot explicitly release. Try the following. You can also try to call GC.Collect() every once in a while, but that would be a sledgehammer of a solution...
RDOFolder contacts = session.GetDefaultFolder(rdoDefaultFolders.olFolderContacts);
RDOFolders folders = contacts.Folders;
RDOFolder subfolder = folders["Contacts Subfolder"];
RDOItems items = subfolder.Items;
RDOMail msg = items.Add("IPM.Contact");
RDOContactItem rci = (RDOContactItem)msg;
...
rci.Save();
Marshal.ReleaseComObject(rci);
Marshal.ReleaseComObject(msg);
Marshal.ReleaseComObject(items);
Marshal.ReleaseComObject(subfolder);
Marshal.ReleaseComObject(folders);
Marshal.ReleaseComObject(contacts);

The process cannot access the file 'filename' because it is being used by another process

I am new in C# and I have a problem connecting to a Firebird database. I want my program to access a Firebird Database [FDB format file]. I have problem, see the code below:
File.Copy(pathway, new_pathway, true);
FbConnection addDetailsConnection = new FbConnection("User=sysdba;Password=masterkey;Dialect=3;Database= " + new_pathway +
";DataSource=localhost;" );
string SQLCOMMAND = " SELECT UOM FROM ST_ITEM_UOM WHERE CODE = 'ANT'";
addDetailsConnection.Open();
FbCommand readCommand = new FbCommand(SQLCOMMAND, addDetailsConnection);
FbDataReader myreader = readCommand.ExecuteReader();
while (myreader.Read())
{
MessageBox.Show(myreader[0].ToString());
}
myreader.Close();
readCommand.Dispose();
addDetailsConnection.Close();
addDetailsConnection.Dispose();
This code lets me read my FDB file and extract the data. When the code executes for the first time, there is no error or problem, However when when I execute it again, this error is shown:
The process cannot access the file 'C:\Users\ACC-0001.FDB' because it is being used by another process.
You can use Handle to check which program is locking the file. It might be caused by your code or by another process running on your machine.
The tool identifies the process, for example:
C:>handle.exe c:\test.xlsx
Handle v3.46 Copyright (C) 1997-2011 Mark Russinovich Sysinternals -
www.sysinternals.com
EXCEL.EXE pid: 3596 type: File 414: C:\test.xlsx
As found here.
If the problem lies within your code, make sure you dispose and close all connections, preferably by using them within using sections:
using (FbConnection addDetailsConnection = new FbConnection("..."))
{
// do work
}
More details on using using can be found here.
You might have bumped into this Firebird issue: FB server reports that DB file is used by another application on secondary attachment attempt through a symlink
It only happens on Windows and only when two non-embedded connections use different path names of which one or both have a symlink in their path so they effectively point to the same location.
Both handle.exe and Process Explorer will only show the canonical (final) filename that fbserver.exe actually opens.
The only way to find out is to:
compare connection strings.
verify with handle.exe or Process Explorer that the files are indeed opened by fbserver.exe (and not by your process itself using an embedded connection)

Adding cabinet file to msi programatically with DTF (wix)

Introduction to the Task at hand: can be skipped if impatient
The company I work for is not a software company, but focus on mechanical and thermodynamic engineering problems.
To help solve their system design challenges, they have developed a software for calculating the system impact of replacing individual components.
The software is quite old, written in FORTRAN and has evolved over a period of 30 years, which means that we cannot quickly re-write it or update it.
As you may imagine the way this software is installed has also evolved, but significantly slower than the rest of the system, meaning that packaging is done by a batch script that gathers files from different places, and puts them in a folder, which is then compiled into an iso, burned to a cd, and shipped with mail.
You young programmers (I am 30), may expect a program to load dll's, but otherwise be fairly self-contained after linking. Even if the code is made up of several classes, from different namespaces etc..
In FORTRAN 70 however.. Not so much. Which means that the software it self consists of an alarming number of calls to prebuilt modules (read: seperate programs)..
We need to be able to distribute via the internet, as any other modern company have been able to for a while. To do this we could just make the *.iso downloadable right?
Well, unfortunately no, the iso contains several files which are user specific.
As you may imagine with thousands of users, that would be thousands of isos, that are nearly identical.
Also we wan't to convert the old FORTRAN based installation software, into a real installation package, and all our other (and more modern) programs are C# programs packaged as MSI's..
But the compile time for a single msi with this old software on our server, is close to 10 seconds, so it is simply not an option for us to build the msi, when requested by the user. (if multiple users requests at the same time, the server won't be able to complete before requests timeout..)
Nor can we prebuild the user specific msi's and cache them, as we would run out of memory on the server.. (total at ~15 giga Byte per released version)
Task Description tl:dr;
Here is what I though I would do: (inspired by comments from Christopher Painter)
Create a base MSI, with dummy files instead of the the user specific files
Create cab file for each user, with the user specific files
At request time inject the userspecific cab file into a temporary copy of the base msi using the "_Stream" table.
Insert a reference into the Media table with a new 'DiskID' and a 'LastSequence' corresponding to the extra files, and the name of the injected cabfile.
Update the Filetable with the name of the user specific file in the new cab file, a new Sequence number (in the range of the new cab files sequence range), and the file size.
Question
My code fails to do the task just described. I can read from the msi just fine, but the cabinet file is never inserted.
Also:
If I open the msi with DIRECT mode, it corrupts the media table, and if I open it in TRANSACTION mode, it fails to change anything at all..
In direct mode the existing line in the Media table is replaced with:
DiskId: 1
LastSequence: -2145157118
Cabinet: "Name of action to invoke, either in the engine or the handler DLL."
What Am I doing wrong ?
Below I have provided the snippets involved with injecting the new cab file.
snippet 1
public string createCabinetFileForMSI(string workdir, List<string> filesToArchive)
{
//create temporary cabinet file at this path:
string GUID = Guid.NewGuid().ToString();
string cabFile = GUID + ".cab";
string cabFilePath = Path.Combine(workdir, cabFile);
//create a instance of Microsoft.Deployment.Compression.Cab.CabInfo
//which provides file-based operations on the cabinet file
CabInfo cab = new CabInfo(cabFilePath);
//create a list with files and add them to a cab file
//now an argument, but previously this was used as test:
//List<string> filesToArchive = new List<string>() { #"C:\file1", #"C:\file2" };
cab.PackFiles(workdir, filesToArchive, filesToArchive);
//we will ned the path for this file, when adding it to an msi..
return cabFile;
}
snippet 2
public int insertCabFileAsNewMediaInMSI(string cabFilePath, string pathToMSIFile, int numberOfFilesInCabinet = -1)
{
//open the MSI package for editing
pkg = new InstallPackage(pathToMSIFile, DatabaseOpenMode.Direct); //have also tried direct, while database was corrupted when writing.
return insertCabFileAsNewMediaInMSI(cabFilePath, numberOfFilesInCabinet);
}
snippet 3
public int insertCabFileAsNewMediaInMSI(string cabFilePath, int numberOfFilesInCabinet = -1)
{
if (pkg == null)
{
throw new Exception("Cannot insert cabinet file into non-existing MSI package. Please Supply a path to the MSI package");
}
int numberOfFilesToAdd = numberOfFilesInCabinet;
if (numberOfFilesInCabinet < 0)
{
CabInfo cab = new CabInfo(cabFilePath);
numberOfFilesToAdd = cab.GetFiles().Count;
}
//create a cab file record as a stream (embeddable into an MSI)
Record cabRec = new Record(1);
cabRec.SetStream(1, cabFilePath);
/*The Media table describes the set of disks that make up the source media for the installation.
we want to add one, after all the others
DiskId - Determines the sort order for the table. This number must be equal to or greater than 1,
for out new cab file, it must be > than the existing ones...
*/
//the baby SQL service in the MSI does not support "ORDER BY `` DESC" but does support order by..
IList<int> mediaIDs = pkg.ExecuteIntegerQuery("SELECT `DiskId` FROM `Media` ORDER BY `DiskId`");
int lastIndex = mediaIDs.Count - 1;
int DiskId = mediaIDs.ElementAt(lastIndex) + 1;
//wix name conventions of embedded cab files is "#cab" + DiskId + ".cab"
string mediaCabinet = "cab" + DiskId.ToString() + ".cab";
//The _Streams table lists embedded OLE data streams.
//This is a temporary table, created only when referenced by a SQL statement.
string query = "INSERT INTO `_Streams` (`Name`, `Data`) VALUES ('" + mediaCabinet + "', ?)";
pkg.Execute(query, cabRec);
Console.WriteLine(query);
/*LastSequence - File sequence number for the last file for this new media.
The numbers in the LastSequence column specify which of the files in the File table
are found on a particular source disk.
Each source disk contains all files with sequence numbers (as shown in the Sequence column of the File table)
less than or equal to the value in the LastSequence column, and greater than the LastSequence value of the previous disk
(or greater than 0, for the first entry in the Media table).
This number must be non-negative; the maximum limit is 32767 files.
/MSDN
*/
IList<int> sequences = pkg.ExecuteIntegerQuery("SELECT `LastSequence` FROM `Media` ORDER BY `LastSequence`");
lastIndex = sequences.Count - 1;
int LastSequence = sequences.ElementAt(lastIndex) + numberOfFilesToAdd;
query = "INSERT INTO `Media` (`DiskId`, `LastSequence`, `Cabinet`) VALUES (" + DiskId.ToString() + "," + LastSequence.ToString() + ",'#" + mediaCabinet + "')";
Console.WriteLine(query);
pkg.Execute(query);
return DiskId;
}
update: stupid me, forgot about "committing" in transaction mode - but now it does the same as in direct mode, so no real changes to the question.
I will answer this my self, since I just learned something about DIRECT mode that I didn't know before, and wan't to keep it here to allow for the eventual re-google..
Apparently we only succesfully updates the MSI, if we closed the database handle before the program eventually chrashed.
for the purpose of answering the question, this destructor should do it.
~className()
{
if (pkg != null)
{
try
{
pkg.Close();
}
catch (Exception ex)
{
//rollback not included as we edit directly?
//do nothing..
//atm. we just don't want to break anything if database was already closed, without dereferencing
}
}
}
after adding the correct close statement, the MSI grew in size
(and a media row was added to the media table :) )
I will post the entire class for solving this task, when its done and tested,
but I'll do it in the related question on SO.
the related question on SO

SQL query in Crystal Reports doesn't update

I'm writing an application which changes Crystal Reports database access parameters in report files. I open reports witin a .NET windows forms app and apply the SDK functionality to change driver type (ODBC/OLEDB), server name, database name, user, password, authentication type etc. I'm having a problem with the database name. My code DOES change the specific properties of the table ConnectionInfo (in subreports too) but fails to update the general SQL Query within the report. This results in the report still accessing the old database.
So if the original report was configured to access database_1 and I'm changing it to database_2, it will have all table properties properly changed to database_2 (verifiable in the Designer). It will still have database_1 in the query though. The database name remains unchanged in both the SDK RowsetController.GetSQLStatement() result and in the Crystal Reports Developer query view (Database->Show SQL Query...).
Also I have to have both databases (database_1 and database_2) online while the conversion takes place, otherwise I get exceptions on either GetSQLStatement(when database_1 is offline; becuase it still refers to it) or SetTableLocation (when database_2 is offline - this is expected and acceptable behavior though). If both db are online, there are no errors.
Here is exactly what I'm using:
1) CrystalDecisions.CrystalReports.Engine.ReportDocument.Load(filePath, OpenReportMethod.OpenReportByTempCopy)
(...)
2) Make and fill CrystalDecisions.ReportAppServer.DataDefModel.PropertyBag
3) Iterate through CrystalDecisions.ReportAppServer.DataDefModel.Tables and apply all properties with SetTableLocaiton() for each one.
4) Repeat with each subreport
5) RowsetController.GetSQLStatement() to view the report's sql query.
Is there some way to update the query basing on the new table ConnectionInfos (which seem to be set properly)? I don't even see any possibility of manually updating the query (GET, search&replace, SET).
I'm using:
.NET 4.5,
Visual Studio 2012,
CR for VS 13.0.5,
Crystal Reports Developer 9.2.2.693 for results verification (source reports are also created with it)
Answer: set propper QualifiedName for each table. The QualifiedName is the full name of the table including the DbName. This later appears in the SQL Query of the report. By qualified name we understand:
myDatabase.mySchema.myTableName
Code example:
CrystalDecisions.ReportAppServer.DataDefModel.Table boTable = new CrystalDecisions.ReportAppServer.DataDefModel.Table();
CrystalDecisions.ReportAppServer.DataDefModel.PropertyBag boMainPropertyBag = new CrystalDecisions.ReportAppServer.DataDefModel.PropertyBag();
CrystalDecisions.ReportAppServer.DataDefModel.PropertyBag boInnerPropertyBag = new CrystalDecisions.ReportAppServer.DataDefModel.PropertyBag();
// Custom function to fill property bags with values which influence the table properties as seen in CR Developer
FillPropertyBags(boMainPropertyBag, boInnerPropertyBag);
CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo boConnectionInfo = new CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo();
boConnectionInfo.Attributes = boMainPropertyBag;
boConnectionInfo.Kind = CrystalDecisions.ReportAppServer.DataDefModel.CrConnectionInfoKindEnum.crConnectionInfoKindCRQE;
boTable.ConnectionInfo = boConnectionInfo;
CrystalDecisions.ReportAppServer.DataDefModel.Tables boTables = boReportDocument.ReportClientDocument.DatabaseController.Database.Tables;
for (int i = 0; i < boTables.Count; i++)
{
boTable.Name = boTables[i].Name;
// the QualifiedName is directly taken into the CR general query so this is a quick fix to change it
boTable.QualifiedName = boTables[i].QualifiedName.Replace("oldDbName", "newDbName");
boTable.Alias = boTables[i].Alias;
boReportDocument.ReportClientDocument.DatabaseController.SetTableLocation(boTables[i], boTable);
}
Uhh...Researching for whole day and found answer after publishing question on SO.

Categories

Resources