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

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.

Related

VB.net connection string works, then doesn't, then does - why?

Using VB.NET, VS 19 v16.10.4
I have a small application which asks a user to provide database server, database name and then builds a connection string. Then, using a data access layer DLL I've written it runs a check to see if a connection can be made to the database.
The problem is unusual:
If I write the connection string direct to the data access layer into a variable it connects;
If I use a string builder to build the string then pass it to the data access layer it fails;
If I write the connection string into a variable then pass it to the data access layer it fails;
If I use the first step again it works.
With DAL
MessageBox.Show("Using hard-coded string")
.ConnectionString = "data source=THEWINELIBRARY\MSSQLSERVER01;initial catalog=TrialDatabase;trusted_connection=true"
.DoConnectionCheck() 'THIS WORKS
MessageBox.Show("Using string builder string")
.ConnectionString = SBConnString.ToString.Trim
.DoConnectionCheck() 'THIS FAILS
MessageBox.Show("Using CS string")
.ConnectionString = CS.Trim
.DoConnectionCheck() 'THIS FAILS
MessageBox.Show("Using hard-coded string")
.ConnectionString = "data source=THEWINELIBRARY\MSSQLSERVER01;initial catalog=TrialDatabase;trusted_connection=true"
.DoConnectionCheck() 'THIS WORKS
End With
The data access layer does the following in the DoConnectionCheck method:
Public Sub DoConnectionCheck()
OpenConnection()
With AppConnection
If (.State = ConnectionState.Open) Then
RaiseEvent ConnectionOpened()
CloseConnection()
End If
End With
End Sub
and the OpenConnection method:
Private Sub OpenConnection()
With AppConnection
'Is the conenction currently closed?
If (.State = ConnectionState.Closed) Then
Try
'Set connection string to POS
.ConnectionString = ConnectionString
'Try opening the connection
.Open()
'Otherwise raise an event.
Catch E As Exception
RaiseEvent ConnectionFailed()
End Try
End If
End With
End Sub
And the connection string is a simple property:
Public ConnectionString As String
So, what I don't understand is why the connection fails with the string builder string and the variable string, but not with the hard-coded string?
If there is any other code you need please let me know, I posted what i think is enough to explain the problem without putting in too much that makes the question unreadable.
here is the code which constructs the connection string from entries in a form:
Dim CS As String = "datasource=" & .TextServer.Text.Trim & ";initial catalog=" & .TextDatabase.Text.Trim & ";trusted_connection=true"
SBConnString = New StringBuilder
With SBConnString
.Clear()
.Append("datasource=")
.Append(Me.TextServer.Text.Trim)
.Append(";initial catalog=")
.Append(Me.TextDatabase.Text.Trim)
.Append(";trusted_connection=true")
End With
When these strings are compared to the hard coded string
DAL.ConnectionString = "data source=THEWINELIBRARY\MSSQLSERVER01;initial catalog=TrialDatabase;trusted_connection=true"
the function returns a value 1, "The first substring follows the second substring in the sort order." according to the documentation.
As far as I can see, these strings are identical but somewhere an additional character must be getting inserted in the form textbox.
Again, any suggestions gratefully received.
All the best,
Dermot
Mea culpa - I had a very simple mistake which I could not see - in the two strings causing the problem I had datasource rather than date source...

OpenXML: Losing Custom Document Property After Editing Word Document

