EPPlus: Cannot open file when another person is blocking - excel

I use the following code to open an excel-file:
Private EPXlApp As OfficeOpenXml.ExcelPackage
Private EPXlFile As FileInfo
Private EPXlSheet As ExcelWorksheet
Private EPXlWorkbook As ExcelWorkbook
Public Sub New(newFilePath As String, Optional password As String = "")
PathFile = newFilePath
OfficeOpenXml.ExcelPackage.LicenseContext = LicenseContext.NonCommercial
EPXlFile = New FileInfo(PathFile)
EPXlApp = New OfficeOpenXml.ExcelPackage(EPXlFile, password) ' ---> here the error occurs
EPXlWorkbook = EPXlApp.Workbook
EPXlSheet = EPXlApp.Workbook.Worksheets(1)
End sub
This works. But if one person block the file, i can't open it again. I don't understand the error message
System.IO.InvalidDataException: "File S:\Team\file.xlsm is not an encrypted package"
because thats the rigt password, it has already worked if no one blocked the excel file.
Thank you very much in advance!

Related

How do i save a pdf file to a desktop directory using vb.net?

I am creating a PDF form in vb.net and saving it to my desktop. The user inputs a name into the textbox and when they click the button the pdf file is created and saved to my desktop.
The issue I am having is that the pdf is being saved like this:
Desktop0001report.pdf
I do not understand why "Desktop" is showing in the front. Is there a way to fix this?
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
Dim pdfdoc As New Document(PageSize.A4, 15, 15, 30, 30)
Dim filename As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + TextBox1.Text + " report.pdf"
Dim file As New FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)
PdfWriter.GetInstance(pdfdoc, file)
pdfdoc.Open()
FillPDF(pdfdoc)
pdfdoc.Close()
Process.Start(filename)
Catch ex As Exception
MessageBox.Show("PDF could not be generated!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End Try
End Sub
End Class
Use System.IO.Path.Combine to assemble paths with directory separators included.
Dim filename = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
TextBox1.Text & " report.pdf")
The function takes a ParamArray argument, so you can provide an arbitrary number of strings to be combined into a single path.
I always try to use methods from System.IO.Path when I'm manipulating file paths. There aren't methods for absolutely everything I've needed to do, but most of the common operations are covered.
Change this line to (note the added slash):
Dim filename As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\" + TextBox1.Text + " report.pdf"

filestream - open excel file read only fails

