C# SetupCopyOEMInf安装驱动并返回DestinationInfFileNameComponent
最近用C#写安装usb驱动,必须得调用API SetupCopyOEMInf:
BOOL WINAPI SetupCopyOEMInf(
_In_ PCTSTR SourceInfFileName,
_In_ PCTSTR OEMSourceMediaLocation,
_In_ DWORD OEMSourceMediaType,
_In_ DWORD CopyStyle,
_Out_opt_ PTSTR DestinationInfFileName,
_In_ DWORD DestinationInfFileNameSize,
_Out_opt_ PDWORD RequiredSize,
_Out_opt_ PTSTR DestinationInfFileNameComponent
);
于是在C#里这么写了:
[DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupCopyOEMInf( string SourceInfFileName, string OEMSourceMediaLocation, OemSourceMediaType OEMSourceMediaType, OemCopyStyle CopyStyle, out string DestinationInfFileName, int DestinationInfFileNameSize, int RequiredSize, out string DestinationInfFileNameComponent );
其中DestinationInfFileName代表驱动成功安装后,inf文件在C:\Windows\inf目录下的绝对路径,这个inf文件名字和原inf文件不一样,但是内容是一模一样的,不知道为啥inf驱动安装成功后会把inf文件换一个名字然后copy到C:\Windows\inf目录下?有高人解答下吗?
DestinationInfFileNameComponent代表copy到C:\Windows\inf目录下的那个inf的名字,这个很有用,调用SetupUninstallOEMInf卸载驱动的时候要用到这个名字。
然后我在C#中这么调用SetupCopyOEMInf:
unsafe { success = SetupCopyOEMInf(infPath, "", OemSourceMediaType.SPOST_PATH, OemCopyStyle.SP_COPY_NEWER, out destinationInfFileName, 260, 0, out destinationInfFileNameComponent); }
260是文件目录的最大长度,查看log,C:\Windows\inf\setupapi.dev.log,可以成功安装,success为true,但是接下来的问题困扰了我好久,destinationInfFileNameComponent和destinationInfFileName始终都没有值,按照https://msdn.microsoft.com/en-us/library/aa376990.aspx上的说话,成功执行后会返回这俩值,destinationInfFileNameComponent是调用SetupUninstallOEMInf卸载驱动的必传参数,没有值就无法卸载了,google了半天没有解决,最后看别人用C++写的SetupCopyOEMInf,destinationInfFileName传的是一个长度为260的TCHAR数组,于是我把C#的SetupCopyOEMInf原型改为:
[DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupCopyOEMInf( string SourceInfFileName, string OEMSourceMediaLocation, OemSourceMediaType OEMSourceMediaType, OemCopyStyle CopyStyle, out char[] DestinationInfFileName, int DestinationInfFileNameSize, int RequiredSize, out string DestinationInfFileNameComponent );
然后把destinationInfFileName声明为一个260长度的char数组,但是调用会报executionexception的异常。后来把原型参数char[] destinationInfFileName前面的out去掉,destinationInfFileNameComponent的值终于正确得到了!!!!!!!!
但是destinationInfFileName依然无法得到,好歹有了一个,终于可以做卸载了,具体为什么可以了也不太清楚,为什么PTSTR DestinationInfFileName传递C#的string不对?传递char数组才可以,但是无法使用out,估计destinationInfFileNameComponent是在destinationInfFileName的基础上得到的,所以传递string类型的destinationInfFileNameComponent是可以的吧,糊涂了!
完美的解决方法:
原型:
[DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupCopyOEMInf( string SourceInfFileName, string OEMSourceMediaLocation, OemSourceMediaType OEMSourceMediaType, OemCopyStyle CopyStyle, StringBuilder DestinationInfFileName, int DestinationInfFileNameSize, int RequiredSize, out string DestinationInfFileNameComponent );
调用:
string msg = ""; int size = 0; StringBuilder destinationInfFileName_builder = new StringBuilder(260); unsafe { success = SetupCopyOEMInf(infPath, "", OemSourceMediaType.SPOST_PATH, OemCopyStyle.SP_COPY_NEWER, destinationInfFileName_builder, destinationInfFileName_builder.Capacity, 0,out destinationInfFileNameComponent); } if (!success) { var errorCode = Marshal.GetLastWin32Error(); var errorString = new Win32Exception(errorCode).Message; msg = errorString; }
把destinationInfFileName设成一个有260个字符长的空stringbuilder,stringbuilder是按引用传值,所以不需要添加out或者ref了,但是destinationInfFileNameComponent必须是out string,设成stringbuilder会是乱码。
这样destinationInfFileNameComponent和destinationInfFileName都能正确得到了!这样是不是可以猜测,C#调用WINAPI,如果函数原型参数是_Out_opt_ PTSTR,并且这个PTSTR要有长度输入,在C#中就可以声明成StringBuilder?
参考:
http://www.pinvoke.net/default.aspx/setupapi.SetupCopyOEMInf
https://msdn.microsoft.com/en-us/library/aa376990.aspx
http://stackoverflow.com/questions/18404660/how-to-use-setupcopyoeminf-during-installer