Basic scenario is that I need to programatically fill out a PDFs text fields that resides on a webserver. The fields will be mapped and filled out with data contained in a CSV. The PDF must be opened in the browser (browser control or ie/ff/chrome/etc) and edited in place. Cannot be downloaded, filled out, and uploaded (it must be filled out and submitted using the submit button in it; I've tried editing the buttons submission path to no avail).
Thus far I've put a web browser control on a form and made it navigate to the website, login, and load the PDF file. How do I interact with the PDF file thats open in the web browser control? Looking through various PDF libraries, they seem to mainly interact with a closed pdf located on the harddrive, make the modifications, and re-save it.
EDIT: Im very open to alternative solutions. I dont know if its possible, but if so - PDF based javascript on my machine that I run on the form? I can do this easily if I download it, but dont seem to be able to find a way to use PDFJS while its open in the webbrowser.
I'm afraid is it not easy to do what you want to do. First you have to find the window handle of the PDF reader embedded in the WebBrowser control. Here is sample code on how to do that:
Public Function GetPdfViewerHandle() As System.IntPtr
Dim tempHandle As System.IntPtr
'--------------------------------------
' get handle to pdf viewer
'--------------------------------------
'--------------------------------------
' first check for the foxit reader
'--------------------------------------
tempHandle = FindChildWindow(WebBrowser1.Handle, "AfxWnd42s", "Reader", 1, True)
If IntPtr.Zero.Equals(tempHandle) = True Then
'---------------------------------
' if not foxit, check for adobe
'---------------------------------
tempHandle = FindChildWindow(WebBrowser1.Handle, "AVL_AVVIEW", "AVPageView", 1, True)
End If
Return tempHandle
End Function
Public Shared Function FindChildWindow(ByVal hParent As IntPtr, ByVal P_childClass As String, ByVal P_childTitle As String, ByVal P_count As Integer, ByVal p_recursive As Boolean) As IntPtr
Dim hChild As IntPtr
Dim className As String
Dim title As String
Dim cnt As Integer
Dim tempPtr As IntPtr
Dim Declare Function FindWindowExA Lib "user32.dll" (ByVal hWnd1 As IntPtr, ByVal hWnd2 As Int32, ByVal lpsz1 As String, ByVal lpsz2 As String) As IntPtr
cnt = 0
hChild = FindWindowExA(hParent, 0, Nothing, Nothing)
While hChild.ToInt32 > 0
If P_childClass Is Nothing Then
className = GetClassName(hChild)
Else
className = GetClassName(hChild)
If P_childClass.Length < className.Length Then
className = className.Substring(0, P_childClass.Length)
End If
End If
If P_childTitle Is Nothing Then
title = GetWindowText(hChild).Replace("&", "")
Else
title = GetWindowText(hChild).Replace("&", "")
If P_childTitle.Length < title.Length Then
title = title.Substring(0, P_childTitle.Length)
End If
End If
Debug.WriteLine("hwnd=" + Hex$(hChild.ToInt32) + ", className = " + className + ", title = " + title)
If (String.Compare(className, P_childClass, True) = 0 And String.Compare(title, P_childTitle, True) = 0) Or (P_childClass = Nothing And String.Compare(title, P_childTitle, True) = 0) Or (String.Compare(className, P_childClass, True) = 0 And P_childTitle = Nothing) Then
cnt += 1
If cnt >= P_count Then
Return hChild
End If
End If
If p_recursive = True Then
tempPtr = FindChildWindow(hChild, P_childClass, P_childTitle, 1, p_recursive)
If IntPtr.Zero.Equals(tempPtr) = False Then
Return tempPtr
End If
End If
hChild = FindWindowExA(hParent, hChild.ToInt32, Nothing, Nothing)
End While
Return Nothing
End Function
Once you have the window handle, there are a lot of different methods for finding the form fields. If you know the order of things, you can simply start sending key commands to the pdf reader handle or use Spy++ to find the handles of the form fields to input data to them via the Win32Api SendMessageA function:
Public Declare Function SendMessageA Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
asciiChar = CByte(Asc(data.Substring(0, 1)))
rc = SendMessageA(hwnd, WM_CHAR, asciiChar, 0)
Good luck.
If you have to submit the data with a button on the PDF just inspect the traffic submited and see what it sends then you can replicate with VB.NET and also will not even have to load the PDF document.
Related
I have no idea how I should solve the following task and I would be pleased if someone can give me a small advice at least.
I've got old sourcecode in visual basic 6.0, active x browser plugins that start an VB .dll module to upload documents to the server. This is part of a webclient. To start the upload-services, the active-x control is called.
Private Sub ProgressStarter()
Dim hModule As Long
hModule = 0
Dim strExe As String
Dim strParam As String
Dim nResult As Long
strExe = App.path & "\ClientServiceStarter.exe"
strParam = "some_params"
' wait for Exe
nResult = ExecCmd(strExe & strParam)
If nResult < 32 Then
MsgBox "Error", vbCritical Or vbOKOnly
End If
Exit Sub
The execcmd-func starts the VB .dll
Public Function ExecCmd(cmdline$)
Dim proc As PROCESS_INFORMATION
Dim Start As STARTUPINFO
' Initialize the STARTUPINFO structure:
Start.cb = Len(Start)
' Start the shelled application:
Dim Ret As Long
Ret& = CreateProcessA(vbNullString, cmdline$, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, _
vbNullString, Start, proc)
' Wait for the shelled application to finish:
ExecCmd = Ret&
Ret& = WaitForSingleObject(proc.hProcess, 1)
While Ret& = WAIT_TIMEOUT
DoEvents
Ret& = WaitForSingleObject(proc.hProcess, 1)
Wend
Call GetExitCodeProcess(proc.hProcess, Ret&)
Call CloseHandle(proc.hThread)
Call CloseHandle(proc.hProcess)
End Function
Now I want to change the VB .dll into a in c# written application. My question is, what kind of application should I use and how should I call it, because just changing the filename in the ProgressStarter-function doesn't work (nothing happened).
I tried it with an wpf-application, that runs on its own, but could not executed from this active-x.
So...what am I doing wrong? Any ideas how to solve this?
Kind regards and
Sorry for my bad english.
following code is used to download an exe file from driveHq
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Try
Dim strFTPpath, strDestPath As String
Cursor.Current = Cursors.WaitCursor
'strFTPpath is the url of where Latest upd is located
'strDestPath is system path to dwonload upd from FTP
strFTPpath = "ftp://MYUSRNAM:MYPWD#ftp.drivehq.com/rstari9kremcos/RStari9.exe"
strDestPath = "D:\Rstari9\GDS\RStari9.exe"
My.Computer.Network.DownloadFile(strFTPpath, strDestPath)
Cursor.Current = Cursors.Arrow
Button1.Enabled = False
MsgBox("latest updation successfully downloaded to 'D:\Rstari9\GDS\RStari9.exe'", MsgBoxStyle.Information, "RStari9 - Download Success")
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Information, "Update downloading aborted...")
End Try
End Sub
and this is my UI to download, i want to add a progressbar in it
code : VB.NET
My.Computer.Network.DownloadFile has a parameter called showUI, you can set it to True to display the progress of the operation.
You can change your code in this way:
My.Computer.Network.DownloadFile(strFTPpath, strDestPath, "", "", True, 100000, False)
Unfortunately, My.Computer.Network.DownloadFile hasn't a way to download the file asynchronously, so you have to use something else if you want to develop your own UI.
Software involved in this question is:
SQL Server Reporting Services 2008 R2
SQL Server Analysis Services 2008 R2
SQL Server 2008 R2 Database Engine
ASP.NET 4.5 Web Forms
ReportViewer component
We have several dozen reports. Some reports use T-SQL queries to our Data Warehouse database and some use MDX queries to our SSAS Cube.
Active Directory Security Groups secure which reports a user can access on the Report Server.
We additionally have made SSAS Roles that have Dimension Permissions on them which effective secures which data each user can access. We used AMO code to generate and maintain these roles and membership due to how many there are but that is beside the point and unrelated to the question.
I understand that there is a feature of SSAS called EffectiveUserName that we can pass to the cube for impersonation.
However, how can we impersonate a user within SSRS such that we will see only the reports that that user has access to?
We are currently trying to work out the software design of a custom report manager with ASP.NET and using the ReportViewer component. We would like to expose a text box or drop down to Administrators that allows them to put in or select employees and effectively run as that employee.
So in other words, even though I am authenticated into the ASP.NET Report Manager site as DOMAIN\User1, if I am in some role on the report server as an Administrator, I want to be able to type into a text box a username like User2 and be able to view all reports on the report server as DOMAIN\User2 would see them.
Thanks for any advice or answers you can offer.
A few things:
You need to do this in code behind in my experience.
You want a 'ReportViewer' object.
I believe if you are hosting you need a reference to 'Microsoft.ReportViewer.WinForms' dll.
The code I used was done with xaml for WPF hosting a ReportViewer (abridged):
< Window x:Class="WPFTester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:rv="clr-namespace:Microsoft.Reporting.WinForms;assembly=Microsoft.ReportViewer.WinForms"
>
......
<WindowsFormsHost Grid.Row="2">
<rv:ReportViewer x:Name="reportViewer"></rv:ReportViewer>
</WindowsFormsHost>
......
</Window>
The important part you get is that I have a 'ReportViewer' Object named 'reportViewer' for my code behind. ASP.NET has some equivalent of this object but you also will also need the dll in the alias of 'rv' or similar. The code works similar to this:
private void ResetReportViewer(ProcessingMode mode)
{
this.reportViewer.Clear();
this.reportViewer.LocalReport.DataSources.Clear();
this.reportViewer.ProcessingMode = mode;
}
private void ReportViewerRemoteWithCred_Load(object sender, EventArgs e)
{
ResetReportViewer(ProcessingMode.Remote);
reportViewer.ServerReport.ReportServerUrl = new Uri(#"(http://myservername/ReportServer");
reportViewer.ServerReport.ReportPath = "/Test/ComboTest";
DataSourceCredentials dsCrendtials = new DataSourceCredentials();
dsCrendtials.Name = "DataSource1"; // default is this you may have different name
dsCrendtials.UserId = "MyUser"; // Set this to be a textbox
dsCrendtials.Password = "MyPassword"; // Set this to be a textbox
reportViewer.ServerReport.SetDataSourceCredentials(new DataSourceCredentials[] { dsCrendtials });
reportViewer.RefreshReport();
}
I never worked with services you have mentioned but I hope, following stuff will help you in some way.
I used kernal32.dll and advapi32.dll to impersonate user as under:
Imports System.Security.Principal
Imports System.Runtime.InteropServices
Public Class UserImpersonation
<DllImport("advapi32.dll")> _
Public Shared Function LogonUserA(ByVal lpszUserName As [String], ByVal lpszDomain As [String], ByVal lpszPassword As [String], ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef phToken As IntPtr) As Integer
End Function
<DllImport("advapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function DuplicateToken(ByVal hToken As IntPtr, ByVal impersonationLevel As Integer, ByRef hNewToken As IntPtr) As Integer
End Function
<DllImport("advapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function RevertToSelf() As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _
Public Shared Function CloseHandle(ByVal handle As IntPtr) As Boolean
End Function
Public Const LOGON32_LOGON_INTERACTIVE As Integer = 2
Public Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Private impersonationContext As WindowsImpersonationContext
Private Const UserName As String = "USER_ID"
Private Const Password As String = "USER_DOMAIN_PASSWORD"
Private Const Domain As String = "USER_DOMAIN_NAME"
Public Function ImpersonateValidUser() As Boolean
Dim tempWindowsIdentity As WindowsIdentity
Dim token As IntPtr = IntPtr.Zero
Dim tokenDuplicate As IntPtr = IntPtr.Zero
If RevertToSelf() Then
If LogonUserA(UserName, Domain, Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
impersonationContext = tempWindowsIdentity.Impersonate()
If impersonationContext IsNot Nothing Then
CloseHandle(token)
CloseHandle(tokenDuplicate)
Return True
End If
End If
End If
End If
If token <> IntPtr.Zero Then
CloseHandle(token)
End If
If tokenDuplicate <> IntPtr.Zero Then
CloseHandle(tokenDuplicate)
End If
Return False
End Function
Public Sub UndoImpersonation()
If impersonationContext IsNot Nothing Then
impersonationContext.Undo()
End If
End Sub
End Class
Now, consume it at appropriate place in your code like:
Public SomeOtherClass
Public Function ReadFile() As CalFileInfo
Try
Dim objImpersonation As New UserImpersonation()
If (objImpersonation.ImpersonateValidUser()) Then
'Do necessary stuff....
objImpersonation.UndoImpersonation()
Else
objImpersonation.UndoImpersonation()
Throw New Exception("User do not has enough permissions to perform the task")
End If
Catch ex As Exception
''MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End Try
Return CalFileInformation
End Function
End Class
I wanted to know whether it is possible to set proxy for each different webbrowser in an application (same process).
I have searched a lot to find codes to change proxy system wide by modifying registry and another process specific proxy. But i want each webbrowser inside same process to have different proxy
Eg: WebBrowser1 Proxy --> 95.188.106.78:3128
WebBrowser 2 Proxy --> 89.218.160.162:9090
WebBrowser 3 Proxy --> 78.39.68.145:3128 and so on
Or if it is not possible by using WebBrowser directly, then can anyone else suggest what can i use to achieve this (not webrequests, i want browser only through which i can load all data and user can interact with it but with different proxies) like using geckofx or webkit? But i don't know if its possible in them or not
Take a look at this - sorry its VB.NET
Its basically saying you can configure it through editing one of the IE DLL's by changing the InernetSetOption. Hope this helps.
Here is another link that may help
Unfortunately, you cannnot set the proxy per insnance. Not even per application - it is global. If you chnage it for a .net app, it will impact IE as well.
Here is complete code to change the proxy.
Create a "Form1" with buttons "B_Get" "B_Set" and "B_Disable" and then paste this code.
It should work for you. I used this app as proof of concept for an automatic proxy cycler i built.
Depending on your applications needs, you may be able to switch it, and then switch it back. Note that it WILL impact internet explorer.
To verify that this is working, check internet explorer. You will probably have to open/close the options dialog for IE, but you shouldnt need to restart the browser.
This will also impact .NET apps on the fly - if you run my program while yours is running, you will be able to change the proxy that your program is using.
Imports Microsoft.Win32
Imports System.Runtime.InteropServices
Public Class Form1
<DllImport("wininet.dll")>
Public Shared Function InternetSetOption(hInternet As IntPtr, dwOption As Integer, lpBuffer As IntPtr, dwBufferLength As Integer) As Boolean
End Function
Public Const INTERNET_OPTION_SETTINGS_CHANGED As Integer = 39
Public Const INTERNET_OPTION_REFRESH As Integer = 37
'This function is what is called after editing the registry - this causes internet explorer to update its proxy even if it is already open.
'It also effects the web browser control in any VB.net application that is running.
Public Sub globalProxy_apply()
Dim settingsReturn As Boolean = False
Dim refreshReturn As Boolean = False
settingsReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0)
If Not settingsReturn Then
MessageBox.Show("Error 001: Line ""InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0)"" failed.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
refreshReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0)
If Not refreshReturn Then
MessageBox.Show("Error 002: Line ""InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0)"" failed.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
End Sub
Public Function globalProxy_IsProxyEnabled() As Boolean
Try
Dim Regs As RegistryKey = Registry.CurrentUser.CreateSubKey("Software\Microsoft\Windows\CurrentVersion\Internet Settings", RegistryKeyPermissionCheck.ReadWriteSubTree)
If Regs.GetValue("ProxyEnable") <> Nothing Then
If Regs.GetValue("ProxyEnable").ToString() = "0" Then
Return False
Else
Return True
End If
Else
Return False
End If
Catch ex As Exception
MessageBox.Show("Error 01X: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return False
End Try
End Function
Public Function globalProxy_GetProxyServer() As String
Try
Dim Regs As RegistryKey = Registry.CurrentUser.CreateSubKey("Software\Microsoft\Windows\CurrentVersion\Internet Settings", RegistryKeyPermissionCheck.ReadWriteSubTree)
If Regs.GetValue("ProxyServer") <> Nothing Then
Return Regs.GetValue("ProxyServer").ToString()
Else
Return ""
End If
Catch ex As Exception
MessageBox.Show("Error 02X: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return ""
End Try
End Function
Public Sub globalProxy_DisableProxy()
Dim regkey As RegistryKey
Try
regkey = Registry.CurrentUser.CreateSubKey("Software\Microsoft\Windows\CurrentVersion\Internet Settings")
regkey.SetValue("ProxyEnable", False, RegistryValueKind.DWord)
regkey.Close()
Catch ex As Exception
MessageBox.Show("Error 03X: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
globalProxy_apply()
End Sub
Public Sub globalProxy_SetProxy(ByVal ServerName As String)
Dim regkey As RegistryKey
Try
regkey = Registry.CurrentUser.CreateSubKey("Software\Microsoft\Windows\CurrentVersion\Internet Settings")
regkey.SetValue("ProxyServer", ServerName, RegistryValueKind.Unknown)
regkey.SetValue("ProxyEnable", True, RegistryValueKind.DWord)
regkey.Close()
Catch ex As Exception
MessageBox.Show("Error 04X: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
globalProxy_apply()
End Sub
Private Sub B_Set_Click(sender As System.Object, e As System.EventArgs) Handles B_Set.Click
If TextBox1.Text = "" Then
globalProxy_DisableProxy()
Else
globalProxy_SetProxy(TextBox1.Text)
End If
End Sub
Private Sub B_Disable_Click(sender As System.Object, e As System.EventArgs) Handles B_Disable.Click
globalProxy_DisableProxy()
End Sub
Private Sub B_Get_Click(sender As System.Object, e As System.EventArgs) Handles B_Get.Click
If globalProxy_IsProxyEnabled() Then
TextBox1.Text = globalProxy_GetProxyServer()
Else
TextBox1.Text = ""
End If
End Sub
End Class
Currently, we have a website that relies on a Microsoft TAPI interface to dial a phone from within a .NET web site. It uses VBScript and tags, and it is bound to a master page. What we are looking for is a server control that would encompass all of this code and only be ran when it is included on a webpage.
The old page does the following:
<object classid="clsid:21D6D48E-A88B-11D0-83DD-00AA003CCABD" id="TAPIOBJ"></object>
<object classid="clsid:E9225296-C759-11d1-A02B-00C04FB6809F" id="MAPPER"></object>
After these lines of code are tags that contains VBScript to initialize the Microsoft TAPI 3.0 library and a few functions to dial. A Dialer control creates a call to one of the functions to dial in an onclick event.
Essentially, we want to create the same type of control without having tags embedded into the HTML of a page directly. We also do not want VBScript in there. Ideally, we would like a server control that works with the TAPI 3.0 API and gains access to the client's phone. Is this possible? Since we are talking about a "server" control, I'm skeptical. I could just as easily create a user control within the project, but we'd like to have this in a controls framework for use elsewhere instead of copying it.
I've been looking at this article on how to create a server control for injection of Client ActiveX controls, but is this down the right path?
UPDATE: Here's the VBScript:
This is what sits in the tag:
<script type="text/vbscript" LANGUAGE="VbScript">
'Constants section
'These constants are copied from tapi3if.idl
Const TAPIMEDIATYPE_AUDIO = &H08&
Const TAPIMEDIATYPE_VIDEO = &H8000&
Const S_MEDIA_AUDIOVIDEO = &H8008&
Const TD_CAPTURE = 0
Const TD_RENDER = 1
Const QSL_NEEDED = 1
Const AS_INSERVICE = 0
Const DC_NORMAL = 0
Const TE_CALLSTATE = 8
Const TE_CALLNOTIFICATION = 4
Const CS_DISCONNECTED = 3
Const CS_IDLE = 0
Const CS_OFFERING = 4
Const CS_CONNECTED = 2
Const CNE_OWNER = 0
Const CIS_CALLERIDNAME = 0
Const CIS_CALLERIDNUMBER = 1
'Interface IDs for casting
'Note: you can find the following IID-s in tapi3.h, tapi3if.idl or rend.idl
Const IID_String_ITMediaSupport = "{B1EFC384-9355-11D0-835C-00AA003CCABD}"
Const IID_String_ITTerminalSupport="{B1EFC385-9355-11D0-835C-00AA003CCABD}"
Const IID_String_ITBasicCallControl = "{B1EFC389-9355-11D0-835C-00AA003CCABD}"
'Const IID_String_ITCallInfo = "{B1EFC390-9355-11d0-835C-00AA003CCABD}"
'New interface
Const IID_String_ITCallInfo = "{350F85D1-1227-11D3-83D4-00C04FB6809F}"
Const IID_String_ITStreamControl= "{EE3BD604-3868-11D2-A045-00C04FB6809F}"
Const IID_String_ITDirectoryObjectConference= "{F1029E5D-CB5B-11D0-8D59-00C04FD91AC0}"
Const IID_String_ITCallStateEvent = "{62F47097-95C9-11d0-835D-00AA003CCABD}"
Const IID_String_ITCallNotificationEvent = "{895801DF-3DD6-11d1-8F30-00C04FB6809F}"
' IID of IVideoWindow
' Note: you can find this IID defined in control.h (from your sdk\inc directory),
' which contains the interface to type library QuartzTypeLib for quartz.dll;
' (search for the interface IVideoWindow)
Const IID_String_IVideoWindow = "{56A868B4-0AD4-11CE-B03A-0020AF0BA770}"
' The following CLSID is defined in tapi3.h
'(and it's used for creating a terminal of class "video window terminal")
Const CLSID_String_VideoWindowTerm = "{F7438990-D6EB-11d0-82A6-00AA00B5CA1B}"
'****************************************************************************
'Global variable section
'****************************************************************************
Dim CallStatus
Dim pICallState
pICallState = 0
'Set on True when we are unable to complete the connecting phase, to skip rest of processing
DIM sUnableToComplete
DIM sbNeedToExit
sUnableToComplete = False
sbNeedToExit = False
' If we want to receive incoming calls, we have to register on the corresponding addresses.
'We don't really use the values returned by registration (they are supposed to be used
'for unregistration), because Unregistration is performed automatically when we shutdown the TAPI object
'The variable pRegisteredCallNotification is an array that contains cookies returned by RegisterCallNotifications;
'these would normally be used to call UnregisterNotifications.
'The variable pRegisteredName holds correspondent AddressName
DIM pRegisteredCallNotification(50)
DIM pRegisteredName(50)
DIM iQtaRegistered
DIM callFrom
iQtaRegistered = 0
'Set by radio button "Select Address Type"
DIM sCurrentAddressType
'sCurrentAddressType = -1
sCurrentAddressType = 1
' This variable will hold a reference to the currently established call
DIM spITCall
spITCall = Empty
DIm pVideoWindow1
DIm pVideoWindow2
'Simplest error processing
Sub CheckError(strMsg)
if not Err.number = 0 Then
MsgBox strMsg & ":" & Err.number & ";"&Err.description
sbNeedToExit = True
Err.Clear
End If
End Sub
Function IsComponentInstalled(ProgId)
Dim tmpObject
On Error Resume Next
Set tmpObject = Server.CreateObject(ProgId)
If Err.Number = 0 Then
IsComponentInstalled = True
Else
IsComponentInstalled = False
End If
Set tmpObject = Nothing
End Function
</script>
Below the end body tag after the tags is:
<script type="text/vbscript" LANGUAGE="vbscript">
' Be sure that you call TAPIOBJ.Initialize before window_onload, otherwise you'll
' never receive events from tapi...
On Error Resume Next
call TAPIOBJ.Initialize
sUnableToComplete = False
TAPIOBJ.EventFilter = &H1FFFF&
if Not Err.number = 0 Then
MsgBox "TAPI software has not been installed on your workstation.",0,"Init"
sUnableToComplete = True
End If
For Each pITAddress in TAPIOBJ.Addresses
if left(pITAddress.AddressName,4) <> "Line" and left(pITAddress.AddressName,29) <> "Shoreline Multi-Line Monitor:" _
and pITAddress.MediaTypes = 8 then
callFrom = pITAddress.AddressName
end if
next
'This section shows how to override Application Priority:
'after the execution of the following lines, our app will always receive incoming calls
'even if there are other running tapi apps that had registered for receiving calls before our app.
call TAPIOBJ.SetApplicationPriority("IEXPLORE.EXE",TAPIMEDIATYPE_AUDIO,TRUE)
call TAPIOBJ.SetApplicationPriority("IEXPLORE.EXE",TAPIMEDIATYPE_VIDEO,TRUE)
' Check parameters of a call before connecting it
Sub PressConnect(pNumber,Status)
On Error Resume Next
'MsgBox (pNumber & "," & Status)
DIM iAddressType
DIM pConnectTo
DIM addressFrom
DIM selStr
'If not IsEmpty(spITCall) Then
' MsgBox "You are currently in call. Disconnect first",0,"connect"
'End If
pConnectTo = pNumber
set CallStatus=Status
addressFrom = callFrom
If addressFrom = "" Then
callStatus.innerHTML = "Feature Unavailable"
MsgBox "The TAPI Feature has not been setup on your phone line.",0,"COnnect"
else
sUnableToComplete = False
callStatus.innerHTML = "Connecting to " & pConnectTo & " ...."
'Create new internal call representation
For Each pITAddress in TAPIOBJ.Addresses
if pITAddress.AddressName = addressFrom Then
'Obtain ITMediaSupport
Set pITAddress_Connect = pITAddress
Exit For
End If
Next
Set pITAddress = Nothing
'Create a Call
DIM MediaTypes
MediaTypes = TAPIMEDIATYPE_AUDIO
Set pCall = pITAddress_Connect.CreateCall(pConnectTo,1,MediaTypes)
Set spITCall = pCall
if sUnableToComplete Then
Call DisconnectCall(1)
callStatus.innerHTML = "Call to "& pConnectTo & " failed."
End If
Call pCall.Connect(false)
' Check for error "invalid address" (see in tapi3err.h TAPI_E_INVALADDRESS=(HRESULT)0x8004000C)
if Err.Number = &H8004000C Then
Err.Clear
Call DisconnectCall(1)
callStatus.innerHTML = "Call to "& pConnectTo & " failed: Address is invalid"
Set pCall = Nothing
Else
if not Err.Number = 0 Then
Err.Clear
Call DisconnectCall(1)
callStatus.innerHTML = "Call to "& pConnectTo & " failed: error " & Hex(Err.number)
Set pCall = Nothing
Else
Set spITCall = pCall
End if
End If
end if
Set pCall = Nothing
end sub
' Disconnect current call
Sub HangUp(callDisc)
'On Error resume Next
if not IsEmpty(spITCall) Then
if not callDisc = 8 and not callDisc = 0 Then
' We need some kind of message pump here. The following call to MsgBox does exactly this:
'MsgBox "A call is disconnected",0,"Disconnect"
End If
Set pVideoWindow1 = Nothing
Set pVideoWindow2 = Nothing
' ConnANN.innerHTML = "Disconnected"
if callDisc=0 Then
spITCall.Disconnect(DC_NORMAL)
End If
Set spITCall = Nothing
spITCall = Empty
callStatus.innerHTML = "Disconnected"
'btnDisconnect.disabled = true
'source.visible = false
End If
End Sub
'*****************************************************************************
' Tapi events processing:
' - call state events ("connected", "disconnected")
' - and call notification events (these calls will be in "offering" state)
Sub TAPIOBJ_Event(event_type, tapi_event)
'On Error Resume Next
'Check For disconnected call
if event_type = TE_CALLSTATE Then
DIM pITCallStateEvent
Set pITCallStateEvent = MAPPER.QueryDispatchInterface(_
IID_String_ITCallStateEvent,tapi_event)
iCallState = pITCallStateEvent.State
if iCallState= CS_DISCONNECTED or iCallState= CS_IDLE Then
cause = pITCallStateEvent.Cause
'pICallState=iCallState
strinnerHTML = ""
Select Case cause
Case 1 ' CEC_DISCONNECT_NORMAL - Normal disconnect
strinnerHTML = "Disconnected"
Case 2 ' CEC_DISCONNECT_BUSY
strinnerHTML = "Your Party is busy.Try Later."
Case 3 ' CEC_DISCONNECT_BADADDRESS
strinnerHTML = "Address is invalid"
case 4 ' CEC_DISCONNECT_NOANSWER
strinnerHTML = "No answer from your party."
case 0 'CEC_NONE
strinnerHTML = "No answer from your party."
Case Else
strinnerHTML = "Your call is cancelled, rejected or failed"
End Select
'Call DisconnectCall(1)
'btnDisconnect.disabled = true
End If 'Call is disconnected
if iCallState = CS_CONNECTED Then 'Call is connected
callStatus.innerHTML = "Call is connected."
'btnDisconnect.disabled = False
End If 'Call is connected
End If ' event: call state
'Check only for incoming calls
if event_type = TE_CALLNOTIFICATION Then ' We have an incoming call (an "offering" call)
DIM pITCallNotificationEvent
Set pITCallNotificationEvent = MAPPER.QueryDispatchInterface(_
IID_String_ITCallNotificationEvent,tapi_event)
Call CheckError("TAPIOBJ_Event:query for pITDirectoryObjectUser")
CallOwnership = pITCallNotificationEvent.Event
DIM pITCallInfo
Set pITCallInfo = pITCallNotificationEvent.Call
Call CheckError("TAPIOBJ_Event:get pITCallInfo")
if not blnShowOnlyOnce and pITCallInfo.CallState = CS_OFFERING and not ( CallOwnership = CNE_OWNER) Then
MsgBox "Unable to accept incoming calls: is other instance of this app running?",0,"Info"
blnShowOnlyOnce = True
Exit Sub
End IF
if CallOwnership = CNE_OWNER Then 'We are the owner!
if not IsEmpty(spITCall) Then
MsgBox "Already in call, disconnect first",0,"Incoming Call"
Exit Sub
End if
if pITCallInfo.CallState = CS_OFFERING Then 'Offering
'-- CIS_CALLERIDNAME Wasn't working so I switched to NUMBER
sCalleeName = pITCallInfo.CallInfoString(CIS_CALLERIDNAME)
if not Err.number = 0 then ' Caller ID name is not supported
sCalleeName = "Unknown Name"
Err.Clear
End if
sCalleeNumber = pITCallInfo.CallInfoString(CIS_CALLERIDNUMBER)
if not Err.number = 0 then ' Caller ID name is not supported
sCalleeNumber = "Unknown Number"
Err.Clear
End if
DIM pITCallOffer
Set pITCallOffer = MAPPER.QueryDispatchInterface( _
IID_String_ITBasicCallControl, pITCallInfo)
Call CheckError("TAPIOBJ_Event:query for pITCall")
response = MsgBox("A call from '" & sCalleeNumber & " " & sCalleeName & "' has arrived. Do you want to accept it?",4,"Incoming call")
if not response = 7 Then 'the did not press "NO", so he pressed "YES"
Call AcceptIncomingCall(pITCallOffer, pITCallInfo)
End If
End If 'Call is offering
End If 'We are owner
End If 'Call Notification has arrived
End Sub
</script>
Is it possible to use the ITAPI3 Managed Library to get rid of this and do this in the code-behind?
Hopefully, someone knows of a cleaner, more modern way of doing this, but here's what I did.
I embedded the VBScript into a script server-side, and registered it as a client script. Before I registered it, I added the tags into Literal controls and added them to the Page header. The server control itself was nothing but a link around a phone icon image.
Since I could call VBScript functions inside javascript, I created a GetPhoneNumber function that accepted the controlID of the control where I would get my phone number. This was set on the ControlID property of the Dialer control. In this instance, it was a textbox. The function parses the number for any bad data, then called PressConnect... done. It works, but I'm not really all that pleased with using VBScript in this manner.
If anyone has any idea how to interact with the client (local) TAPI of a user (Actual examples would be appreciated), post them here.