Obtaining a CreateFile()device path (MS DOS Path) using SetupDi* for a device

I have a SCSI HBA (ATTO 348) installed on my machine. I plugged a RAID expander into the HBA controller. My goal is to obtain a device path / MS DOS path that I can use with CreateFile, so that I can then call DeviceIOControl.

I am working with C#.Net. I started off with WMI, but then migrated to using the Windows DDK install library (SetupDi*). I got most of the way, but am stuck at the last part. Let me explain.

As a first step, I want to open a channel to the ATTO controller, but the expander has the same problem and that is the concern, as the HBA was merely a stepping point. There are two basic ways to go about the task. The first is call SetupDiGetClassDev and then enumerate the devices using SetupDiEnumDeviceInfo, while the second method is to enumerate all GUID_DEVINTERFACE_STORAGEPORT interfaces. The latter method does not yield the expander, while the first one does.

I am able to call SetupDiGetDeviceInterfaceDetail and get the device path, which in theory should be what I pass to CreateFile, but the device path returned is not CreateFile friendly. That just gives an error message of no file/device found. I did some research and the problem is the interface GUID used in the path.

The device path returned by the DDK is:

\?\PCI#VEN_117C&DEV_002C&SUBSYS_002D117C&REV_00#4&f051ea2&0&0008#{4d36e97b-e325-11ce-bfc1-08002be10318}

The correct device path should be:

\?\PCI#VEN_117C&DEV_002C&SUBSYS_002D117C&REV_00#4&F051EA2&0&0008#{2accfe60-c130-11d2-b082-00a0c91efb8b}

The difference is the GUID. The GUID of {2accfe60-c130-11d2-b082-00a0c91efb8b} is of course GUID_DEVINTERFACE_STORAGEPORT. Now, I figured that out by searching the registry and going to the HKLM\SYSTEM\CurrentControlSet\DeviceClasses. Under the STORAGEPORT hive, there is the HBA card. That has a symbolic link entry, which CreateFile likes.

So far I have seen NO method either using WMI or SetupDi that gives me the correct path. I see no way to obtain {2accfe60-c130-11d2-b082-00a0c91efb8b} from the device info enumeration list.

Why is that important, when I do not want to talk to the HBA card? The answer is that knowing how to talk to one device allows me to talk to my device of interest. Sadly, okay VERY SADLY, there is no entry in HLM\SYSTEM\CurrentControlSet\DeviceClasses for the expander. Windows does enumerate the device: HKLM\SYSTEM\CurrentControlSet\Enum\SCSI, but there is nothing there that yields the magic interface GUID that I need. I already have the base part. That is easy. The hard part is the last GUID.

The expander uses Microsoft’s default driver. Microsoft lists the expander as a system device, although in the registry I see the device under SCSI.

After my rambling, the question is how can I get the device path, MS DOS Name, whatever, programmatically so that I can use that with a call to CreateFile(). I hesitate to say “device name”, “device path”, “MS DOS Name”. Many web documents, not to mention Microsoft documents, do indicate that the path returned by SetupDiGetInterfaceDetail, the so called device path, can be passed to CreateFile, but as my experiment shows, the claim is not true and at best only works for some devices. The SCSI HBA card is an example. My goal is to get a method that will allow me to talk to the expander box attached. I want to use SCSI Pass Through, passthrough, to talk to the expander.

Thanks in advance,

Sarah

xxxxx@jmr.com wrote:

As a first step, I want to open a channel to the ATTO controller, but the expander has the same problem and that is the concern, as the HBA was merely a stepping point. There are two basic ways to go about the task. The first is call SetupDiGetClassDev and then enumerate the devices using SetupDiEnumDeviceInfo, while the second method is to enumerate all GUID_DEVINTERFACE_STORAGEPORT interfaces. The latter method does not yield the expander, while the first one does.

I am able to call SetupDiGetDeviceInterfaceDetail and get the device path, which in theory should be what I pass to CreateFile, but the device path returned is not CreateFile friendly. That just gives an error message of no file/device found. I did some research and the problem is the interface GUID used in the path.

The device path returned by the DDK is:

\?\PCI#VEN_117C&DEV_002C&SUBSYS_002D117C&REV_00#4&f051ea2&0&0008#{4d36e97b-e325-11ce-bfc1-08002be10318}

The correct device path should be:

\?\PCI#VEN_117C&DEV_002C&SUBSYS_002D117C&REV_00#4&F051EA2&0&0008#{2accfe60-c130-11d2-b082-00a0c91efb8b}

