Bring specific Word instance to front VB - vb.net

I am using the following Namespace: Imports Microsoft.Office.Interop
I need a function, that brings a specific Word-Instance to front.
Optimal solution would be
Public Sub toFront(ByVal wdObj as Word.Application, ByVal filePath as String)
'sends by filePath specified Word-Instance to Front
End Sub
I know there is Word.Application.Activate, but that does not seem to be working all the time and also it only activates the ActiveDocument.
I already tried the following functions where wdObj is a Word.Application
wdObj.Activate()
wdObj.Application.Documents(My.Settings.DocPath).Activate()

There is no such a function in Interop. Try to use WinApi instead:
void BringWindowToTop (Microsoft.Office.Interop.Word.Document doc) {
// To make it active document
doc.Activate();
// I'm not 100% sure, but I think MainWindowHandle gives
// a handle of currently active window of word
IntPtr hwnd = Process.GetCurrentProcess().MainWindowHandle;
BringWindowToTop(hwnd);
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);

Related

How to copy multiple files/folders using single windows copy GUI in VB.Net

I am making an app in VB.Net that copies many files and folders to the same directory and I wish to use windows explorer for that (so the user has GUI and I do not have to worry about showing any errors or compare files).
So, if I do this for each file/folder:
My.Computer.FileSystem.CopyDirectory(source_path, target_path, FileIO.UIOption.AllDialogs)
My.Computer.FileSystem.CopyFile(source_path, target_path, FileIO.UIOption.AllDialogs)
It works correctly and shows this window:
Which is fine, however, if I have many files and/or folders and I loop through them and call commands above, they launch a new copy window for each file/folder, instead of launching a single GUI that combines them all, like so:
Is it possible to combine multiple files/folders copy process into a single windows explorer copy window GUI?
Thanks to #Jimi, I got pointed in the direction of SHFileOperations, so i figured out how to do this. I made a small class to do this:
Imports System.Runtime.InteropServices
Public Class NativeCopy
Private Enum FO_Func As Short
FO_COPY = &H2
FO_DELETE = &H3
FO_MOVE = &H1
FO_RENAME = &H4
End Enum
Private Structure SHFILEOPSTRUCT
Public hwnd As IntPtr
Public wFunc As FO_Func
<MarshalAs(UnmanagedType.LPWStr)>
Public pFrom As String
<MarshalAs(UnmanagedType.LPWStr)>
Public pTo As String
Public fFlags As UShort
Public fAnyOperationsAborted As Boolean
Public hNameMappings As IntPtr
<MarshalAs(UnmanagedType.LPWStr)>
Public lpszProgressTitle As String
End Structure
<DllImport("shell32.dll", CharSet:=CharSet.Unicode)>
Private Shared Function SHFileOperation(
<[In]> ByRef lpFileOp As SHFILEOPSTRUCT) As Integer
End Function
Private Shared _ShFile As SHFILEOPSTRUCT
Public Shared Sub Copy(ByVal sSource As List(Of String), ByVal sTarget As String)
_ShFile.wFunc = FO_Func.FO_COPY
_ShFile.pFrom = String.Join(vbNullChar, sSource) + vbNullChar
_ShFile.pTo = sTarget
SHFileOperation(_ShFile)
End Sub
End Class
To copy files and/or folders is as simple as this:
Dim copy_items_paths As List(Of String)
Dim target_path As String
NativeCopy.Copy(copy_items_paths, target_path)

Window explorer is freezing when open

