DEVCON.EXE (driver tool) and OpenVPN
Asked Answered
L

2

3

We have written an application that manages OpenVPN from the tray as an add-on for a bigger software package.

OpenVPN includes a file called tapinstall.exe that installs the OpenVPN adapter (or any driver for that matter). Doing some research, this file is the exact same as a command-line tool called devcon that Microsoft includes in the Windows DDK. The OpenVPN guys just renamed it for their use.

So we use it during our setup (msi) installer in a custom action to install the driver, which for the most part, works just fine.

Every now and again, devcon fails and hangs--never exiting. After that point, you can re-run devcon and it will install the driver twice... which basically breaks OpenVPN.

Has anyone seen this issue with devcon, know what it's doing, or know a way to fix it?

As an alternate solution, does anyone know how to install a driver from C#? (we have a .inf and a .sys file)

UPDATE: We've found this issues to be pretty rare. It occurs most often when we've applied an update where we uninstall the V8 version of the OpenVPN adapter and then install the new version (V9) of the OpenVPN adapter. It also seems to not happen if you restart your PC in between installs, so we might be better off forcing a PC restart on uninstall....

SIDE NOTE: I've heard of people using WiX and the DifxAPI (I think that is what it's called) to install drivers from an MSI installer. Any ideas if this can be done from plain C# in a custom action? We don't really want to start over with our setup project using WiX (it could be time consuming).

Liz answered 22/7, 2009 at 19:15 Comment(0)
M
4

I don't have a solution for your problem, but here are some ideas:

  • The source code for DevCon is available as part of the Windows DDK under DDK root\Src\Setup\Devcon. If your problem is reproducable, you could build your own version and debug it in your IDE.

  • The sources of the OpenVPN installer can be found in the OpenVPN SVN repository. You could compare how DevCon is invoked and see if the OpenVPN is doing it in a way that prevents the problem.

  • INF files can be installed from the command line using something like

    rundll32 syssetup,SetupInfObjectInstallAction DefaultInstall 128 .\<file>.inf

    but I'd guess that DevCon is doing more than that, so I don't know if this is a viable way. There has to be some reason obviously why the OpenVPN installer is using DevCon, right?


@update:

The OpenVPN installer seems to set a "Reboot Flag" depending of the return value of DevCon.

;------------------------------------------
;Set reboot flag based on tapinstall return

Function CheckReboot
  IntCmp $R0 1 "" noreboot noreboot
  IntOp $R0 0 & 0
  SetRebootFlag true
  DetailPrint "REBOOT flag set"
 noreboot:
FunctionEnd

@side note:

I'd guess you should be able to port DevCon to C# using P/Invokes. DevCon apparently is just a wrapper around SetupAPI and DIFxAPI.


DIFxAPI

Documentation:

P/Invokes:

Test program:

SetDifxLogCallback(DIFLogCallbackFunc, IntPtr.Zero);

bool needReboot;

var error =
    DriverPackageInstall(driverPackageInfPath, 0, IntPtr.Zero, out needReboot);

if (error != 0)
    throw new Win32Exception(error);

Output:

INFO: ENTER:  DriverPackageInstallW. Error code: 0
INFO: Installing INF file 'C:\Program Files (x86)\OpenVPN\driver\OemWin2k.inf' (Plug and Play).. Error code: 0
INFO: Looking for Model Section [tap0901.NTamd64].... Error code: 0
INFO: Installing devices with Id "tap0901" using INF "C:\Windows\system32\DriverStore\FileRepository\oemwin2k.inf_128556d6\OemWin2k.inf".. Error code: 0
INFO: ENTER UpdateDriverForPlugAndPlayDevices.... Error code: 0
SUCCESS: RETURN UpdateDriverForPlugAndPlayDevices.. Error code: 0
INFO: Installation was successful.. Error code: 0
SUCCESS: Install completed. Error code: 0
INFO: RETURN: DriverPackageInstallW  (0x0). Error code: 0

The program must be run as administrator, otherwise you get an ERROR_ACCESS_DENIED.

If the driver is already installed you get an ERROR_NO_MORE_ITEMS.

Mathewson answered 22/7, 2009 at 19:53 Comment(7)
I will look into your suggestions, I also edited my question with some more info.Liz
I bet the return code is the problem, I have no way of knowing if your answer is correct, but I will mark it anyways. We are going force a restart based on the exit code of devcon. I may post back if we still find the issue in the future.Liz
Side note: The SetupAPI also looks like quite a bit of work, and we'd rather skip making a wheel.Liz
The DIFxAPI looks quite simple. Installing an INF is apparently just a single method call. msdn.microsoft.com/en-us/library/ms790268.aspxMathewson
I have tried the DIFxAPI, and could not get it to work. Do you know of any examples in C# of using this API? C++ might work too.Liz
There's another question on SO: #678186 I will try this with the TAP-Win32 driver and report back.Mathewson
I've tested this on Vista64 and updated my answer with my test program. It works perfectly with the TAP-Win32 driver that comes with openvpn-2.1_rc19-install.exeMathewson
P
0