The difference is the GUID. The GUID of {2accfe60-c130-11d2-b082-00a0c91efb8b} is of course GUID_DEVINTERFACE_STORAGEPORT. Now, I figured that out by searching the registry and going to the HKLM\SYSTEM\CurrentControlSet\DeviceClasses. Under the STORAGEPORT hive, there is the HBA card. That has a symbolic link entry, which CreateFile likes.

That’s interesting. 4d36e97b… is GUID_DEVCLASS_SCSIADAPTER, the INF
install class ID for SCSIAdapter. It’s not a device interface GUID. If
the device has a STORAGEPORT interface, then SetupDiGetClassDevs with
DIGCF_DEVICEINTERFACE should have returned it.

Can you post the SetupDi code that doesn’t work? It’s awfully easy to
get that wrong.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

> -----Original Message-----

From: xxxxx@lists.osr.com [mailto:bounce-472050-
xxxxx@lists.osr.com] On Behalf Of Tim Roberts
Sent: Friday, August 26, 2011 11:18 AM
To: Windows System Software Devs Interest List
Subject: Re: [ntdev] Obtaining a CreateFile()device path (MS DOS Path)
using
SetupDi* for a device

xxxxx@jmr.com wrote:
> As a first step, I want to open a channel to the ATTO controller, but
the
expander has the same problem and that is the concern, as the HBA was
merely a
stepping point. There are two basic ways to go about the task. The first
is
call SetupDiGetClassDev and then enumerate the devices using
SetupDiEnumDeviceInfo, while the second method is to enumerate all
GUID_DEVINTERFACE_STORAGEPORT interfaces. The latter method does not
yield
the expander, while the first one does.
>
> I am able to call SetupDiGetDeviceInterfaceDetail and get the device
path,
which in theory should be what I pass to CreateFile, but the device path
returned is not CreateFile friendly. That just gives an error message of
no
file/device found. I did some research and the problem is the interface
GUID
used in the path.
>
> The device path returned by the DDK is:
>
>
\?\PCI#VEN_117C&DEV_002C&SUBSYS_002D117C&REV_00#4&f051ea2&0&0008#{4d36e97b-
e325-11ce-bfc1-08002be10318}
>
> The correct device path should be:
>
>
\?\PCI#VEN_117C&DEV_002C&SUBSYS_002D117C&REV_00#4&F051EA2&0&0008#{2accfe60-
c130-11d2-b082-00a0c91efb8b}
>
> The difference is the GUID. The GUID of {2accfe60-c130-11d2-b082-
00a0c91efb8b} is of course GUID_DEVINTERFACE_STORAGEPORT. Now, I figured
that
out by searching the registry and going to the
HKLM\SYSTEM\CurrentControlSet\DeviceClasses. Under the STORAGEPORT hive,
there is the HBA card. That has a symbolic link entry, which CreateFile
likes.

That’s interesting. 4d36e97b… is GUID_DEVCLASS_SCSIADAPTER, the INF
install class ID for SCSIAdapter. It’s not a device interface GUID. If
the device has a STORAGEPORT interface, then SetupDiGetClassDevs with
DIGCF_DEVICEINTERFACE should have returned it.

Can you post the SetupDi code that doesn’t work? It’s awfully easy to
get that wrong.

In all likelihood, that device doesn’t *have* an interface, so you can
locate instances using the CLASS Guid, but there isn’t an instance under the
INTERFACE Guid.

Or something like that. I ran into that when attempting to locate ATA HBAs
under the ATAPI.sys a *long* time ago. Since the ATAPI.sys driver didn’t
implement any INTERFACE with a defined GUID, there was no INTERFACE GUID by
which it could be located and a handle to it opened.

I suspect the OP is running into the same iceberg.

Phil

Philip D. Barila

Hi Tim and Philip,

I know that there is a solution, because ATTO (and other programs) can not only talk to the ATTO card, BUT in HUGE letters, show the expander and can send/receive SCSI pass-through commands. I just do not have the source code for the other apps. That is okay, becuase in this way I learn more and gain a lot in the long run.

Yes, it is easy to get the DDK install library wrong. Heck, for quite some time I kept getting either crashes, 1784 or 87 error return codes. Working successfully was not one of the options. Code on the internet was usually wrong, even code that says “trust me” (see pinvoke.net). It would appear that there are very few people that truly work with SetupDi functions and even fewer on C#.

{4d36e97b-e325-11ce-bfc1-08002be10318} is the SCSIAdapter class and not an interface class for the HBA. Yes, I know that the hard way. I would have thought that SetupDiGetInterfaceDetail would have returned an interface, but it does not.

