[wdmaudiodev] Re: Help installing custom sAPO for a USB audio device

  • From: Matthew van Eerde <Matthew.van.Eerde@xxxxxxxxxxxxx>
  • To: "wdmaudiodev@xxxxxxxxxxxxx" <wdmaudiodev@xxxxxxxxxxxxx>
  • Date: Fri, 6 Mar 2015 17:43:37 +0000

This is the problem.
AddInterface=%KSCATEGORY_AUDIO%,%KSNAME_TopologyMicIn%, 
TestAudio.SysFx.PrimaryMicTopo

Compare:
; wdma_usb.inf
AddInterface=%KSCATEGORY_AUDIO%,"GLOBAL",USBAudio.Interface.Curve,

The second argument to AddInterface needs to match the name of the Kernel 
Streaming filter factory where the hardware-connected pin factory is.

For a portcls.sys miniport, the filter factories come in pairs – a “wave” 
filter factory, and a “topology” filter factory.

On the other hand, direct AVStream drivers like usbaudio.sys, which don’t go 
through portcls, have the freedom to put as many filter factories in the signal 
path as it likes – one, or two, or more.

usbaudio.sys happens to choose to expose a single filter factory that contains 
both the hardware-connected pin factory and the streaming pin factory; it calls 
this “GLOBAL”. So usbaudio.sys doesn’t have a notion of “wave” or “topology” 
filter factories.

You can use KS Studio to see the name of the filter factory directly and 
confirm that is it “GLOBAL”.

The EP\ and FX\ properties are not being picked up because the filter factory 
name doesn’t match.

The MSEP\ and MSFX\ properties are being looked at, but ignored, because they 
all have specific PKEY_FX_ASSOCIATION values which don’t match 
KSNODETYPE_MICROPHONE – usbaudio.sys’s inbox effects only apply to playback 
devices like speakers and headphones.

From: wdmaudiodev-bounce@xxxxxxxxxxxxx 
[mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx] On Behalf Of Justin Davis
Sent: Friday, March 6, 2015 9:27 AM
To: wdmaudiodev@xxxxxxxxxxxxx
Subject: [wdmaudiodev] Re: Help installing custom sAPO for a USB audio device

Hi Matthew,

Thanks for your help so far.  I've implemented the changes you suggested (not 
using RegisterDlls and removing the MFX key).

I believe the correct type is KSNODETYPE_MICROPHONE.  This is a really simple 
USB microphone.  When I look at the USB descriptors, under the audio control 
input terminal descriptor, it lists:

wTerminalType            : 0x0201 (Microphone)

According to the page below, it sounds like this corresponds to 
KSNODETYPE_MICROPHONE.
https://msdn.microsoft.com/en-us/library/windows/hardware/ff537742%28v=vs.85%29.aspx

My registry keys are making it to HKLM/SYSTEM/ccs/Control/DeviceClasses/{Audio 
Class GUID}/{device entry}/#TopologyMicIn/Device Parameters/FX/0.

But there's also the "#GLOBAL" one in there as well.  Maybe the #GLOBAL thing 
is messing this up?  However, the effects listed under #GLOBAL don't seem to be 
running either.

Here's what the HKLM/SYSTEM/ccs/Control/DeviceClasses/{Audio Class 
GUID}/{device entry}/ key looks like:
[Inline image 1]

