Access Cookbook, 2nd Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Access Cookbook, 2nd Edition [Electronic resources] - نسخه متنی

Ken Getz; Paul Litwin; Andy Baron

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید










Recipe 11.14 Create and Cancel Network Connections Programmatically



11.14.1 Problem


You'd like to
be able to connect to remote network devices from within your own
Access applications. You know that you could do this manually, using
Explorer or File Manager, but there must be some internal API for
controlling these connections. Is there some way you can manage
connections from within Access?


11.14.2 Solution


Windows provides a rich interface to its networking subsystem through
its API. Many of the function calls are difficult, if not impossible,
to call from VBA because of the language's lack of
pointer variable types. Some important calls, however, are quite
simple to use, as you'll see in this solution. The
example form will demonstrate connecting to and disconnecting from
remote devices (printers and drives) using common dialogs or using
code with no user interface.

Load and run frmNetworkSample from

11-14.MDB . Figure 11-16 shows
the form in use on a small Windows 2000 network. This sample form,
demonstrating all the capabilities covered in this solution, does the
following:

  • Retrieves the current username and
    computer name.

  • Walks through all 26 possible drive letters and displays any drive
    mappings connected to those drives.

  • Allows you to delete any of the displayed drive connections.

  • Provides a method for adding new connections, where you supply the
    four necessary parameters.

  • Uses the common dialogs for adding
    and canceling drive and printer connections.



Figure 11-16. frmNetworkSample allows you to add and cancel connections manually or by using the common dialogs