I need to open the specific folder for a file and I am doing it with:
file = Directory.GetFiles(filepath,Filename,
SearchOption.AllDirectories).FirstOrDefault()
Process.Start("explorer.exe", "/select," & file.ToString)
This code is immediately opening the folder which is already fully loaded, but it doesnt seem enabled, endeed I cant do any action in it. The form is not freezing.
Thanks
I'll give you an answer in two parts...
Firstly, if the GetFiles() call takes to long and freezes the form (which doesn't seem to be the current problem), you should do the following:
Use EnumerateFiles() instead because in this case, FirstOrDefault() will return immediately after finding a matching file, unlike GetFiles() which will get all the files first before calling FirstOrDefault().
Wrap the call to EnumerateFiles() in a Task.Run() to execute it on a worker thread in case the search takes a little too long:
' Or:
' Private Async Sub SomeEventHandler()
Private Async Function ParentMethod() As Task
Dim filePath As String = Await Task.Run(
Function()
Return Directory.EnumerateFiles(dirPath, FileName, SearchOption.AllDirectories).
FirstOrDefault()
End Function)
' TODO: Use `filePath` to open the folder and select the file.
End Function
Secondly, do not use Process.Start("explorer.exe", "/select") because a) it starts a new Explorer.exe process rather than opening the directory in the current one, b) it seems to be causing you some issues, and c) it has some limitations.
Instead, use the approach demonstrated in the answer linked in point (c) above. The code is in C# but it can be easily converted to VB. Here's the VB version of the code (with an additional overload).
Add the following class to your project:
Imports System.IO
Imports System.Runtime.InteropServices
Public Class NativeMethods
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Function SHOpenFolderAndSelectItems(
pidlFolder As IntPtr, cidl As UInteger,
<[In], MarshalAs(UnmanagedType.LPArray)> apidl As IntPtr(),
dwFlags As UInteger) As Integer
End Function
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Sub SHParseDisplayName(
<MarshalAs(UnmanagedType.LPWStr)> name As String,
bindingContext As IntPtr, <Out> ByRef pidl As IntPtr,
sfgaoIn As UInteger, <Out> ByRef psfgaoOut As UInteger)
End Sub
Public Shared Sub OpenFolderAndSelectFile(filePath As String)
Dim dirPath As String = Path.GetDirectoryName(filePath)
Dim fileName As String = Path.GetFileName(filePath)
OpenFolderAndSelectFile(dirPath, fileName)
End Sub
Public Shared Sub OpenFolderAndSelectFile(dirPath As String, fileName As String)
Dim nativeFolder As IntPtr
Dim psfgaoOut As UInteger
SHParseDisplayName(dirPath, IntPtr.Zero, nativeFolder, 0, psfgaoOut)
If nativeFolder = IntPtr.Zero Then
' Log error, can't find folder
Return
End If
Dim nativeFile As IntPtr
SHParseDisplayName(Path.Combine(dirPath, fileName),
IntPtr.Zero, nativeFile, 0, psfgaoOut)
Dim fileArray As IntPtr()
If nativeFile = IntPtr.Zero Then
' Open the folder without the file selected if we can't find the file
fileArray = New IntPtr(-1) {}
Else
fileArray = New IntPtr() {nativeFile}
End If
SHOpenFolderAndSelectItems(nativeFolder, CUInt(fileArray.Length), fileArray, 0)
Marshal.FreeCoTaskMem(nativeFolder)
If nativeFile <> IntPtr.Zero Then
Marshal.FreeCoTaskMem(nativeFile)
End If
End Sub
End Class
Then, you can easily call it like this:
NativeMethods.OpenFolderAndSelectFile(filePath)
Some additional notes:
You should choose meaningful variable names. filePath should refer to the path of a file. If you want to refer to a folder/directory path, use something like dirPath or folderPath instead.
You don't need to call .ToString() on a variable that's already of a String type.
I would change the variable name file to something else. Maybe foundFile. After all File is the name of a class in System.IO and vb.net is not case sensitive. Your code works fine for me with the variable name change. Also got rid of the .ToString. I used .EnumerateFiles as commented by #jmcilhinney in the question you deleted. I purposely chose a path with all sorts of strange characters and it still worked.
Private Sub OPCode()
Dim filepath = "C:\Users\xxxx\Documents\TextNotes\Dogs & Cats (Pets)"
Dim Filename = "Specialty Vets.txt"
Dim foundFile = Directory.EnumerateFiles(filepath, Filename,
IO.SearchOption.AllDirectories).FirstOrDefault()
Process.Start("explorer.exe", "/select," & foundFile)
End Sub

callback function from unmanaged dll in VB .NET