Before continuing or showing the code, I wanted to give the reason for the HBA. One, I assume that the CreateFile() friendly path usually follows the same construction on devices. Yes, there are the even shorter paths (e.g. C:), but if the “usual” construction seems like a safe bet. Since I know everything about the HBA and it is a related product, it makes the most sense to try out my code on for the real device. Yes, I could have interfaced to any other device for testing, such as video card or a CD-ROM, but I do not have the information for that and the ATTO 348 HBA seemed like a good a choice as any.

Another interesting point is that SetupDi seems to not give back as much information as the WMI interface. Sadly, WMI does not return the MS-DOS or NT Device Name/Path, hence my choice of the DDK Install library (SetupDi).

Code:

// We start at the “root” of the device tree and look for all
// devices that match the interface GUID of a disk
DiGetClassFlags iFlags = DiGetClassFlags.DIGCF_PRESENT | DiGetClassFlags.DIGCF_ALLCLASSES | DiGetClassFlags.DIGCF_PROFILE;
int iArgFlags = (int)iFlags;
hDevInfo = SetupDiGetClassDevs(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, iArgFlags);
if (INVALID_HANDLE_VALUE == hDevInfo.ToInt32())
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
return false;
}

and then enumerate using

// Enumerate the next devices or the first device, if the first pass.
bSuccess = SetupDiEnumDeviceInfo(hDevInfo, (UInt32)iDevice, sDevInfoData);
if (false == bSuccess)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

The call to GetDeviceDetails() to get the device path.

Boolean GetDeviceDetails(IntPtr hDevInfo, ref SP_DEVINFO_DATA sDevInfoData, ref String strDevDevicePath)
{
// Now we can get some more detailed information.
int iErrorCode;
UInt32 uRequiredSize;
IntPtr lpBuffer;
SP_DEVICE_INTERFACE_DATA sDevData;
SP_DEVICE_INTERFACE_DETAIL_DATA sDevDetails;

// Initialize the necessary buffers.
iErrorCode = 0;
uRequiredSize = 0;
sDevData = new SP_DEVICE_INTERFACE_DATA();
sDevDetails = new SP_DEVICE_INTERFACE_DETAIL_DATA();

// Initialize the Device Interface Data structure.
sDevData.cbSize = (uint)Marshal.SizeOf(sDevData);

//
bResult = SetupDiCreateDeviceInterface(hDevInfo, sDevInfoData, ref sDevInfoData.ClassGuid, IntPtr.Zero, (UInt32)0L, sDevData);

// First call to get amount of memory needed.
bResult = SetupDiGetDeviceInterfaceDetail(hDevInfo, sDevData, IntPtr.Zero, 0, out uRequiredSize, sDevInfoData);
if (false == bResult)

… // Allocate buffers, etc.

// Second call to actually retrieve data.
bResult = SetupDiGetDeviceInterfaceDetail(hDevInfo, sDevData, lpBuffer, uRequiredSize, out uRequiredSize, sDevInfoData);

// Extract the path.

strDevDevicePath = Marshal.PtrToStringAuto(pDevicePath);


}

Hi All,

I stripped out all the try/catch/finally and other code, so as to streamline things.

I also have obtain all the other pieces of information. I have an if-statement, not shown, which tests the name of the device I want (the expander or the HBA) to that name returned by the DDK install functions. If there is a match, then and only then do I call the SetupDiGetInterfaceDetail.

It is a bit annoying that the class returned by the “interface” function is not an interface at all. Is that a bug with Microsoft? The GUID returned is the same as the class GUID. In fact, almost every place that returns a GUID shows the exact same GUID. I do not need to know the device class GUID. I already have that.

There is most definitely an interface GUID, as I have seen it with the HBA. I can talk to the card nicely. Just I obtained that the hard way, through a human search in the regisry. A search in the registry was not helpful for the expander. I do know that other user mode applications solve the problem, so I will too.

Thanks in advance,

Sarah

xxxxx@jmr.com wrote:

{4d36e97b-e325-11ce-bfc1-08002be10318} is the SCSIAdapter class and not an interface class for the HBA. Yes, I know that the hard way. I would have thought that SetupDiGetInterfaceDetail would have returned an interface, but it does not.

Well, it does, but only because you have told device manager that your
device implements that interface.

// We start at the “root” of the device tree and look for all
// devices that match the interface GUID of a disk
DiGetClassFlags iFlags = DiGetClassFlags.DIGCF_PRESENT | DiGetClassFlags.DIGCF_ALLCLASSES | DiGetClassFlags.DIGCF_PROFILE;
int iArgFlags = (int)iFlags;
hDevInfo = SetupDiGetClassDevs(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, iArgFlags);

That’s not looking for device interfaces, that’s looking for install
classes. If you want device interfaces, you need DIGCF_DEVICEINTERFACE.

There are some weird interactions in the SetupDi APIs with this. If you
don’t search by device interface, you can get unexpected information

// Initialize the Device Interface Data structure.
sDevData.cbSize = (uint)Marshal.SizeOf(sDevData);

//
bResult = SetupDiCreateDeviceInterface(hDevInfo, sDevInfoData, ref sDevInfoData.ClassGuid, IntPtr.Zero, (UInt32)0L, sDevData);

THIS is the cause of your problems! YOU are telling the system that
“the device I just enumerated implements this interface”. The GUID you
are passing is the SCSIAdapter install class. So, the reason you are
getting that string back as an file name is because you told the system
that file name could be used!

I guarantee you that you do not want to call this API.

What on earth did you think that was doing?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

xxxxx@jmr.com wrote:

It is a bit annoying that the class returned by the “interface” function is not an interface at all. Is that a bug with Microsoft? The GUID returned is the same as the class GUID. In fact, almost every place that returns a GUID shows the exact same GUID. I do not need to know the device class GUID. I already have that.

I’m afraid not. It’s a bug in your code. It’s returning the class as
an interface because you told it that was an interface GUID.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

> -----Original Message-----

From: xxxxx@lists.osr.com [mailto:bounce-472081-
xxxxx@lists.osr.com] On Behalf Of xxxxx@jmr.com
Sent: Friday, August 26, 2011 12:41 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Obtaining a CreateFile()device path (MS DOS Path)
using
SetupDi* for a device

Hi All,

I stripped out all the try/catch/finally and other code, so as to
streamline
things.

I also have obtain all the other pieces of information. I have an if-
statement, not shown, which tests the name of the device I want (the
expander
or the HBA) to that name returned by the DDK install functions. If there
is a
match, then and only then do I call the SetupDiGetInterfaceDetail.

It is a bit annoying that the class returned by the “interface” function
is
not an interface at all. Is that a bug with Microsoft? The GUID returned
is
the same as the class GUID. In fact, almost every place that returns a
GUID
shows the exact same GUID. I do not need to know the device class GUID.
I
already have that.

There is most definitely an interface GUID, as I have seen it with the
HBA. I
can talk to the card nicely. Just I obtained that the hard way, through a
human search in the regisry. A search in the registry was not helpful for
the
expander. I do know that other user mode applications solve the problem,
so I
will too.

Thanks in advance,

Sarah

You mentioned in your original post that “The expander uses Microsoft’s
default driver. Microsoft lists the expander as a system device, although in
the registry I see the device under SCSI.” What is the “default driver”?
Perhaps that driver exposes a documented interface?

Is it possible that the software you are attempting to emulate sends CDBs
directly to the Expander device, either through an IOCTL_SCSI_MINIPORT to
the ATTO miniport or via IOCTL_SCSI_PASSTHROUGH?

Phil

Philip D. Barila

Hi,

I have no clue what others do, none. I would be happy if Microsoft had an SDK function and/or a .net function called,

HANDLE SCSIExpanderCallThisToTalkToYourExpander()

That works for me fine, but there is no such function or roadmap, so I keep trying and learning along the way.

The other day, yesterday maybe?, I created another function based on a comment that someone else made. The function is meant to step through with a debugger and to move the cursor and set values of variables as necessary, not run straight through. It really is not meant to run straight. The code, does not show the expander, but does show the HBA. The only interface it gives is the one that I already have, the wrong one.

By the way, should not the right one be there too as one of the enumerated interfaces?

Here is that code:

private void GetDeviceInterface(IntPtr hDevInfoIn, ref SP_DEVINFO_DATA sDevInfoDataIn, ref Guid guidDevInterfaceLocal)
{
// Initialize the necessary variables.
guidDevInterfaceLocal = new Guid(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);

try
{
Boolean bSuccess;
int iErrorCode;
UInt32 uMemberIndex;
IntPtr hDevInfo;
DiGetClassFlags iFlags;
SP_DEVICE_INTERFACE_DATA sDeviceInterfaceData;

// Work with the SCSI interface. We want to enumerate the SCSI bus.
iFlags = DiGetClassFlags.DIGCF_PRESENT | DiGetClassFlags.DIGCF_DEVICEINTERFACE;
hDevInfo = SetupDiGetClassDevs(ref GUID_DEVINTERFACE_STORAGEPORT, IntPtr.Zero, IntPtr.Zero, (int)iFlags);
if (INVALID_HANDLE_VALUE == hDevInfo.ToInt32())
return;

// Allocate a new structure to hold the device interface data information.
sDeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
sDeviceInterfaceData.cbSize = (UInt32)Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));