Using DocumentFormat.OpenXML, I am trying to add a custom property to a Word document and then later read the property. The following code "appears" to do just that:
Dim os As OpenSettings = New OpenSettings() With {
.AutoSave = False
}
Dim propVal As String = "Test Value"
Using doc As WordprocessingDocument = WordprocessingDocument.Open(filename, True, os)
Dim cPart As CustomFilePropertiesPart = doc.CustomFilePropertiesPart
If cPart Is Nothing Then
cPart = doc.AddCustomFilePropertiesPart
cPart.Properties = New DocumentFormat.OpenXml.CustomProperties.Properties()
End If
Dim cPart As CustomFilePropertiesPart = doc.CustomFilePropertiesPart
Dim cProps As Properties = cPart.Properties
For Each prop As CustomDocumentProperty In cProps
If prop.Name = "TranscriptID" Then
prop.Remove()
Exit For
End If
Next
Dim newProp As CustomDocumentProperty = New CustomDocumentProperty() With {
.Name = "TranscriptID"
}
newProp.VTBString = New VTBString(propVal)
newProp.FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
cProps.AppendChild(newProp)
Dim pid As Integer = 2
For Each item As CustomDocumentProperty In cProps
item.PropertyId = pid
pid += 1
Next
cProps.Save()
End Using
This code is modeled after code found here:
https://learn.microsoft.com/en-us/office/open-xml/how-to-set-a-custom-property-in-a-word-processing-document
It appears to work in this scenario:
Execute code from above.
Execute code from above again.
At #2 I expect to find the CustomFilePropertiesPart and the property value and my expectation is met.
The problem appears in this scenario:
Execute code from above.
Open document using Microsoft Word, save and close.
Execute code from above again.
What happens in this scenario is that the CustomFilePropertiesPart is missing, whereas it should be found. It is as if Microsoft Word does not successfully read this object, so when the document is save, the object is lost. This suggests to me that there is something that there is something wrong with my code. If you can see what it is, or if you have a comparable working example that I could compare it with, I would appreciate hearing from you. I feel like I correctly followed the Microsoft example, but obviously I did not and I am having trouble seeing where I departed. Thanks.
OK, I found this wonderful tool called the Office Productivity Tool. It has a code generation feature, so I was able to compare what I was doing with what Word does. Basically the problem was with setting the property value. This snippet does the trick:
Dim cProps As Properties = cPart.Properties
Dim val As DocumentFormat.OpenXml.VariantTypes.VTLPWSTR = New DocumentFormat.OpenXml.VariantTypes.VTLPWSTR
val.Text = tr.ID.ToString
Dim newProp As CustomDocumentProperty = New CustomDocumentProperty() With {
.Name = "TranscriptID",
.FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
}
newProp.Append(val)
cProps.AppendChild(newProp)

Fail to open connection #Error Rdlc Custom Code