I'm trying to use an unmanaged dll in VB.NET. The example source code provided with the dll is in VB6 and below is my attempt to convert it to .NET. When the dll tries to do a callback I get a "Attempted to read or write protected memory" exception. I really don't care about the callback function getting actually called.
My code:
<DllImport("AlertMan.dll")> _
Public Shared Function AlertManC( _
ByVal CallbackAddr As AlertManCallbackDel) As Long
End Function
Public Delegate Sub AlertManCallbackDel(ByVal data As Long)
Public Sub AlertManCallback(ByVal data As Long)
End Sub
Public mydel As New AlertManCallbackDel(AddressOf AlertManCallback)
'protected memeory exception here
Dim IStat as Long = AlertManC(mydel)
Original VB6 example code:
Declare Function AlertManC _
Lib "AlertMan.dll" _
Alias "AlertManC" (ByVal CallbackAddr As Long) As Long
Private Sub AlertManCallback(ByVal data As Long)
End Sub
' calling code
Dim IStat As Long
IStat = AlertManC(AddressOf AlertManCallBack)
Original dll header
typedef void TACBFUNC(char *);
int AlertManC(TACBFUNC *WriteCaller cHANDLEPARM);
Can you post the original native definiton for AlertManC?
My guess though is that the data parameter of the callback function is actually an Integer vs. a Long. In VB6 I believe Long's were actually only 32 bits vs. VB.Net where they are 64 bit. Try this
<DllImport("AlertMan.dll")> _
Public Shared Function AlertManC(ByVal CallbackAddr As AlertManCallbackDel) As Long
End Function
Public Delegate Sub AlertManCallbackDel(ByVal data As IntPtr)
Public Sub AlertManCallback(ByVal data As IntPtr)
End Sub
Edit
I updated the code based on the native signature you posted. Can you try this out?
Your callback should look like this:
Public Delegate Sub AlertManCallbackDel(ByRef data As Byte)
The reason for this being that you are passing a single-byte value by reference.
As for the declaration of the unmanaged function, it should look like this:
<DllImport("AlertMan.dll")> _
Public Shared Function AlertManC( _
ByVal CallbackAddr As AlertManCallbackDel) As Integer
End Function
Note that the return type is an Integer, which in VB.NET is a 32-bit value. In VB6, a Long was a 32-bit value, hence the need for a change in VB.NET.
The callback definition is important to get right as well, btw.
If the callback's calling convention is cdecl, you cant do that directly in C# or VB.NET.
You will have to modify the IL of the delegate to behave correctly.
You can search on CodeProject for an in-depth article.
Update:
I guess not the correct answer :) But will leave my response.

Icon.ExtractAssociatedIcon for things that are not files?

Is this possible? It gives me an error, and I had previously thought it could work for folders and drives and stuff like that as well.
Icon.ExtractAssociatedIcon("C:\") did not work when I tried it, and threw an error.
How can I get the associated icon from EVERYTHING? This is vb.net
The SHGetFileInfo() shell function can provide you with the icon you are looking for. This code worked well, it generated appropriate icons for drives, folders and files:
Imports System.Drawing
Imports System.Reflection
Imports System.Runtime.InteropServices
Public Module NativeMethods
Public Function GetShellIcon(ByVal path As String) As Icon
Dim info As SHFILEINFO = New SHFILEINFO()
Dim retval as IntPtr = SHGetFileInfo(path, 0, info, Marshal.SizeOf(info), &H100)
If retval = IntPtr.Zero Then Throw New ApplicationException("Could not retrieve icon")
'' Invoke private Icon constructor so we do not have to copy the icon
Dim cargt() As Type = { GetType(IntPtr) }
Dim ci As ConstructorInfo = GetType(Icon).GetConstructor(BindingFlags.NonPublic Or BindingFlags.Instance, Nothing, cargt, Nothing)
Dim cargs() As Object = { info.IconHandle }
Dim icon As Icon = CType(ci.Invoke(cargs), Icon)
Return icon
End Function
'' P/Invoke declaration
Private Structure SHFILEINFO
Public IconHandle As IntPtr
Public IconIndex As Integer
Public Attributes As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public DisplayString As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=80)> _
Public TypeName As String
End Structure
Private Declare Auto Function SHGetFileInfo lib "Shell32.dll" (path As String, _
attributes As Integer, byref info As SHFILEINFO, infoSize As Integer, flags As Integer) As IntPtr
End Module
It is not possible to use Icon.ExtractAssociatedIcon on anything other than files. This API is a thin wrapper on top of the Win32 call ExtractAssociatedIcon. While the documentation for the managed code is a bit ambiguous, the native documentation is much clearer that the target must be a file. It goes further to say that it must be an executable file.
Unfortunately I'm not sure if there is an equivalent function for Directories or not.