I read an excel file (.xlsm; password-protected) with a vb.net application. I use the following interfaces:
System.IO.filestream
Syncfusion.XlsIO (NuGet)
That works, but I want to open it in ReadOnly-Mode, so that any other people can use this file with MS Office (write, save, ...).
Code:
Imports Syncfusion.XlsIO
Private SyncEE As Syncfusion.XlsIO.ExcelEngine
Private SyncWB As Syncfusion.XlsIO.IWorkbook
Private SyncFS As System.IO.FileStream
Public Sub new
SyncEE = New ExcelEngine
SyncFS = New FileStream(PathFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
SyncEE.Excel.DefaultVersion = ExcelVersion.Excel2016
SyncWB = SyncEE.Excel.Workbooks.Open(SyncFS, ExcelParseOptions.Default, openReadOnly, password)
SyncWB.Unprotect(password)
SyncWB = SyncEE.Excel.Workbooks.Open(SyncFS, ExcelOpenType.Automatic)
' read....
'discard
SyncWB.Close()
SyncWB = Nothing
SyncEE.Dispose()
SyncEE = Nothing
SyncFS.Dispose()
SyncFS = Nothing
End Sub
I tried it step by step and found out that i block the file at row:
SyncFS = New FileStream(PathFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
and I release the file at the 'discard' rows.
Blocking means that no one can edit and save the excel-file manually in MS Excel while I read it.
Greetings from Syncfusion.
You are blocking the file on loading the file using FileStream, as mentioned by you and you are discarding the FileStream object only at the end of your entire program. You can discard the FileStream object immediately after loading it into a workbook object so that others can use the file with MS Office (write, save, …) while you are reading it. Hence, we have modified your code as below to achieve your requirement.
Code snippet:
Private Sub new
SyncEE = New ExcelEngine
SyncFS = New FileStream(PathFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
SyncEE.Excel.DefaultVersion = ExcelVersion.Excel2016
SyncWB = SyncEE.Excel.Workbooks.Open(SyncFS, ExcelParseOptions.Default, openReadOnly, password)
'Discard FileStream
SyncFS.Dispose()
SyncFS = Nothing
' read....
'discard
SyncWB.Close()
SyncWB = Nothing
SyncEE.Dispose()
SyncEE = Nothing
End Sub
Note: I work for Syncfusion.
Regards,
Shamini

IIS - VBScript to create HTTPS binding - is my way correct?

I would like to automatize the process of SSL certificate installation for IIS 7.5. The preferred way is to use VBScript. I work on the problem to create a new HTTPS binding and to bind correct certificate to this binding.
I actually solved this problem activating Add IIS Management Scripts and Tools role for my web-server and using script like this:
Set serverWebAdmin = GetObject("winmgmts:root\WebAdministration")
' EC8BCFF70983EA26BFEA087683329CB8C07366A5 is an certificate hash of the fake certificate
' that i obtain from the staging environment of Let's Encrypt
' "MY" is the name of certificate storage
serverWebAdmin.Get("SSLBinding").Create "*", 443,"EC8BCFF70983EA26BFEA087683329CB8C07366A5", "MY"
Set newBinding = serverWebAdmin.Get("BindingElement").SpawnInstance_
newBinding.BindingInformation = "*:443:"
newBinding.Protocol = "https"
Set issuedWebSite = serverWebAdmin.Get("Site.Name='sitename.com'")
webSiteBindings = issuedWebSite.Bindings
ReDim Preserve webSiteBindings(UBound(webSiteBindings) + 1)
Set webSiteBindings(UBound(webSiteBindings)) = newBinding
issuedWebSite.Bindings = webSiteBindings
Set pathResult = issuedWebSite.Put_
It works well but before to use WMI to manage the server i tried to use (and expand a little) an example from MSDN how to create a binding. I took the example on VBScript and added the declaration of certificate hash and certificate storage name (i checked also these properties, they are existing so seems to be possible to set them. I also checked the code of some open-source projects like WinAcme - written in C# - and they use the same properties).
So my code was looking like this (the part that sets properties of binding):
Set bindingElement1 = bindingsCollection.CreateNewElement("binding")
bindingElement1.Properties.Item("protocol").Value = "https"
bindingElement1.Properties.Item("bindingInformation").Value = "*:443:"
bindingElement1.Properties.Item("certificateHash").Value = "EC8BCFF70983EA26BFEA087683329CB8C07366A5"
bindingElement1.Properties.Item("certificateStoreName").Value = "MY"
bindingsCollection.AddElement(bindingElement1)
adminManager.CommitChanges()
It works BUT it only creates the binding and DOES NOT append good certificate to this binding. My problem is solved by the previous code snippet but I would like to understand: is it the second code snippent wrong? Is it possible to bind good certificate this way?
Thank you by advance.
The reason it did not work with your bindingElement1 variant is simply because you can't add it to the bindingCollection, instead you have to add it to a method:
First part which you already had:
Dim bindingElement1 As ConfigurationElement = bindingsCollection.CreateElement("binding")
bindingElement1("protocol") = "https"
bindingElement1("bindingInformation") = "192.168.1.1:443:contoso.com"
bindingsCollection.Add(bindingElement1)
After that simply add:
Dim method = bindingElement1.Methods.Item("AddSslCertificate").CreateInstance()
method.Input.Attributes.Item("certificateHash").Value = "EC8BCFF70983EA26BFEA087683329CB8C07366A5"
method.Input.Attributes.Item("certificateStoreName").Value = "MY"
method.Execute()
Commit changes:
serverManager.CommitChanges()
So in total with some error-catching it could look like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim serverManager As ServerManager = New ServerManager
Dim config As Configuration = serverManager.GetApplicationHostConfiguration
Dim sitesSection As ConfigurationSection = config.GetSection("system.applicationHost/sites")
Dim sitesCollection As ConfigurationElementCollection = sitesSection.GetCollection
Dim siteElement As ConfigurationElement = FindElement(sitesCollection, "site", "name", "contoso")
If (siteElement Is Nothing) Then
MsgBox("Element not found!")
End If
Dim bindingsCollection As ConfigurationElementCollection = siteElement.GetCollection("bindings")
Dim bindingElement1 As ConfigurationElement = bindingsCollection.CreateElement("binding")
bindingElement1("protocol") = "https"
bindingElement1("bindingInformation") = "192.168.1.1:443:contoso.com"
Try
bindingsCollection.Add(bindingElement1)
Catch ex As Exception : MsgBox(ex.Message) : End Try
Dim method = bindingElement1.Methods.Item("AddSslCertificate").CreateInstance()
method.Input.Attributes.Item("certificateHash").Value = "EC8BCFF70983EA26BFEA087683329CB8C07366A5"
method.Input.Attributes.Item("certificateStoreName").Value = "MY"
Try
method.Execute()
Catch ex As Exception : MsgBox(ex.Message) : End Try
serverManager.CommitChanges()
End Sub
Private Function FindElement(ByVal collection As ConfigurationElementCollection, ByVal elementTagName As String, ByVal ParamArray keyValues() As String) As ConfigurationElement
For Each element As ConfigurationElement In collection
If String.Equals(element.ElementTagName, elementTagName, StringComparison.OrdinalIgnoreCase) Then
Dim matches As Boolean = True
Dim i As Integer
For i = 0 To keyValues.Length - 1 Step 2
Dim o As Object = element.GetAttributeValue(keyValues(i))
Dim value As String = Nothing
If (Not (o) Is Nothing) Then
value = o.ToString
End If
If Not String.Equals(value, keyValues((i + 1)), StringComparison.OrdinalIgnoreCase) Then
matches = False
Exit For
End If
Next
If matches Then
Return element
End If
End If
Next
Return Nothing
End Function

How to intercept and replace Quartz.NET's default logging at runtime?

In order to eliminate human error and avoid the tedium of maintaining who-knows-how-many App.config files, I've decided to set all of my Quartz.NET jobs' Log4Net configurations at runtime, completely in code. No App.config files allowed. I've been successful in getting a standalone console app to do this, as demonstrated here.
However, it's a different story in the scheduler itself. For some reason it's not using the configuration that I'm specifying:
Dim oProperties As New NameValueCollection
' ... Set Quartz.NET properties here
BasicConfigurator.Configure(New Logger.DbAppender)
With New StdSchedulerFactory(oProperties)
Manager.Scheduler = .GetScheduler
End With
I'm running it as a TopShelf service and I'd like the Scheduler's logs to go to the same destination as each of the IJobs. Here, Quartz.NET stubbornly continues to send its output to TopShelf's debugging console window (STDOUT).
This construct works in the console app (linked above); why isn't it working here?
OK, got it. (That didn't take long!)
For some reason, the TopShelf/Quartz.NET combo insists on suppressing logging output if the requisite App.config entries are missing.
In this case, since it's the central scheduler we're dealing with and not a plethora of jobs, I'll bite the bullet and go ahead and use an App.config file for this one.
So the root problem remains unsolved; this is just a workaround for the time being. If you know the answer I'd still be pleased to hear from you. Thanks.
OK, got it. (For REAL this time.)
Clarification: the suppression of logging in the absence of a .config file occurs in Common.Logging when run under the TopShelf/Quartz.NET combination. For some reason it consistently refuses to recognize a runtime-loaded Appender, even when the first line of code in the service configures logging. Still a mystery, but not important. (At least not for me, with this latest discovery—YMMV.)
The only logging that's suppressed is that which follows the buildup and teardown of the Common.Logging library itself; everything that occurs in the service itself is still logged.
I was able to get it to work using the code below (in combination with the code linked to above, of course).
As I said, YMMV.
[Service]
Public Module Main
Sub Main()
Try
BasicConfigurator.Configure(New Logger.DbAppender("ConnectionString"))
Logger = LogManager.GetLogger(GetType(Main))
Environment.ExitCode = HostFactory.Run(Sub(Configurator)
Configurator.Service(Of Manager)(Sub(Service)
Service.ConstructUsing(Function(Factory) As ServiceControl
Return New Manager
End Function)
Service.WhenStarted(Function(Manager, HostControl) As Boolean
Return Manager.StartService(HostControl)
End Function)
Service.WhenStopped(Function(Manager, HostControl) As Boolean
Return Manager.StopService(HostControl)
End Function)
End Sub)
Configurator.SetDescription(ServiceInfo.Description)
Configurator.SetServiceName(ServiceInfo.Product)
Configurator.SetDisplayName(ServiceInfo.Title)
Configurator.StartAutomatically()
Configurator.RunAsLocalSystem()
End Sub)
Catch ex As Exception
EventLog.WriteEntry(ServiceInfo.Product, ex.Message, EventLogEntryType.Error, 1, 1, ex.ToBytes)
End Try
End Sub
Public Property Logger As ILog
End Module
[DbAppender] (updated w/add'l constructor that accepts a connection string)
Public Class DbAppender
Inherits AdoNetAppender
Public Sub New()
Me.New(String.Empty)
End Sub
Public Sub New(ConnectionString As String)
If Trim(ConnectionString) <> String.Empty Then
MyBase.ConnectionString = ConnectionString
End If
MyBase.CommandText = Me.CommandText
MyBase.BufferSize = 1
Me.Parameters.ForEach(Sub(Parameter As DbParameter)
MyBase.AddParameter(Parameter)
End Sub)
Me.ActivateOptions()
End Sub
Protected Overrides Function CreateConnection(ConnectionType As Type, ConnectionString As String) As IDbConnection
Return MyBase.CreateConnection(GetType(System.Data.SqlClient.SqlConnection), MyBase.ConnectionString)
End Function
Private Overloads ReadOnly Property CommandText As String
Get
Dim _
sColumns,
sValues As String
sColumns = Join(Me.Parameters.Select(Function(P As DbParameter) P.DbColumn).ToArray, ",")
sValues = Join(Me.Parameters.Select(Function(P As DbParameter) P.ParameterName).ToArray, ",")
Return COMMAND_TEXT.ToFormat(sColumns, sValues)
End Get
End Property
Private ReadOnly Property Parameters As List(Of DbParameter)
Get
Parameters = New List(Of DbParameter)
Parameters.Add(Me.Date)
Parameters.Add(Me.Thread)
Parameters.Add(Me.Level)
Parameters.Add(Me.Source)
Parameters.Add(Me.Message)
Parameters.Add(Me.Exception)
End Get
End Property
Private ReadOnly Property [Date] As DbParameter
Get
Return New DbParameter("Date", New DbPatternLayout(PATTERN_DATE), DbType.Date, 0)
End Get
End Property
Private ReadOnly Property Thread As DbParameter
Get
Return New DbParameter("Thread", New DbPatternLayout(PATTERN_THREAD), DbType.String, 255)
End Get
End Property
Private ReadOnly Property Level As DbParameter
Get
Return New DbParameter("Level", New DbPatternLayout(PATTERN_LEVEL), DbType.String, 50)
End Get
End Property
Private ReadOnly Property Source As DbParameter
Get
Return New DbParameter("Source", New DbPatternLayout(PATTERN_SOURCE), DbType.String, 255)
End Get
End Property
Private ReadOnly Property Message As DbParameter
Get
Return New DbParameter("Message", New DbPatternLayout(PATTERN_MESSAGE), DbType.String, 4000)
End Get
End Property
Private ReadOnly Property Exception As DbParameter
Get
Return New DbParameter("Exception", New DbExceptionLayout)
End Get
End Property
Private Const PATTERN_MESSAGE As String = "%message"
Private Const PATTERN_THREAD As String = "%thread"
Private Const PATTERN_SOURCE As String = "%logger.%M()"
Private Const PATTERN_LEVEL As String = "%level"
Private Const PATTERN_DATE As String = "%date{yyyy-MM-dd HH:mm:ss.fff}"
Private Const COMMAND_TEXT As String = "INSERT INTO Log ({0}) VALUES ({1})"
'======================================================================================
' Available patterns:
' http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html
'======================================================================================
End Class

Load/save documents to SFTP Linux server with VB.NET

So I am using Visual Studio 2013 (Community)
And so far I've build a program that can create files using textboxes and so forth.
It saves to XML, and hopefully reads from XML (Even if I am getting access denied)
The time has come for the Application to talk to a server, where all the files will be saved, and read from.
The server is a Linux Server Edition (Latest) and its up and running fine.
I want my application to connect to it, log in, and then just list and read files from the server.
So far, it does this a bit.
Private Sub Loginbutton_Click(sender As Object, e As EventArgs) Handles Loginbutton.Click
Dim mySessionOptions As New SessionOptions
With mySessionOptions
.Protocol = Protocol.Sftp
.HostName = "192.168.0.247"
.UserName = "username" - these are default on purpose
.Password = "password"
.SshHostKeyFingerprint = "ssh-rsa 2048 [Hidden]"
End With
Using mySession As Session = New Session
' Connect
mySession.Open(mySessionOptions)
End Using
Form1.Show()
Me.Close()
End Sub
That works like a charm, and it moves on.
Once Form1 is loaded, its showing me the correct files from the server folder..
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
For Each i As String In Directory.GetFiles("\\192.168.0.247\Database")
Objectlist1.Items.Add(Path.GetFileName(i))
Next
Objectlist1.Refresh()
End Sub
And when I save files to it
Private Sub Savebutton_Click(sender As Object, e As EventArgs) Handles Savebutton.Click
If IO.File.Exists(Pholderbox.Text) = False Then
Dim settings As New XmlWriterSettings()
settings.Indent = True
Dim XmlWrt As XmlWriter = XmlWriter.Create("\\192.168.0.247\Database\" + Pholderbox.Text, settings)
With XmlWrt
All of that, works as intended.
I want to mention that the folder in Question, or "Share" in question on the server, is password protected, and the username and password are inserted in the Login Code (Temporary)
My problem comes here when I doubleclick the file (activate) to READ it.
Private Sub Objectlist1_ItemActivate(sender As Object, e As EventArgs) Handles Objectlist1.ItemActivate
Caseworker.Show()
Me.Objectlist1.MultiSelect = False
Dim selectedListViewItem As String
selectedListViewItem = Me.Objectlist1.SelectedItems.Item(0).ToString
Const basepath As String = "\\192.168.0.247\Database"
Dim xmlpath = IO.Path.Combine(basepath, Objectlist1.SelectedItems.Item(0).Text)
If (IO.File.Exists(xmlpath)) Then
Dim document As XmlReader = New XmlTextReader(basepath)
Dim mySessionOptions As New SessionOptions
While (document.Read())
' - This little bugger screams out everytime
' "An unhandled exception of type 'System.UnauthorizedAccessException' occurred in System.Xml.dll
' Additional information: Access to the path '\\192.168.0.247\Database' is denied."
What in the world is wrong here? I would assume since it can list the content of that folder, and for testing I gave EVERYONE Full access to that folder (User, Group, Other) FULL Access on Linux (0777)
I put it like that to test if it would help.
This might be out of your expertize, as it involves the library WinSCP and is in fact a Linux Server.
Being that its only the "Read XML" feature that denies it, I must be very close?
I see that very many suggest other Third Party libraries, the best for me, would be a solution in plain VB.NET if possible.
You are combining SFTP login with an access to a remote resource via UNC path. This cannot work. Either use the SFTP only (what you can use WinSCP .NET assembly for) or login to the remote (Samba?) server, so that you can use UNC paths only.
An SFTP solution follows. I do not know VB.NET, so excuse mistakes in syntax. Also note, that you need to make the mySession global, so that you can access it from other functions.
Loading list of remote files:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
For Each i As RemoteFileInfo In mySession.ListDirectory("/Database").Files
Objectlist1.Items.Add(i.Name)
Next
Objectlist1.Refresh()
End Sub
Reference: https://winscp.net/eng/docs/library_session_listdirectory
Saving:
Private Sub Savebutton_Click(sender As Object, e As EventArgs) Handles Savebutton.Click
Dim settings As New XmlWriterSettings()
settings.Indent = True
Dim TempPath As String = IO.Path.Combine(IO.Path.GetTempPath, Pholderbox.Text);
Dim XmlWrt As XmlWriter = XmlWriter.Create(TempPath , settings)
With XmlWrt
End With
mySession.PutFiles(TempPath, "/Database/").Check()
End Sub
Reference: https://winscp.net/eng/docs/library_session_putfiles
Loading:
Private Sub Objectlist1_ItemActivate(sender As Object, e As EventArgs) Handles Objectlist1.ItemActivate Caseworker.Show()
Me.Objectlist1.MultiSelect = False
Dim selectedListViewItem As String
selectedListViewItem = Me.Objectlist1.SelectedItems.Item(0).ToString
Dim xmlpath = IO.Path.Combine(IO.Path.GetTempPath, Objectlist1.SelectedItems.Item(0).Text)
mySession.GetFiles("/Database/" + Objectlist1.SelectedItems.Item(0).Text, xmlpath).Check();
If (IO.File.Exists(xmlpath)) Then
Dim document As XmlReader = New XmlTextReader(basepath)
Dim mySessionOptions As New SessionOptions
While (document.Read())
Reference: https://winscp.net/eng/docs/library_session_getfiles

Resources