m in a big trouble please help...I have Rdlc report the expression is using custom code which has Connection string but when i run the report it doesnt work :(
the connection is failed to open. this is so far i have done :
My custom Rdlc code here..
Function GradeCal(ByVal SubDivID As Integer, ByVal Perc As Decimal) As String
Dim oConn As New System.Data.SqlClient.SqlConnection
oConn.ConnectionString = "Data Source=*****;Initial Catalog=PSIDB; Persist Security Info=True; User ID=sa; Password=*******"
oConn.Open()
Dim oCmd As New System.Data.SqlClient.SqlCommand
oCmd.Connection = oConn
oCmd.CommandText = "SELECT * from dbo.[funcGradesCal](" + SubDivID + "," + Perc + ")"
Dim nRetVal As String = oCmd.ExecuteScalar()
oConn.Close()
If (nRetVal <> Nothing) Then
Return nRetVal
Else
Return "0-0"
End If
End Function
and here its my my expression...
=Code.GradeCal(1,42)
but it results #Error :(
i have added refernece in custom code..
System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Please give me any idea
Embedding connection string in custom code will result in deployment nightmares.
A better approach would be to create a report parameter with default value of "0-0", fetch data Dim nRetVal As String = oCmd.ExecuteScalar() at time of calling the report and assign the report parameter with the nRetVal.
Update
Move your function GradeCal() from the report to another class or module
Open your report in design view
Define a report parameter
Select View -> Report Data from the menu
Right click Parameters and click on Add Parameter...
Assign a name to your parameter: GradeCal4.
Optionally set a Default value for the parameter
Modify your report item from =Code.GradeCal(1,42) to =Parameters!GradeCal.Value or just drag and drop the parameter onto the report.
In your program call your function dim nRetVal as integer = GradeCal(1,42) to fetch data
Pass value to parameter report
Dim param As New ReportParameter("GradeCal",nRetVal)
ReportViewer1.LocalReport.SetParameters(param)

Set a report datasource instance at run time

I have created a report that is based on a business object - this works great. I am now trying to add a button that renders the report directly to PDF (in a winforms application).
I know what I need to do - in code I am creating a ReportViewer, setting the DataSource, specifying the report (it's an embedded resource), then rendering the report into a byte array before using System.IO.File.WriteAllBytes to flush the byte array to disk. One thing I'm hung up on though, is how do I specify the instance of the object properly? I keep getting the "An error has occurred during the report processing" error. In IntelliTrace I can see that an exception is thrown "A data source instance has not been supplied for the data source 'IssRep'" (IssRep is the dataset name in the report. Here is the code:
Dim warning As Warning() = Nothing
Dim streamids As String() = Nothing
Dim mimetype As String = Nothing
Dim encoding As String = Nothing
Dim extension As String = Nothing
Dim viewer As New ReportViewer
Dim bs As New BindingSource
bs.DataSource = issuedet
Dim rds As New ReportDataSource
rds.Value = bs
viewer.LocalReport.DataSources.Add(rds)
viewer.ProcessingMode = ProcessingMode.Local
viewer.LocalReport.ReportEmbeddedResource = "FRSFE.SR.rdlc"
Dim pdfbytes As Byte()
Try
pdfbytes = viewer.LocalReport.Render("PDF", Nothing, mimetype, encoding, extension, streamids, warning)
File.WriteAllBytes("C:\Shared\FRS\SR.PDF", pdfbytes)
Catch ex As Exception
MsgBox(ex.Message)
End Try
I'm pretty sure whatever I'm stuck on is pretty simple as I'm very rusty on .NET but I just can't figure it out!
Try setting rds.Name = "IssRep" before adding it to viewer.LocalReport.DataSources.

How to do Mailmerge in Openoffice using Vb.net

Its 5th Question and apart of one I didn't get response from the experts....
Hope this time I will get the helping hand.
I want to do mailmerge in openoffice using Vb.net and I am totally new with openoffice.
I searched on net for some help to understand how to use openoffice with vb.net but all I get is half info.....So can you please help me and give me code for mailmerge in vb.net for openoffice.
Well i have list of workers in DB and there is this facility that if they want to mail to all or some of the workers then they can do it.I have completed this task using Microsoft Office now as a Add in we are providing the facility to perform the same task using Open Office.
What they have to do is just select the List of workers and click on a button and it will automate the mailmerge using the field of those workers data from DB. The Code of mine is as shown below
Public Sub OpenOfficeMail(ByVal StrFilter As String)
Dim oSM ''Root object for accessing OpenOffice from VB
Dim oDesk, oDoc As Object ''First objects from the API
Dim arg(-1) ''Ignore it for the moment !
''Instanciate OOo : this line is mandatory with VB for OOo API
oSM = CreateObject("com.sun.star.ServiceManager")
''Create the first and most important service
oDesk = oSM.createInstance("com.sun.star.frame.Desktop")
''Create a new doc
oDoc = oDesk.loadComponentFromURL("private:factory/swriter", "_blank", 0, arg)
''Close the doc
oDoc.Close(True)
oDoc = Nothing
''Open an existing doc (pay attention to the syntax for first argument)
oDoc = oDesk.loadComponentFromURL("file:///C:\Users\Savan\Documents\1.odt", "_blank", 0, arg)
Dim t_OOo As Type
t_OOo = Type.GetTypeFromProgID("com.sun.star.ServiceManager")
Dim objServiceManager As New Object
objServiceManager = System.Activator.CreateInstance(t_OOo)
Dim oMailMerge As New Object
oMailMerge = t_OOo.InvokeMember("createInstance", Reflection.BindingFlags.InvokeMethod, Nothing, _
objServiceManager, New [Object]() {"com.sun.star.text.MailMerge"}) 'com.sun.star.text.MailMerge"})
oMailMerge.DocumentURL = "file:///C:\Users\Savan\Documents\1.odt"
oMailMerge.DataSourceName = CreateSource(StrFilter)''Function that will return the datasource name which will be a text file's path
oMailMerge.CommandType = 0
oMailMerge.Command = "file:///C:\Mail.txt"
oMailMerge.OutputType = 2
oMailMerge.execute(New [Object]() {})**---->I am getting Error here**
End Sub