How to Access Custom Add-In Ribbon Check Box in VBA?

I've spent 2 days now trying and searching and nothing seems to be working...
I created a custom ribbon Add-In for Visio in VSTO that installs and buttons work fine. I just recently added a couple of checkboxes to the ribbon whose states I want to read from within a VBA project.
I can't for the life of me figure out how to access the checkbox state in VBA. I tried a bunch of things with CommandBars and ToolBars but got nowhere and then I found this walkthrough which seemed promising and followed it to make the Add-In's methods visible to VBA: https://msdn.microsoft.com/en-us/library/bb608614
The VBA code does recognize the add-in and I assign the add-in object but when I try to call the object's function (getIOPressedState which refers to the state of one of the checkboxes), I get "object doesn't support this property or method".
Am I missing something here??
This is my ribbon class I want to make visible
<ComVisible(True)> _
Public Interface IAddInUtilities
Function getIOPressed() As Boolean
Function getDDPressed() As Boolean
Sub doNothing()
End Interface
<Runtime.InteropServices.ComVisible(True)> _
<ClassInterface(ClassInterfaceType.None)> _
Public Class StructuredAnalysisRibbon
Implements Office.IRibbonExtensibility, IAddInUtilities
Public ioPressedState As Boolean = False
Public ddPressedState As Boolean = False
Public ribbon As Office.IRibbonUI
Public Function GetCustomUI(ByVal ribbonID As String) As String Implements Office.IRibbonExtensibility.GetCustomUI
Return getResourceText("SAVisioAddIn.StructuredAnalysisRibbon.xml")
End Function
Public Function getIOPressed() As Boolean Implements IAddInUtilities.getIOPressed
Return ioPressedState
End Function
Public Function getDDPressed() As Boolean Implements IAddInUtilities.getDDPressed
Return ddPressedState
End Function
Public Sub doNothing() Implements IAddInUtilities.doNothing
'do nothing-added this to see if function As boolean in interface was causing issues
End Sub
ThisAddIn.vb
Public SARibbon As StructuredAnalysisRibbon
Protected Overrides Function CreateRibbonExtensibilityObject() As Microsoft.Office.Core.IRibbonExtensibility
Return SARibbon
End Function
Protected Overrides Function RequestComAddInAutomationService() As Object
If SARibbon Is Nothing Then
SARibbon = New StructuredAnalysisRibbon
End If
Return SARibbon
End Function
Visio VBA Code
Public Sub bloop()
Dim addIn As COMAddIn
Dim addInObject As Object
Dim ioPressed As Boolean
ioPressed = False
Set addIn = Application.COMAddIns.Item("SAVisioAddIn")
Set addInObject = addIn.Object
ioPressed = addInObject.getIOPressed 'fails here bc method not recognized for object
'Also tried addIn.Object.doNothing and still didn't work
If ioPressed = True Then
MsgBox "checked"
Else
MsgBox "not checked"
End If
End Sub
I think the problem has nothing to do with checkboxes, the point is VBA by default returns you the default object interface (which in your code is NOT the IAddInUtilities). Just swap the interfaces. The IAddInUtilities should be default (first). Or remove IAddInUtilities at all, along with fancy COM stuff like ClassInterface(ClassInterfaceType.None) which is considered harmful :) Anyways, the easiest may be:
Implements IAddInUtilities, Office.IRibbonExtensibility