Which api functions to install a file system driver in Windows? - winapi

I want to install a driver for Ext2 partitions under Windows XP, the installation will be done with a self written Inno Setup script. Which API functions do I need to call for this? From what I googled so far I don't seem to be needing an inf file, and therefore no call to SetupCopyOEMInf. But I don't know what to do instead...
Please enlighten me!

As you asked which API functions are required to install a driver here is some code I use to load a driver in C:
bool LoadDriver( const char * cpDriverPath, const char * cpDriverName )
{
SC_HANDLE hSCService;
SC_HANDLE hSCManager;
hSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
if( hSCManager == NULL )
return false;
hSCService = CreateService( hSCManager, cpDriverName, cpDriverName,
SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
cpDriverPath, NULL, NULL, NULL, NULL, NULL );
if( hSCService == NULL && GetLastError() == ERROR_SERVICE_EXISTS )
hSCService = OpenService( hSCManager, cpDriverName, SERVICE_ALL_ACCESS );
if( hSCService == NULL )
return false;
if( !StartService( hSCService, 0, NULL ) )
{
if( GetLastError() != ERROR_SERVICE_ALREADY_RUNNING )
return false;
}
CloseServiceHandle( hSCManager );
CloseServiceHandle( hSCService );
return true;
}
You probably want to use SERVICE_ BOOT_ START for a file system driver instead of SERVICE_ DEMAND_ START. If you cant write an Inno script to do the above you could always make a small EXE from the above and have Inno run that as a post installation.
If you want to go down the INF route, check out this Microsoft article.

All right, I found some more information:
The MSDN states that 'you should install your file system drivers by using an INF file' (by calling SetupCopyOEMInf etc), but also notes that on Win2k and older systems, 'file system drivers were commonly installed by the Service Control Manager'
And I found another Ext2 driver at sourceforge that comes with an Inno Setup installation script. There they just add a few registry entries under HKLM\SYSTEM\CurrentControlSet\Services\.
I believe that adding these entries is equal to creating a service with the sc.exe tool, which internally calls the OpenSCManager and CreateService api. I prefer the registry way, because it is easier and I see no advantage in using the api calls, since a reboot is always required.
So I ended up with this solution:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\YourService]
"ErrorControl"=dword:00000001
"Start"=dword:00000003
"Type"=dword:00000001
"DisplayName"="YourServiceDisplayName"
"ImagePath"="System32\Drivers\YourService.sys"
Of course depending on the specifics of the driver in question, some more entries in a Parameters subkey may be required.
I am still hoping that someone will be able to shed some more light on this question, specifically some details on how to use an inf file for the installation, since this is the way recommended by MS.
Come on, folks! There's a nice bounty to catch here!