// Loop for each interface class.
uMemberIndex = 0;
do
{
// Get the next device interface class.
bSuccess = SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref GUID_DEVINTERFACE_STORAGEPORT, uMemberIndex, sDeviceInterfaceData);
if (false == bSuccess)
{
// Get the last error code. We except insufficient buffer size.
iErrorCode = Marshal.GetLastWin32Error();
if (ERROR_NO_MORE_ITEMS == iErrorCode)
break;
Marshal.ThrowExceptionForHR(iErrorCode);
}

// Get the next enumerated item.
uMemberIndex++;

} while (true);

uMemberIndex = 0;
while (true)
{
String strDevDevicePath, strDevClassName, strDevDriver, strDevDescription, strDevName, strDevInstanceId;

// Initialize the required variables.
strDevDevicePath = “”;

// Create a Device Interface Data structure.
SP_DEVINFO_DATA sDevInfoData = new SP_DEVINFO_DATA();
sDevInfoData.cbSize = (uint)Marshal.SizeOf(sDevInfoData);

// Enumerate the next devices or the first device, if the first pass.
bSuccess = SetupDiEnumDeviceInfo(hDevInfo, (UInt32)uMemberIndex, sDevInfoData);
if (false == bSuccess)
{
iErrorCode = Marshal.GetLastWin32Error();
if (ERROR_NO_MORE_ITEMS == iErrorCode)
break;
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}

// Get Device: Details
bSuccess = GetDeviceDetails(hDevInfo, ref sDevInfoData, ref strDevDevicePath);

// Get Device: Class name
strDevClassName = GetDeviceClassName(hDevInfo, ref sDevInfoData);

// Get Device: Driver Key Name
strDevDriver = GetDeviceDriver(hDevInfo, ref sDevInfoData);

// Get Device: Description
strDevDescription = GetDeviceDescription(hDevInfo, sDevInfoData.ClassGuid);

// Get Device: Name
strDevName = GetDeviceName(hDevInfo, ref sDevInfoData);

// Get Device: Instance ID
strDevInstanceId = GetDeviceInstanceId(hDevInfo, ref sDevInfoData);

// Increment the device enumeration index.
uMemberIndex++;
}

MessageBox.Show(uMemberIndex.ToString());
}

catch (Exception ex)
{
MessageBox.Show(ex.ToString(), Application.ProductName, MessageBoxButtons.OK);
}

finally
{
}
}

On a different topic, I am still learning taking, mixing, creating, and whatever code to get to the promised land, so to speak, which in this case is the call to SetupDiGetInterfaceDevice().

Starting from the SetupDiEnumDeviceInfo() call, what is the best way to then get to the SetupDiGetInterfaceDevice call.

By the way, thank you both for responding and educating me in this area. Information is not that easy to come by with respect to communicating to devices.

Thanks,

Sarah

xxxxx@jmr.com wrote:

Starting from the SetupDiEnumDeviceInfo() call, what is the best way to then get to the SetupDiGetInterfaceDevice call.

You should only need four calls:
SetupDiGetClassDevs to get the whole matching device set
SetupDiEnumDeviceInterfaces to run through all the devices in that
set, one at a time
SetupDiGetDeviceInterfaceDetail to get information about each device
in that set, including the DevicePath
SetupDiDestroyDeviceInfoList to clean up

OK, so you need 5 calls, because you have to call
SetupDiGetDeviceInterfaceDetail twice: once to get the size, once to get
the data.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Hi Both,

