I have a batch file that does several things and the last step is to open an excel document and run the macro contained in it which updates the contents of the file. The macro runs perfectly with the click of a button but I want it all to be done when you run the .bat file.
I know I could attach the macro to the open event so it runs when you open the macro but I only want it to update automatically when you run the bat file, not every time you open the thing.
Maybe I could pass a parameter to let it know that it's been run from a .bat? or run it directly with an excel command?
like this?
run excel.exe /runMacro "mymacro"
I can't find what I need anywhere, thanks.
Yes, basically the easy way is to move the contents of your "mymacro" into your ThisWorkBook
Private Sub Workbook_Open()
With security, the user may still have to click the enable macros button unless you want this to be unattended. If you want to pass arguments into the workbook open, then you can do this by parsing the command line. You can search code examples on Google for "excel GetCommandLineW"
Private Declare Function GetCommandLine Lib "kernel32" Alias "GetCommandLineW" () As Long
Private Declare Function lstrlenW Lib "kernel32" (ByVal lpString As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (MyDest As Any, MySource As Any, ByVal MySize As Long)
Function CmdToSTr(cmd As Long) As String
Dim Buffer() As Byte
Dim StrLen As Long
If cmd Then
StrLen = lstrlenW(cmd) * 2
If StrLen Then
ReDim Buffer(0 To (StrLen - 1)) As Byte
CopyMemory Buffer(0), ByVal cmd, StrLen
CmdToSTr = Buffer
End If
End If
End Function
Private Sub Workbook_Open()
Dim CmdRaw As Long
Dim CmdLine As String
CmdRaw = GetCommandLine
CmdLine = CmdToSTr(CmdRaw)
' From here you can parse the CmdLine
' ...snip...
On my version of Excel 2013 (15.0.4649.1000 64 bits), I was obliged to write the following code :
#If VBA7 Then
Private Declare PtrSafe Function GetCommandLine Lib "kernel32" Alias "GetCommandLineW" () As LongPtr
Private Declare PtrSafe Function lstrlenW Lib "kernel32" (ByVal lpString As LongPtr) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (MyDest As Any, MySource As Any, ByVal MySize As LongPtr)
#Else
' Declare Function GetCommandLine Lib "kernel32" Alias "GetCommandLineW" () As Long
' Declare Function lstrlenW Lib "kernel32" (ByVal lpString As Long) As Long
' Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (MyDest As Any, MySource As Any, ByVal MySize As Long)
#End If
#If VBA7 Then
Function CmdToSTr(Cmd As LongPtr) As String
#Else
Function CmdToSTr(Cmd As Long) As String
#End If
Dim Buffer() As Byte
Dim StrLen As Long
If Cmd Then
StrLen = lstrlenW(Cmd) * 2
If StrLen Then
ReDim Buffer(0 To (StrLen - 1)) As Byte
CopyMemory Buffer(0), ByVal Cmd, StrLen
CmdToSTr = Buffer
End If
End If
End Function
Private Sub Workbook_Open()
Dim CmdRaw As LongPtr
Dim CmdLine As String
Dim TabName As String
CmdRaw = GetCommandLine
CmdLine = CmdToSTr(CmdRaw)
MsgBox(CmdLine)
End Sub
Related
I use the code down below to open a cmd.exe window and move it into a panel on my form. I do this so that I can change the title permanently during execution.
When cmd.exe is started, the window layout looks like this:
Which is the standard Windows 10 layout. But as soon as it's moved into the panel in my form, the window layout changes to the old Aero layout:
I don't know why the layout changes, does anybody know why?
Thanks for any help in advance!
Kind regards,
Eric
Imports System.Runtime.InteropServices
Public Class Form1
Private WithEvents Tmr As New Timer With {.Interval = 100}
Private Const HWND_BOTTOM As Integer = &H1
Private WithEvents proc As New Process
<DllImport("user32.dll", EntryPoint:="SetParent")>
Private Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="SetWindowPos")>
Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Text = "My title"
proc.EnableRaisingEvents = True
proc.StartInfo.FileName = "cmd"
proc.Start()
Tmr.Start()
End Sub
Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Tmr.Tick
If SetParent(proc.MainWindowHandle, Panel1.Handle) <> IntPtr.Zero Then
Tmr.Stop()
SetWindowPos(proc.MainWindowHandle, New IntPtr(HWND_BOTTOM), 0, 0, Panel1.ClientSize.Width, Panel1.ClientSize.Height, 0)
End If
End Sub
Private Sub Proc_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles proc.Exited
Invoke(Sub() Close())
End Sub
End Class
This issue seems to be related to the way a MDI child is handled. Since MDI is no longer maintained, this issue will probably never be fixed by Microsoft.
I have an application which starts a character based application and then changes the name of that window. It works, but only until you select a character in that application, at which point the name changes back to the original name. I read that this is normal behavior and there's little you can do about that.
So I thought about embedding this application in my own form/panel, so I can change the name of this form to my liking, but that only seems to work for GUI applications, like notepad.exe.
Does anybody know of a way to run such an application in a panel of how you can create a form around a character based application?
Thanks for any help in advance.
Kind regards,
Eric
I found the answer myself on how to run a cmd window inside a form:
Imports System.Runtime.InteropServices
Public Class Form1
Private WithEvents Tmr As New Timer With {.Interval = 100}
Private Const HWND_BOTTOM As Integer = &H1
Private WithEvents proc As New Process
<DllImport("user32.dll", EntryPoint:="SetParent")>
Private Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="SetWindowPos")>
Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Text = "My title"
proc.StartInfo.FileName = "cmd"
proc.Start()
Tmr.Start()
End Sub
Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Tmr.Tick
If SetParent(proc.MainWindowHandle, Panel1.Handle) <> IntPtr.Zero Then
Tmr.Stop()
SetWindowPos(proc.MainWindowHandle, New IntPtr(HWND_BOTTOM), 0, 0, Panel1.ClientSize.Width, Panel1.ClientSize.Height, 0)
End If
End Sub
End Class
We are creating an Add-In to Microsoft word. In this Add-In there is a modeless form and we set it as the Top Most form too as per the requirement. When we minimize the Microsoft word application, the modeless form is not minimized. If we able to set the MS Word as the owner of this form, this problem will solve. Please tell me a way to do this.
This is the code that I am using currently to load the form.
Dim Test As Long
With frmSelectStyle
.aaInitialize SelectStyleDlg:=Me
.Show vbModeless
End With
Test = SetTopMostWindow(frmSelectStyle.hwnd, True)
I know that we can set an Owner to a form as follows.
frmSelectStyle.Show vbModeless , frmMain
But in my case, I need to set the MS Word as the owner. Please help me.
The key is to use the SetWindowLong Windows API function. You can wrap that function in your own "SetOwner" function to make it a bit easier to use. SetOwner accepts two Long Windows handles: the first is for your modeless window and the second is for the Word Application main window (the code below is a variation of code originally published at DevX.com).
I've tested the code below and the modeless window does minimize along with Word if Word is minimized.
Option Explicit
Private hwndOriginal As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SetWindowLong& Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long)
Const GWL_HWNDPARENT = (-8)
Function SetOwner(ByVal HwndtoUse, ByVal HwndofOwner) As Long
SetOwner = SetWindowLong(HwndtoUse, GWL_HWNDPARENT, HwndofOwner)
End Function
Private Sub Form_Load()
Dim hWndWord As Long
' start an instance of Microsoft Word
Dim WordApp As Word.Application
Set WordApp = CreateObject("Word.Application")
' make sure it's visible
WordApp.Application.Visible = True
' use the FindWindow API to find a window class of "OpusApp" with the specified Word-application caption
hWndWord = FindWindow("OpusApp", WordApp.Caption)
hwndOriginal = SetOwner(Me.hWnd, hWndWord)
End Sub
Private Sub Form_Unload(Cancel As Integer)
SetOwner Me.hWnd, hwndOriginal
End Sub
I have gone through the custom dotted line separator example but unable
do it in visual basic is there any example in vb
I am getting following errors
1)'Dash' is already declared as 'Protected Friend dash As Single' in this class.
2)'Phase' is already declared as 'Protected Friend phase As Single' in this class.
3)'LineWidth' is not a member of 'iTextSharp.text.pdf.PdfContentByte'.
4) Variable 'Dash' is used before it has been assigned a value. A null
reference exception could result at runtime.
5) Variable 'Phase' is used before it has been assigned a value. A null
reference exception could result at runtime.
6) sub 'draw' shadows an overridable method in the base class 'DottedLineSeparator'. To override the base method, this method must be declared 'Overrides'.
Option Strict On
Option Explicit On
Imports iTextSharp.text
Imports iTextSharp.text.pdf
Imports iTextSharp.text.pdf.draw
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs)
Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs)
Handles Button1.Click
Dim FileName As String = System.IO.Path.Combine
(AppDomain.CurrentDomain.BaseDirectory, "Customers.pdf")
'Dim PageSize As New iTextSharp.text.Rectangle
(iTextSharp.text.PageSize.A4)
Dim Document As Document = New Document(iTextSharp.text.PageSize.A4,
50, 10, 10, 10)
Try
PdfWriter.GetInstance(Document, New System.IO.FileStream(FileName,
System.IO.FileMode.Create))
Document.Open()
Dim separator As New CustomDashedLineSeparator()
separator.dash = 10
separator.Gap = 7
separator.LineWidth = 3
Dim linebreak As New Chunk(separator)
Document.Add(linebreak)
Document.Close()
Catch ex As Exception
MsgBox("Pdf is Created")
End Try
End Sub
End Class
Friend Class CustomDashedLineSeparator
Inherits pdf.draw.DottedLineSeparator
Protected Friend dash As Single = 5
Protected Friend phase As Single = 2.5F
Public Overridable Property Dash As Single
Get
Return Dash
End Get
Set(ByVal dash As Single)
Me.dash = dash
End Set
End Property
Public Overridable Property Phase As Single
Get
Return Phase
End Get
Set(ByVal phase As Single)
Me.phase = phase
End Set
End Property
Public Overridable Sub draw(ByVal canvas As PdfContentByte, ByVal llx As
Single, ByVal lly As Single, ByVal urx As Single, ByVal ury As Single,
ByVal y As Single)
canvas.SaveState()
canvas.LineWidth = LineWidth
canvas.SetLineDash(dash, Gap, phase)
DrawLine(canvas, llx, urx, y)
canvas.RestoreState()
End Sub
End Class
Visual Basic is generally case-insensitive unlike C# and Java. You have both a property and field named Dash and also the same with Phase. That's your first two errors as well as your fourth and fifth. You can fix this by renaming your fields. One common way is to just prefix them with underscores.
For the third error, you need to use the SetLineWidth() method instead of using the LineWidth property.
For your sixth error, you are trying to override a method without explicitly telling VB that you want to. To do that you need to use Overrides instead of Overridable.
Your cleaned up class should look like this:
Friend Class CustomDashedLineSeparator
Inherits iTextSharp.text.pdf.draw.DottedLineSeparator
Protected Friend _dash As Single = 5
Protected Friend _phase As Single = 2.5F
Public Overridable Property Dash As Single
Get
Return Dash
End Get
Set(ByVal dash As Single)
Me._dash = dash
End Set
End Property
Public Overridable Property Phase As Single
Get
Return _Phase
End Get
Set(ByVal phase As Single)
Me._phase = phase
End Set
End Property
Public Overrides Sub Draw(ByVal canvas As PdfContentByte, ByVal llx As Single, ByVal lly As Single, ByVal urx As Single, ByVal ury As Single, ByVal y As Single)
canvas.SaveState()
canvas.SetLineWidth(LineWidth)
canvas.SetLineDash(Dash, Gap, phase)
DrawLine(canvas, llx, urx, y)
canvas.RestoreState()
End Sub
End Class
However, I'm betting that you don't really need those internal properties in the first place. If not, you can make that class even simpler:
Friend Class CustomDashedLineSeparator
Inherits iTextSharp.text.pdf.draw.DottedLineSeparator
Public Property Dash As Single
Public Property Phase As Single
Public Overrides Sub Draw(ByVal canvas As PdfContentByte, ByVal llx As Single, ByVal lly As Single, ByVal urx As Single, ByVal ury As Single, ByVal y As Single)
canvas.SaveState()
canvas.SetLineWidth(LineWidth)
canvas.SetLineDash(Dash, Gap, phase)
DrawLine(canvas, llx, urx, y)
canvas.RestoreState()
End Sub
End Class
Lastly, please never try/catch giant blocks of code. At best, your app will politely crash however you'll have no idea why and you'll never be able to fix it. Although not as pretty "Unable to open file XYZ.pdf for writing" is way more helpful than "Unable to create PDF".
I am new in VBA and classes.
I have this procedure in my class:
Public Sub InputData()
Dim blnLoaded As Boolean
Dim path As String
Dim file As String
path = MyForm.TextPath
file = MyForm.TextFile
If LoadData(path, file) = False Then
MsgBox FileErrorString
Else
blnLoaded = LoadData(path, file)
End If
End Sub
and I want to call it using 2 text boxes in my form:
Private Sub CmdLoad_Click()
Call clsData.InputData
End Sub
What I am doing wrong? Thank you in advance!
You need to create a new instance of your class first.
Something like this depending on your implementation of your class:
Private Sub CmdLoad_Click()
Dim myData as New clsData
Call myData.InputData()
End Sub