And here's the device entry under 
HKLM/SOFTWARE/Microsoft/Windows/CurrentVersion/MMDevices/Audio/Capture:
[Inline image 2]
(Notice that there's no "FxProperties" key)

Here's my slightly updated INF file.

[Version]
Signature="$Windows NT$"
Class=MEDIA
ClassGUID={4d36e96c-e325-11ce-bfc1-08002be10318}
provider=%ProviderName%
DriverVer=06/22/2005,6.00.2246.1

[SourceDisksNames]
222=%DiskDescription%,,,

[SourceDisksFiles]
SwapAPO.dll=222

[DestinationDirs]
SfxFx.CopyList=11     ; %windir%\system32

[Manufacturer]
%MfgName%=MfgModelSection, NT$ARCH$

[MfgModelSection.NT$ARCH$]
%TestMic.DeviceDesc%=TestAudio.SysFx,USB\VID_0C76&PID_1529&MI_00

[TestAudio.SysFx]
Include = ks.inf, wdmaudio.inf, wdma_usb.inf
Needs = KS.Registration, WDMAUDIO.Registration, USBAudioOEM.CopyFiles, 
USBAudioOEM.AddReg
CopyFiles=SfxFx.CopyList
AddReg = SfxFx.COM.AddReg

[TestAudio.SysFx.HW]
; Do I need anything here?  wdma_usb.inf doesn't seem to have a .HW section

[TestAudio.SysFx.Interfaces]
AddInterface=%KSCATEGORY_AUDIO%,%KSNAME_WaveMicIn%, 
TestAudio.SysFx.PrimaryMicWave
AddInterface=%KSCATEGORY_REALTIME%,%KSNAME_WaveMicIn%, 
TestAudio.SysFx.PrimaryMicWave
AddInterface=%KSCATEGORY_CAPTURE%,%KSNAME_WaveMicIn%, 
TestAudio.SysFx.PrimaryMicWave
AddInterface=%KSCATEGORY_AUDIO%,%KSNAME_TopologyMicIn%, 
TestAudio.SysFx.PrimaryMicTopo
AddInterface=%KSCATEGORY_TOPOLOGY%,%KSNAME_TopologyMicIn%, 
TestAudio.SysFx.PrimaryMicTopo

[TestAudio.SysFx.PrimaryMicWave]
AddReg = TestAudio.SysFx.PrimaryMicWave.AddReg

[TestAudio.SysFx.PrimaryMicWave.AddReg]
HKR,,FriendlyName,,%PrimaryMicWaveDeviceName%
HKR,,CLSID,,%Proxy.CLSID%

[TestAudio.SysFx.PrimaryMicTopo]
AddReg = TestAudio.PrimaryMicTopo.AddReg, SysFx.AddReg

[TestAudio.PrimaryMicTopo.AddReg]
HKR,,FriendlyName,,%PrimaryMicTopoDeviceName%
HKR,,CLSID,,%Proxy.CLSID%

[TestAudio.SysFx.Services]
Include = wdma_usb.inf
Needs = USBAudio.NT.Services

[SfxFx.COM.AddReg]
;; sAPO COM registration
HKCR,CLSID\%SYSFX_PREMIX_CLSID%,,,%SYSFX_FriendlyName%
HKCR,CLSID\%SYSFX_PREMIX_CLSID%\InProcServer32,,,%11%\swapapo.dll
HKCR,CLSID\%SYSFX_PREMIX_CLSID%\InProcServer32,ThreadingModel,,"Both"

[SysFx.AddReg]
;; Not exactly sure what this does, but wdma_usb.inf does it...
HKR,"EP\\0",%PKEY_AudioEndpoint_Association%,,%KSNODETYPE_ANY%

HKR,"FX\\0",%PKEY_ItemNameDisplay%,,%SYSFX_FriendlyName%
HKR,"FX\\0",%PKEY_SYSFX_PreMixClsid%,,%SYSFX_PREMIX_CLSID%

;; Note: We have to associate with a specific node type here (ie. not
;; use KSNODETYPE_ANY) or else the MSFX entries in wdma_usb.inf will
;; override our entries here.
HKR,"FX\\0",%PKEY_SYSFX_Association%,,%KSNODETYPE_MICROPHONE%

;; I think Win 8.l uses StreamEffectsClsid instead of PreMixClsid
HKR,"FX\\0",%PKEY_SYSFX_StreamEffectClsid%,,%SYSFX_PREMIX_CLSID%

HKR,"FX\\0",%PKEY_SFX_ProcessingModes_Supported_For_Streaming%,0x00010000,%AUDIO_SIGNALPROCESSINGMODE_DEFAULT%

[SfxFx.CopyList]
SwapAPO.dll,,,0x0100

[Strings]
ProviderName="Test Provider Name"
MfgName="Test MFG Name"
DiskDescription="Installation Media"
TestMic.DeviceDesc = "Test Microphone with Basic Filtering"

KSNAME_WaveMicIn = "WaveMicIn"
KSNAME_TopologyMicIn = "TopologyMicIn"

PrimaryMicWaveDeviceName  = "Test Microphone"
PrimaryMicTopoDeviceName  = "Test Microphone Topo"

PKEY_SYSFX_Association          = "{D04E05A6-594B-4FB6-A80D-01AF5EED7D1D},0"
PKEY_SYSFX_PreMixClsid          = "{D04E05A6-594B-4FB6-A80D-01AF5EED7D1D},1"
PKEY_ItemNameDisplay            = "{B725F130-47EF-101A-A5F1-02608C9EEBAC},10"

PKEY_SYSFX_StreamEffectClsid   = "{D04E05A6-594B-4fb6-A80D-01AF5EED7D1D},5"

SYSFX_PREMIX_CLSID  = "{B48DEA3F-D962-425a-8D9A-9A5BB37A9904}"
SYSFX_FriendlyName  = "Test Microphone Filter Effect"

KSCATEGORY_AUDIO    = "{6994AD04-93EF-11D0-A3CC-00A0C9223196}"
KSCATEGORY_CAPTURE  = "{65E8773D-8F56-11D0-A3B9-00A0C9223196}"
KSCATEGORY_REALTIME = "{EB115FFC-10C8-4964-831D-6DCB02E6F23F}"
KSCATEGORY_TOPOLOGY = "{DDA54A40-1E4C-11D1-A050-405705C10000}"

KSNODETYPE_ANY   = "{00000000-0000-0000-0000-000000000000}"
KSNODETYPE_MICROPHONE = "{DFF21BE1-F70F-11D0-B917-00A0C9223196}"

Proxy.CLSID         = "{17CCA71B-ECD7-11D0-B908-00A0C9223196}"

PKEY_SFX_ProcessingModes_Supported_For_Streaming = 
"{D3993A3F-99C2-4402-B5EC-A92A0367664B},5"
AUDIO_SIGNALPROCESSINGMODE_DEFAULT = "{C18E2F7E-933D-4965-B7D1-1EEF228D2AF3}"

PKEY_AudioEndpoint_Association = "{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},2"



Thanks,
Justin

On Thu, Mar 5, 2015 at 4:38 PM, Matthew van Eerde 
<Matthew.van.Eerde@xxxxxxxxxxxxx<mailto:Matthew.van.Eerde@xxxxxxxxxxxxx>> wrote:
> RegisterDlls

Don’t use RegisterDlls; instead, declare all your COM registrations statically 
via AddReg.

> My understanding is that these are migrated to the 
> MMDevices/Audio/Capture/{EP Guid} key...but they don't seem to be

Did you confirm that the jack sub type of this particular device is 
KSNODETYPE_MICROPHONE?

> HKR,"FX\\0",%PKEY_MFX_ProcessingModes_Supported_For_Streaming%,0x00010000,%AUDIO_SIGNALPROCESSINGMODE_DEFAULT%

Since you only have a stream effect, you should only set the _SFX_ streaming 
property. Remove the _MFX_ one.

HKLM/SYSTEM/ccs/Control/DeviceClasses/{Audio Class GUID}/{device 
entry}/#GLOBAL/Device Parameters/MSFX

What about Device Parameters/FX/0? Are your properties getting this far?

From: wdmaudiodev-bounce@xxxxxxxxxxxxx<mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx> 
[mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx<mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx>]
 On Behalf Of Justin Davis
Sent: Thursday, March 5, 2015 4:26 PM
To: wdmaudiodev@xxxxxxxxxxxxx<mailto:wdmaudiodev@xxxxxxxxxxxxx>
Subject: [wdmaudiodev] Re: Help installing custom sAPO for a USB audio device

Ok, So I've had a chance to try this, and so far I'm still not having luck 
getting my sAPO loaded.  Here are a few observations I've made.

- When using the inbox usbaudio driver for my microphone (just trying to learn 
more about how this works), I see sAPO effects registered under 
HKLM/SYSTEM/ccs/Control/DeviceClasses/{Audio Class GUID}/{device 
entry}/#GLOBAL/Device Parameters/MSFX.  My understanding is that these are 
migrated to the MMDevices/Audio/Capture/{EP Guid} key...but they don't seem to 
be.  Consequently the effects registered for the microphone by default (looks 
like they're in wmalfxgfxdsp.dll) are not loaded.  Are these disabled by 
default maybe?

- Regardless, I tried changing my .inf file to use KSNODETYPE_MICROPHONE 
instead of KSNODETYPE_ANY, and that didn't seem to have an effect (keys not 
migrated to MMDevices, sAPO dll not loaded by audiodg process).

Here's the .inf file I'm using.  I have a feeling there's probably something 
else basic that I'm missing.

Thank you so much for your help!
-Justin

========

[Version]
Signature="$Windows NT$"
Class=MEDIA
ClassGUID={4d36e96c-e325-11ce-bfc1-08002be10318}
provider=%ProviderName%
DriverVer=06/22/2005,6.00.2246.1

[SourceDisksNames]
222=%DiskDescription%,,,

[SourceDisksFiles]
SwapAPO.dll=222

[DestinationDirs]
SfxFx.CopyList=11     ; %windir%\system32

[Manufacturer]
%MfgName%=MfgModelSection, NT$ARCH$

[MfgModelSection.NT$ARCH$]
%TestMic.DeviceDesc%=TestAudio.SysFx,USB\VID_0C76&PID_1529&MI_00

[TestAudio.SysFx]
Include = ks.inf, wdmaudio.inf, wdma_usb.inf
Needs = KS.Registration, WDMAUDIO.Registration, USBAudioOEM.CopyFiles, 
USBAudioOEM.AddReg
CopyFiles=SfxFx.CopyList
RegisterDlls=SfxFx.RegisterDlls

[TestAudio.SysFx.HW]
; Do I need anything here?  wdma_usb.inf doesn't seem to have a .HW section

[TestAudio.SysFx.Interfaces]
AddInterface=%KSCATEGORY_AUDIO%,%KSNAME_WaveMicIn%, 
TestAudio.SysFx.PrimaryMicWave
AddInterface=%KSCATEGORY_REALTIME%,%KSNAME_WaveMicIn%, 
TestAudio.SysFx.PrimaryMicWave
AddInterface=%KSCATEGORY_CAPTURE%,%KSNAME_WaveMicIn%, 
TestAudio.SysFx.PrimaryMicWave
AddInterface=%KSCATEGORY_AUDIO%,%KSNAME_TopologyMicIn%, 
TestAudio.SysFx.PrimaryMicTopo
AddInterface=%KSCATEGORY_TOPOLOGY%,%KSNAME_TopologyMicIn%, 
TestAudio.SysFx.PrimaryMicTopo

[TestAudio.SysFx.PrimaryMicWave]
AddReg = TestAudio.SysFx.PrimaryMicWave.AddReg

[TestAudio.SysFx.PrimaryMicWave.AddReg]
HKR,,FriendlyName,,%PrimaryMicWaveDeviceName%
HKR,,CLSID,,%Proxy.CLSID%

[TestAudio.SysFx.PrimaryMicTopo]
AddReg = TestAudio.PrimaryMicTopo.AddReg, SysFx.AddReg

[TestAudio.PrimaryMicTopo.AddReg]
HKR,,FriendlyName,,%PrimaryMicTopoDeviceName%
HKR,,CLSID,,%Proxy.CLSID%

[TestAudio.SysFx.Services]
Include = wdma_usb.inf
Needs = USBAudio.NT.Services

[SysFx.AddReg]
;; Not exactly sure what this does, but wdma_usb.inf does it...
HKR,"EP\\0",%PKEY_AudioEndpoint_Association%,,%KSNODETYPE_ANY%

HKR,"FX\\0",%PKEY_ItemNameDisplay%,,%SYSFX_FriendlyName%
HKR,"FX\\0",%PKEY_SYSFX_PreMixClsid%,,%SYSFX_PREMIX_CLSID%

;; Note: We have to associate with a specific node type here (ie. not
;; use KSNODETYPE_ANY) or else the MSFX entries in wdma_usb.inf will
;; override our entries here.
HKR,"FX\\0",%PKEY_SYSFX_Association%,,%KSNODETYPE_MICROPHONE%

;; I think Win 8.l uses StreamEffectsClsid instead of PreMixClsid
HKR,"FX\\0",%PKEY_SYSFX_StreamEffectClsid%,,%SYSFX_PREMIX_CLSID%

HKR,"FX\\0",%PKEY_SFX_ProcessingModes_Supported_For_Streaming%,0x00010000,%AUDIO_SIGNALPROCESSINGMODE_DEFAULT%
HKR,"FX\\0",%PKEY_MFX_ProcessingModes_Supported_For_Streaming%,0x00010000,%AUDIO_SIGNALPROCESSINGMODE_DEFAULT%

[SfxFx.CopyList]
SwapAPO.dll,,,0x0100

[SfxFx.RegisterDlls]
11,,SwapAPO.dll,1

[Strings]
ProviderName="Test Provider Name"
MfgName="Test MFG Name"
DiskDescription="Installation Media"
TestMic.DeviceDesc = "Test Microphone with Basic Filtering"

KSNAME_WaveMicIn = "WaveMicIn"
KSNAME_TopologyMicIn = "TopologyMicIn"

PrimaryMicWaveDeviceName  = "Test Microphone"
PrimaryMicTopoDeviceName  = "Test Microphone Topo"

PKEY_SYSFX_Association          = "{D04E05A6-594B-4FB6-A80D-01AF5EED7D1D},0"
PKEY_SYSFX_PreMixClsid          = "{D04E05A6-594B-4FB6-A80D-01AF5EED7D1D},1"
PKEY_ItemNameDisplay            = "{B725F130-47EF-101A-A5F1-02608C9EEBAC},10"

PKEY_SYSFX_StreamEffectClsid   = "{D04E05A6-594B-4fb6-A80D-01AF5EED7D1D},5"

SYSFX_PREMIX_CLSID  = "{B48DEA3F-D962-425a-8D9A-9A5BB37A9904}"
SYSFX_FriendlyName  = "Test Microphone Filter Effect"

KSCATEGORY_AUDIO    = "{6994AD04-93EF-11D0-A3CC-00A0C9223196}"
KSCATEGORY_CAPTURE  = "{65E8773D-8F56-11D0-A3B9-00A0C9223196}"
KSCATEGORY_REALTIME = "{EB115FFC-10C8-4964-831D-6DCB02E6F23F}"
KSCATEGORY_TOPOLOGY = "{DDA54A40-1E4C-11D1-A050-405705C10000}"

KSNODETYPE_ANY   = "{00000000-0000-0000-0000-000000000000}"
KSNODETYPE_MICROPHONE = "{DFF21BE1-F70F-11D0-B917-00A0C9223196}"

Proxy.CLSID         = "{17CCA71B-ECD7-11D0-B908-00A0C9223196}"

PKEY_SFX_ProcessingModes_Supported_For_Streaming = 
"{D3993A3F-99C2-4402-B5EC-A92A0367664B},5"
PKEY_MFX_ProcessingModes_Supported_For_Streaming = 
"{D3993A3F-99C2-4402-B5EC-A92A0367664B},6"
AUDIO_SIGNALPROCESSINGMODE_DEFAULT = "{C18E2F7E-933D-4965-B7D1-1EEF228D2AF3}"

PKEY_AudioEndpoint_Association = "{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},2"


On Mon, Mar 2, 2015 at 9:45 AM, Justin Davis 
<justindvs@xxxxxxxxx<mailto:justindvs@xxxxxxxxx>> wrote:
Thank you so much for the explanation Matthew!  That makes sense (I was using 
KSNODETYPE_ANY).

I'll give the specific category a try and report back.

Thanks,
Justin

On Mon, Mar 2, 2015 at 9:36 AM, Matthew van Eerde 
<Matthew.van.Eerde@xxxxxxxxxxxxx<mailto:Matthew.van.Eerde@xxxxxxxxxxxxx>> wrote:
Here’s how effects property registration works, in general.

Before registering the KSCATEGORY_AUDIO device interface, set your desired 
properties with any of the following prefixes:

1.       MSFX\n with PKEY_FX_Assocation = KSNODETYPE_ANY, or

2.       FX\n with PKEY_FX_Assocation = KSNODETYPE_ANY, or

3.       MSFX\n with PKEY_FX_Association = KSPIN_DESCRIPTOR.Category where the 
pin descriptor is for the pin factory on the hardware end of the signal path, or

4.       FX\n with PKEY_FX_Association = KSPIN_DESCRIPTOR.Category where the 
pin descriptor is for the pin factory on the hardware end of the signal path

Stores higher up in the list are overridden by stores lower down in the list.

Only Microsoft inbox drivers should use MSFX\n. This allows third-party drivers 
to use FX\n and override them. You should use FX\n.

wdma_usb.inf uses PKEY_FX_Association = KSPIN_DESCRIPTOR.Category:
; wdma_usb.inf
HKR,"MSFX\\0",%PKEY_FX_Association%,,%KSNODETYPE_SPEAKER%
HKR,"MSFX\\1",%PKEY_FX_Association%,,%KSNODETYPE_HEADPHONES%
HKR,"MSFX\\2",%PKEY_FX_Association%,,%KSNODETYPE_DESKTOP_SPEAKER%
HKR,"MSFX\\3",%PKEY_FX_Association%,,%KSNODETYPE_HEADSET_SPEAKERS%

So you will need to use FX\0 with a PKEY_FX_Association = 
KSPIN_DESCRIPTOR.Category yourself, or be overridden by the more specific 
MSFX\n match.

> I'm pretty sure those keys need to be installed as part of an "AddInterface" 
> statement

That’s correct; they should be under the “.AddReg” section from an 
AddInterface=… KSCATEGORY_AUDIO … interface.

From: wdmaudiodev-bounce@xxxxxxxxxxxxx<mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx> 
[mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx<mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx>]
 On Behalf Of Justin Davis
Sent: Saturday, February 28, 2015 3:44 PM
To: wdmaudiodev@xxxxxxxxxxxxx<mailto:wdmaudiodev@xxxxxxxxxxxxx>
Subject: [wdmaudiodev] Help installing custom sAPO for a USB audio device

Hi all,

I've developed drivers for a number of years, but I'm a newbie to audio drivers.

I'm dipping my toes in the water by trying to install a custom sAPO (the swap 
example) for a standard USB microphone (and USB headphones at some point too).