Okay, I feel a bit dumb now. The call to SetupDiCreateDeviceInterface(0 was obviously wrong, so that means two things.

  1. I need to call SetupDiRemoveDeviceInterface() to remove that interface.

  2. I need to another way to get to SetupDiGetDeviceInterfaceDetail().

By the way,

Is there a way on this OSR forum to:
* have a refresh on the page not take me back to the base page
* Get email notification of replies to a particular thread

Thanks,

Sarah

> -----Original Message-----

From: xxxxx@lists.osr.com [mailto:bounce-472095-
xxxxx@lists.osr.com] On Behalf Of xxxxx@jmr.com
Sent: Friday, August 26, 2011 1:25 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Obtaining a CreateFile()device path (MS DOS Path)
using
SetupDi* for a device

Hi,

I have no clue what others do, none. I would be happy if Microsoft had an
SDK
function and/or a .net function called,

OK, here’s where you actually should care how they do it, because your
chosen path *may* not lead you where you want to go, if the “default driver”
doesn’t implement an interface you can use. You might want to use Process
Monitor and/or IRP Tracker to see if you can discern how they are going
about it.

[snip]

On a different topic, I am still learning taking, mixing, creating, and
whatever code to get to the promised land, so to speak, which in this case
is
the call to SetupDiGetInterfaceDevice().

Actually, your “promised land” is SetupDiGetDeviceInterfaceDetail(). That
will return the string you want. *If* the driver in question implements an
interface you can use. You can probably determine which interface that
*might* be by searching out the name of the “default driver”, and searching
the WDK docs for Expander. I don’t know whether either of those approaches
will actually reveal whether there is an interface implemented in the
“default driver”, but you have to find that out, or you are just blindly
shooting in the dark, hoping to hit something.

Starting from the SetupDiEnumDeviceInfo() call, what is the best way to
then
get to the SetupDiGetInterfaceDevice call.

You’ve got it backward. Start with the doc for
SetupDiGetDeviceInterfaceDetail(), and it will tell you where it’s various
parameters come from. Go to the docs for those API calls, and figure out
where the parameters they take come from. Repeat until you get to calls
that only take data provided by you, not by another API call.

By the way, thank you both for responding and educating me in this area.
Information is not that easy to come by with respect to communicating to
devices.

And while I commend you for your persistence in attempting to deal with this
stuff using C#, it’s an order of magnitude easier to use managed C++ to wrap
the SetupDi API than it is to try to get the P/Invoke stuff right in C#.
Just write yourself one C++ Assembly and throw all the calls to unmanaged
APIs in there, and the pain of using the whole Win32 API will drop a bunch.
Nowhere near to zero, but a lot less than trying to figure it out in C#.

Phil

Philip D. Barila

You can subscribe to the forum as a mailinglist, or as a newsgroup. I would personally use one of those options instead of the forum interface for more than occasional usage.

  • S

-----Original Message-----
From: xxxxx@jmr.com
Sent: Friday, August 26, 2011 12:41
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Obtaining a CreateFile()device path (MS DOS Path) using SetupDi* for a device

Hi Both,

Okay, I feel a bit dumb now. The call to SetupDiCreateDeviceInterface(0 was obviously wrong, so that means two things.

  1. I need to call SetupDiRemoveDeviceInterface() to remove that interface.

  2. I need to another way to get to SetupDiGetDeviceInterfaceDetail().

By the way,

Is there a way on this OSR forum to:
* have a refresh on the page not take me back to the base page
* Get email notification of replies to a particular thread

Thanks,

Sarah


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

xxxxx@jmr.com wrote:

  1. I need to call SetupDiRemoveDeviceInterface() to remove that interface.

Rebooting should serve the same purpose. Device interfaces are not
persistent.

Is there a way on this OSR forum to:
* have a refresh on the page not take me back to the base page
* Get email notification of replies to a particular thread

I use the mailing list interface. For a long time, I assumed almost
everyone did, because the other interfaces are so dreadful for
information exchange, but I have since been informed that I am mistaken…


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Hi,

I sort of did that. Anyways,

SetupDiGetInterfaceDetail takes its main argument from SetupDiEnumDeviceInterfaces. That API has the form

BOOL SetupDiEnumDeviceInterfaces(
__in HDEVINFO DeviceInfoSet,
__in_opt PSP_DEVINFO_DATA DeviceInfoData,
__in const GUID *InterfaceClassGuid,
__in DWORD MemberIndex,
__out PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
);

I already have DeviceInfoSet and DeviceInfoData, however the main “gotcha” is InterfaceClassGuid. IF I had the InterfaceClassGuid, I would not need to call any API. With that being said, I kind of did that approach with my test function, code above. I used the main STORAGEPORT interface class for the HBA, not that I care about the card. From the enumeration, I called SetupDiGetInterfaceDetail.

This approach only yielded that eroneous interface class GUID that I created, no others for the ATTO. The Expander did not show.

The expander may or may not expose an interface class GUID, no clue on what Microsoft exposes.

It appears that I may have to write a device driver for the expander, although that would be a definite undertaking. I would have to find out which DDK sample to start. Microsoft does not provide a sample driver. I would prefer to avoid the writing a driver, if I can avoid doing that.

I did a search on MSDN and a grep on the WinDDK folder for ‘Expander’. MSDN turned up 21 hits, none interesting. The grep on WinDDK turned up only one hit:

Searching for: Expander
7600.16385.1\inc\ddk\hbaapi.h(176): #define HBA_PORTTYPE_SASEXPANDER 32 /* SAS Expander */
Found 1 occurrence(s) in 1 file(s)

Obviously, Microsoft does not use that constant anywhere. My expander is a SCSI SAS Expander, but the constant at least for now does ot help.

Comment : The ATTO HBA DOES implement an interface. I know that for a fact. Why? I did a manual search through the Windows Registry (regedit) and turned up the symbolic link along with the real Interface GUID. I tried out the symbolic link and it works nicely with CreateFile. I was able to use the handle that I got back and use DeviceIOControl nicely. That means that some method of using the SetupDi* routines should turn up either that symbolic link or help get the parts, thereby enabling me to create the symbolic link. So far, I have not seen any call to SetupDi* that yields he correct interface GUID or the symbolic link. Because of that, I cannot discern that there is or is not an interface for the expander. If I can get the code to work for the ATTO HBA, then I can determine if I need to write a driver or not.

Another method might be to use a different apporach. Using SCSI Pass-through messages require that I open a channel to the device first. That is my first task.

Process Monitor or IRP tracker might help. I have to find those tools and try it out.

I thought about an assembly, not that I created one yet, but I finally figured out the P/Invoke stuff with C#. There was a huge learning curve, mostly because every web page that Google found that says that it work did not, all for different reasons. Seriosuly, do people not test their code. I created a Win32 class, which currently has many of the SetupDi* routines. Now it is easy with only the occasional 1784/87/crash behavior. My last 1784/crash issues were because I did not follow the acronym, RTFM. RTFM would have shown that I need to set the size of the structure before calling the API. Not doing so or not passing a valid size yields cryptic error behavior. Passing stuff by reference yields other issues, etc.

One more point, I need to learn C# for many reasons, so struggling with SetupDi and the other issues recently help me to learn. For instance, I was dismayed that I cannot create a class based upon multiple inheritance. I would have loved to do something like:

public class EnumerateInstall : Win32SetupDi, Win32General, cTools
{
}

Microsoft does not allow that. The best that Microsoft allows is a class derived with multiple interfaces (multiple pure virtual classes to my speak / terminology, but whatever).


The web interface for OSR has a lot to be desired. Microsoft laid an egg on this site. Mark sold out to Microsoft, so… Going to My Profile and unchecking the two ‘spam me’ options yields on Windows 7 IE an error (blank really) screen. I did not know why I did not think of that for any website that I ever created. Users must chose the annoyance option.

Thanks in advance,

Sarah

Hi tim,

I almost, okay want to say never but someone will always point to a period, so almost, never use mailing lists.

My preference would be a newsgroup reader, such as what comes with the Outlook Express replacement, but most companies do not support the NMTP method anymore, which is sad. That leaves the web interface. Mail lists get to complicated to follow threads, at least for me.

xxxxx@jmr.com wrote:

SetupDiGetInterfaceDetail takes its main argument from SetupDiEnumDeviceInterfaces. That API has the form

BOOL SetupDiEnumDeviceInterfaces(
__in HDEVINFO DeviceInfoSet,
__in_opt PSP_DEVINFO_DATA DeviceInfoData,
__in const GUID *InterfaceClassGuid,
__in DWORD MemberIndex,
__out PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
);

I already have DeviceInfoSet and DeviceInfoData, however the main “gotcha” is InterfaceClassGuid. IF I had the InterfaceClassGuid, I would not need to call any API.

The SetupDi APIs use the word “class” in several confusing ways. You
should put the same device interface in InterfaceClassGuid that you used
in SetupDiGetClassDevs.

One more point, I need to learn C# for many reasons, so struggling with SetupDi and the other issues recently help me to learn. For instance, I was dismayed that I cannot create a class based upon multiple inheritance. I would have loved to do something like:

public class EnumerateInstall : Win32SetupDi, Win32General, cTools
{
}

Microsoft does not allow that. The best that Microsoft allows is a class derived with multiple interfaces (multiple pure virtual classes to my speak / terminology, but whatever).

True. Multiple inheritance is a concept with some controversy. C#
chooses to allow inheritance from multiple interfaces, but not multiple
classes.

The web interface for OSR has a lot to be desired. Microsoft laid an egg on this site. Mark sold out to Microsoft, so… Going to My Profile and unchecking the two ‘spam me’ options yields on Windows 7 IE an error (blank really) screen. I did not know why I did not think of that for any website that I ever created. Users must chose the annoyance option.

Umm, OSR is not related to either Microsoft or SysInternals…


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

I thought that OSR was founded by Mark Rassinovich, who owned / was involved with SysInternals, which Microsoft bought not too long ago. I might have his last name mispelled. I took a class with him a few years back.

Back to topic.

  1. I rebooted my computer and unplugged the expander across the reboot.

  2. I rewrote / restructured the code thanks to the help here.

What I found out is that eliminating the SetupDiCreateInterface() call, rebooting, and reworking the code yields the following facts.

* I am now able to get a single Device Path from the ATTO HBA controller and that path is the right one, which is CreateFile() friendly.

* I found out that there is no interface for my expander, which means that I need to create a driver for it or find some other solution.

Code Changes Made:

// We start at the “root” of the device tree and look for all
// devices that match the interface GUID of a disk
DiGetClassFlags iFlags = DiGetClassFlags.DIGCF_PRESENT | DiGetClassFlags.DIGCF_ALLCLASSES | DiGetClassFlags.DIGCF_PROFILE | DiGetClassFlags.DIGCF_DEVICEINTERFACE;
int iArgFlags = (int)iFlags;
hDevInfo = SetupDiGetClassDevs(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, iArgFlags);
if (INVALID_HANDLE_VALUE == hDevInfo.ToInt32())
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
return false;
}

// Boolean bResult;
bool bSuccess = true;
int iDevice = 0;
while (bSuccess)
{
String strDevDevicePath, strDevClassName, strDevDriver, strDevDescription, strDevName, strDevInstanceId;
Guid guidDevInterfaceLocal;

// Initialize the required variables.
strDevDevicePath = “”;
guidDevInterfaceLocal = new Guid(0x0, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);

// Create a Device Interface Data structure.
SP_DEVINFO_DATA sDevInfoData = new SP_DEVINFO_DATA();
sDevInfoData.cbSize = (uint)Marshal.SizeOf(sDevInfoData);

// Enumerate the next devices or the first device, if the first pass.
bSuccess = SetupDiEnumDeviceInfo(hDevInfo, (UInt32)iDevice, sDevInfoData);
if (false == bSuccess)
{
iErrorCode = Marshal.GetHRForLastWin32Error();
Marshal.ThrowExceptionForHR(iErrorCode);
}

// Get Device: Name
strDevName = GetDeviceName(hDevInfo, ref sDevInfoData);

// See if we have the board in question.
if (strDevName == strDevNameToFind)
{
// Get Device: Interface Class GUID
GetDeviceInterface(hDevInfo, ref sDevInfoData, ref guidDevInterfaceLocal, ref strDevDevicePath);
break;
}

// Increment the device enumeration index.
iDevice++;
}
}

