ProcessStartInfo.CreateNoWindow doesn't work - vb.net

I'm printing a pdf directly towards a printer, but I want to do this in the background.
At the moment everything is working, but you can see that adobe is starting up and opening a document, while I want that to stay hidden.
For this I tried to use:
Process1.StartInfo.CreateNoWindow = True
But it won't work, while I do everything the same as written in:
MSDN
It is mentioned that you should place
psi.UseShellExecute = False
In order to let CreateNoWindow work, but when I place this in my code I get an error message: The system cannot find the file specified, while without this line, it works.
Below you can find the entire code:
Dim Process1 As New System.Diagnostics.Process
Dim psi As New ProcessStartInfo("AcroRd32.exe", "/t " + temppdf + " " + General.pdfprinter + "")
'psi.UseShellExecute = False
psi.CreateNoWindow = True
Process1.StartInfo = psi
Process1.Start()
Process1.WaitForInputIdle()
Process1.Kill()
Any idea?

How about this?
Define the function ShowWindow:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As ShowWindowCommands) As Boolean
End Function
And then call it:
Process1.Start()
ShowWindow(Process1.MainWindowHandle, 0)

Related

Open/Close Operation Equivalence VB6 / VB.NET

I am working on converting parts of a VB6 Project to VB.Net and there are some code segments I am having issues with because I can't seem to find alternatives for the VB6 code in VB.Net. Here is the code block in question right now:
Public Sub ProcessError(ByVal strModule As String, ByVal strProcedure As String, _
ByVal strDescription As String, ByVal bLogError As Boolean, _
ByVal bShowError As Boolean, Optional ByVal strMsg As String)
On Error GoTo 100
Dim intFile As Integer: Dim strPathName As String
strPathName = AddBackSlash(gsLogPath) & gsErrLogName
If bLogError = True Then
If GetFileSize(strPathName) > gcuMaxLogFileSize Then
Call CopyFile(strPathName, strPathName & ".bak")
Call DeleteFile(strPathName)
End If
intFile = FreeFile
Open strPathName For Append As #intFile
Write #intFile, Format(Now, "MMM-DD-YYYY HH:MM:SS AMPM"), strModule, strProcedure, strDescription)
Close #intFile
End If
If bShowError Then
Call Prompt("Error occurred in " & strModule & vbCrLf & "Error Description :" & strDescription, 1, vbRed)
End If
Exit Sub
100:
Close #intFile
End Sub
So the lines I am having issue with are:
Open strPathName For Append As #intFile
Write #intFile
Close #intFile
I understand I should probably be using the StreamWriter object in place of these, but what throws me off is the Error section. If an error is thrown and it goes to the 100 mark, how would Close #intFile work if it hasn't been opened or created yet?
For most of the other conversion annoyances I've had with porting this over this one has been confusing me the most, so any help is appreciated. Thanks for your time.
This fixes the errors, and also updates a lot of the code to use styles and APIs more typical for modern VB.Net. For this to work as-is, make sure there is an Imports System.IO directive at the top of the file.
Public Sub ProcessError(ByVal ModuleName As String, ByVal ProcedureName As String, _
ByVal Description As String, ByVal LogError As Boolean, _
ByVal ShowError As Boolean, Optional ByVal Message As String)
If LogError Then
Dim logFile As New FileInfo(Path.Combine(gsLogPath, gsErrLogName))
If logFile.Length > gcuMaxLogFileSize Then
logFile.MoveTo(logFile.FullName & ".bak")
End If
Try
File.AppendAllText(PathName, String.Format("{0:d},""{1}"",""{2}"",""{3}""", DateTime.Now, ModuleName, ProcedureName, Description))
Catch
End Try
End If
If ShowError Then
MsgBox(String.Format("Error occurred in {0}{1}Error Description:{2}", ModuleName, vbCrLf, Description))
End If
End Sub
One thing worth pointing out here is the style guidelines published by Microsoft for VB.Net now explicitly recommend against hungarian type-prefixes.
If you just have one line to write to, you can use the build-in method that does all the work for you.
Dim inputString As String = "This is a test string."
My.Computer.FileSystem.WriteAllText(
"C://testfile.txt", inputString, True)
More help here: https://learn.microsoft.com/en-us/dotnet/visual-basic/developing-apps/programming/drives-directories-files/how-to-append-to-text-files?view=netframework-4.7.2