There are a number of ways you could approach this.
1/ See if the product has a silent install mode (e.g., using a configuration file to install). Then you just incorporate that into your own install.
2/ There are programs that can emulate a user, such as Rational Robot (I wouldn't suggest that one since it's commercial, but there's likely to be free ones around). We mostly use these for automated testing of graphical applications but they can just as easily use it to run an installer.
3/ Contact the developer to see what the actual install process entails, then copy that.
4/ Microsoft has a number of system tools (here) that can track changes to the filesystem, registry and so on. You could use these to reverse-engineer the install process and do those same things in your own install.
Personally, I'd go with number 3 first. The developer would be happy to know their product is being used and may provide the information readily. They may even write a silent installer for you as this would be an extra selling point for their product in a corporate environment (suggest this to them).

I wrote a TDI filter driver.
When I came to write the .inf file, it took two weeks of sheer pain to get something working and I still didn't really understand it, because it didn't really make sense; what you had to do seemed very arbitrary. INF files were, when I was trying to use them, almost wholly undocumented and bizzarely complex.

Related

Creating an MSI or Script to deploy an EXE through AD

First:
I know there are other questions that ask similar/the same thing, I have looked at them tried all of them but no solution has helped.
The problem:
We use Epson Iprojection (downloaded from here: https://www.epson.co.nz/products/projectors/software/) However I can not find a way to get an MSI out of it (I am starting to think there is no MSI bundled in the .exe), and I can not seem to install it through CMD on computer start using AD. Any help will be greatly appreciated.
Vague Answer: Let me try to formulate an answer out of those messy comments above. I only briefly tested this software, it might not deploy as badly as expressed. Remember that this is a generic answer for whoever would find this in the future, and not for OP per-se (there is no real answer in here).
Due Diligence: I always try to consult package tip databases if I have problems with a package. Somebody, somewhere will have seen the same problem (eventually).
Silent Installation: Silent installation of legacy setups is usually possible (not always), but never really reliable. For Installshield it involves recording answer files (setup.iss) that record dialog answers. However, unknown dialogs can show up suddenly on some systems (low disk space, reboot prompts, unexpected lock or application in-use warnings, unexpected service running warnings,etc...) and hence halt the install unexpectedly as the response file has no recorded value for the dialog in question. This particular Epson setup also has an unfortunate reboot requirement on uninstall that is hard to deal with for large scale deployment (spontaneous reboot likely - without warning).
Repackaging: Personally I would try to capture the install using a repackaging tool. Most of these are expensive, but can output MSI, MSIX or other deployment package types. Repackaging fails when the package contains complex, custom logic that - for example - create dynamic content (ciphers, unique GUIDs, etc...) and in a number of very specific technical cases.
Contact Vendor: If you capture an MSI that doesn't work, why not try to send it back to the vendor with some comments on how hard this software is to deploy and maybe mention the major benefits of MSI? I would tell them you have to ditch the whole software from your network if they can't deliver a deployment solution that works. Time is of the essence. "Some solutions are only free if your time is worthless" (quote from Joel Spolsky himself - slightly out of context, but the same issue: we need solutions that work in a timely fashion).
Note: I once had to compile a special setup to deal with a client's
deployment problem that was our fault. Deployment problems need fixing at a standardization level. Standards!
Buried within the single-exe download is iProjection_inst.exe, some sort of wrapper that prompts for language selection, and doesn't seem to have a standard "non-interactive" mode.
Inside this is instData\Setup.exe, a vintage "full screen" InstallShield installer with external ini / cab files.
Running this with Setup.exe /? doesn't give any command line arguments, as it would for a modern InstallShield setup.exe.
Although it supports recording an answer file via Setup.exe /r, when I try to replay the answer file with Setup.exe /s it seems to still prompt for EULA, and then fails to install.
So this is really a design flaw in the innermost installer to do with the dialog sequence.
Assuming Epson won't fix it and repackaging is too hard, a hacky alternative is to use Powershell to automate the UI of instData\Setup.exe once the "Welcome" screen is showing, but I don't think this will work running as an AD script.
$wshell = New-Object -ComObject wscript.shell
$aName = "Epson iProjection Setup"
function Next() {
if ($wshell.AppActivate($aName)) {
$wshell.SendKeys(' ');
start-sleep 1;
}
}
function AcceptEula() {
if ($wshell.AppActivate($aName)) {
$wshell.SendKeys("{TAB}");
$wshell.SendKeys("{TAB}");
$wshell.SendKeys(' ');
start-sleep 1;
}
}
Next; AcceptEula; AcceptEula; start-sleep 20; Next;
I know this is an old thread but it looks like it may be easy now. The latest 2.41 from here
http://www.downloads.epson.com.au/DownloadFile.asp?filename=iProV2411Win%5FWEB%2Eexe&path=Drivers
seems to have an MSI in it. Just jump into the %temp% folder and organize by date so you can see the latest files. Run the installer and a new folder named with a GUID should show up, in my case {27CDEEE8-B6F2-45a7-A48E-696862573D9B}.
Under this folder there is a series of InstData folders like InstDataX64 where you can find an MSI.

winUSB error (possible .inf issue)

I would really really appreciate some help with winUSB.
I followed all the steps that were listed in msdn website
http://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff540174(v=vs.85).aspx
I tried to run it on Windows 7 with Visual Studio 2010. Even though it compiles with no errors, I cannot pass the function
SetupDiEnumDeviceInfo, and it errors out (I think error #259).
I don't know how to pass this error.
However I am not sure of the few steps in the process:
What does msdn talk about with "A signed catalog file for the package. This file is required to install WinUSB on x64 versions"? What is a signed catalog package? How do I set this?
It also talks about making some directory (For example, c:\UsbDevice) to put the .inf file in there ! But how does my VS project know that I am pointing to c:\UsbDevice, and how does it know what the name of the ".inf' file should be? what should I call the .inf file? And how to point the project to it?
Inside the .inf file there are two GUID numbers. I can get the Class GUID number by going to the Device Manager. But how do I set the Device Interface GUID?
Please help.
Is is possible that the error that I am getting is because I don't have one of the following items set up correctly?
Thanks,
--Rudy
I developed an application about 2 years ago that I needed to use WinUSB, it's not fresh in my mind but looking at my code now I can see that I did use this function: SetupDiEnumDeviceInfo, but as I remember it wasn't a problem for me.
As you probably know, the WinUSB is an API of Windows for you to communicate with an usb hardware. The hardware must have it's driver already, so, you have to know the GUID of your device
Answering your steps:
1. Signed catalog is the .cat file generated from an inf, you can genarate it using inf2cat.exe, it's somwhere in your machine. This is just a step for signing your winusb driver. To sign it you will need to submit your .inf and the .cat files for the WHQL (windows hardware quality labs) http://www.microsoft.com/whdc/whql/ , this is not important at this moment, I've never signed my WinUSB driver, and it works in the x64 machines with a inelegant warning saying that my WinUSB driver might be dangerous (despite it's not!)
2. The application you are developing doens't need to know where the .inf file is, the .inf file is meant to install the WinUSB driver, the application will communicate with the driver trough the GUID number of the device after the driver is properly installed. Here is part of the code that is necessary to connect to the device after the WinUSB driver is correctly installed.
LPGUID _lpGuid = (LPGUID) malloc (sizeof(GUID));
HRESULT result = CLSIDFromString (L"{A54E04AD-E06A-4A03-95BB-25AACC4E6CCA}", _lpGuid);
...
bResult = SetupDiEnumDeviceInterfaces(deviceInfo,
NULL,
_lpGuid,
0,
&interfaceData); //If the function succeeds
//the return value is nonzero.
// [1]
deviceInfo = SetupDiGetClassDevs(_lpGuid,
NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (deviceInfo == INVALID_HANDLE_VALUE)
{
//lblDispStatus->Caption = "Erro 1 - deviceInfo";
return false;
}
// [2]
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
bResult = SetupDiEnumDeviceInterfaces(deviceInfo,
NULL,
_lpGuid,
0,
&interfaceData); //If the function succeeds
//the return value is nonzero.
InterfaceData is passed as reference, so you will have the ability to call the next function, that is SetupDiGetDeviceInterfaceDetail
3.Who developed the driver? He must know the GUIDs you need.
All I know from WinUSB I fount at the poor WinUSB documentation, you will need tons of patiante to read all their messy information. And for your unlucky, it's not fresh in my mind right know, I would have to take a deep look at this subject to help you more.
This page seems important at the point you are, take a look at this: http://msdn.microsoft.com/en-us/library/windows/hardware/ff540174%28v=vs.85%29.aspx

Visual Studio C++ RegOpenKeyEx() registry Access is denied error Windows 7 64 bit

I'm running a C++ program (which works great on 32 bit Win XP) on Windows 7 64 bit in debugger under Visual Studio 2010 and I am unable to open an existing registry key with the following code:
#define ACCESS (KEY_WRITE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS)
HKEY hKey;
long dwErrorCode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\MYTHING", 0, ACCESS|KEY_WOW64_64KEY, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
// display error message
}
The error code returned is: 5 with the system message being "Access is Denied".
This does work if I run the Visual Studio 2010 as Admin, however I'd rather not have to do that. Can anybody offer a suggestion?
Update: I forgot to mention, what I am doing here is porting legacy code from Windows XP. As such, I don't have the option of changing the fundamental structure of how this software was written. Since the legacy code uses the registry, that is what the ported code must do also.
Also, I would rather not make changes to my specific computer -- since that means I would have to change every computer that I want to run this on. This could get messy as there are a lot of machines affected. For example, I don't want to turn off UAC for the entire machine.
Further update: I haven't found a solution that I'm happy with. Have decided to ignore error code 5 for purposes of debugging and that seems to be working well enough for now. I'm trying to understand how standard apps like Word, Firefox, etc appear to be using the registry for all kinds of settings and yet are not elevated nor do I have to give them special permissions to make changes to the registry?
Ok, I found the answer to my question so I'll post it here in case anybody else needs it for future reference. This thread turned out to be helpful even though it is actually on the topic of C#:
http://social.msdn.microsoft.com/Forums/da-DK/netfx64bit/thread/92f962d6-7f5e-4e62-ac0a-b8b0c9f552a3
Basically, I needed to change my permission to read the 32 bit registry instead of the 64 bit one like so:
HKEY hKey;
long dwErrorCode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\MYTHING", 0, ACCESS|KEY_WOW64_32KEY, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
// display error message
}
Voila, everything works now! Thanks for your efforts everyone.
Update: it turns out this didn't work as well on my other machine, which led me to discover that someone must have changed access rights to the 32 bit registry on one of my machines. So it is still necessary to give the User access rights to the registry key you want to work with.
You need to run the process in elevated mode, or turn off UAC. You can of course assign access rights to your specific registry key that allows you access.
I have faced same issue. I have solved using following:
LPCTSTR subKey = TEXT("Software\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths");
LONG openRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_WOW64_32KEY && KEY_ALL_ACCESS, &hKey);

The Uninstall section of my .INF

I'm working on extending an existing USB device driver, and have been struggling learn how the .INF file works. I believe that I have it right now, the driver installs and works. It also shows up in the "Add/Remove Programs" list.
I find that when I uninstall the driver from Add/Remove, it seems to do little if anything. This was in the .INF as I inherited it:
[myUninstall]
DelReg = myDelReg
[MyDelReg]
HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\myName"
I can find no other references, not even where "myName" is installed in the registry Uninstall.
I've changed the .INF to have this:
[DefaultUninstall]
myUninstall
[myUninstall]
DelFiles = ... a list of files ...
DelDirs = UNINST.Dirs
DelReg = UNINST.Regs
Cleanup = 1
[UNINST.Dirs]
%16422%\%InstallDir%
[UNINST.Regs]
myDev.AddReg
myDev.CommonRegs
[myDev.AddReg] ; same definition used for installing
HKR,,Drivers,Subclasses,,"wave,midi,mixer,aux"
...
[myDev,CommonRegs] ; same definition used for installing
HKR, Parameters, BufferSize, 0x0010001, 256
...
Still, whether I uninstall from the Control Panel Add/Remove programs or do it from the device manager, the system doesn't complain and nothing seems to happen. I've looked at the output of "dpinst /c" and the setupapi.log, and neither tell me much. dpinst shows no error but is clearly not even trying to execute my uninstall section.
I find a lot of verbiage online and examples of uninstall sections, but very little in the way of explanation. I did stumble across one sentence in 1 forum today that implied that PnP device .INF's don't use Uninstall. Perhaps this is my issue?
am I wrong in thinking that I can have Uninstall in this .INF?
is there some other way to tell what is happening?
is my only recourse to write a separate uninstaller?
AFIK, the INF files do not participate in uninstallation.
You will need to write a separate uninstaller if you want to perform proper cleanup.
Further note that with Vista and beyond, Windows also includes a Driver Store, where it silently copies the driver package (inf and files references from the inf). If you want to perform a full cleanup, the driver store should also be cleaned up.
I don't think DefaultUninstall is a standard section in the way DefaultInstall is. Often you will see the UninstallString ARP entry just calling DefaultUninstall by name.
The documentation in this area is pretty bad and there is a split between the basic SetupAPI/SetupX INF files and the Advanced INF files (AdvPack).
The only 3rd-party exception I know of is TweakUI.inf from the Windows 95 PowerToys.
It has parts that look like this:
...
[Optional Components]
TweakUIInstall
[TweakUIInstall]
CopyFiles = ...
AddReg = ...
Ini2Reg = ...
...
InstallType = 10 ;Typical, Custom.
Uninstall = DefaultUnInstall
[DefaultUnInstall]
DelFiles = ...
DelReg = ...
But I think that only works with the optional components feature, not basic uninstallation. TweakUI v1.33 (Tweakui.exe SFX archive) has some more NT related entries and I believe the way it writes the UninstallString for NT even writes the wrong INF section. Wrong or not, it manually has to specify a INF section for normal uninstall.

Programatically registering .dll's on Windows Vista (using DllRegisterServer)

Instead of calling regsvr32.exe, one can register a .DLL using the following steps:
HINSTANCE hLib = ::LoadLibraryEx(dllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
HRESULT (STDAPICALLTYPE* lpDllEntryPoint)(void);
(FARPROC&)lpDllEntryPoint = ::GetProcAddress(hLib, "DllRegisterServer");
const HRESULT hRes = (*lpDllEntryPoint)();
This works fine on Windows XP. Regrettably, it fails on Vista, but only with some specific DLLs. hRes becomes E_ACCESSDENIED. I guess this is a security issue. Does anyone know how to register a .DLL from code on Windows Vista?
Note: I was logged in as administrator when running this code.
COM registration requires write access to the HKEY_LOCAL_MACHINE part of the registry.
Under UAC, write access to the HKEY_LOCAL_MACHINE requires an elevated administrator.
The easiest way to get an elevated process is to create it with a manifest that specifies 'requireAdministrator' access. - Look under the Project Properties -> Configuration Properties->Linker->Manifest File->UAC Execution Level to set the correct setting.
This means you will probably want to split your EXE into two parts. The 'normal' asInvoker part, and, when self registration is detected as a requirement, an elevated InstallMyself part. When the non elevated part detects a first-run type condition, it needs to use ShellExecute(Ex) to execute the FirstInstall.exe part - using CreateProcess or some other API will simply fail with a insufficient privilege error. ShellExecute will present the UAC prompt.
It is possible to use Application Isolation to load COM dll's without any registration step at all.
Is is unfortunate that the cause cannot be determined. However, if you are interested in doing further research, a tool that will help a lot would be Process Monitor from SysInternals. Process Monitor can log all the File, Registry and other access for a process, including all success and fail codes making it a lot easier to debug problems like this without having to resort to deeper means of reverse engineering.
Regrettably, I couldn't get this to work for all DLLs, even with Chris Becke's excellent tips. I didn't want to spend too much time solving the problem, so now I simply call regsvr32.exe. I expect this .exe to be present on all Windows machines, so I guess it is a good enough solution.

Resources