我们已经编写了一个应用程序,可以从托盘中管理OpenVPN,作为更大软件包的附件。
OpenVPN包含一个名为tapinstall.exe的文件,用于安装OpenVPN适配器(或任何驱动程序)。做一些研究,这个文件与Microsoft在Windows DDK中包含的名为devcon的命令行工具完全相同。 OpenVPN的人刚刚将其重命名为使用它。
因此我们在安装程序(msi)安装程序中使用它来自定义操作来安装驱动程序,这在大多数情况下都可以正常工作。
每隔一段时间,devcon就会失败并挂起 - 永不退出。在那之后,你可以重新运行devcon,它将安装驱动程序两次......这基本上打破了OpenVPN。有没有人与devcon见过这个问题,知道它在做什么,或者知道修复它的方法?
作为替代解决方案,有谁知道如何从C#安装驱动程序? (我们有.inf和.sys文件)
更新:我们发现这个问题非常罕见。它最常发生在我们应用更新时,我们卸载OpenVPN适配器的V8版本,然后安装OpenVPN适配器的新版本(V9)。如果你在两次安装之间重新启动电脑,似乎也不会发生这种情况,所以我们最好在卸载时强行重启电脑....
SIDE注意:我听说有人使用WiX和DifxAPI(我认为这就是所谓的)从MSI安装程序安装驱动程序。如果可以通过自定义操作中的纯C#来完成任何想法?我们真的不想重新开始使用WiX的设置项目(这可能非常耗时)。
答案 0 :(得分:4)
我没有针对您的问题的解决方案,但这里有一些想法:
DevCon的源代码作为 DDK root \ Src \ Setup \ Devcon 下Windows DDK的一部分提供。如果您的问题是可重现的,您可以构建自己的版本并在IDE中进行调试。
可以在OpenVPN SVN repository中找到OpenVPN安装程序的来源。您可以比较调用DevCon的方式,并查看OpenVPN是否以防止问题的方式执行此操作。
可以使用
之类的命令从命令行安装INF文件 rundll32 syssetup,SetupInfObjectInstallAction DefaultInstall 128 .\<file>.inf
但我猜DevCon的功能不止于此,所以我不知道这是不是一种可行的方法。显然,为什么OpenVPN安装程序使用DevCon,对吧?
@Update:
OpenVPN安装程序似乎set a "Reboot Flag"取决于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:
我猜你应该能够使用P / Invokes将DevCon移植到C#。 DevCon显然只是SetupAPI和DIFxAPI的包装。
<强> DIFxAPI 强>
文档:
P /调用:
测试程序:
SetDifxLogCallback(DIFLogCallbackFunc, IntPtr.Zero);
bool needReboot;
var error =
DriverPackageInstall(driverPackageInfPath, 0, IntPtr.Zero, out needReboot);
if (error != 0)
throw new Win32Exception(error);
输出:
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
该程序必须以管理员身份运行,否则您将获得ERROR_ACCESS_DENIED
。
如果已安装驱动程序,则会获得ERROR_NO_MORE_ITEMS
。
答案 1 :(得分:0)
只是补充,如果某人无法运行difxapi函数,您需要通过某种方式将项目链接到WDK附带的difxapi.h
和difxapi.lib
。
快捷方式,只需将difxapi.h
和difxapi.lib
复制到您的文件夹项目中即可添加到您的项目中。请注意选择与wdk文件夹中的x86兼容的文件。
一个简单的代码示例,仅用于测试,使用在win 7 32bit上运行的C:
#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;
}