Though you would never use this exact form in an application, it
allows you to experiment with all the functionality covered in this
solution. To use these API calls in your own applications, follow
these steps:

  1. Import the module basNetwork from

    11-14.MDB . This module contains all the API
    function declarations, wrapper functions, data type declarations, and
    error constants you'll need.

  2. The sample form, frmNetworkSample, displays the
    current username. To retrieve this information in your own code, call
    the acbGetUser function from basNetwork. Its
    return value is the name of the currently logged-in user. For
    example:

    Debug.Print acbGetUser( )
  3. The sample form also displays the current computer name. To retrieve
    this information yourself, call the acbGetComputerName function from
    basNetwork. Its return value is the name of the
    current computer. For example:

    Debug.Print acbGetComputerName( )
  4. The list box on the form displays all the current connections. You
    can choose one and delete it (see Step 5). To retrieve a list of all
    26 possible drives and their connections in your own application,
    call acbListConnections, a function that takes as
    a parameter an array of 26 acbConnectionInfo
    structures. The following example fills the list with drive
    information, then prints it out to the Immediate window:

    Dim aci(0 To 25) As acbConnectionInfo
    intCount = acbListDriveConnections(aci( ))
    For intI = 0 To intCount
    Debug.Print aci(intCount).strDrive, aci(intCount).strConnection
    Next intI
  5. To delete a drive connection once you've selected a
    drive from the list box, click on the Delete button to the right of
    the drive list box. When you do, the code calls the
    acbCancelConnections function, deleting the connection for the drive
    selected in the list box:

    blnOK = (acbCancelConnection(Me.lstConnections.Column(0), True) = 0)
  6. To manually add a new printer or drive connection, first select
    Printer or Drive from the option group on the form, then enter the
    four pieces of information that the acbAddDriveConnection and
    acbAddPrintConnection functions need: local name (e.g.,
    "LPT1:"), remote name (e.g.,
    "\\GATEWAY\HPLJ4"), username, and
    password. The remote name is the only required value. Once
    you've entered the values, click on the Add button
    to the right of the text boxes. This calls the following code:

    If Me.grpDeviceType = 1 Then
    ' The '& "' below converts from null values to strings.
    '
    ' Drive
    blnOK = (acbAddDriveConnection(Me.txtLocalName & ", _
    Me.txtRemoteName & ", Me.txtUserName & ", Me.txtPassword & ") = 0)
    Else
    ' Printer
    blnOK = (acbAddPrinterConnection(Me.txtLocalName & ", _
    Me.txtRemoteName & ", Me.txtUserName & ", Me.txtPassword & ") = 0)
    End If
    End If
  7. To use the common dialogs for adding or canceling connections, click
    on any of the four buttons at the bottom of the form. Each calls a
    single line of Windows API code that pops up the appropriate dialog.
    The next section describes these function calls in detail.



11.14.3 Discussion


The following sections describe
all you need to know to use the networking functionality demonstrated
on the sample form. Though you could call the API functions directly,
in each case we've provided a wrapper function to
shield you from as much detail as possible. For each of the various
wrapper functions, we provide information on how to call them, what
parameters to send, and what values to expect back.

Most of the functions either return or
set an error value, indicating the outcome of the function call.
Though there are too many possible errors to list them all here,
Table 11-15 lists most of the common ones that
you'll receive when making these function calls.

Table 11-15. Common networking errors

Value


Constant


Description


0


 NO_ERROR


No error occurred.


5


 ERROR_ACCESS_DENIED


Access is denied.


66


 ERROR_BAD_DEV_TYPE


The network resource type is not correct.


67


 ERROR_BAD_NET_NAME


The network name cannot be found.


85


 ERROR_ALREADY_ASSIGNED


The local device name is already in use.


86


 ERROR_INVALID_PASSWORD


The specified network password is not correct.


170


 ERROR_BUSY


The requested resource is in use.


234


 ERROR_MORE_DATA


More data is available.


1200


 ERROR_BAD_DEVICE


The specified device name is invalid.


1201


 ERROR_CONNECTION_UNAVAIL


The device is not currently connected, but it is a remembered
connection.


1202


 ERROR_DEVICE_ALREADY_REMEMBERED


An attempt was made to remember a device that had previously been
remembered.


1203


 ERROR_NO_NET_OR_BAD_PATH


No network provider accepted the given network path.


1204


 ERROR_BAD_PROVIDER


The specified network provider name is invalid.


1205


 ERROR_CANNOT_OPEN_PROFILE


Unable to open the network connection profile.


1206


 ERROR_BAD_PROFILE


The network connection profile is corrupt.


1208


 ERROR_EXTENDED_ERROR


An extended error has occurred.


1222


 ERROR_NO_NETWORK


The network is not present or not started.


1223


 ERROR_CANCELED


The user canceled a dialog.


2250


 ERROR_NOT_CONNECTED


This network connection does not exist.


11.14.3.1 Retrieving information

To retrieve the current
user's name, call the acbGetUser function:

Public Function acbGetUser(Optional varErr As Variant) As String
Dim strBuffer As String
Dim lngRetval As Long
Dim lngSize As Long
lngSize = conMaxPath
Do
strBuffer = Space(lngSize)
lngRetval = WNetGetUser(0&, strBuffer, lngSize)
Loop Until lngRetval <> ERROR_MORE_DATA
If lngRetval <> NO_ERROR Then
acbGetUser = "
Else
acbGetUser = TrimNull(strBuffer)
End If
varErr = lngRetval
End Function

The acbGetUser function calls the Windows API to retrieve the
currently logged-in user's name. Note that there are
several ways for the Windows API and Access to communicate the length
of data to be returned. In this case, the code sets up a buffer of
arbitrary length and calls the Windows API. If the buffer was large
enough, it fills it in with the requested name. If not, it returns
the value ERROR_MORE_DATA, indicating that it
needs more space. It then passes back in the
lngSize variable the actual number of
characters it does need, and the code loops around, trying again with
the specified size.

If you want to know the exact error that occurred in the attempt to
retrieve the current user's name, you can pass a
variant variable in as a parameter to acbGetUser.
It's optional, but if you supply the value, the
function will pass back the error code to you in that variable. For
example:

Dim varErr as Variant
' If you care about the error:
Debug.Print acbGetUser(varErr)
Debug.Print "The error was: "; varError
' If you don't care about any errors:
Debug.Print acbGetUser( )

To
retrieve the current computer's name, call the
acbGetComputerName wrapper function. Windows stores the current
computer's name in the registry database and reads
it from there when necessary. To shield your code from having to know
exactly where that piece of information is stored, Windows provides
the GetComputerName API function.

The following function, acbGetComputerName,
handles the passing of data between Access and Windows for you:

Public Function acbGetComputerName( ) As String
' Retrieve the network name of the current computer.
Dim strBuffer As String
Dim lngSize As Long
Dim blnOK As Integer
lngSize = conMaxComputerNameLength+ 1
strBuffer = Space(lngSize)
blnOK = GetComputerName(strBuffer, lngSize)
acbGetComputerName = Left$(strBuffer, lngSize)
End Function

Note that in this case, the API function gives you no second chance.
If the buffer wasn't large enough, it just returns
as much as it could fit into the buffer you passed.

To retrieve the name of the remote device connected to a named local
device, call the acbGetConnection function. Pass to it the local
device name and an optional variable in which to receive the error
code. It will return to you the remote device name connected to the
requested local name. For example:

Debug.Print acbGetConnection("LPT1:")

might return a value like this (a
\\server\share name):

\\WOMBAT\HPLJ4

The function works the same way for drive connections.

The acbGetConnection function works the same way as the acbGetUser
function: it calls the API function once with an arbitrarily sized
buffer. If that isn't enough room,
it'll try again with the buffer resized to fit. Its
source code is:

Public Function acbGetConnection( _
strLocalName As String, Optional varErr As Variant) As String
Dim strBuffer As String
Dim lngRetval As Long
Dim lngSize As Long
lngSize = acbcMaxPath
Do
strBuffer = Space(lngSize)
lngRetval = WNetGetConnection(strLocalName, strBuffer, lngSize)
Loop Until lngRetval <> ERROR_MORE_DATA
If lngRetval <> NO_ERROR Then
acbGetConnection = "
Else
acbGetConnection = TrimNull(strBuffer)
End If
varErr = lngRetval
End Function


11.14.3.2 Adding and canceling connections using common dialogs

Adding or canceling a connection
with a common dialog in Windows is easy: just make a single function
call, as shown in Table 11-16. Each wrapper function
expects a single parameter: a window handle for the parent of the
dialog window. Most of the time, this will just be
Me.hWnd or
Screen.ActiveForm.hWnd.

Table 11-16. Wrapper functions for common dialog connections

Function name


Action


acbConnectDriveDialog


Add a drive connection.


acbDisconnectDriveDialog


Cancel a drive connection.


acbConnectPrintDialog


Add a printer connection.


acbDisconnectPrintDialog


Cancel a printer connection.

For example, to pop up the common drive connection dialog,
you'd call:

blnOK = acbConnectDriveDialog(Me.hWnd)

The code in each of the wrapper functions is similar and quite
trivial. In each case, the code just calls a single Windows API
function. We've provided the wrappers only to
provide a consistent interface for all the API functions;
there's no real reason not to call the API functions
directly, except for a tiny bit of convenience. For example, the
acbConnectPrintDialog function looks like this:

Public Function acbConnectPrintDialog(hWnd As Long) As Long
' Use the common print connection dialog to create a new connection.
acbConnectPrintDialog = WNetConnectionDialog(hWnd, RESOURCETYPE_PRINT)
End Function


11.14.3.3 Adding and canceling connections with no user intervention

Adding or canceling a connection
"silently" requires a bit more
work, but it's not a problem. Table 11-17 lists the available wrapper functions, and the
information they require.

Table 11-17. Functions to manually add and cancel connections

Function name


Action


Parameters


Description


acbAddDriveConnection


Add a drive connection.


strLocalName As String


Local name, like "LPT1:" or
"G:".


strRemoteName As String


Remote name, like "\\SERVER\SHARE".


strUserName As String


Username to be used. If empty, uses default user's
name.


strPassword As String


Password for the user specified. If empty, uses the default
user's password.


acbAddPrintConnection


Add a printer connection.


strLocalName As String,
strRemoteName As String,
strUserName As String,
strPassword As String


See parameters for

acbAddDriveConnection .


acbCancelConnection


Cancel any connection.


strLocalName As String


Local name of resource to disconnect.


blnForce As Boolean


If True, forces disconnection even if the device is in use. If False,
the function returns an error if it tries to disconnect an active
device.

For example, the following code fragment adds a new printer
connection for LPT2: to the CanonColor printer on server
Bart, set up for the current user and password:

blnOK = acbAddPrintConnection("LPT2:", "\\BART\CanonColor", ", ")

Each of these functions will
return an error value (NO_ERROR (0)) if there was
no error, or return some other error from Table 11-15 if an error occurs. Functions that add
connections call the private function
AddConnection, which in turn calls the Windows API
to create that connection, as shown here:

Public Function acbAddDriveConnection( _
strLocalName As String, strRemoteName As String, _
strUserName As String, strPassword As String)
acbAddDriveConnection = AddConnection( _
RESOURCETYPE_DISK, strLocalName, _
strRemoteName, strUserName, strPassword)
End Function
Private Function AddConnection(intType As Integer, _
strLocalName As String, strRemoteName As String, _
strUserName As String, strPassword As String)
' Internal function, provided for adding new connections.
' Call acbAddPrinterConnection or acbAddDriveConnection instead.
Dim nr As NETRESOURCE
Dim lngRetval As Long
nr.lpLocalName = strLocalName
nr.lpRemoteName = strRemoteName
nr.dwType = intType
lngRetval = WNetAddConnection2(nr, strPassword, _
strUserName, CONNECT_UPDATE_PROFILE)
AddConnection = lngRetval
End Function

The acbCancelConnection function is simple. It calls directly to the
Windows API, canceling the connection for the named local device:

Public Function acbCancelConnection( _
strName As String, blnForce As Boolean) As Long
acbCancelConnection = WNetCancelConnection2( _
strName, CONNECT_UPDATE_PROFILE, blnForce)
End Function

You may find it interesting to work through all the code in
basNetwork. There are some interesting twists
involved in transferring information between Access and the Windows
API, especially since it seems that every API function that involves
strings uses a different mechanism for indicating how much space it
needs.

It would be useful to have a function that could enumerate all
network resources, and of course Windows itself provides functions to
do this. Unfortunately, calling these functions from Access requires
a great deal of effort, because VBA just doesn't
support the necessary mechanisms (specifically, pointers) to make it
possible. It's possible, but it's
beyond the scope of this book.


/ 232