catch (Exception ex)
{
MessageBox.Show(ex.ToString(), Application.ProductName, MessageBoxButtons.OK);
}

finally
{
SetupDiDestroyDeviceInfoList(hDevInfo);
}

return true;

The call to GetDeviceInterface() uses hDevInfo and sDevInfoData in the call to SetupDiEnumDeviceInterfaces(). That yields one entry and returns a SP_DEVICE_INTERFACE_DATA, which I can then pass to SetupDiGetDeviceInterfaceDetail, which gives the right path name.

When calling this new code, I do not even see the expander, even though it shows nicely in the Windows Device Manager. I can plug in and unplug the expander and see the entry in Device Manager disappear and appear depending on if I plug the expander in or not, but no hit on the expander, hence I need another solution or just write a device driver.

QUESTION:
When completes and has no more matches, I get an error code of, -2147024637. The size of the container variable is an ‘int’, which is the size requested. According to

http://msdn.microsoft.com/en-us/library/ms681382(v=VS.85).aspx

there is no code that low / high. Is there a nice C/C++ define for this value? Should I be casting to something?

xxxxx@jmr.com wrote:

QUESTION:
When completes and has no more matches, I get an error code of, -2147024637. The size of the container variable is an ‘int’, which is the size requested. According to

http://msdn.microsoft.com/en-us/library/ms681382(v=VS.85).aspx

there is no code that low / high. Is there a nice C/C++ define for this value? Should I be casting to something?

Codes that are in the -2 billion range should be printed in hex. That’s
80070103, which is an HRESULT-style wrapping of the Windows error code
0x0103, which is ERROR_NO_MORE_ITEMS.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

I probably also need to use the IRP / Process Monitor tool, because two applications that I know of can talk to the expander nicely and not need a driver.

Maybe I can open a handle to the HBA, and use IOCTL_SCSI_PASSTHROUGH with DeviceIOControl or something with it, to pass SCSI commands to the expander. That is worth investigation.