Access Application, Hidden Application Window With Form Taskbar Icon

I have an access application with one main form. When you open the application, an AutoExec macro hides the application via the Windows API apiShowWindow. Then, the AutoExec opens up the main form which is set to Popup. This all works beautifully; my database guts are hidden and the form opens up and just floats along.
However, when the access database gets hidden, you loose the taskbar icon. I have a custom icon set in the Options\Current Database\Application Icon setting. If I don't hide the database, this icon displays just fine in the task bar.
I found an API workaround that will show an icon in the taskbar for just the form. It goes a little something like this:
Public Declare Function GetWindowLong Lib "user32" _
Alias "GetWindowLongA" _
(ByVal hWnd As Long, _
ByVal nIndex As Long) As Long
Public Declare Function SetWindowLong Lib "user32" _
Alias "SetWindowLongA" _
(ByVal hWnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Public Declare Function SetWindowPos Lib "user32" _
(ByVal hWnd As Long, _
ByVal hWndInsertAfter As Long, _
ByVal X As Long, _
ByVal Y As Long, _
ByVal cx As Long, _
ByVal cy As Long, _
ByVal wFlags As Long) As Long
Public Sub AppTasklist(frmHwnd)
Dim WStyle As Long
Dim Result As Long
WStyle = GetWindowLong(frmHwnd, GWL_EXSTYLE)
WStyle = WStyle Or WS_EX_APPWINDOW
Result = SetWindowPos(frmHwnd, HWND_TOP, 0, 0, 0, 0, _
SWP_NOMOVE Or _
SWP_NOSIZE Or _
SWP_NOACTIVATE Or _
SWP_HIDEWINDOW)
Result = SetWindowLong(frmHwnd, GWL_EXSTYLE, WStyle)
Debug.Print Result
Result = SetWindowPos(frmHwnd, HWND_TOP, 0, 0, 0, 0, _
SWP_NOMOVE Or _
SWP_NOSIZE Or _
SWP_NOACTIVATE Or _
SWP_SHOWWINDOW)
End Sub
This approach does work; I get an icon in the taskbar dedicated to just the form. However, the icon in the taskbar is the standard Access icon.
In response to this problem, I added another API call using the SendMessageA:
Public Declare Function SendMessage32 Lib "user32" Alias _
"SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, _ ByVal wParam
As Long, ByVal lParam As Long) As Long
Executed thus:
Private Const WM_SETICON = &H80
Private Const ICON_SMALL = 0&
Private Const ICON_BIG = 1&
Dim icoPth As String: icoPth = CurrentProject.Path & "\MyAppIcon.ico"
Dim NewIco As Long: NewIco = ExtractIcon32(0, icoPth, 0)
Dim frmHwnd As Long: frmHwnd = Me.hwnd 'the form's handle
SendMessage32 frmHwnd, WM_SETICON, ICON_SMALL, NewIco
SendMessage32 frmHwnd, WM_SETICON, ICON_BIG, NewIco
SendMessage32 hWndAccessApp, WM_SETICON, ICON_SMALL, NewIco
SendMessage32 hWndAccessApp, WM_SETICON, ICON_BIG, NewIco
Keep in mind that I have tried the above four lines of 'SendMessages' in various orders inside of and outside of, top of and bottom of the AppTasklist Sub to no avail.
It does appear to work at the application level, but it never seems to work at the form level.
For those of you familiar with this particular predicament, let me list some of the other options outside of VBA that I have tried.
1.) Taskbar\Properties\Taskbar buttons. I've changed this menu option to 'Never Combine' and 'Combine When Taskbar Is Full'. So, basically, this does work; I now get my icon for just the folder and the little label. BUT(!), it only works if the users have these settings checked on their end. In my experience, almost no one uses any of the options except 'Always combine, hide labels'.
2.) Changing the Registry settings. You can change the following registry key to change the default icon an application uses: "HKEY_CLASSES_ROOT\Access.Application.14\DefaultIcon(Default)." However, most users (myself included) don't have access to the HKEY_CLASSES_ROOT part of the registry. Furthermore, I would have to write some code to find the proper key and then change it (which I could do) but, it's unclear if this change would be immediate--not to mention I'd have to change it back when exiting the application.
3.) Right-clicking the pinned application, then right clicking the application in the menu does give you a properties menu with a button called 'Change Icon...' in the 'Shortcut' tab. However, for a program like Access, this button is greyed out.
I am using Windows 7 and Access 2010.
Is it possible to force the Taskbar Icon in the above scenario to something other than the standard Access Icon?
I feel like there's ether a little something I'm missing, or an API function that could be used, or a better SendMessage constant, or that, maybe, it just can't be done.
Any help would be greatly appreciated.
Also, as a disclaimer (I guess): obviously the above code was pulled from other posts from this forum and others. Most of it was unattributed from whence I got it. I've made some minor tweeks to make it work in Access as opposed to that other piece of Microsoft Software that keeps getting in my search results so I will not name it here.
Thanks!
Okay. So I'm going to answer my own question. Apparently all I really needed was a break from the action and to type out my problem. A short time after I posted the question, I got back on the horse and tried some more search terms. I eventually came across a post which really didn't seem fruitfull at the outset because it wasn't clear to me if the poster and myself were dealing with the same scenario.
I found my answer here:
http://www.access-programmers.co.uk/forums/showthread.php?t=231422
First off, your application must open via a shortcut. Fortunately for me, I've been using a Desktop shortcut from the begining.
When I started building the application, I knew from the outset that I would be using a VBScript to do the installation of the application (as well as updating when a newer version get's released). In that script, I create a Desktop shortcut to the application which I store in the user's Documents directory. I also store the application icon in that directory which I attach to the application shortcut.
If you've never created a shortcut via vba/script, you'll essentially do something like this (written in vbScript):
Dim WinShell
Dim ShtCut
Set WinShell = CreateObject("WScript.Shell")
Set ShtCut = WinShell.CreateShortcut(strDesktopPath & "\MyCoolApp.lnk")
With ShtCut
.TargetPath = (See below)
.Arguments = (See below)
.IconLocation = ...
.Desciption = "This is the coolest app, yo!"
.WorkingDirectory = strAppPath
.WindowStyle = 1
.Save
End With
Now, the target of the shortcut started out being something like this:
"C:\Users\UserName\Public\Documents\MyCoolApp\MyCoolApp.accdb"
Obviously your users may have a different enterprise structure for the Documents location...
What the above reference post suggests doing is turning that shortcut into a psuedo command line script.
To do this:
First, your actual target is going to be the path to the user's version of access (Runtime or full), like such:
"C:\Program Files (x86)\Microsoft Office\Office14\MSACCESS.EXE"
IMPORTANT! You will have to wrap this target in double-quotes, i.e.:
.TargetPath = Chr(34) & strAccessPath & Chr(34)
Next (and you won't find this in the above post, I had to figure it out), you'll need to set the Argument to the directory of the application, like such:
"C:\Users\UserName\Public\Documents\MyCoolApp\MyCoolApp.accdb"
IMPORTANT! Again, you'll have to wrap the arguments in double-quotes, i.e.:
.Arguments = Chr(34) & strAppPath & Chr(34)
Also important, I hope your app IS cool.
Once you have this shortcut set up, essentially you're making it so your application will have it's own workgroup on the taskbar. I.e., if Access is open in general, your application will have it's own group on the taskbar. This group will have your cool, custom icon associated with it. And as a huge unexpected bonus, you'll be able to pin your shortcut to the taskbar outside of Access. SWEEEEEET!
Good luck!

VBA Modules and passing arguments

I have a module that can be called from any part of my application to check if a particular font is installed on the users system, and if not - install the font and check again before continuing
Main Applicaton
if RequiredFont.Run(strFontName) = FALSE Then '(error message and exit sub)
Module "RequiredFont"
Public Function Run(Font As String) As Boolean
if check(Font) = FALSE Then
Run = FALSE
Install(Font)
if check(Font) = TRUE Then Run = TRUE
Else
Run = TRUE
End If
Private Function Check(Font as String) as Boolean
'code to check the font exists on the users localmachine, returns true/false
End Sub
Private Sub Install(Font as String)
'code to install the font on the users localmachine,
End Sub
My first question is:
Is the best way to make arguments available to all functions and subs, to pass them through each time I call? (as shown above).... or is there a simple way to declare an argument as variable to the whole module when Run() is called?
My Second Question is:
Is there a way I can avoid Run() alltogether and just call the module name "RequiredFont" directly, I remember that in other languages, calling a sub by a certain name would automatically run that sub when the module is called
Thank You
EDIT - This is how my code looks now:
Private FontName As String
Private FontFile As String
Public Function Run(strFontName As String, strFontFile As String) As Boolean
FontName = strFontName
FontFile = strFontFile
Run = False
If CheckFont() = False Then InstallFont
If CheckFont() = True Then
Run = True
Else
'message error"
End If
End Function
Private Function CheckFont() As Boolean
'code to check if the font is installed
On Error Resume Next
'Create a temporary StdFont object
With New StdFont
' Assign the proposed font name
.Name = FontName
' Return true if font assignment succeded
If (StrComp(FontName, .Name, vbTextCompare) = 0) = False Then
CheckFont = False
Else
CheckFont = True
End If
End With
End Function
Private Sub InstallFont()
' code to install the font
MsgBox "You need the following font installed to continue." _
& vbNewLine _
& vbNewLine & "'" & FontName & "'" _
& vbNewLine _
& vbNewLine & "Click OK to launch the font. Please click the INSTALL button at the top"
OpenFile (PATH_TO_FONTS & FontFile)
End Sub
Using function arguments is a good coding practise, that way you know exactly what goes in and what goes out a function.
You can however use a global variable, which would be set once when Run is called and still be accessible to the other functions.
'could also be Private to hide it from other modules
Public myFont As String
Public Function Run(Font As String) As Boolean
myFont = Font
'...
End Sub
Private Function Check() as Boolean
' you can access myFont here
End Sub
Private Sub Install()
'idem
End Sub
Regarding your second question, I don't think you can.
You can declare optional functions, and set defaults:
Public Function fxMyFunction _
(Optional lngProj As Variant, _
Optional strFruit As Variant = "banana", _
Optional booTest As Boolean = False) As String
'' IsMissing requires that lngProj be a Variant
booNoProject = IsMissing(lngProj)
fxMyFunction = strFruit
End Function
The Optional arguments must follow non-optional arguments.
About functions that "run on inclusion"
You do have to call functions and subs by name. There is no "self-running function" feature for a VBA standard module. VBA "includes" all modules on compiling.
VBA class modules are where you will find the equivalent of constructors. Investing in VBA's version of object-orientation doesn't seem helpful for your current need. If you do go that direction, some aspects will start looking familiar to you (though perhaps just enough to get frustrating, as OO remains a feature that was added later and looks the part).
As #z states, you can use a global variable, although this is bad practice.
Regarding question 2, you can give your function a unique name and omit naming your module to run it, e.g.
findOrInstallFont(Fontname)

Running a .bat in the background

So I have this in my coding:
vb Code:
file = My.Computer.FileSystem.OpenTextFileWriter("c:\command.bat", False)
file.WriteLine("#echo off")
file.WriteLine("cd " & TextBox2.Text)
file.WriteLine("adb shell dumpsys meminfo " & TextBox1.Text & " > C:\Sample.txt")
file.Close()
Shell("C:\command.bat")
what I want it to do is to run the batch file without it opening if that makes sense. Right now this runs on a loop for 10 minutes and on every 2 second tick it opens and then closes the .bat. Which is really annoying to see a .bat open and close every two seconds. Is there anyway to get this process to run silently in the background so the user doesnt even know that it is running?
Shell("C:\command.bat", AppWinStyle.Hide)
That will run the batch file but the window is hidden.
or use Process.Start as suggested by David. with WindowStyle = ProcessWindowStyle.Hidden
Here is an example on how to use Process.Start with a hidden window
Dim params As String = "C:\command.bat"
Dim myProcess As New ProcessStartInfo
myProcess.FileName = "cmd.exe"
myProcess.Arguments = params
myProcess.WindowStyle = ProcessWindowStyle.Hidden
Process.Start(myProcess)
if you run into the issue of file not found errors with the path you can try to add the following Windows API call and run your file path through this function as well.
'This would be declared at the top of your Form Code/Class Code
Private Declare Auto Function GetShortPathName Lib "kernel32" ( _
ByVal lpszLongPath As String, _
ByVal lpszShortPath As StringBuilder, _
ByVal cchBuffer As Integer) As Integer
And here is the function to return back a ShortPath (win98 style path (ie. c:/progra~1/myfolder/myfile.bat)
Public Function GetShortPath(ByVal longPath As String) As String
Dim requiredSize As Integer = GetShortPathName(longPath, Nothing, 0)
Dim buffer As New StringBuilder(requiredSize)
GetShortPathName(longPath, buffer, buffer.Capacity)
Return buffer.ToString()
End Function
then simply call your path like this in your process.start function
Dim params As String = GetShortPathName("C:\command.bat")

Open Google Chrome from VBA/Excel

I'm trying to open a Chrome browser from VBA. I understand Chrome does not support ActiveX settings so I'm curious if theres any work-arounds?
Dim ie As Object
Set ie = CreateObject("ChromeTab.ChromeFrame")
ie.Navigate "google.ca"
ie.Visible = True
shell("C:\Users\USERNAME\AppData\Local\Google\Chrome\Application\Chrome.exe -url http:google.ca")
Worked here too:
Sub test544()
Dim chromePath As String
chromePath = """C:\Program Files\Google\Chrome\Application\chrome.exe"""
Shell (chromePath & " -url http:google.ca")
End Sub
I found an easier way to do it and it works perfectly even if you don't know the path where the chrome is located.
First of all, you have to paste this code in the top of the module.
Option Explicit
Private pWebAddress As String
Public Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
After that you have to create this two modules:
Sub LoadExplorer()
LoadFile "Chrome.exe" ' Here you are executing the chrome. exe
End Sub
Sub LoadFile(FileName As String)
ShellExecute 0, "Open", FileName, "http://test.123", "", 1 ' You can change the URL.
End Sub
With this you will be able (if you want) to set a variable for the url or just leave it like hardcode.
Ps: It works perfectly for others browsers just changing "Chrome.exe" to opera, bing, etc.
You can use the following vba code and input them into standard module in excel. A list of websites can be entered and should be entered like this on cell A1 in Excel - www.stackoverflow.com
ActiveSheet.Cells(1,2).Value merely takes the number of website links that you have on cell B1 in Excel and will loop the code again and again based on number of website links you have placed on the sheet. Therefore Chrome will open up a new tab for each website link.
I hope this helps with the dynamic website you have got.
Sub multiplechrome()
Dim WebUrl As String
Dim i As Integer
For i = 1 To ActiveSheet.Cells(1, 2).Value
WebUrl = "http://" & Cells(i, 1).Value & """"
Shell ("C:\Program Files (x86)\Google\Chrome\Application\chrome.exe -url " & WebUrl)
Next
End Sub
The answer given by #ray above works perfectly, but make sure you are using the right path to open up the file. If you right click on your icon and click properties, you should see where the actual path is, just copy past that and it should work.
You could use selenium basic to launch and interact with Chrome. After installation you will need to add a reference to Selenium Type library.
Option Explicit
Public Sub Demo()
Dim d As WebDriver
Set d = New ChromeDriver
Const URL = "https://www.google.com/"
With d
.Start "Chrome"
.get URL
.FindElementById("lst-ib").SendKeys "Selenium basic GitHub"
.FindElementsByTag("form")(1).FindElementByCss("input[value='Google Search']").Click
'.Quit
End With
End Sub