Reading values from an Excel File - vb.net

I want to get a value from 12 excel sheet. is there any way that i get the values without opening the excel sheet?
I am using vb.net. Please post an example code, if there is a way to read values without opening the excel file.
thanks

You can't read the values without opening the Excel file at all. But you may read the values without having to open Excel.
If the file is saved in the xml format it's going to be easier. If not, the easiest method is to still use Excel but use Office Automation to do it. The hard way is to create an excel file parser - quite hard on the non-open xml excel format (pre Office 2003) - hard but still possible.
However, it is quite impossible to read from an excel spreadsheet without opening the file at all..
Here's a snippet of code you could use to open a spreadsheet from VB.NET, by leveraging Office Automation (it still opens the file, an relies on Excel automation dlls, but doesn't require opening Excel):
DISCLAIMER
The following code is not intended to be used as is, but merely it is a sample to guide the reader to their own solution which should be thoroughly tested.
' The code below requires you to add references to Office Interop assemblies
' into your VB.NET project (if you don't know how to do that search Google)
xlApp = New Excel.ApplicationClass
xlWorkBook = xlApp.Workbooks.Open("<YOUR EXCEL SPREADSHEET FILE HERE")
xlWorkSheet = xlWorkBook.Worksheets("sheet1")
range = xlWorkSheet.UsedRange
For rCnt = 1 To range.Rows.Count
For cCnt = 1 To range.Columns.Count
Obj = CType(range.Cells(rCnt, cCnt), Excel.Range)
' Obj.value now contains the value in the cell..
Next
Next

You can use ADO.NET to read values from an Excel sheet. For more information on the connection string, see http://www.connectionstrings.com/excel-2007
<connectionStrings>
<add name="Default"
connectionString='Microsoft.ACE.OLEDB.12.0;Data Source=c:\your\folder\file.xlsx;Extended Properties="Excel 12.0 Xml;HDR=YES";'
providerName="System.Data.OleDb" />
</connectionStrings>
You can then use a standard System.Data.OleDb.OleDbConnection to read values from the data source. For example, consider an Excel file that has a sheet named Users, with two columns, UserName and Age.
using System.Data;
using System.Data.Common;
public int UserExists(string userName, int age)
{
var provider = ConfigurationManager.ConnectionStrings["Default"].ProviderName;
var factory = DbProviderFactories.GetFactory(provider);
var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
using (var connection = factory.CreateConnection())
{
connection.ConnectionString = connectionString;
using (DbCommand command = connection.CreateCommand())
{
DbParameter userNameParameter = factory.CreateParameter();
userNameParameter.ParameterName = "#UserName";
userNameParameter.DbType = DbType.String;
userNameParameter.Direction = ParameterDirection.Input;
userNameParameter.IsNullable = false;
userNameParameter.Value = userName;
DbParameter ageParameter = factory.CreateParameter();
ageParameter.ParameterName = "#Age";
ageParameter.DbType = DbType.Int32;
ageParameter.Direction = ParameterDirection.Input;
ageParameter.IsNullable = false;
ageParameter.Value = age;
command.CommandText = "SELECT COUNT(*) FROM [Users$] WHERE UserName=#UserName AND Age=#Age";
command.Parameters.Add(userNameParameter);
command.Parameters.Add(ageParameter);
connection.Open();
int usersExits = (int) command.ExecuteScalar();
return usersExits == 1;
}
}
}

I don't know of any way to get a value from an Excel spreadsheet without actually opening it but you can access the spreadsheet without having Office installed if that is the problem you are having. Have a look at using the Office primary interop assemblies (see here).

One way is to create an excel application object and set visible = false, then open the excel. I don't know if you are looking for something increase speed or just to avoid having the user see the open and close excel files. I've used this and it works. I'm thinking about using the ADO connections; I've used this with access and they work great, and excel can be used as a database; I just don't know what happens if some of these files don't have the database style array (fields on top, values going down)??

Related

Get address of cell copied from excel via clipboard

How to get the Address of the cells copied from excel via clipboard in Vb.net ?
In Vb6 exe by using
Clipboard.GetText(vbCFLink)
Will able to to get Output as
Excel|[excel path]Sheet1!R48C2:R57C3
How to get the Range of cell copied in vb.net.
In vb.net and vb6 existing methods.
VB6 VB.NET
vbCFBitmap System.Windows.Forms.DataFormats.Bitmap
vbCFDIB System.Windows.Forms.DataFormats.DIB
vbCFEMetafile System.Windows.Forms.DataFormats.EnhancedMetafile
vbCFFiles System.Windows.Forms.DataFormats.FileDrop
vbCFMetafile System.Windows.Forms.DataFormats.MetafilePict
vbCFPalette System.Windows.Forms.DataFormats.Palette
vbCFRTF System.Windows.Forms.DataFormats.Rtf
vbCFText System.Windows.Forms.DataFormats.Text
As per this link For vbCFLink - No equivalent in vb.net
I created a VB6 ocx/dll and used it to get the same result in VB.net appliation but this not feasible for production currently.
Is there any other method to get the Address range of the cells copied from excel via clipboard in Vb.net?
You can still do this with Clipboard.GetData(), you just have to read the stream yourself:
Using reader = New StreamReader(CType(Clipboard.GetData("Link"), Stream))
str = reader.ReadToEnd().Replace(vbNullChar, "|"c)
End Using
(Excuse my VB, it's auto translated from C# so may be a little funny.)
You can examine the current clipboard and all it's formats using this handy utility from Nirsoft, and use GetData() to retreive them.
And with edits suggested by #dr.null, you can create a function to replicate the GetText function:
Function GetTextFromClipboard(type As String) As String
If Not Clipboard.ContainsData(type) Then Return String.Empty
Dim stream = TryCast(Clipboard.GetData(type), Stream)
If stream Is Nothing Then Return String.Empty
Using reader = New StreamReader(stream)
Return reader.ReadToEnd().TrimEnd(ControlChars.NullChar).Replace(ControlChars.NullChar, "|"c)
End Using
End Function
And use it for excel links:
Var str = GetTextFromClipboard("Link")

VB.net insert .msg file into access database [duplicate]

I'm writing a VB application where I need to store an image in the database. The user selects the image on their computer, which gives me the path as a string. Here's my attempt at it, however I'm getting the error "An INSERT INTO query cannot contain a multi-valued field."
Here is my code:
Dim buff As Byte() = Nothing
Public Function ReadByteArrayFromFile(ByVal fileName As String) As Byte()
Dim fs As New FileStream(fileName, FileMode.Open, FileAccess.Read)
Dim br As New BinaryReader(fs)
Dim numBytes As Long = New FileInfo(fileName).Length
buff = br.ReadBytes(CInt(numBytes))
Return buff
End Function
Sub ....
Dim connImg As New OleDbConnection
Dim sConnString As String
Dim cmdImg As New OleDbCommand
sConnString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & My.Settings.DB & ";Persist Security Info=False;"
connImg = New OleDbConnection(sConnString)
connImg.Open()
cmdImg.Connection = connImg
cmdImg.CommandType = CommandType.Text
If d.slogo <> "" Then
cmdImg.CommandText = "INSERT INTO Logo ( refId, [type], [img] ) VALUES(#refId, #type, #imgBinary)"
cmdImg.Parameters.Add("#refId", OleDbType.Double).Value = refId
cmdImg.Parameters.Add("#type", OleDbType.Double).Value = 0
cmdImg.Parameters.Add("#imgBinary", OleDbType.VarBinary).Value = ReadByteArrayFromFile(PathToImage)
cmdImg.ExecuteNonQuery()
End If
....
End Sub
I've tried searching for other solutions online, but it seems everything I find is VB6 or VBA code. And I know people are going to argue that images should not be stored in the database, but in this case, it is my only option.
Thank-you for any help!
As you have discovered, you cannot use a SQL statement to insert files into an Attachment field in an Access database. You have to use the LoadFromFile() method of an ACE DAO Field2 object. The following C# code works for me. It is adapted from the Office Blog entry here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Access.Dao;
namespace daoConsoleApp
{
class Program
{
static void Main(string[] args)
{
// This code requires the following COM reference in your project:
//
// Microsoft Office 14.0 Access Database Engine Object Library
//
var dbe = new DBEngine();
Database db = dbe.OpenDatabase(#"C:\__tmp\testData.accdb");
try
{
Recordset rstMain = db.OpenRecordset(
"SELECT refId, img FROM Logo WHERE refId = 1",
RecordsetTypeEnum.dbOpenDynaset);
if (rstMain.EOF)
{
// record does not already exist in [Logo] table, so add it
rstMain.AddNew();
rstMain.Fields["refId"].Value = 1;
}
else
{
rstMain.Edit();
}
// retrieve Recordset2 object for (potentially multi-valued) [img] field
// of the current record in rstMain
Recordset2 rstAttach = rstMain.Fields["img"].Value;
rstAttach.AddNew();
Field2 fldAttach =
(Field2)rstAttach.Fields["FileData"];
fldAttach.LoadFromFile(#"C:\__tmp\testImage.jpg");
rstAttach.Update();
rstAttach.Close();
rstMain.Update();
rstMain.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
I did the same thing based off of the above code and the blog entry from here.
Here is my vb.net code which allows me to do an OpenFileDialog and multiple selections and the function will handle storing multiple files just fine. Though I did have to add a reference to my project for Microsoft Office 15.0 Access database engine Object Library to get it to work properly. I think you can go down to 12.0 or 14.0 as well.
Private Sub AddAttachment(ByVal Files() As String)
Const Caller = "AddAttachment"
Dim dbe = New Microsoft.Office.Interop.Access.Dao.DBEngine()
Dim db As Microsoft.Office.Interop.Access.Dao.Database
db = dbe.OpenDatabase(dbPath)
Try
Dim rstMain As Microsoft.Office.Interop.Access.Dao.Recordset
rstMain = db.OpenRecordset("SELECT ID, fieldName FROM tableName WHERE ID = " + (dt.Rows(currentRow).Item("ID").ToString), Microsoft.Office.Interop.Access.Dao.RecordsetTypeEnum.dbOpenDynaset)
If (rstMain.EOF) Then
rstMain.AddNew()
rstMain.Fields("ID").Value = 1
Else
For Each value As String In Files
rstMain.Edit()
Dim rstAttach As Microsoft.Office.Interop.Access.Dao.Recordset2
rstAttach = rstMain.Fields("ATTACHMENTS").Value
rstAttach.AddNew()
Dim fldAttach As Microsoft.Office.Interop.Access.Dao.Field2
fldAttach = rstAttach.Fields("FileData")
fldAttach.LoadFromFile(value)
rstAttach.Update()
rstAttach.Close()
Next
rstMain.Update()
rstMain.Close()
End If
Catch ex As Exception
If Err.Number <> 3820 Then
MsgBox(ex.Message)
Else
MsgBox("File of same name already attached", MsgBoxStyle.Critical, "Cannot attach file" & Caller)
MessageBox.Show(ex.Message)
End If
End Try
End Sub
I'm now working on the functions to RemoveAttachments and save files from the attachements field. I'll post those here later.
Another thing to add to this. If your database is encrypted then you will need to add to the OpenDatabase command.
This is the code I used in C# but the VB.NET code will be very similiar.
db = dbe.OpenDatabase(dbPath, false, false,"MS Access;PWD=password");
It took me ages to try and track this down on my own and I will try break down the various parts of the method. The MSDN Article for it can be found here.
1st Argument: dbPath, that's the same as the usage in the original post. The location and filename of the database you want to open.
2nd Argument: false. This is a true/false argument that if true opens the database in Exclusive-Mode. So that only this single program can use it. Most of the time this should be false. Only use exclusive mode if you have to.
3rd Argument: false. This is another true/false argument. This time if it's true then it opens the database in Read-only mode.This means you can only use recordsets to read information and you cannot use the Edit, Add or Delete methods. This can be true or false depending on your needs.
4th Argument: This sets specific properties in how to open the database. In this case. It says to set the Microsoft Access property 'PWD' to 'password'. In this property setting will tell method to open up an encrypted database using the password 'password'. Of course you will need to change "password" to whatever the database's actual password is to open but I'd been hunting this down for a while.
I hope this helps.

Update a table linked to a PowerPivot datamodel using EPPlus and it corrupts the datamodel

Using EPPlus I read an XLSX file.
I replace the data in a table and set the table range.
When I open the resulting spreadsheet I get an error:
"We found a problem with some content in 'MySpreadsheet.xlsx'. Do you want us to try to recover as much as we can?" -- I click Yes and I get another error:
"Excel was able to open the file by repairing or removing the unreadable content. Removed Part: Data store"
The error only happens after I add this table to a PowerPivot data model.
[EDIT] - I created a win forms app that reproduces this problem. You
can download it at here
I found the problem but don't know how
to fix it.
rename the xlsx to zip
Open the zip and browse to the xl\workbook.xml file
Look for the node collection.
Notice how EPPlus changes the <definedNames> collection to use absolute cell addresses.
Excel: <definedName name="_xlcn.LinkedTable_MyDate" hidden="1">MyDate[]</definedName>
EPPlus: <definedName name="_xlcn.LinkedTable_MyDate" hidden="1">'MyDate'!$A$2:$A$5</definedName>
If I modify this line after EPPlus is done saving then I can pull it
up in Excel without corrupting the Data Model.
I tried changing the WorkbookXml but it is happening when the
ExcelPackage.Save method runs.
For Each node In pck.Workbook.WorkbookXml.GetElementsByTagName("definedNames")(0).ChildNodes
node.innerText = "MyDate[]"
Next
Any ideas?
Try this first: create a spreadsheet with one table in it. Name the worksheet and table "DateList". Save it and run the below code on it -- it will work.
Then do this: open the same spreadsheet and add the DateList table to a pivottable data model. Save it and run the below code on it -- it will fail.
Here's some code from my MVC Controller -- only the relevant bits:
Public Class ScorecardProgressReportDatesVM
Public Property WeekRange As Date
End Class
Public Function GetScorecardProgressReport(id As Integer) As ActionResult
Dim contentType As String = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
Dim DateList As New List(Of ScorecardProgressReportDatesVM)
DateList.Add(New ScorecardProgressReportDatesVM With {.WeekRange = CDate("Jan 1, 2015")})
DateList.Add(New ScorecardProgressReportDatesVM With {.WeekRange = CDate("Jan 1, 2015")})
Dim templateFile As New IO.FileInfo("c:\test.xlsx")
Dim ms As New IO.MemoryStream
Using pck As New ExcelPackage(templateFile)
ExtendTable(pck, "DateList", DateList)
pck.SaveAs(ms)
ms.Position = 0
End Using
Dim fsr = New FileStreamResult(ms, contentType)
fsr.FileDownloadName = "StipProgress.xlsx"
Return fsr
End Function
Private Sub ExtendTable(package As ExcelPackage, tableName As String, newList As Object)
Dim ws As OfficeOpenXml.ExcelWorksheet
ws = package.Workbook.Worksheets(tableName)
Dim OutRange = ws.Cells("A1").LoadFromCollection(newList, True)
Dim t = ws.Tables(tableName)
Dim te = t.TableXml.DocumentElement
Dim newRange = String.Format("{0}:{1}", t.Address.Start.Address, OutRange.End.Address)
te.Attributes("ref").Value = newRange
te("autoFilter").Attributes("ref").Value = newRange
End Sub
In order to fix this we had to change the EPPlus v4.04 source code.
ExcelWorkbook.cs # line 975 was changed
//elem.InnerText = name.FullAddressAbsolute; This was causing issues with power pivot
to
elem.InnerText = name.FullAddress; //Changed to full address... so far everything is working

Remove sas content from Excel with VBA (MS Office Add-in)

I've figured out how to use the MS Office Add-in to run a SAS Stored Process from within Excel (using an input stream) to obtain outputs.
Everytime I run this, new columns are inserted to accommodate the new output. Also, additional content is added to the workbook.
My understanding of the SAS Add-in object model is poor, so I was hoping someone could help me:
1) Remove the existing content with VBA before running the process again; or
2) Write VBA code to refresh existing content only (within the same output range).
The code I'm using is:
Sub sasTests()
Application.ScreenUpdating = False
Dim sas As SASExcelAddIn
Set sas = Application.COMAddIns.Item("SAS.ExcelAddIn").Object
Dim inputStream As SASRanges
Set inputStream = New SASRanges
inputStream.Add "Prompts", Worksheets("sasInput").Range("sasInput")
sas.InsertStoredProcess "/Shared Data/C139/sasTests", _
Worksheets("sasOutput").Range("A1"), , , inputStream
End Sub
Many thanks!
PS. If anyone has any handy references for the SAS Add-in for MS Office, please provide links
You could clear the range containing the existing content with the .Clear method. If this range were in columns M:P, you could use the following line:
sheetNameHere.Range("M:P").Clear
If you added that line after setting
Application.ScreenUpdating = False
I believe this would imitate a "refresh" of the data.
This will remove all old components from the workbook. I am not sure how to refresh all but assuming its similar:
Sub DeleteSasContent()Dim sas As SASExcelAddIn
Set sas = Application.COMAddIns.Item("sas.exceladdin").Object
Dim stpList As SASStoredProcesses
Dim dataList As SASDataViews
Dim pivotList As SASPivotTables
Dim reportList As SASReports
Set stpList = sas.GetStoredProcesses(ThisWorkbook)
Set dataList = sas.GetDataViews(ThisWorkbook)
Set pivotList = sas.GetPivotTables(ThisWorkbook)
Set reportList = sas.GetReports(ThisWorkbook)
For i = 1 To stpList.Count
stpList.Item(i).Delete
Next i
For i = 1 To dataList.Count
dataList.Item(i).Delete
Next i
For i = 1 To pivotList.Count
pivotList.Item(i).Delete
Next i
For i = 1 To reportList.Count
reportList.Item(i).Delete
Next i
End Sub
See the SAS help here http://support.sas.com/kb/45/606.html

Programmatically save as PowerPoint 2007 (pptx), from PowerPoint 2003

I need to be able to save Presentations (programatically) in PowerPoint 2003 as OpenXML (".pptx").
I installed the Microsoft Office Compatibility Pack. This indeed allows me to perform "Save as PowerPoint 2007 Presentation" from PowerPoint 2003.
How can I do this programmatically? (e.g. VBA)
I tried Presentation.SaveAs:
While there is no inherent PpSaveAsFileType enum value in PowerPoint 2003 for ppSaveAsOpenXMLPresentation, I made a program which prints the PpSaveAsFileType values and found out that during run-time, ppSaveAsOpenXMLPresentation = 24.
However, I tried:
SaveAs(#"c:\temp\saveas\pupik.pptx", (PpSaveAsFileType) ((int) 24), MsoTriState.msoTrue);
And got an "Invalid Enumeration Value" Exception
Any ideas how to make this work?
(PS - I am aware that this question was already asked by several people on the web, but no solutions were offered.)
Thanks,
Arie
Edit > Some grammar
AFAIK the pptx format does not support macro-enabled presentations, so if your code is in the presentation you are trying to save, it will not work.
I don't have Excel 2003 at hand now, but if the Compatibility Pack enabled the option "pptx" in the Configuration Dialog, Default Save Format, and you are trying to save ANOTHER presentation I guess you can if you use something like:
MyOtherPresentation.SaveAs "C:\Mypres", ppSaveAsDefault
Please note that this may work only if the presentation had not been saved before in ppt format
EDIT
If the above doesn't work, you could try a different approach. Save the file in the old format and the call a conversion program:
ppcnvcom.exe
See here for an example (using wordconv.exe, but essentially the same)
Be sure to have all the office upgrades installed, because if not the program ends
reporting no error and doing nothing.
ofc
See here for instructions
And here for a good discussion
HTH!
A trick is to modify the default save format of the application in the Registry, then save and finally to restore the original save format again.
The relevant key is
Software\Microsoft\Office\11.0\PowerPoint\Options
Create a DWORD value with name DefaultFormat and set it to 0x21 to save as PPTX.
public void SomeMethod()
{
...
using (PptxSaver pptxSaver = new PptxSaver())
{
presentation.SaveAs("sample.pptx")
}
...
}
class PptxSaver : IDisposable
{
private const string OptionKey = #"Software\Microsoft\Office\11.0\PowerPoint\Options";
private const string OptionValue = "DefaultFormat";
private const int SaveFormatPptx = 0x21;
private int oldFormat;
public PptxSaver()
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(OptionKey, true))
{
oldFormat = (int)key.GetValue(OptionValue, -1);
key.SetValue(OptionValue, SaveFormatPptx, RegistryValueKind.DWord);
}
}
public void Dispose()
{
// Delete the value
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(OptionKey, true))
{
if (oldFormat == -1)
{
key.DeleteValue(OptionValue);
}
else
{
key.SetValue(OptionValue, oldFormat);
}
}
}
}
I used ppcnvcom.exe but note that unlike a considerable amount of posts I used only the -oice switch without the -nme switch
For VBA this works:
Sub TestSaveas()
SaveAs "c:\somefilepath\"
End sub
Private Sub SaveAs(fp As String)
Dim dlgSaveAs As FileDialog
Dim strMyFile As String
Set dlgSaveAs = Application.FileDialog(msoFileDialogSaveAs)
With dlgSaveAs
.InitialFileName = fp
If .Show = -1 Then
strMyFile = .SelectedItems(1)
Application.ActivePresentation.SaveAs strMyFile
'MsgBox strMyFile
''-- save your file to strMyFile here
Else
MsgBox "File not saved"
End If
End With
dlgSaveAs.Execute
Set dlgSaveAs = Nothing
End Sub
I know this is an old question, but I got around the problem recently using:
Presentation.SaveCopyAs "c:\temp\saveas\pupik.pptx"
instead of SaveAs. Works well, regardless wether the original is in ppt- or pptx-format.
(I could not get the registry-change method mentioned to work for me without reopening the presentation.)