The following is the code i m using to convert doc file to image.this works well for a file that contains only one page but if there are more than one page in doc file then it converts only first page of file to image.Can some one suggest me how to convert all pages of doc file to seperate images.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim objWord As New Microsoft.Office.Interop.Word.Application
Dim objDoc As Microsoft.Office.Interop.Word.Document
Const CF_ENHMETAFILE As Integer = 14
objDoc = objWord.Documents.Open("F:\Study\Constructor.docx")
objWord.Activedocument.Select()
objWord.Selection.CopyAsPicture()
Dim ip As IntPtr
Dim metaFile As System.Drawing.Imaging.Metafile
Dim bRet As Boolean
bRet = ClipboardAPI.OpenClipboard(Me.Handle)
If bRet = True Then
'Verify the clipboard contains data available
'as an enhanced metafile.
bRet = ClipboardAPI.IsClipboardFormatAvailable(CF_ENHMETAFILE) <> 0
End If
If bRet = True Then
'Store the clipboard's contents in the IntPtr.
ip = ClipboardAPI.GetClipboardData(CF_ENHMETAFILE)
End If
'Verify the IntPrt contains data before proceeding. Passing
'an empty IntPtr to System.Drawing.Imaging.Metafile results
'in an exception.
If Not IntPtr.Zero.Equals(ip) Then
metaFile = New System.Drawing.Imaging.Metafile(ip, True)
ClipboardAPI.CloseClipboard()
Dim image As System.Drawing.Image = metaFile
'Me.PictureBox1.Image = metaFile
Dim objImageWriter As Image = New Bitmap(image.Width, image.Height)
Dim objGraphics As Graphics = Graphics.FromImage(objImageWriter)
objGraphics.Clear(Color.White)
'objGraphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
objGraphics.DrawImage(image, 0, 0, image.Width, image.Height)
image.Dispose()
objGraphics.Dispose()
Dim ep As Imaging.EncoderParameters = New Imaging.EncoderParameters
ep.Param(0) = New System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100)
Dim codecs() As Imaging.ImageCodecInfo = Imaging.ImageCodecInfo.GetImageEncoders()
Dim iciInfo As Imaging.ImageCodecInfo
Dim item As Imaging.ImageCodecInfo
For Each item In codecs
If (item.MimeType = "image/jpeg") Then iciInfo = item
Next
objImageWriter.Save("F:\Study\test1.jpg", iciInfo, ep)
objImageWriter.Dispose()
End If
Public Class ClipboardAPI
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="OpenClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function OpenClipboard(ByVal hWnd As IntPtr) As Boolean
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="EmptyClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function EmptyClipboard() As Boolean
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="SetClipboardData", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function SetClipboardData(ByVal uFormat As Integer, ByVal ByValhWnd As IntPtr) As IntPtr
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="CloseClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function CloseClipboard() As Boolean
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="GetClipboardData", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function GetClipboardData(ByVal uFormat As Integer) As IntPtr
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="IsClipboardFormatAvailable", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function IsClipboardFormatAvailable(ByVal uFormat As Integer) As Short
End Function
End Class
The problem is that the line "objWord.Activedocument.Select()" references the entire document rather than the document's individual pages. I've added a bit to your code to snap an image of each page's contents:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim objWord As New Microsoft.Office.Interop.Word.Application
Dim objDoc As Microsoft.Office.Interop.Word.Document
Const CF_ENHMETAFILE As Integer = 14
objDoc = objWord.Documents.Open("F:\Study\Constructor.docx")
objDoc.Repaginate()
For i As Integer = 1 To objDoc.ActiveWindow.Panes(1).Pages.Count
If i = 1 Then
With objWord.ActiveDocument
.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, 1)
.Bookmarks("\Page").Range.Select()
End With
Else
With objWord.Selection
.GoTo(What:=WdGoToItem.wdGoToPage, Which:=WdGoToDirection.wdGoToNext)
.Bookmarks("\Page").Range.Select()
End With
End If
objWord.Selection.CopyAsPicture()
Dim ip As IntPtr
Dim metaFile As System.Drawing.Imaging.Metafile
Dim bRet As Boolean
bRet = ClipboardAPI.OpenClipboard(Me.Handle)
If bRet = True Then
'Verify the clipboard contains data available
'as an enhanced metafile.
bRet = ClipboardAPI.IsClipboardFormatAvailable(CF_ENHMETAFILE) <> 0
End If
If bRet = True Then
'Store the clipboard's contents in the IntPtr.
ip = ClipboardAPI.GetClipboardData(CF_ENHMETAFILE)
End If
'Verify the IntPrt contains data before proceeding. Passing
'an empty IntPtr to System.Drawing.Imaging.Metafile results
'in an exception.
If Not IntPtr.Zero.Equals(ip) Then
metaFile = New System.Drawing.Imaging.Metafile(ip, True)
ClipboardAPI.CloseClipboard()
Dim image As System.Drawing.Image = metaFile
'Me.PictureBox1.Image = metaFile
Dim objImageWriter As Image = New Bitmap(image.Width, image.Height)
Dim objGraphics As Graphics = Graphics.FromImage(objImageWriter)
objGraphics.Clear(Color.White)
'objGraphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
objGraphics.DrawImage(image, 0, 0, image.Width, image.Height)
image.Dispose()
objGraphics.Dispose()
Dim ep As Imaging.EncoderParameters = New Imaging.EncoderParameters
ep.Param(0) = New System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100)
Dim codecs() As Imaging.ImageCodecInfo = Imaging.ImageCodecInfo.GetImageEncoders()
Dim iciInfo As Imaging.ImageCodecInfo
Dim item As Imaging.ImageCodecInfo
For Each item In codecs
If (item.MimeType = "image/jpeg") Then iciInfo = item
Next
objImageWriter.Save("F:\Study\test" & i.ToString & ".jpg", iciInfo, ep)
objImageWriter.Dispose()
End If
Next
End Sub
Summary of the additional code changes:
I added "objDoc.Repaginate()" to get accurate page references. Word does not really make use of pages ordinarily, it is constantly querying the system's print driver to decide where it needs to break text up into pages. This ensures that we have an accurate page count in accordance with the current machine.
I enclosed your image logic in this for-loop: "For i As Integer = 1 To objDoc.ActiveWindow.Panes(1).Pages.Count". The if-else directly following that line will select the first page in the first iteration, and then any subsequent additional pages thereafter. Everything else that follows is unchanged except for the save-filename.
Lastly, I just concatenated the page number into the image's save-path for obvious reasons...
I tested this on my own computer and it worked as intended, I hope this helps!
...Just an off-topic sidenote, I don't know if the code disposing of the Word handles just wasn't included in your question or if it's actually missing, but you may want to make sure you add that; Interop class loves to leave running office processes in the background even after the program has closed if they aren't disposed of properly, this example was leaving them open on my computer.
Related
I have this program that shows files with its icons using a ListView and it works a little bit fine but there's a problem, some files(.exe, .docx etc...) don't show their right icon like this. how do I fix that?
This is how I call the Shell:
' declare the Win32 API function SHGetFileInfo'
Public Declare Auto Function SHGetFileInfo Lib "shell32.dll" (ByVal pszPath As String, ByVal dwFileAttributes As Integer, ByRef psfi As SHFILEINFO, ByVal cbFileInfo As Integer, ByVal uFlags As Integer) As IntPtr
' declare some constants that SHGetFileInfo requires'
Public Const SHGFI_ICON As Integer = &H100
Public Const SHGFI_SMALLICON As Integer = &H1
' define the SHFILEINFO structure'
Structure SHFILEINFO
Public hIcon As IntPtr
Public iIcon As Integer
Public dwAttributes As Integer
<Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst:=260)> _
Public szDisplayName As String
<Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst:=80)> _
Public szTypeName As String
End Structure
Function RetrieveShellIcon(ByVal argPath As String) As Image
Dim mShellFileInfo As SHFILEINFO
Dim mSmallImage As IntPtr
Dim mIcon As System.Drawing.Icon
Dim mCompositeImage As Image
mShellFileInfo = New SHFILEINFO
mShellFileInfo.szDisplayName = New String(Chr(0), 260)
mShellFileInfo.szTypeName = New String(Chr(0), 80)
mSmallImage = SHGetFileInfo(argPath, 0, mShellFileInfo, System.Runtime.InteropServices.Marshal.SizeOf(mShellFileInfo), SHGFI_ICON Or SHGFI_SMALLICON)
' create the icon from the icon handle'
Try
mIcon = System.Drawing.Icon.FromHandle(mShellFileInfo.hIcon)
mCompositeImage = mIcon.ToBitmap
Catch ex As Exception
' create a blank black bitmap to return'
mCompositeImage = New Bitmap(16, 16)
End Try
' return the composited image'
Return mCompositeImage
End Function
Function GetIcon(ByVal argFilePath As String) As Image
Dim mFileExtension As String = System.IO.Path.GetExtension(argFilePath)
' add the image if it doesn't exist'
If cIcons.ContainsKey(mFileExtension) = False Then
cIcons.Add(mFileExtension, RetrieveShellIcon(argFilePath))
End If
' return the image'
Return cIcons(mFileExtension)
End Function
and this is how I show file icons in my `ListView.
Sub lv1items()
Dim lvi As ListViewItem
Dim di As New DirectoryInfo(Form2.TextBox1.Text)
Dim exts As New List(Of String)
ImageList1.Images.Clear()
If di.Exists = False Then
MessageBox.Show("Source path is not found", "Directory Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
For Each fi As FileInfo In di.EnumerateFiles("*.*")
lvi = New ListViewItem
lvi.Text = fi.Name
lvi.SubItems.Add(((fi.Length / 1024)).ToString("0.00"))
lvi.SubItems.Add(fi.CreationTime.ToShortDateString)
If exts.Contains(fi.Extension) = False Then
Dim mShellIconManager As New Form1
For Each mFilePath As String In My.Computer.FileSystem.GetFiles(Form2.TextBox1.Text)
ImageList1.Images.Add(fi.Extension, GetIcon(mFilePath))
exts.Add(fi.Extension)
Next
End If
lvi.ImageKey = fi.Extension
ListView1.Items.Add(lvi)
Next
End If
End Sub
That appears to be a weird limitation of the .net implication
its really just making a call to shell32.dll
You should call the function in shell32 directly
something like this should work
<DllImport("shell32.dll")>
Private Shared Function ExtractAssociatedIcon(hInst As IntPtr, lpIconPath As StringBuilder, ByRef lpiIcon As UShort) As IntPtr
End Function
_
Dim handle As IntPtr = SafeNativeMethods.ExtractAssociatedIcon(New HandleRef(Nothing, IntPtr.Zero), iconPath, index)
If handle <> IntPtr.Zero Then
Return Icon.FromHandle(handle)
End If
The syntax might not be exactly correct, also there is a good blog post about how to pull that information from the registry (which won't always give you the correct answer, but its faster)
Building a Better ExtractIcon (he uses the SHGetFileInfo API in shell32.dll if that blog ever dies it will give people a place to start looking)
I'm re-visiting a tool that I wrote in VB.Net for my helpdesk team a while back and want to add a couple of checkboxes to replicate the same function that Windows uses to show hidden files and folders / re-hide, as well as protected operating system files.
I know I can do this by editing a registry entry and restarting explorer.exe, but that closes all open Explorer Windows and I don't want that.
Does anyone know how Windows is able to do this by a simple click of a checkbox and how I may be able to code it in VB.net?
Any input on this is greatly appreciated in advance.
EDIT: So it looks like I have found a refresh method that works to refresh Windows Explorer / File Explorer which can be applied to Drarig's answer below but I am having trouble converting it to VB.net as the original example is in C#.
'Original at http://stackoverflow.com/questions/2488727/refresh-windows-explorer-in-win7
Private Sub refreshExplorer(ByVal explorerType As String)
Dim CLSID_ShellApplication As Guid = Guid.Parse("13709620-C279-11CE-A49E-444553540000")
Dim shellApplicationType As Type = Type.GetTypeFromCLSID(CLSID_ShellApplication, True)
Dim shellApplication As Object = Activator.CreateInstance(shellApplicationType)
Dim windows As Object = shellApplicationType.InvokeMember("Windows", Reflection.BindingFlags.InvokeMethod, Nothing, shellApplication, New Object() {})
Dim windowsType As Type = windows.GetType()
Dim count As Object = windowsType.InvokeMember("Count", Reflection.BindingFlags.GetProperty, Nothing, windows, Nothing)
For i As Integer = 0 To CType(count, Integer)
Dim item As Object = windowsType.InvokeMember("Item", Reflection.BindingFlags.InvokeMethod, Nothing, windows, New Object() {i})
Dim itemType As Type = item.GetType()
'Only fresh Windows explorer Windows
Dim itemName As String = CType(itemType.InvokeMember("Name", Reflection.BindingFlags.GetProperty, Nothing, item, Nothing), String)
If itemName = explorerType Then
itemType.InvokeMember("Refresh", Reflection.BindingFlags.InvokeMethod, Nothing, item, Nothing)
End If
Next
End Sub
I am getting an exception Object reference not set to an instance of an object when I set itemType as Type = item.GetType() above. I can't figure out which object isn't being created. When I step through the code it looks like windowsType contains an object for windows. Does anyone have any idea on this? Once this is worked out I can then apply it to Drarig's solution below.
Alright I wish I could have got this to you sooner, but busy lately at work. I took a little time today to figure this out as I love digging into something I have not done before. This is the whole class from a new project; didn't have time to wrap it up in a separate class. I am sure this will get you what you need. It was a little harder than I thought as getting the correct handle and then send the command, but I got it. I hope you find it useful.
P.S. Some of the things you can leave out, specifically the boolean used for loading, this was so I can pull the current value back on load and either check/uncheck the CheckBox.
Note: This is tried and tested on Windows 7, 8 and 10
Imports Microsoft.Win32
Imports System.Reflection
Imports System.Runtime.InteropServices
Public Class Form1
<Flags()> _
Public Enum KeyboardFlag As UInteger
KEYBOARDF_5 = &H74
End Enum
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function GetWindow(ByVal hl As Long, ByVal vm As Long) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function PostMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
End Function
Private blnLoading As Boolean = False
Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
Form1.HideFilesExtension(Me.CheckBox1.Checked)
If Not blnLoading Then NotifyFileAssociationChanged()
RefreshExplorer()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim name As String = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
Dim key As RegistryKey = Registry.CurrentUser.OpenSubKey(name, False)
blnLoading = True
Me.CheckBox1.Checked = CBool(key.GetValue("Hidden"))
key.Close()
blnLoading = False
End Sub
Private Shared Sub HideFilesExtension(ByVal Hide As Boolean)
Dim name As String = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
Dim key As RegistryKey = Registry.CurrentUser.OpenSubKey(name, True)
key.SetValue("Hidden", If(Hide, 1, 0))
key.Close()
End Sub
Public Shared Sub RefreshExplorer()
Dim clsid As New Guid("13709620-C279-11CE-A49E-444553540000")
Dim typeFromCLSID As Type = Type.GetTypeFromCLSID(clsid, True)
Dim objectValue As Object = Activator.CreateInstance(typeFromCLSID)
Dim obj4 As Object = typeFromCLSID.InvokeMember("Windows", BindingFlags.InvokeMethod, Nothing, objectValue, New Object(0 - 1) {})
Dim type1 As Type = obj4.GetType
Dim obj2 As Object = type1.InvokeMember("Count", BindingFlags.GetProperty, Nothing, obj4, Nothing)
If (CInt(obj2) <> 0) Then
Dim num2 As Integer = (CInt(obj2) - 1)
Dim i As Integer = 0
Do While (i <= num2)
Dim obj5 As Object = type1.InvokeMember("Item", BindingFlags.InvokeMethod, Nothing, obj4, New Object() {i})
Dim type3 As Type = obj5.GetType
Dim str As String = CStr(type3.InvokeMember("Name", BindingFlags.GetProperty, Nothing, obj5, Nothing))
If (str = "File Explorer") Then
type3.InvokeMember("Refresh", BindingFlags.InvokeMethod, Nothing, obj5, Nothing)
End If
i += 1
Loop
End If
End Sub
Public Shared Sub NotifyFileAssociationChanged()
'Find the actual window...
Dim hwnd As IntPtr = FindWindow("Progman", "Program Manager")
'Get the window handle and refresh option...
Dim j = GetWindow(hwnd, 3)
'Finally post the message...
PostMessage(j, 256, KeyboardFlag.KEYBOARDF_5, 3)
End Sub
End Class
Here's a solution for everything excepting the refreshing of the explorer.
I've translated the code, but I'm unable to find how to refresh the explorer/desktop without restarting it.
Const keyName As String = "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
Const Hidden As String = "Hidden"
Const SHidden As String = "ShowSuperHidden"
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim St As Integer = GetRegValue(Hidden)
If St = 2 Then
SetRegValue(Hidden, 1)
SetRegValue(SHidden, 1)
Else
SetRegValue(Hidden, 2)
SetRegValue(SHidden, 0)
End If
End Sub
Private Function GetRegValue(valueName As String) As Integer
Return CInt(My.Computer.Registry.GetValue(keyName, valueName, 0))
End Function
Private Sub SetRegValue(valueName As String, value As Integer)
My.Computer.Registry.SetValue(keyName, valueName, value, Microsoft.Win32.RegistryValueKind.DWord)
End Sub
I have a few ideas to refresh the desktop :
Send a key to a running process. I tried this (source) :
Dim pp As Process() = Process.GetProcessesByName("explorer")
If pp.Length > 0 Then
For Each p In pp
AppActivate(p.Id)
SendKeys.SendWait("{F5}")
Next
End If
Refresh using SHChangeNotify (source),
Refresh broadcasting a WM_SETTINGCHANGE message (source),
etc.
I think you'll be forced to manually refresh or restart the explorer.
Hi Below is the steps through which one can capture word document first page image in PNG format. If user need to capture image in any other format. Then just replace the image extension with your needed output image extension in below code. If this answer is useful then don't forget to vote.
First Step: Very beginning you need to Import / Reference below namespace in your project:
Import Imports Microsoft.Office.Interop.Word
Second Step: Add blank class file in your project and copy the below vb.net code for word document:
Public Class ClipboardAPI
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="OpenClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function OpenClipboard(ByVal hWnd As IntPtr) As Boolean
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="EmptyClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function EmptyClipboard() As Boolean
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="SetClipboardData", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function SetClipboardData(ByVal uFormat As Integer, ByVal ByValhWnd As IntPtr) As IntPtr
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="CloseClipboard", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function CloseClipboard() As Boolean
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="GetClipboardData", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function GetClipboardData(ByVal uFormat As Integer) As IntPtr
End Function
<Runtime.InteropServices.DllImport("user32.dll", EntryPoint:="IsClipboardFormatAvailable", SetLastError:=True, ExactSpelling:=True, CallingConvention:=System.Runtime.InteropServices.CallingConvention.StdCall)> _
Public Shared Function IsClipboardFormatAvailable(ByVal uFormat As Integer) As Short
End Function
End Class
Third Step: Here is the code for capturing image from word document only for first page.
Dim Width, Height, Orientation as Integer
Dim objWord As New Microsoft.Office.Interop.Word.Application
Dim objDoc As Microsoft.Office.Interop.Word.Document
Const CF_ENHMETAFILE As Integer = 14
objDoc = objWord.Documents.Open(YourSourcePath)
objWord.ActiveDocument.Select()
objWord.Selection.CopyAsPicture()
Try
Dim ip As IntPtr
Dim metaFile As System.Drawing.Imaging.Metafile
Dim bRet As Boolean
bRet = ClipboardAPI.OpenClipboard(Me.Handle)
If bRet = True Then
'Verify the clipboard contains data available
'as an enhanced metafile.
bRet = ClipboardAPI.IsClipboardFormatAvailable(CF_ENHMETAFILE) <> 0
End If
If bRet = True Then
'Store the clipboard's contents in the IntPtr.
ip = ClipboardAPI.GetClipboardData(CF_ENHMETAFILE)
End If
'Verify the IntPrt contains data before proceeding. Passing
'an empty IntPtr to System.Drawing.Imaging.Metafile results
'in an exception.
If Not IntPtr.Zero.Equals(ip) Then
metaFile = New System.Drawing.Imaging.Metafile(ip, True)
ClipboardAPI.CloseClipboard()
Dim image As System.Drawing.Image = metaFile
'Me.PictureBox1.Image = metaFile
Dim objImageWriter As Image = New Bitmap(image)
Dim objGraphics As Graphics = Graphics.FromImage(objImageWriter)
objGraphics.Clear(Color.White)
objGraphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
If Orientation = 1 Then
Width = image.Width
Height = image.Height
Else
Width = image.Height
Height = image.Width
End If
objGraphics.DrawImage(image, 0, 0, Width, Height)
image.Dispose()
objGraphics.Dispose()
Dim ep As Imaging.EncoderParameters = New Imaging.EncoderParameters
ep.Param(0) = New System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100)
Dim codecs() As Imaging.ImageCodecInfo = Imaging.ImageCodecInfo.GetImageEncoders()
Dim iciInfo As Imaging.ImageCodecInfo
Dim item As Imaging.ImageCodecInfo
For Each item In codecs
If (item.MimeType = "image/png") Then iciInfo = item
Next
ImageFileName = Format(Now, "ddMMMyyyy_hhmmss_") & RandNumber.Next & ".png"
ImageFilePath = Application.StartupPath & "\" & ImageFileName
objImageWriter.Save(ImageFilePath, iciInfo, ep)
objImageWriter.Dispose()
End If
Catch ex As Exception
ExceptionGenerated = True
Throw New System.Exception(ex.Message.ToString())
Finally
objDoc.Close()
objWord.Application.Quit(False)
objDoc = Nothing
objWord = Nothing
ReleaseComObject(objWord)
ReleaseComObject(objDoc)
End Try
Fourth Step: Add ReleaseComObject function in your project:
Public Sub ReleaseComObject(ByVal obj As Object)
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
obj = Nothing
Catch ex As Exception
obj = Nothing
End Try
End Sub
You can even check your word page orientation using below vb.net code:
objDoc.PageSetup.Orientation = Microsoft.Office.Interop.Word.WdOrientation.wdOrientPortrait / wdOrientLandscape
I am new to stackoverflow but I registered because I think here is the right place to get professional help for programming :)
My goal is to create a webcam snapshot tool which directly saves the snapshot to a file.
I don't need any preview in a picturebox or something like that.
I am thinking about a application like this:
A simple Interface with a Combobox for the connected webcam devices and one button which will take a snapshot and saves it to a file.
I like to use DirectShow for this because all other ways using AForge or advcap32.dll, because they sometimes cause
a Videosourcedialog to popup, which I don't want to.
I like to select a webcamdevice in my combobox manually and be able to take a snapshot.
So that way I like to use DirectShow.
I already added the DirectShowLib-2005.dll to my VB.Net Project
And I also added this class:
Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Imports System.Diagnostics
Imports DirectShowLib
Public Class Capture
Implements ISampleGrabberCB
Implements IDisposable
#Region "Member variables"
Private m_graphBuilder As IFilterGraph2 = Nothing
Private m_mediaCtrl As IMediaControl = Nothing
Private mediaEventEx As IMediaEventEx = Nothing
Private videoWindow As IVideoWindow = Nothing
Private UseHand As IntPtr = MainForm.PictureBox1.Handle
Private Const WMGraphNotify As Integer = 13
Private m_takePicture As Boolean = False
Public mytest As String = "yes"
Dim sampGrabber As ISampleGrabber = Nothing
Private bufferedSize As Integer = 0
Private savedArray() As Byte
Public capturedPic As bitmap
Public captureSaved As Boolean
Public unsupportedVideo As Boolean
' <summary> Set by async routine when it captures an image </summary>
Public m_bRunning As Boolean = False
' <summary> Dimensions of the image, calculated once in constructor. </summary>
Private m_videoWidth As Integer
Private m_videoHeight As Integer
Private m_stride As Integer
Private m_bmdLogo As BitmapData = Nothing
Private m_Bitmap As Bitmap = Nothing
#If DEBUG Then
' Allow you to "Connect to remote graph" from GraphEdit
Private m_rot As DsROTEntry = Nothing
#End If
#End Region
#Region "API"
Declare Sub CopyMemory Lib "Kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As IntPtr, ByVal Source As IntPtr, <MarshalAs(UnmanagedType.U4)> ByVal Length As Integer)
#End Region
' zero based device index, and some device parms, plus the file name to save to
Public Sub New(ByVal iDeviceNum As Integer, ByVal iFrameRate As Integer, ByVal iWidth As Integer, ByVal iHeight As Integer)
Dim capDevices As DsDevice()
' Get the collection of video devices
capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice)
If (iDeviceNum + 1 > capDevices.Length) Then
Throw New Exception("No video capture devices found at that index!")
End If
Dim dev As DsDevice = capDevices(iDeviceNum)
Try
' Set up the capture graph
SetupGraph(dev, iFrameRate, iWidth, iHeight)
Catch
Dispose()
If unsupportedVideo Then
msgbox("This video resolution isn't supported by the camera - please choose a different resolution.")
Else
Throw
End If
End Try
End Sub
' <summary> release everything. </summary>
Public Sub Dispose() Implements IDisposable.Dispose
CloseInterfaces()
If (Not m_Bitmap Is Nothing) Then
m_Bitmap.UnlockBits(m_bmdLogo)
m_Bitmap = Nothing
m_bmdLogo = Nothing
End If
End Sub
Protected Overloads Overrides Sub finalize()
CloseInterfaces()
End Sub
' <summary> capture the next image </summary>
Public Sub Start()
If (m_bRunning = False) Then
Dim hr As Integer = m_mediaCtrl.Run()
DsError.ThrowExceptionForHR(hr)
m_bRunning = True
End If
End Sub
' Pause the capture graph.
' Running the graph takes up a lot of resources. Pause it when it
' isn't needed.
Public Sub Pause()
If (m_bRunning) Then
Dim hr As Integer = m_mediaCtrl.Pause()
DsError.ThrowExceptionForHR(hr)
m_bRunning = False
End If
End Sub
'Added by jk
Public Sub TakePicture()
m_takePicture = True
End Sub
' <summary> Specify the logo file to write onto each frame </summary>
Public Sub SetLogo(ByVal fileName As String)
SyncLock Me
If (fileName.Length > 0) Then
m_Bitmap = New Bitmap(fileName)
Dim r As Rectangle = New Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height)
m_bmdLogo = m_Bitmap.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb)
Else
If Not m_Bitmap Is Nothing Then
m_Bitmap.UnlockBits(m_bmdLogo)
m_Bitmap = Nothing
m_bmdLogo = Nothing
End If
End If
End SyncLock
End Sub
' <summary> build the capture graph for grabber. </summary>
Private Sub SetupGraph(ByVal dev As DsDevice, ByVal iFrameRate As Integer, ByVal iWidth As Integer, ByVal iHeight As Integer)
Dim hr As Integer
Dim baseGrabFlt As IBaseFilter = Nothing
Dim capFilter As IBaseFilter = Nothing
Dim muxFilter As IBaseFilter = Nothing
Dim fileWriterFilter As IFileSinkFilter = Nothing
Dim capGraph As ICaptureGraphBuilder2 = Nothing
Dim sampGrabberSnap As ISampleGrabber = Nothing
' Get the graphbuilder object
m_graphBuilder = DirectCast(New FilterGraph(), IFilterGraph2)
m_mediaCtrl = DirectCast(m_graphBuilder, IMediaControl)
'if taking a picture (a still snapshot), then remove the videowindow
If Not m_takePicture Then
mediaEventEx = DirectCast(m_graphBuilder, IMediaEventEx)
videoWindow = DirectCast(m_graphBuilder, IVideoWindow)
Else
mediaEventEx = Nothing
videoWindow = Nothing
End If
#If DEBUG Then
m_rot = New DsROTEntry(m_graphBuilder)
#End If
Try
' Get the ICaptureGraphBuilder2
capGraph = DirectCast(New CaptureGraphBuilder2(), ICaptureGraphBuilder2)
' Get the SampleGrabber interface
sampGrabber = DirectCast(New SampleGrabber(), ISampleGrabber)
sampGrabberSnap = DirectCast(New SampleGrabber(), ISampleGrabber)
' Start building the graph
hr = capGraph.SetFiltergraph(DirectCast(m_graphBuilder, IGraphBuilder))
DsError.ThrowExceptionForHR(hr)
' Add the video device
hr = m_graphBuilder.AddSourceFilterForMoniker(dev.Mon, Nothing, dev.Name, capFilter)
DsError.ThrowExceptionForHR(hr)
baseGrabFlt = DirectCast(sampGrabber, IBaseFilter)
ConfigureSampleGrabber(sampGrabber)
' Add the frame grabber to the graph
hr = m_graphBuilder.AddFilter(baseGrabFlt, "Ds.NET Grabber")
DsError.ThrowExceptionForHR(hr)
' If any of the default config items are set
If (iFrameRate + iHeight + iWidth > 0) Then
SetConfigParms(capGraph, capFilter, iFrameRate, iWidth, iHeight)
End If
hr = capGraph.RenderStream(PinCategory.Capture, MediaType.Video, capFilter, baseGrabFlt, muxFilter)
DsError.ThrowExceptionForHR(hr)
'if you set the m_takePicture it won't
If Not m_takePicture Then
'Set the output of the preview
hr = mediaEventEx.SetNotifyWindow(UseHand, WMGraphNotify, IntPtr.Zero)
DsError.ThrowExceptionForHR(hr)
'Set Owner to Display Video
hr = videoWindow.put_Owner(UseHand)
DsError.ThrowExceptionForHR(hr)
'Set window location - this was necessary so that the video didn't move down and to the right when you pushed the start/stop button
hr = videoWindow.SetWindowPosition(0, 0, 320, 240)
DsError.ThrowExceptionForHR(hr)
'Set Owner Video Style
hr = videoWindow.put_WindowStyle(WindowStyle.Child)
DsError.ThrowExceptionForHR(hr)
End If
SaveSizeInfo(sampGrabber)
Finally
If (Not fileWriterFilter Is Nothing) Then
Marshal.ReleaseComObject(fileWriterFilter)
fileWriterFilter = Nothing
End If
If (Not muxFilter Is Nothing) Then
Marshal.ReleaseComObject(muxFilter)
muxFilter = Nothing
End If
If (Not capFilter Is Nothing) Then
Marshal.ReleaseComObject(capFilter)
capFilter = Nothing
End If
If (Not sampGrabber Is Nothing) Then
Marshal.ReleaseComObject(sampGrabber)
sampGrabber = Nothing
End If
End Try
End Sub
' <summary> Read and store the properties </summary>
Private Sub SaveSizeInfo(ByVal sampGrabber As ISampleGrabber)
Dim hr As Integer
' Get the media type from the SampleGrabber
Dim media As AMMediaType = New AMMediaType()
hr = sampGrabber.GetConnectedMediaType(media)
DsError.ThrowExceptionForHR(hr)
If (Not (media.formatType.Equals(FormatType.VideoInfo)) AndAlso Not (media.formatPtr.Equals(IntPtr.Zero))) Then
Throw New NotSupportedException("Unknown Grabber Media Format")
End If
' Grab the size info
Dim vInfoHeader As VideoInfoHeader = New VideoInfoHeader()
Marshal.PtrToStructure(media.formatPtr, vInfoHeader)
m_videoWidth = vInfoHeader.BmiHeader.Width
m_videoHeight = vInfoHeader.BmiHeader.Height
m_stride = CInt(m_videoWidth * (vInfoHeader.BmiHeader.BitCount / 8))
DsUtils.FreeAMMediaType(media)
media = Nothing
End Sub
' <summary> Set the options on the sample grabber </summary>
Private Sub ConfigureSampleGrabber(ByVal sampGrabber As ISampleGrabber)
Dim hr As Integer
Dim media As AMMediaType = New AMMediaType()
media.majorType = MediaType.Video
media.subType = MediaSubType.RGB24
media.formatType = FormatType.VideoInfo
hr = sampGrabber.SetMediaType(media)
DsError.ThrowExceptionForHR(hr)
DsUtils.FreeAMMediaType(media)
media = Nothing
' Configure the samplegrabber callback
hr = sampGrabber.SetOneShot(False)
DsError.ThrowExceptionForHR(hr)
If m_takePicture Then
hr = sampGrabber.SetCallback(Me, 0)
Else
hr = sampGrabber.SetCallback(Me, 0)
End If
DsError.ThrowExceptionForHR(hr)
DsError.ThrowExceptionForHR(hr)
'set the samplegrabber
sampGrabber.SetBufferSamples(False)
End Sub
' Set the Framerate, and video size
Private Sub SetConfigParms(ByVal capGraph As ICaptureGraphBuilder2, ByVal capFilter As IBaseFilter, ByVal iFrameRate As Integer, ByVal iWidth As Integer, ByVal iHeight As Integer)
Dim hr As Integer
Dim o As Object = Nothing
Dim media As AMMediaType = Nothing
Dim videoStreamConfig As IAMStreamConfig
Dim videoControl As IAMVideoControl = DirectCast(capFilter, IAMVideoControl)
' Find the stream config interface
hr = capGraph.FindInterface(PinCategory.Capture, MediaType.Video, capFilter, GetType(IAMStreamConfig).GUID, o)
videoStreamConfig = DirectCast(o, IAMStreamConfig)
Try
If (videoStreamConfig Is Nothing) Then
Throw New Exception("Failed to get IAMStreamConfig")
End If
' Get the existing format block
hr = videoStreamConfig.GetFormat(media)
DsError.ThrowExceptionForHR(hr)
' copy out the videoinfoheader
Dim v As VideoInfoHeader = New VideoInfoHeader()
Marshal.PtrToStructure(media.formatPtr, v)
' if overriding the framerate, set the frame rate
If (iFrameRate > 0) Then
v.AvgTimePerFrame = CLng(10000000 / iFrameRate)
End If
' if overriding the width, set the width
If (iWidth > 0) Then
v.BmiHeader.Width = iWidth
End If
' if overriding the Height, set the Height
If (iHeight > 0) Then
v.BmiHeader.Height = iHeight
End If
' Copy the media structure back
Marshal.StructureToPtr(v, media.formatPtr, False)
' Set the new format
hr = videoStreamConfig.SetFormat(media)
If hr <> 0 Then unsupportedVideo = True Else unsupportedVideo = False
DsError.ThrowExceptionForHR(hr)
DsUtils.FreeAMMediaType(media)
media = Nothing
' Fix upsidedown video
If (Not videoControl Is Nothing) Then
Dim pCapsFlags As VideoControlFlags
Dim pPin As IPin = DsFindPin.ByCategory(capFilter, PinCategory.Capture, 0)
hr = videoControl.GetCaps(pPin, pCapsFlags)
DsError.ThrowExceptionForHR(hr)
If (CDbl(pCapsFlags & VideoControlFlags.FlipVertical) > 0) Then
hr = videoControl.GetMode(pPin, pCapsFlags)
DsError.ThrowExceptionForHR(hr)
hr = videoControl.SetMode(pPin, 0)
End If
End If
Finally
Marshal.ReleaseComObject(videoStreamConfig)
End Try
End Sub
' <summary> Shut down capture </summary>
Private Sub CloseInterfaces()
Dim hr As Integer
Try
If (Not m_mediaCtrl Is Nothing) Then
' Stop the graph
hr = m_mediaCtrl.Stop()
m_mediaCtrl = Nothing
m_bRunning = False
'Release Window Handle, Reset back to Normal
hr = videoWindow.put_Visible(OABool.False)
DsError.ThrowExceptionForHR(hr)
hr = videoWindow.put_Owner(IntPtr.Zero)
DsError.ThrowExceptionForHR(hr)
If mediaEventEx Is Nothing = False Then
hr = mediaEventEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero)
DsError.ThrowExceptionForHR(hr)
End If
End If
Catch ex As Exception
Debug.WriteLine(ex)
End Try
#If DEBUG Then
If (Not m_rot Is Nothing) Then
m_rot.Dispose()
m_rot = Nothing
End If
#End If
If (Not m_graphBuilder Is Nothing) Then
Marshal.ReleaseComObject(m_graphBuilder)
m_graphBuilder = Nothing
End If
GC.Collect()
End Sub
' <summary> sample callback, Originally not used - call this with integer 0 on the setcallback method </summary>
Function SampleCB(ByVal SampleTime As Double, ByVal pSample As IMediaSample) As Integer Implements ISampleGrabberCB.SampleCB
myTest = "In SampleCB"
Dim i As Integer = 0
'jk added this code 10-22-13
If IsDBNull(pSample) = True Then Return -1
Dim myLen As Integer = pSample.GetActualDataLength()
Dim pbuf As IntPtr
If pSample.GetPointer(pbuf) = 0 And mylen > 0 Then
Dim buf As Byte() = New Byte(myLen) {}
Marshal.Copy(pbuf, buf, 0, myLen)
'Alter the video - you could use this to adjust the brightness/red/green, etc.
'for i = myLen-1 to 0 step -1
' buf(i) = (255 - buf(i))
'Next i
If m_takePicture Then
Dim bm As New Bitmap(m_videoWidth, m_videoHeight, Imaging.PixelFormat.Format24bppRgb)
Dim g_RowSizeBytes As Integer
Dim g_PixBytes() As Byte
mytest = "Execution point #1"
Dim m_BitmapData As BitmapData = Nothing
Dim bounds As Rectangle = New Rectangle(0, 0, m_videoWidth, m_videoHeight)
mytest = "Execution point #2"
m_BitmapData = bm.LockBits(bounds, Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format24bppRgb)
mytest = "Execution point #4"
g_RowSizeBytes = m_BitmapData.Stride
mytest = "Execution point #5"
' Allocate room for the data.
Dim total_size As Integer = m_BitmapData.Stride * m_BitmapData.Height
ReDim g_PixBytes(total_size)
mytest = "Execution point #10"
'this writes the data to the Bitmap
Marshal.Copy(buf, 0, m_BitmapData.Scan0, mylen)
capturedPic = bm
mytest = "Execution point #15"
' Release resources.
bm.UnlockBits(m_BitmapData)
g_PixBytes = Nothing
m_BitmapData = Nothing
bm = Nothing
buf = Nothing
m_takePicture = False
captureSaved = True
mytest = "Execution point #20"
End If
End If
Marshal.ReleaseComObject(pSample)
Return 0
End Function
' <summary> buffer callback, Not used - call this with integer 1 on the setcallback method </summary>
Function BufferCB(ByVal SampleTime As Double, ByVal pBuffer As IntPtr, ByVal BufferLen As Integer) As Integer Implements ISampleGrabberCB.BufferCB
SyncLock Me
myTest = "In BufferCB"
End SyncLock
Return 0
End Function
End Class
Can someone help to achieve my goal described above.
1) Enumerating Devices in Combobox
2) Snapshot selected webcam device to a file.
Any help is appreciated :)
I'm using AForge (My program does a bit more, but this will give you a start)
Mine does not pop-up the dialog, because it enumerates it itself (You may want just that code chunk)
You can also set all the My.Settings to hard-coded settings.
This does create a display for the video, but you can simply set vspMonitor.visible = False if you don't want it to display.
Imports AForge.Controls
Imports AForge.Video
Imports AForge.Video.DirectShow
Imports AForge.Video.VFW
Imports System.IO
Public Class Main
Private WithEvents timer As New Timer
'Stores the file path, e.g.: "F:\Temp"
Friend Shared strICLocation As String = My.Settings.ICSet
'Stores the common name for the file, such as "Capture" (Screenshot, whatever you want)
Friend Shared strICFileRootName As String = My.Settings.ICRootName
'Stores the image format to save in a 3 char string: PNG, JPG, BMP
Friend Shared strICType As String = My.Settings.ICType
Dim VideoCaptureSource As VideoCaptureDevice
Dim VideoDevices As New FilterInfoCollection(FilterCategory.VideoInputDevice)
Private Property VideoCapabilities As VideoCapabilities()
Dim frame As System.Drawing.Bitmap
Dim filename As String
Private Sub Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'You'll need the following items in your UI at minimum:
'Button named btnConnect, button named btnDisconnect, Video Source Player (From AForge libraries) named vspMonitor, a Combo Box named cmbVideoSource
EnumerateVideoDevices()
btnDisconnect.Enabled = False
btnConnect.Enabled = True
strICFileRootName = My.Settings.ICRootName
strICLocation = My.Settings.ICSet
lblICLocation.Text = strICLocation
End Sub
Private Sub EnumerateVideoDevices()
' enumerate video devices
VideoDevices = New FilterInfoCollection(FilterCategory.VideoInputDevice)
If VideoDevices.Count <> 0 Then
' add all devices to combo
For Each device As FilterInfo In VideoDevices
cmbVideoSource.Items.Add(device.Name)
cmbVideoSource.SelectedIndex = 0
VideoCaptureSource = New VideoCaptureDevice(VideoDevices(cmbVideoSource.SelectedIndex).MonikerString)
EnumerateVideoModes(VideoCaptureSource)
Next
Else
cmbVideoSource.Items.Add("No DirectShow devices found")
End If
cmbVideoSource.SelectedIndex = 0
End Sub
Private Sub EnumerateVideoModes(device As VideoCaptureDevice)
' get resolutions for selected video source
Me.Cursor = Cursors.WaitCursor
cmbVideoModes.Items.Clear()
Try
Dim VideoCapabilities = device.VideoCapabilities
For Each capabilty As VideoCapabilities In VideoCapabilities
If Not cmbVideoModes.Items.Contains(capabilty.FrameSize) Then
cmbVideoModes.Items.Add(capabilty.FrameSize)
End If
Next
If VideoCapabilities.Length = 0 Then
cmbVideoModes.Items.Add("Not supported")
End If
cmbVideoModes.SelectedIndex = 0
Finally
Me.Cursor = Cursors.[Default]
End Try
End Sub
#Region "IC (Image Capture)"
Private Sub btnICOptions_Click(sender As Object, e As EventArgs) Handles btnICOptions.Click
' I use a form to set to image save type; handle it however you want, including hard-coding it
Dim frm As New frmICOptions
frm.Show()
End Sub
Private Sub btnICSet_Click(sender As Object, e As EventArgs) Handles btnICSet.Click
'Make a button called btnICSet to set the save path
Dim dialog As New FolderBrowserDialog()
dialog.Description = "Select Image Capture save path"
If dialog.ShowDialog() = Windows.Forms.DialogResult.OK Then
strICLocation = dialog.SelectedPath
lblICLocation.Text = strICLocation
End If
End Sub
Private Sub ICCapture_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnICCapture.Click
'Need a button called btnICCapture. This is what will initiate the screen cap.
Try
If vspMonitor.IsRunning = True Then
If My.Settings.ICType = "PNG" Then
Dim strFilename As String = strICFileRootName & " " & Format(Now, "yyyy-MMM-dd HH.mm.ss.fff") & ".png"
vspMonitor.GetCurrentVideoFrame.Save(strICLocation & "\" & strFilename, System.Drawing.Imaging.ImageFormat.Png)
ElseIf My.Settings.ICType = "JPG" Then
Dim strFilename As String = strICFileRootName & " " & Format(Now, "yyyy-MMM-dd HH.mm.ss.fff") & ".jpg"
vspMonitor.GetCurrentVideoFrame.Save(strICLocation & "\" & strFilename, System.Drawing.Imaging.ImageFormat.Jpeg)
Else
Dim strFilename As String = strICFileRootName & " " & Format(Now, "yyyy-MMM-dd HH.mm.ss.fff") & ".bmp"
vspMonitor.GetCurrentVideoFrame.Save(strICLocation & "\" & strFilename, System.Drawing.Imaging.ImageFormat.Bmp)
End If
End If
Catch ex As Exception
MessageBox.Show("Try taking snapshot again when video image is visible.", "Cannot Save Image", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
#End Region
End Class
There may be some extraneous (to your purpose) variables and settings (my app does Image capture, screen capture, video capture, Stop Motion capture (to images or video) and Motion Detect Capture to video, so I basically yanked this code from it trying to get you in the right direction.) I'll be happy to modify it if I've left any errors.
how i can show textbox in FolderBrowserDialog like below image,
This is not directly possible, you have to fallback to using the shell function. Project + Add Reference, Browse tab, select c:\windows\system32\shell32.dll. An example of how to use it in a Winforms app:
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
Dim options As Integer = &H40 + &H200 + &H20
options += &H10 '' Adds edit box
Dim shell = New Shell32.ShellClass
Dim root = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Dim folder = CType(shell.BrowseForFolder(CInt(Me.Handle), _
"Select folder", options, root), Shell32.Folder2)
If folder IsNot Nothing Then
MsgBox("You selected " + folder.Self.Path)
End If
End Sub
Check this out : FolderBrowserDialogEx: A C# customization of FolderBrowserDialog
The code is in C#, Here is the VB Conversion
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Imports System.Diagnostics
Namespace DaveChambers.FolderBrowserDialogEx
Public Class FolderBrowserDialogEx
#Region "Fields that mimic the same-named fields in FolderBrowserDialog"
Public Property RootFolder() As Environment.SpecialFolder
Get
Return m_RootFolder
End Get
Set
m_RootFolder = Value
End Set
End Property
Private m_RootFolder As Environment.SpecialFolder
Public Property SelectedPath() As String
Get
Return m_SelectedPath
End Get
Set
m_SelectedPath = Value
End Set
End Property
Private m_SelectedPath As String
Public Property ShowNewFolderButton() As Boolean
Get
Return m_ShowNewFolderButton
End Get
Set
m_ShowNewFolderButton = Value
End Set
End Property
Private m_ShowNewFolderButton As Boolean
Public Property StartPosition() As FormStartPosition
Get
Return m_StartPosition
End Get
Set
m_StartPosition = Value
End Set
End Property
Private m_StartPosition As FormStartPosition
#End Region
' Fields specific to CustomFolderBrowserDialog
Public Property Title() As String
Get
Return m_Title
End Get
Set
m_Title = Value
End Set
End Property
Private m_Title As String
Public Property ShowEditbox() As Boolean
Get
Return m_ShowEditbox
End Get
Set
m_ShowEditbox = Value
End Set
End Property
Private m_ShowEditbox As Boolean
' These are the control IDs used in the dialog
Private Structure CtlIds
Public Const PATH_EDIT As Integer = &H3744
'public const int PATH_EDIT_LABEL = 0x3748; // Only when BIF_NEWDIALOGSTYLE
Public Const TITLE As Integer = &H3742
Public Const TREEVIEW As Integer = &H3741
Public Const NEW_FOLDER_BUTTON As Integer = &H3746
Public Const IDOK As Integer = 1
Public Const IDCANCEL As Integer = 2
End Structure
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Unicode)> _
Public Structure InitData
' Titles shouldn't too long, should they?
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 128)> _
Public Title As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := Win32.MAX_PATH)> _
Public InitialPath As String
Public ShowEditbox As Boolean
Public ShowNewFolderButton As Boolean
Public StartPosition As FormStartPosition
Public hParent As IntPtr
Public Sub New(dlg As FolderBrowserDialogEx, hParent As IntPtr)
' We need to make copies of these values from the dialog.
' I tried passing the dlg obj itself in this struct, but Windows will barf after repeated invocations.
Me.Title = dlg.Title
Me.InitialPath = dlg.SelectedPath
Me.ShowNewFolderButton = dlg.ShowNewFolderButton
Me.ShowEditbox = dlg.ShowEditbox
Me.StartPosition = dlg.StartPosition
Me.hParent = hParent
End Sub
End Structure
Public Sub New()
Title = "Browse For Folder"
' Default to same caption as std dialog
RootFolder = Environment.SpecialFolder.Desktop
SelectedPath = "c:\"
ShowEditbox = False
ShowNewFolderButton = False
StartPosition = FormStartPosition.WindowsDefaultLocation
End Sub
Public Function ShowDialog(owner As IWin32Window) As DialogResult
Dim initdata As New InitData(Me, owner.Handle)
Dim bi As New Win32.BROWSEINFO()
bi.iImage = 0
bi.hwndOwner = owner.Handle
If 0 <> Win32.SHGetSpecialFolderLocation(owner.Handle, CInt(Me.RootFolder), bi.pidlRoot) Then
bi.pidlRoot = IntPtr.Zero
End If
bi.lpszTitle = ""
bi.ulFlags = Win32.BIF_RETURNONLYFSDIRS
' do NOT use BIF_NEWDIALOGSTYLE or BIF_STATUSTEXT
If Me.ShowEditbox Then
bi.ulFlags = bi.ulFlags Or Win32.BIF_EDITBOX
End If
If Not Me.ShowNewFolderButton Then
bi.ulFlags = bi.ulFlags Or Win32.BIF_NONEWFOLDERBUTTON
End If
bi.lpfn = New Win32.BrowseCallbackProc(_browseCallbackHandler)
' Initialization data, used in _browseCallbackHandler
Dim hInit As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(initdata))
Marshal.StructureToPtr(initdata, hInit, True)
bi.lParam = hInit
Dim pidlSelectedPath As IntPtr = IntPtr.Zero
Try
pidlSelectedPath = Win32.SHBrowseForFolder(bi)
Dim sb As New StringBuilder(256)
If Win32.SHGetPathFromIDList(pidlSelectedPath, sb) Then
SelectedPath = sb.ToString()
Return DialogResult.OK
End If
Finally
' Caller is responsible for freeing this memory.
Marshal.FreeCoTaskMem(pidlSelectedPath)
End Try
Return DialogResult.Cancel
End Function
Private Function _browseCallbackHandler(hDlg As IntPtr, msg As Integer, lParam As IntPtr, lpData As IntPtr) As Integer
Select Case msg
Case Win32.BFFM_INITIALIZED
' remove context help button from dialog caption
Dim lStyle As Integer = Win32.GetWindowLong(hDlg, Win32.GWL_STYLE)
lStyle = lStyle And Not Win32.DS_CONTEXTHELP
Win32.SetWindowLong(hDlg, Win32.GWL_STYLE, lStyle)
lStyle = Win32.GetWindowLong(hDlg, Win32.GWL_EXSTYLE)
lStyle = lStyle And Not Win32.WS_EX_CONTEXTHELP
Win32.SetWindowLong(hDlg, Win32.GWL_EXSTYLE, lStyle)
_adjustUi(hDlg, lpData)
Exit Select
Case Win32.BFFM_SELCHANGED
If True Then
Dim ok As Boolean = False
Dim sb As New StringBuilder(Win32.MAX_PATH)
If Win32.SHGetPathFromIDList(lParam, sb) Then
ok = True
Dim dir As String = sb.ToString()
Dim hEdit As IntPtr = Win32.GetDlgItem(hDlg, CtlIds.PATH_EDIT)
Win32.SetWindowText(hEdit, dir)
#If UsingStatusText Then
' We're not using status text, but if we were, this is how you'd set it
Win32.SendMessage(hDlg, Win32.BFFM_SETSTATUSTEXTW, 0, dir)
#End If
#If SHBrowseForFolder_lists_links Then
' This check doesn't seem to be necessary - the SHBrowseForFolder dirtree doesn't seem to list links
Dim sfi As New Win32.SHFILEINFO()
Win32.SHGetFileInfo(lParam, 0, sfi, Marshal.SizeOf(sfi), Win32.SHGFI_PIDL Or Win32.SHGFI_ATTRIBUTES)
' fail if pidl is a link
If (sfi.dwAttributes And Win32.SFGAO_LINK) = Win32.SFGAO_LINK Then
ok = False
#End If
End If
End If
' if invalid selection, disable the OK button
If Not ok Then
Win32.EnableWindow(Win32.GetDlgItem(hDlg, CtlIds.IDOK), False)
End If
Exit Select
End If
End Select
Return 0
End Function
Private Sub _adjustUi(hDlg As IntPtr, lpData As IntPtr)
' Only do the adjustments if InitData was supplied
If lpData = IntPtr.Zero Then
Return
End If
Dim obj As Object = Marshal.PtrToStructure(lpData, GetType(InitData))
If obj Is Nothing Then
Return
End If
Dim initdata As InitData = DirectCast(obj, InitData)
' Only do the adjustments if we can find the dirtree control
Dim hTree As IntPtr = Win32.GetDlgItem(hDlg, CtlIds.TREEVIEW)
If hTree = IntPtr.Zero Then
hTree = Win32.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "SysTreeView32", IntPtr.Zero)
If hTree = IntPtr.Zero Then
' This usually means that BIF_NEWDIALOGSTYLE is enabled.
hTree = Win32.FindWindowEx(hDlg, IntPtr.Zero, "SHBrowseForFolder ShellNameSpace Control", IntPtr.Zero)
End If
End If
If hTree = IntPtr.Zero Then
Return
End If
' Prep the basic UI
Win32.SendMessage(hDlg, Win32.BFFM_SETSELECTIONW, 1, initdata.InitialPath)
Win32.SetWindowText(hDlg, initdata.Title)
If initdata.StartPosition = FormStartPosition.CenterParent Then
_centerTo(hDlg, initdata.hParent)
ElseIf initdata.StartPosition = FormStartPosition.CenterScreen Then
_centerTo(hDlg, Win32.GetDesktopWindow())
End If
' else we do nothing
' Prep the edit box
Dim rcEdit As New Win32.RECT()
Dim hEdit As IntPtr = Win32.GetDlgItem(hDlg, CtlIds.PATH_EDIT)
If hEdit <> IntPtr.Zero Then
If initdata.ShowEditbox Then
Win32.GetWindowRect(hEdit, rcEdit)
Win32.ScreenToClient(hEdit, rcEdit)
Else
Win32.ShowWindow(hEdit, Win32.SW_HIDE)
End If
End If
' make the dialog larger
Dim rcDlg As Win32.RECT
Win32.GetWindowRect(hDlg, rcDlg)
rcDlg.Right += 40
rcDlg.Bottom += 30
If hEdit <> IntPtr.Zero Then
rcDlg.Bottom += (rcEdit.Height + 5)
End If
Win32.MoveWindow(hDlg, rcDlg, True)
Win32.GetClientRect(hDlg, rcDlg)
Dim vMargin As Integer = 10
' Accomodate the resizing handle's width
Dim hMargin As Integer = 10
' SystemInformation.VerticalScrollBarWidth;
' Move the Cancel button
Dim rcCancel As New Win32.RECT()
Dim hCancel As IntPtr = Win32.GetDlgItem(hDlg, CtlIds.IDCANCEL)
If hCancel <> IntPtr.Zero Then
Win32.GetWindowRect(hCancel, rcCancel)
Win32.ScreenToClient(hDlg, rcCancel)
rcCancel = New Win32.RECT(rcDlg.Right - (rcCancel.Width + hMargin), rcDlg.Bottom - (rcCancel.Height + vMargin), rcCancel.Width, rcCancel.Height)
Win32.MoveWindow(hCancel, rcCancel, False)
End If
' Move the OK button
Dim rcOK As New Win32.RECT()
Dim hOK As IntPtr = Win32.GetDlgItem(hDlg, CtlIds.IDOK)
If hOK <> IntPtr.Zero Then
Win32.GetWindowRect(hOK, rcOK)
Win32.ScreenToClient(hDlg, rcOK)
rcOK = New Win32.RECT(rcCancel.Left - (rcCancel.Width + hMargin), rcCancel.Top, rcOK.Width, rcOK.Height)
Win32.MoveWindow(hOK, rcOK, False)
End If
' Manage the "Make New Folder" button
Dim hBtn As IntPtr = Win32.GetDlgItem(hDlg, CtlIds.NEW_FOLDER_BUTTON)
If Not initdata.ShowNewFolderButton Then
' Make sure this button is not visible
Win32.ShowWindow(hBtn, Win32.SW_HIDE)
ElseIf hBtn = IntPtr.Zero Then
' Create a button - button is only auto-created under BIF_NEWDIALOGSTYLE
' This is failing, and I don't know why!
hBtn = Win32.CreateWindowEx(&H50010000, "button", "&Make New Folder", &H4, hMargin, rcOK.Top, _
105, rcOK.Height, hDlg, New IntPtr(CtlIds.NEW_FOLDER_BUTTON), Process.GetCurrentProcess().Handle, IntPtr.Zero)
End If
' Position the path editbox and it's label
' We'll repurpose the Title (static) control as the editbox label
Dim treeTop As Integer = vMargin
If hEdit <> IntPtr.Zero Then
Dim xEdit As Integer = hMargin
Dim cxEdit As Integer = rcDlg.Width - (2 * hMargin)
Dim hLabel As IntPtr = Win32.GetDlgItem(hDlg, CtlIds.TITLE)
If hLabel <> IntPtr.Zero Then
Dim labelText As String = "Folder: "
Win32.SetWindowText(hLabel, labelText)
' This code obtains the required size of the static control that serves as the label for the editbox.
' All this GDI code is a bit excessive, but I figured "what the hell".
Dim hdc As IntPtr = Win32.GetDC(hLabel)
Dim hFont As IntPtr = Win32.SendMessage(hLabel, Win32.WM_GETFONT, IntPtr.Zero, IntPtr.Zero)
Dim oldfnt As IntPtr = Win32.SelectObject(hdc, hFont)
Dim szLabel As Size = Size.Empty
Win32.GetTextExtentPoint32(hdc, labelText, labelText.Length, szLabel)
Win32.SelectObject(hdc, oldfnt)
Win32.ReleaseDC(hLabel, hdc)
Dim rcLabel As New Win32.RECT(hMargin, vMargin + ((rcEdit.Height - szLabel.Height) / 2), szLabel.Width, szLabel.Height)
Win32.MoveWindow(hLabel, rcLabel, False)
xEdit += rcLabel.Width
cxEdit -= rcLabel.Width
End If
' Expand the folder tree to fill the dialog
rcEdit = New Win32.RECT(xEdit, vMargin, cxEdit, rcEdit.Height)
Win32.MoveWindow(hEdit, rcEdit, False)
treeTop = rcEdit.Bottom + 5
End If
Dim rcTree As New Win32.RECT(hMargin, treeTop, rcDlg.Width - (2 * hMargin), rcDlg.Bottom - (treeTop + (2 * vMargin) + rcOK.Height))
Win32.MoveWindow(hTree, rcTree, False)
End Sub
Private Sub _centerTo(hDlg As IntPtr, hRef As IntPtr)
Dim rcDlg As Win32.RECT
Win32.GetWindowRect(hDlg, rcDlg)
Dim rcRef As Win32.RECT
Win32.GetWindowRect(hRef, rcRef)
Dim cx As Integer = (rcRef.Width - rcDlg.Width) / 2
Dim cy As Integer = (rcRef.Height - rcDlg.Height) / 2
Dim rcNew As New Win32.RECT(rcRef.Left + cx, rcRef.Top + cy, rcDlg.Width, rcDlg.Height)
Win32.MoveWindow(hDlg, rcNew, True)
End Sub
End Class
End Namespace
'=======================================================
'Service provided by Telerik (www.telerik.com)
'Conversion powered by NRefactory.
'Twitter: #telerik, #toddanglin
'Facebook: facebook.com/telerik
'=======================================================
I see two issues with the above dialogboxes (and any other dialog I've seen):
1: You cannot specify a custom start folder which will be preselected when the dialogbox opens, let's say "c:\temp"
2: When you type a path in the textbox and push TAB or ENTER this should NOT be seen as the final selected folder, but the treeview should instead move and expand to that path (just as if you did the same in Windows Explorer).
(sorry for putting this as an answer, cannot make a comment)