Just a complement, if someone cannot run difxapi functions, you need by someway link your project to difxapi.h and difxapi.lib that came with WDK.

Fast way, just copy difxapi.h and difxapi.lib to your folder project and add to your project. Beware to chose this files that are compatilhe to x86 in wdk folder.

A simple code example, just for test, using C that run on win 7 32bit:

#include <windows.h>
#include <stdio.h>
#include "difxapi.h"    

int main(void)
{
    DWORD dwRet = 0;

    PCTSTR DriverPackageInfPath = TEXT("D:\\MYDRIVER.INF");
    DWORD Flags = 0;
    INSTALLERINFO InstallerInfo;
    BOOL bNeedReboot;

    char chName[] = "Thing Name";
    char chGUID[] = "{4D36E979-E325-11CE-BFC1-08002BE10318}"; //printer GUID

    InstallerInfo.pDisplayName = &chName;
    InstallerInfo.pProductName = &chName;
    InstallerInfo.pMfgName = &chName;
    InstallerInfo.pApplicationId = &chGUID;

    dwRet = DriverPackageInstall( DriverPackageInfPath, Flags, &InstallerInfo , &bNeedReboot );

    switch(dwRet)
    {
    case ERROR_SUCCESS:
        printf("\n\n ERROR_SUCCESS - Ret: %d, %xh", dwRet, dwRet);
        break;
    case CERT_E_EXPIRED:
        printf("\n\n CERT_E_EXPIRED - Ret: %d, %xh", dwRet, dwRet);
        break;
    case CERT_E_UNTRUSTEDROOT:
        printf("\n\n CERT_E_UNTRUSTEDROOT - Ret: %d, %xh", dwRet, dwRet);
        break;
    case CERT_E_WRONG_USAGE:
        printf("\n\n CERT_E_WRONG_USAGE - Ret: %d, %xh", dwRet, dwRet);
        break;
    case CRYPT_E_FILE_ERROR:
        printf("\n\n CRYPT_E_FILE_ERROR - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_ACCESS_DENIED:
        printf("\n\n ERROR_ACCESS_DENIED - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_BAD_ENVIRONMENT:
        printf("\n\n ERROR_BAD_ENVIRONMENT - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_CANT_ACCESS_FILE:
        printf("\n\n ERROR_CANT_ACCESS_FILE - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_FILE_NOT_FOUND:
        printf("\n\n ERROR_FILE_NOT_FOUND - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_FILENAME_EXCED_RANGE:
        printf("\n\n ERROR_FILENAME_EXCED_RANGE - Ret: %d, %xh", dwRet, dwRet);
        break;
    /*case ERROR_IN_WOW64:
        printf("\n\n ERROR_IN_WOW64 - Ret: %d, %xh", dwRet, dwRet);
        break;*/
    case ERROR_INSTALL_FAILURE:
        printf("\n\n ERROR_INSTALL_FAILURE - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_INVALID_CATALOG_DATA:
        printf("\n\n ERROR_INVALID_CATALOG_DATA - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_INVALID_NAME:
        printf("\n\n ERROR_INVALID_NAME - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_INVALID_PARAMETER:
        printf("\n\n ERROR_INVALID_PARAMETER - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_NO_DEVICE_ID:
        printf("\n\n ERROR_NO_DEVICE_ID - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_NO_MORE_ITEMS:
        printf("\n\n ERROR_NO_MORE_ITEMS - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_NO_SUCH_DEVINST:
        printf("\n\n ERROR_NO_SUCH_DEVINST - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_OUTOFMEMORY:
        printf("\n\n ERROR_OUTOFMEMORY - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_SHARING_VIOLATION:
        printf("\n\n ERROR_SHARING_VIOLATION - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH:
        printf("\n\n ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH - Ret: %d, %xh", dwRet, dwRet);
        break;
    case ERROR_UNSUPPORTED_TYPE:
        printf("\n\n ERROR_UNSUPPORTED_TYPE - Ret: %d, %xh", dwRet, dwRet);
        break;
    case TRUST_E_NOSIGNATURE:
        printf("\n\n TRUST_E_NOSIGNATURE - Ret: %d, %xh", dwRet, dwRet);
        break;
    default:
        printf("\n\n default - Ret: %d, %xh", dwRet, dwRet);
        break;
    }
    printf("\n\n");
    system("pause");

    return 1;
}
Peacemaker answered 22/1, 2015 at 14:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.