Solved

Controlling 3rd Party Application

Posted on 2009-07-03
20
299 Views
Last Modified: 2012-05-07
I am using EnumWindows and EnumChildWindows to get the handles of all running applications.

Say I am looking for a specific ClassName(CN) or MainWindowTitle(MWT) and once I find it I want to iterate through the controls of that specific form. How can I do this?

Basically what I am looking to do is launch an application, wait for the CN or MWT to show up, change the selection in a specific dropdown combo box, click a button (which launches a new form), wait for its CN or MWT to show up, fill in a few textboxes, and click another button.

I obviously don't expect all that code, but if I can get the control handles and maybe an example of how to do any function (click a button) I should be able to figure out the rest.

I tried uploading a 2MB .Zip file of my project but needless to say just about every file extension wasn't in the list.

Added a .JPG, thought it might help. The code snippet is what populates 'Form1' of the .JPG
    Private Sub RadButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadButton1.Click
 

        Dim winEnum As New WindowsEnumerator

        Dim winList As New System.Collections.Generic.List(Of WindowsEnumTest.ApiWindow)

        Dim childList As New System.Collections.Generic.List(Of WindowsEnumTest.ApiWindow)
 

        winList = winEnum.GetTopLevelWindows()
 

        RadTextBox1.Clear()

        RadTextBox2.Clear()

        RadTextBox3.Clear()
 

        For Each apiWin As WindowsEnumTest.ApiWindow In winList

            RadTextBox1.Text += apiWin.MainWindowTitle + vbCrLf

            RadTextBox2.Text += apiWin.ClassName + vbCrLf
 

            childList = winEnum.GetChildWindows(apiWin.hWnd)
 

            For Each apiChild As WindowsEnumTest.ApiWindow In childList

                RadTextBox3.Text += apiWin.MainWindowTitle + ":" + apiChild.MainWindowTitle + vbCrLf
 

            Next

        Next
 

    End Sub

Open in new window

0
Comment
Question by:revstudio
  • 10
  • 10
20 Comments
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Did you get the WindowsEnumerator code from here?
http://kellychronicles.spaces.live.com/blog/cns!A0D71E1614E8DBF8!217.entry

If YOU are starting the application then simply pass the MainWindowHandle() property to the GetChildWindows() function:

        Dim P As Process = Process.Start("calc")
        P.WaitForInputIdle()

        Dim childList As System.Collections.Generic.List(Of WindowsEnumTest.ApiWindow)
        childList = winEnum.GetChildWindows(P.MainWindowHandle.ToInt32) ' <--- using our instance of Process called "P"
        For Each apiChild As WindowsEnumTest.ApiWindow In childList
            RadTextBox3.Text += apiWin.MainWindowTitle + ":" + apiChild.MainWindowTitle + vbCrLf
        Next

To click a button, you could use:

    Private Const WM_LBUTTONDOWN As Integer = &H201
    Private Const BM_SETSTATE As Integer = &HF3
    Private Const BM_CLICK As Integer = &HF5
    Private Const WM_LBUTTONUP As Integer = &H202
 
    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
        (ByVal parent As IntPtr, ByVal child As IntPtr, _
        ByVal className As String, ByVal caption As String) As IntPtr
 
    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
        (ByVal handle As IntPtr, ByVal uMsg As Integer, _
         ByVal wParam As Integer, ByVal lParam As Integer) As Integer
 
    Private Sub ClickButton(ByVal handle As IntPtr)
        Call SendMessage(handle, WM_LBUTTONDOWN, 0, 0)
        Call SendMessage(handle, BM_SETSTATE, 1, 0)
        Call SendMessage(handle, BM_CLICK, 0, 0)
        Call SendMessage(handle, WM_LBUTTONUP, 0, 0)
    End Sub
0
 

Author Comment

by:revstudio
Comment Utility
Based on your code snippet I created the following snippet, however no controls were returned.
Private Sub RadButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadButton1.Click
 

        Dim winEnum As New WindowsEnumerator

        Dim childList As New System.Collections.Generic.List(Of WindowsEnumTest.ApiWindow)
 

        RadTextBox3.Clear()
 

        Dim p As Process = Process.Start("C:\Program Files\3Shape\ScanItLegato\Scanit.exe")

        p.WaitForInputIdle()
 

        childList = winEnum.GetChildWindows(p.MainWindowHandle.ToInt32)
 

        For Each apiChild As WindowsEnumTest.ApiWindow In childList

            RadTextBox3.Text += p.MainWindowTitle + ":" + _

                apiChild.MainWindowTitle + ":" + apiChild.ClassName + _

                ":" + apiChild.hWnd.ToString + vbNewLine

        Next

    End Sub