I've read up on as much info as I can find and I've tried several examples.  
I've had success building and installing the "slate audio" example from the WDK 
8.1 samples.

However, I'm having problems adapting this example to my simple USB audio 
device.  I really just want to use the standard USBAudio.sys driver but 
supplement it with my sAPO.  I think I have the proper "Include" and "Needs" 
section to pull that stuff in from the wdma_usb.inf file.

I think the root of my problem is with putting the right sAPO registration data 
in the registry.  I've generally tried to follow the guidance from this page 
(and the Vista Audio Effects white paper):
https://msdn.microsoft.com/en-us/library/windows/hardware/ff536810(v=vs.85).aspx

However, it uses lots of INF fragments with HKR, which is relative to how you 
get to that INF section.  So it's not 100% clear where those are supposed to 
go.  Through looking at the example, I'm pretty sure those keys need to be 
installed as part of an "AddInterface" statement...but again it's not 100% 
clear.

My problem is that I install via my INF, and I can see the registry keys get 
installed to what looks like the proper locations.  But...it sounds like the 
installation process is supposed to migrate the sAPO registration data to 
another section (below), and this step doesn't appear to be happening.

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Capture\{EP 
GUID}\FxProperties

I've found that if I manually hack the above keys to point to my sAPO, it seems 
to work (I see my sAPO getting loaded in the audiodg process and my 
APOProcess() function is being called).  However...I know I'm not supposed to 
hack those keys manually, the INF file is supposed to do something that causes 
the keys to be migrated there.  It's just not clear on what's going wrong in 
that process.

I'm not at my work machine right now so I can't copy the INF file I've been 
working with (I'll add that on Monday).

But any advice on how to troubleshoot this would be greatly appreciated!  I 
feel like what I'm trying to do should be simple, I'm just not familiar enough 
with the audio subsystem to know what to look for.

BTW, I've seen the "Troubleshooting sAPO Load Failures" page, and I do have 
DisableProtectedAudioDG set.  My problem isn't that my sAPO fails to load, it's 
that the system doesn't even try to load it (verified this in the debugger).

Thanks for your help!
Justin



JPEG image

PNG image

Other related posts: