C# Compact and repair .accdb files using DAO or ADOX - c#

As stated here I'm rebuilding tables from SQL Server to Access by using C#
Thanks to the help I received I could finish the process but since the .accdb files are pretty large, I need to compact and repair them afterwards.
For that, I used the marked answer from here. Strangely, there was only a reference for "Microsoft Office 16.0 Access Database Engine Object Library" that I could add to my project.
using Microsoft.Office.Interop.Access.Dao;
var engine = new DBEngine(); // Exception
var destFile = Path.GetFileNameWithoutExtension(filepath) + "_Compact" + ".accdb";
var destFilePath = Path.Combine(Path.GetDirectoryName(filepath), destFile);
engine.CompactDatabase(filepath, destFilePath);
At the Initialization of the DBEngine - Object, an exception is thrown:
Retrieving the COM class factory for component with CLSID {CD7791B9-43FD-42C5-AE42-8DD2811F0419} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
Also, is there a way to use ADOX for this task, since I'm already using it for creating my catalogs?

Unfortunately, JRO, ADO, nor ADOX can be used to Compact and Repair a Microsoft Access .accdb (Access 2007 and above) database file. However, you are on the right track by using the DBEngine object. One method you can use to avoid relying on the PIA would be to use late binding on the ACE DAO engine (which replaced the JET DAO engine for the older .mdb format).
This method requires no PIA's or Project References. But it does require that the ACE Engine be installed on the machine. The ACE is freely distributable and can be downloaded from Microsoft - Microsoft Access Database Engine 2010 Redistributable
using System;
// Use ACE DAO to Compact an Access .ACCDB file
// This method uses late binding to create the ACE DAO.DBEngine object
public bool CompactDatabaseACE(string SourceDb, string TempPath)
{
string Temp1Db, Temp2Db;
object[] oParams;
bool retVal = false;
Temp1Db = Path.Combine(TempPath, Path.GetFileNameWithoutExtension(SourceDb) + ".cmp");
Temp2Db = Path.Combine(Path.GetDirectoryName(SourceDb),Path.GetFileNameWithoutExtension(SourceDb) + ".old");
if (File.Exists(Temp1Db))
File.Delete(Temp1Db);
if (File.Exists(Temp2Db))
File.Delete(Temp2Db);
oParams = new object[]
{
SourceDb, Temp1Db
};
try
{
object DBE = Activator.CreateInstance(Type.GetTypeFromProgID("DAO.DBEngine.120"));
DBE.GetType().InvokeMember("CompactDatabase", System.Reflection.BindingFlags.InvokeMethod, null, DBE, oParams);
if (File.Exists(Temp1Db))
{
try
{
File.Move(SourceDb, Temp2Db);
}
catch { }
if (File.Exists(Temp2Db))
{
try
{
File.Move(Temp1Db, SourceDb);
}
catch { }
if (File.Exists(SourceDb))
{
retVal = true;
}
}
if (File.Exists(Temp1Db))
File.Delete(Temp1Db);
if (File.Exists(Temp2Db))
File.Delete(Temp2Db);
}
System.Runtime.InteropServices.Marshal.ReleaseComObject(DBE);
DBE = null;
}
catch (Exception ex)
{
// Do something with the error
}
return retVal;
}

Related

Why OleDbConnection.Open() throws Unrecognized database format if Ole.DB provider is available on the system

I tried next code on Windows x64, but the code is compiled and run as x86. The same behaviour is if I run the application in Windows 7 or 10 x86.
static IList<string> GetOleDbProviders()
{
OleDbEnumerator oleDbEnumerator = new OleDbEnumerator();
DataTable oleDbProviders = oleDbEnumerator.GetElements();
IDictionary<string, string> descriptions = new Dictionary<string, string>();
int typeColumnIndex = oleDbProviders.Columns.IndexOf("SOURCES_TYPE");
int nameColumnIndex = oleDbProviders.Columns.IndexOf("SOURCES_NAME");
int descriptionColumnIndex = oleDbProviders.Columns.IndexOf("SOURCES_DESCRIPTION");
foreach (DataRow dataRow in oleDbProviders.Rows)
{
int type = (int)dataRow[typeColumnIndex];
if (type == 1)
{
string name = (string)dataRow[nameColumnIndex];
string description = (string)dataRow[descriptionColumnIndex];
descriptions.Add(name, description);
}
}
IList<string> providers = new List<string>();
foreach (KeyValuePair<string, string> pair in descriptions)
{
providers.Add(pair.Value);
}
return providers;
}
static void Test5()
{
// has item 'Microsoft.Jet.Ole.DB.4.0'
IList<string> providers = GetOleDbProviders();
string connectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=.\my.accdb";
System.Data.Common.DbConnection connection = new OleDbConnection(connectionString);
try
{
// throws OleDbException on 32 bit with message 'Unregonized database format'
connection.Open();
}
catch (InvalidOperationException e)
{
// break point when running on 64-bit runtime
}
catch (OleDbException e)
{
// break point when running on 32-bit runtime
}
}
Why would connection.Open() throw an exception if the Jet.OleDb is provided by the system? Or does this mean that Jet.OleDb has support for other formats but not *.accdb.
Of course after installing Microsoft Access 2013 Runtime it works, but still? Wouldn't be more correct if Microsoft.Jet.Ole.Db.4.0 provider won't be returned from oleDbEnumerator.GetElements() and ProviderNotFound exception would be thrown?
EDIT
After installing Microsoft Access 2013 Runtime it still does not work. You have to use newer ACE provider instead of Jet. Connection string must be updated accordingly.
Or does this mean that Jet.OleDb has support for other formats but not
*.accdb.
Yes - the older versions of the driver will support the older mdb format.
As per the docs:
Starting with Access 2007, .accdb is the default Access file format.
Before the .accdb file format was introduced in Access 2007, Access
file formats used the .mdb file extension.
That link will give you more information about the differences between mdb and accdb if you are interested.
Wouldn't be more correct if Microsoft.Jet.Ole.Db.4.0 provider won't be
returned from oleDbEnumerator.GetElements() and ProviderNotFound
exception would be thrown?
No, it wouldn't be. The provider is there. The fact it doesn't support all versions of Access databases doesn't mean it doesn't exist. If it wasn't visible at all, people wouldn't be able to use the driver to access mdb files - as an example (breaking lots of old VB6 apps).

Program using LinqToExcel works on my computer but after publishing and installation on another one it does not

Published and installed program using LinqToExcel throws an "Exception has been thrown by the target of an invocation" exception. Inner exeption is not displayed but on my computer if I run exe file separately w/o another source files there is Could not load file or assembly linqtoexcel. but it is only on my PC when I tried to run it separately. But I beleave it is similar?
Both PCs are Win10, Access DB Engine 64bits or 64bits Offices are installed.
public void ImportNewData(String file)
{
ExcelConnector excel = new ExcelConnector(file);
foreach(var result in excel.ReadNewData())
{
this.loopsList.Add((Loop) result);
}
}
public IEnumerable ReadNewData() {
try
{
var query = from a in this.ExcelConnection.Worksheet < Loop > ("Data")
select a;
return query;
}
catch (Exception exeption)
{
MessageBox.Show(exeption.Message + "\n" + exeption.InnerException.Message + "\n" + exeption.InnerException.Source, "Warning");
return null;
}
}
I expect it will run on any Windows machine :)
I unchecked this check box and now it works also with 64bit Office.

How to read value of a registry key c#

At start up of my application I am trying to see if the user has a specific version of a software installed, specifically the MySQL connector, all using c#. In the registry, the MySQL contains a version entry. So what I am trying to accomplish is this.
My app starts up. Somewhere in the start up code I need to do the following things in order. Check to see if the user has the MySQL connector installed, which is located at...
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\MySQL AB\MySQL Connector/Net
If the user has the connector installed, I wanted to check what version they have, which is stored as Name = "Version" and Data = x.x.x (Picture below)
Now if the user has a specific version installed, then I will execute other code, which is where I can take from.
What would be the best way of going about this?
EDIT: Below is the code I currently have and I am getting an error on line 19 (It is commented). My error says "error CS1001: Identifier Expected" I wasnt able to figure out what that means. Any help?
using System;
using Microsoft.Win32;
using System.Data;
public class regTest
{
public static void Main()
{
try
{
RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Wow6432Node\\MySQL AB\\MySQL Connector\\Net");
if (key != null)
{
Object o = key.GetValue("Version");
if (o != null)
{
Version version = new Version(o as String); //"as" because it's REG_SZ...otherwise ToString() might be safe(r)
Version broken = new Version("6.7.4");
if (version.Equals.(broken)) //This is where the error is occuring
{
DataSet dataSet = ConfigurationManager.GetSection("system.data") as ystem.Data.DataSet;
DataView vi = dataSet.Tables[0].DefaultView;
vi.Sort = "Name";
if (vi.Find("MySql") == -1)
{
dataSet.Tables[0].Rows.Add("MySql"
, "MySql.Data.MySqlClient"
, "MySql.Data.MySqlClient"
,
typeof(MySql.Data.MySqlClient.MySqlClientFactory).AssemblyQualifiedName);
}
}
}
}
}
catch (Exception ex) //just for demonstration...it's always best to handle specific exceptions
{
//react appropriately
}
}
}
You need to first add using Microsoft.Win32; to your code page.
Then you can begin to use the Registry classes:
try
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Wow6432Node\\MySQL AB\\MySQL Connector\\Net"))
{
if (key != null)
{
Object o = key.GetValue("Version");
if (o != null)
{
Version version = new Version(o as String); //"as" because it's REG_SZ...otherwise ToString() might be safe(r)
//do what you like with version
}
}
}
}
catch (Exception ex) //just for demonstration...it's always best to handle specific exceptions
{
//react appropriately
}
BEWARE: unless you have administrator access, you are unlikely to be able to do much in LOCAL_MACHINE. Sometimes even reading values can be a suspect operation without admin rights.
#DonBoitnott have a good code, but require admin rights. I use this (only need Read Rights)
try
{
var subKey = "Software\\Wow6432Node\\MySQL AB\\MySQL Connector\\Net";
using (var key = Registry.LocalMachine.OpenSubKey(subKey, false)) // False is important!
{
var s = key?.GetValue("Version") as string;
if (!string.IsNullOrWhiteSpace(s))
{
var version = new Version(s);
}
}
}
catch (Exception ex) //just for demonstration...it's always best to handle specific exceptions
{
//react appropriately
}
Change:
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Wow6432Node\\MySQL AB\\MySQL Connector\\Net"))
To:
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\Wow6432Node\MySQL AB\MySQL Connector\Net"))

Connect to C# Program Data Source from Microsoft Access

I have a small C# console program that retrieves an item list from quickbooks, and I am trying to figure out how to expose that data to Microsoft Access. It is in XML format.
I want to retrieve the data in real-time, since it only takes about a second to get the data, whenever Access calls for it. I am using Access 2003 and VS 2010.
If there is a way to do this with VBA that would work fine as well. I can get the XML data using VBA already, but I don't know how to go from there.
Here is the code I use in C#:
public string DoQBQuery(XmlDocument doc)
{
bool sessionBegun = false;
bool connectionOpen = false;
RequestProcessor2 rp = null;
string ticket = "";
try
{
//Create the Request Processor object
rp = new RequestProcessor2();
//Connect to QuickBooks and begin a session
rp.OpenConnection2("", "QB Transaction Item Retriever", QBXMLRPConnectionType.localQBD);
connectionOpen = true;
ticket = rp.BeginSession("", QBFileMode.qbFileOpenDoNotCare);
sessionBegun = true;
//Send the request and get the response from QuickBooks
string responseStr = rp.ProcessRequest(ticket, doc.OuterXml);
//End the session and close the connection to QuickBooks
rp.EndSession(ticket);
sessionBegun = false;
rp.CloseConnection();
connectionOpen = false;
return responseStr;
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Error");
if (sessionBegun)
rp.EndSession(ticket);
if (connectionOpen)
rp.CloseConnection();
throw;
}
}
"I am trying to figure out how to expose that data to Microsoft Access. It is in XML format"
I've never tried this before, but I think this is possible by putting the c# classes that accomplish what you discussed in a dll and calling from Access. Here is a post from this site about that.
A Simple C# DLL - how do I call it from Excel, Access, VBA, VB6?
P.S. Given I've never tried this before, I would have put this in the comment section of your original post, but I'm new to this site and am unable to do that.

Trouble Saving Excel ony my computer using c#

I have a app that should create a excel spreadsheet and save it. The app works runs without any problems on other machines, including my bosses computer(he has the same ver of office and VS as me). I don't know if this is relevant but I have office 2013 and VS 2012.
Edit I'm trying to save as a .xls (exel 97').
SNIPIT
public static bool XLSaveAs(ref Excel._Workbook oWBTemplate, string FileName)
{
System.IO.FileInfo x = new System.IO.FileInfo(FileName);
if (x.Exists)
{
try
{
x.Delete();
}
catch
{
MyControls.MsgFunctions.WarningMsg("Make sure " + FileName + " is not open.");
return false;
}
}
try
{
if(FileName.Contains(".xlsx"))
oWBTemplate.SaveAs(FileName);
else
//Error occurs here
oWBTemplate.SaveAs(FileName, Excel.XlFileFormat.xlExcel8);
}
catch
{
MyControls.MsgFunctions.WarningMsg("Unable to save " + FileName);
return false;
}
return true;
}
If I take out the try catch statement this is the error I get.
ERROR
COMException as unhandled
Exception from HRESULT: 0x800A03EC
I found a solution that worked for me.
I had incorrect permissions to edit the folder that excel was writing too.
Adding full control to the folder resolved the issue.
Also the command oWBTemplate.SaveAsCopy() could help if you have a similar issue.

Categories

Resources