Open in new window

0
 

Author Comment

by:revstudio
Comment Utility
Oddly enough, I can change the process that I start to another application like "notepad.exe" and it does return controls :(
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Use Spy++ or the free WinSpector to explore the application further:
http://www.windows-spy.com/

Do you get separate windows with these tools?  Probably not.  If this is the case then your options become much more limited!

Options?...
(1) Use a possibly less reliable automation process by injecting keystrokes.  This could be simply like using SendKeys.Send() or more sophisticated with SendMessage() or SendInput().
(2) Use some kind of framework provided by "ScanIt".  Is there an API, COM interface, ActiveX control etc?...
(3) Does the app allow any kind of automation via command-line switches?
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Can you post a screenshot of the application running?
0
 

Author Comment

by:revstudio
Comment Utility
Here is a screenshot of the application running, as well as Winspector's findings. The document also includes explanations of the steps I need to take.

Thanks in advance.
Screenshots.doc
0
 

Author Comment

by:revstudio
Comment Utility
A little more info...

This application AutoIT, actually show's where I click within the Toolbar), but I'm still getting more and more confused as I get deeper and deeper.
Screenshots2.doc
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 500 total points
Comment Utility
The standard .Net Toolstrip also has Buttons on it where each Button does NOT have its own handle so I've tested with that.

The example below clicks at the point (45, 10) relative to the ToolStrip upper left corner.  This clicked on the second button in my screenshot.

So all you need to do is figure out the handle to the ToolBar in your external app and then figure out the correct "offset" coords to click on:
Public Class Form1
 

    Private Sub ToolStripButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripButton1.Click

        MessageBox.Show("ToolStripButton 1")

    End Sub
 

    Private Sub ToolStripButton2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripButton2.Click

        MessageBox.Show("ToolStripButton 2")

    End Sub
 

    Private Sub ToolStripButton3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripButton3.Click

        MessageBox.Show("ToolStripButton 1")

    End Sub
 

    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _

        (ByVal handle As IntPtr, ByVal uMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
 

    Private Const WM_LBUTTONUP As Integer = &H202

    Private Const WM_LBUTTONDOWN As Integer = &H201
 

    Private Sub LeftClickAt(ByVal handle As IntPtr, ByVal OffsetPt As Point)

        If Not handle.Equals(IntPtr.Zero) Then

            Dim coords As Integer = CombineCoords(OffsetPt.X, OffsetPt.Y)

            Call SendMessage(handle, WM_LBUTTONDOWN, 0, coords)

            Call SendMessage(handle, WM_LBUTTONUP, 0, coords)

        End If

    End Sub
 

    Private Function CombineCoords(ByVal X As Integer, ByVal Y As Integer) As Integer

        Return X + Y * 65536

    End Function
 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        LeftClickAt(Me.ToolStrip1.Handle, New Point(45, 10))

    End Sub
 

End Class

Open in new window

ToolStripButtons.jpg
0
 

Author Comment

by:revstudio
Comment Utility
You are the man.
Can you give me one example of sending text to a textbox :)
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 

Author Comment

by:revstudio
Comment Utility
Thank you I'll take a look at your other post.

I noticed that when executing the code below that my application behaves as if RadButton3.Click is still "active" or in process. Is this normal behavior?
   Private Sub RadButton2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadButton2.Click
 

        RadTextBox3.Clear()

        RadTextBox4.Clear()
 

        winList = winEnum.GetTopLevelWindows("TShapeItMainForm.UnicodeClass")
 

        For Each apiWin As WindowsEnumTest.ApiWindow In winList
 

            childList = winEnum.GetChildWindows(apiWin.hWnd)
 

            For Each apiChild As WindowsEnumTest.ApiWindow In childList

                If apiChild.MainWindowTitle = "Toolbar3Shape3" And apiChild.ClassName = "TdfToolbar97.UnicodeClass" Then

                    GlobHwnd = apiChild.hWnd

                End If

            Next

        Next

    End Sub
 

    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _

        (ByVal handle As IntPtr, ByVal uMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
 

    Private Const WM_LBUTTONUP As Integer = &H202

    Private Const WM_LBUTTONDOWN As Integer = &H201

    Private Const WM_SETTEXT = &HC
 

    Private Sub LeftClickAt(ByVal handle As IntPtr, ByVal OffsetPt As Point)

        If Not handle.Equals(IntPtr.Zero) Then

            Dim coords As Integer = CombineCoords(OffsetPt.X, OffsetPt.Y)

            Call SendMessage(handle, WM_LBUTTONDOWN, 0, coords)

            Call SendMessage(handle, WM_LBUTTONUP, 0, coords)

        End If

    End Sub
 

    Private Function CombineCoords(ByVal X As Integer, ByVal Y As Integer) As Integer

        Return X + Y * 65536

    End Function
 

    Private Sub RadButton3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadButton3.Click

        LeftClickAt(GlobHwnd, New Point(100, 25))

    End Sub

Open in new window

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Most likely...since we are not giving focus to other control and also we have not physically moved the mouse over it so probably won't get any visual feedback off of it.
0
 

Author Comment

by:revstudio
Comment Utility
Ok,

I'm going to award you the points but figured I'd ask a few more questions (on topic) if you don't mind.

Once I have the next form opened. I need to put some data in the text boxes. However, as I look at the Winspector information on the text boxes they all have the same ClassName and no MainWindowTitle. How do I figure out which text box is the one I am looking for?  Or would you like me to ask as another question :)

Look at the .JPG. I am looking for the TCompactEdit.UnicodeClass that is expanded.

screenshot.jpg
0
 
LVL 85

Assisted Solution

by:Mike Tomlinson
Mike Tomlinson earned 500 total points
Comment Utility
When you have more than one control of the same type (classname) then you can make multiple calls to FindWindowEx() to get the correct one:

    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
        (ByVal parent As IntPtr, ByVal child As IntPtr, _
        ByVal className As String, ByVal caption As String) As IntPtr

So...

(1) Get a handle to the TdfInfoGroup.UnicodeClass "Order" control either using your enumerator code or thru FindWindow()/FindWindowEx():

    Handle1 = somehow get the root window handle...    

(2) Pass handle #1 to FindWindowEx() to get the TdfInfoPanel control:

    Handle2 = FindWindowEx(Handle1, IntPtr.Zero, "TdInfopanel", Nothing)

(3) Get a handle to the First TdInfoRow:

    Handle3 = FindWindowEx(Handle2, IntPtr.Zero, "TdInfoRow", Nothing)

(4) Pass Handle3 as the "child" using Handle2 as the parent to get the Second TdInfoRow:

    Handle4 = FindWindowEx(Handle2, Handle3, "TdInfoRow", Nothing)

I coded this in the browser but I'm pretty sure it's correct.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Oops, forgot step #5...

(5) Pass Handle4 to get the final control:

    Handle5 = FindWindowEx(Handle4, IntPtr.Zero, "TCompactEdit.UnicodeClass", Nothing)

Now Handle5 should point to your desired control.
0
 

Author Comment

by:revstudio
Comment Utility
Thanks for Step #5, I noticed it was missing but followed your example and figured the last step out.

The main issue I am having now is with calling the other form up.Once it is up using "LeftClickAt" the calling application is stuck waiting for LeftClickAt to return. I added a message box and it won't display until I close the form we called with LeftClickAt. So needless to say I can't interact with the form any further. Any suggestions?
0
 
LVL 85

Assisted Solution

by:Mike Tomlinson
Mike Tomlinson earned 500 total points
Comment Utility
Try using PostMessage() instead of SendMessage():

    Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" _
        (ByVal handle As IntPtr, ByVal uMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
0
 

Author Comment

by:revstudio
Comment Utility
The PostMessage() worked much better.

I had to use a combination of the EnumWindows, EnumChildWindows, and FindWindowsEx to accomplish it but your help was invaluable.

Here's the code I ended up with:

RadButton4_Click sends text to a control nested...

Form, Panel, Panel, Panel, Group, Group, Group, Group, Panel, Row, Row, Edit (Order No)

I included RadButton5_Click because it was the other fields I needed to populate and the way they were nested I had to use a slightly different approach and thought it might be helpful to someone in the future.
RadButton5_Click sends text to a control nested...

Form, Panel, Panel, Panel, Group, Group, Group, Panel, Row, Row, Row, Row, Row, Edit (Last Name), Row, Edit (First Name)


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _

        (ByVal handle As IntPtr, ByVal uMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
 

Private Declare Function SendMessageByString Lib "user32" Alias "SendMessageA" _

    (ByVal handle As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer
 

Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" _

        (ByVal handle As IntPtr, ByVal uMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
 

Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _

        (ByVal parent As IntPtr, ByVal child As IntPtr, ByVal className As String, ByVal caption As String) As IntPtr
 

Private Const WM_LBUTTONUP As Integer = &H202

Private Const WM_LBUTTONDOWN As Integer = &H201

Private Const WM_SETTEXT = &HC
 

Private Sub RadButton4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadButton4.Click
 

        Dim Handle1, Handle2, Handle3, Handle4, Handle5, Handle6 As Integer

        Dim GroupCount As Integer = 0
 

        winList.Clear()

        childList.Clear()
 

        winList = winEnum.GetTopLevelWindows("TShowShellManagerOrderForm.UnicodeClass")
 

        For Each apiWin As WindowsEnumTest.ApiWindow In winList
 

            childList = winEnum.GetChildWindows(apiWin.hWnd)
 

            For Each apiChild As WindowsEnumTest.ApiWindow In childList

                If apiChild.ClassName = "TdfInfoPanel" Then

                    If GroupCount = 2 Then

                        Handle1 = apiChild.hWnd

                    End If

                    GroupCount += 1

                End If
 

            Next

        Next
 

        GroupCount = 0

        childList.Clear()

        childList = winEnum.GetChildWindows(Handle1)
 

        For Each apiChild As WindowsEnumTest.ApiWindow In childList

            If apiChild.ClassName = "TdfInfoGroup.UnicodeClass" Then

                If GroupCount = 3 Then

                    Handle2 = apiChild.hWnd

                End If

                GroupCount += 1

            End If
 

        Next
 

        Handle3 = FindWindowEx(Handle2, IntPtr.Zero, "TdfInfoPanel", Nothing)

        Handle4 = FindWindowEx(Handle3, IntPtr.Zero, "TdfInfoRow", Nothing)

        Handle5 = FindWindowEx(Handle3, Handle4, "TdfInfoRow", Nothing)

        Handle6 = FindWindowEx(Handle5, IntPtr.Zero, "TCompactEdit.UnicodeClass", Nothing)
 

        Call SendMessageByString(Handle6, WM_SETTEXT, 0, "1000001")
 

    End Sub
 

    Private Sub RadButton5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadButton5.Click
 

        Dim Handle1, Handle2, Handle3, Handle4, Handle5, Handle6, Handle7 As Integer

        Dim GroupCount As Integer = 0
 

        winList.Clear()

        childList.Clear()
 

        winList = winEnum.GetTopLevelWindows("TShowShellManagerOrderForm.UnicodeClass")
 

        For Each apiWin As WindowsEnumTest.ApiWindow In winList
 

            childList = winEnum.GetChildWindows(apiWin.hWnd)
 

            For Each apiChild As WindowsEnumTest.ApiWindow In childList

                If apiChild.ClassName = "TdfInfoPanel" Then

                    If GroupCount = 2 Then

                        Handle1 = apiChild.hWnd

                    End If

                    GroupCount += 1

                End If
 

            Next

        Next
 

        GroupCount = 0

        childList.Clear()

        childList = winEnum.GetChildWindows(Handle1)
 

        For Each apiChild As WindowsEnumTest.ApiWindow In childList

            If apiChild.ClassName = "TdfInfoGroup.UnicodeClass" Then

                If GroupCount = 2 Then

                    Handle2 = apiChild.hWnd

                End If

                GroupCount += 1

            End If
 

        Next
 

        Handle3 = FindWindowEx(Handle2, IntPtr.Zero, "TdfInfoPanel", Nothing)
 

        GroupCount = 0

        childList.Clear()

        childList = winEnum.GetChildWindows(Handle3)

        'MessageBox.Show(childList.Count.ToString, "childList.Count")
 

        For Each apiChild As WindowsEnumTest.ApiWindow In childList

            If apiChild.ClassName = "TdfInfoRow" Then

                If GroupCount = 4 Then

                    Handle4 = apiChild.hWnd

                ElseIf GroupCount = 5 Then

                    Handle5 = apiChild.hWnd

                End If

                GroupCount += 1

            End If

        Next
 

        Handle6 = FindWindowEx(Handle4, IntPtr.Zero, "TCompactEdit.UnicodeClass", Nothing)

        Handle7 = FindWindowEx(Handle5, IntPtr.Zero, "TCompactEdit.UnicodeClass", Nothing)
 

        Call SendMessageByString(Handle6, WM_SETTEXT, 0, "LastName")

        Call SendMessageByString(Handle7, WM_SETTEXT, 0, "FirstName")
 

    End Sub

Open in new window

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Awesome...glad you were able to put it all together!

The very nature of these types of questions make them the most difficult at EE to answer.  Just getting the necessary preliminary information is tricky in itself...as the experts don't have the app in front of them to interrogate and experiment with.

As you've found out, there are a myriad of Win APIs and approaches that can be used...and often no concrete, clear path to which is best and/or whether a combination is needed.

Glad I was able to gently guide you in the right direction.  It's obvious to me that you are a fairly proficient programmer who just needed some help with a new territory.  =)
0
 

Author Comment

by:revstudio
Comment Utility
Thanks again for all the help.

I updated the code above to run through the entire hierarchy and get all the handles. I think it is cleaner although challenging to follow if  you haven't "been through the pain"


Private Sub RadButton5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadButton5.Click
 

        Dim Handle1, Handle2, Handle3, Handle4, Handle5, Handle6, Handle7, Handle8, Handle9, Handle10, Handle11 As Integer

        Dim GroupCount As Integer = 0
 

        winList.Clear()

        childList.Clear()
 

        winList = winEnum.GetTopLevelWindows("TShowShellManagerOrderForm.UnicodeClass")
 

        For Each apiWin As WindowsEnumTest.ApiWindow In winList
 

            childList = winEnum.GetChildWindows(apiWin.hWnd)

            For Each apiChild As WindowsEnumTest.ApiWindow In childList

                If apiChild.ClassName = "TdfInfoPanel" Then

                    If GroupCount = 2 Then

                        Handle1 = apiChild.hWnd

                    End If

                    GroupCount += 1

                End If
 

            Next

        Next
 

        GroupCount = 0

        childList.Clear()

        childList = winEnum.GetChildWindows(Handle1)
 

        For Each apiChild As WindowsEnumTest.ApiWindow In childList

            If apiChild.ClassName = "TdfInfoGroup.UnicodeClass" Then

                If GroupCount = 2 Then

                    Handle2 = apiChild.hWnd

                ElseIf GroupCount = 3 Then

                    Handle3 = apiChild.hWnd

                End If

                GroupCount += 1

            End If
 

        Next
 

        Handle4 = FindWindowEx(Handle2, IntPtr.Zero, "TdfInfoPanel", Nothing)

        Handle5 = FindWindowEx(Handle3, IntPtr.Zero, "TdfInfoPanel", Nothing)
 

        GroupCount = 0

        childList.Clear()

        childList = winEnum.GetChildWindows(Handle4)
 

        'First and Last Name

        For Each apiChild As WindowsEnumTest.ApiWindow In childList

            If apiChild.ClassName = "TdfInfoRow" Then

                If GroupCount = 4 Then

                    Handle6 = apiChild.hWnd

                ElseIf GroupCount = 5 Then

                    Handle7 = apiChild.hWnd

                End If

                GroupCount += 1

            End If

        Next
 

        GroupCount = 0

        childList.Clear()

        childList = winEnum.GetChildWindows(Handle5)
 

        'Order No.

        For Each apiChild As WindowsEnumTest.ApiWindow In childList

            If apiChild.ClassName = "TdfInfoRow" Then

                If GroupCount = 1 Then

                    Handle8 = apiChild.hWnd

                End If

                GroupCount += 1

            End If

        Next
 

        Handle9 = FindWindowEx(Handle6, IntPtr.Zero, "TCompactEdit.UnicodeClass", Nothing)

        Handle10 = FindWindowEx(Handle7, IntPtr.Zero, "TCompactEdit.UnicodeClass", Nothing)

        Handle11 = FindWindowEx(Handle8, IntPtr.Zero, "TCompactEdit.UnicodeClass", Nothing)
 

        Call SendMessageByString(Handle9, WM_SETTEXT, 0, "LastName")

        Call SendMessageByString(Handle10, WM_SETTEXT, 0, "FirstName")

        Call SendMessageByString(Handle11, WM_SETTEXT, 0, "1000001")
 

    End Sub

Open in new window

0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

If you're writing a .NET application to connect to an Access .mdb database and use pre-existing queries that require parameters, you've come to the right place! Let's say the pre-existing query(qryCust) in Access takes a Date as a parameter and l…
Creating an analog clock UserControl seems fairly straight forward.  It is, after all, essentially just a circle with several lines in it!  Two common approaches for rendering an analog clock typically involve either manually calculating points with…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now