Forum Discussion

oconobee's avatar
oconobee
Tin Contributor
Dec 09, 2025

Trying to MSIX package LOB application that needs TWAIN access

So I think this isn't possible but I just wanted to ask.

 

I've a LOB application that needs to access TWAIN scanning devices.

How this works in a normal environment is that the TWAINDSM.dll (TWAIN Data source manager) is loaded from c:\windows\system32

This then scans the C:\Windows\twain_32 or C:\Windows\twain_64 depending on if the app is x86 or x64

The scanning manufacturers will have their TWAIN drivers installed to c:\windows\twain_32 (or 64) as subfolders and the TWAIN Data source manager scans these subfolders for available TWAIN devices that can be connected to.

 

All this breaks down in an MSIX environment because the container doesn't have access to C:\windows\twain_32\ManfactureDriverN folder

It can see the folders and when the TWAINDSM tries to initialize the driver it fails, most likely due to sandbox limitations.

 

Does anyone know of a solution to this, or apps like this will always need to stick to MSI packaging because of this limiation.

 

Thanks.

15 Replies

  • oconobee's avatar
    oconobee
    Tin Contributor

    Thank Tim,

    I had looked at the LoaderSearchOrder extension but it's limited to relative path and the 5 directory limit.

    In my case it would be impossible to include the scan drivers with my package. They are like printer drivers, there would be thousands of them and I wouldn't know in advance what piece of scanner hardware a client has or the version of that driver.

  • oconobee's avatar
    oconobee
    Tin Contributor

    Just wanted to post an update.

    I tried Peer's proposed solution but I didn't find any success with it. I still couldn't access the TWAIN data sources through the Datasource manager.

    What did work for me after a lot of trial and error was to perform the following two actions when the application starts (or at least before you open the TWAIN Data source manager). The MSIX package is also built with runFullTrust.

    1. A PInvoke call to SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
    2. Enumerate every subdirectory of C:\Windows\twain_32\ (if app is x86, twain_64 if app is x64) and AddDllDirectory() each one.

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern int SetDefaultDllDirectories(uint flags);
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern IntPtr AddDllDirectory(string dir);
    
    const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;
    
    public static void Register(string twainRoot = @"C:\WINDOWS\twain_32")
    {
    	SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
    
    	if (!Directory.Exists(twainRoot)) return;
    
    	AddDllDirectory(twainRoot);
    	foreach (var d in Directory.EnumerateDirectories(twainRoot, "*", SearchOption.AllDirectories))
    	{
    		AddDllDirectory(d);   // every DS subfolder, so each DS's siblings resolve
    	}
    }

     

    I believe the issue is down to how the TWAIN Data source manager loads each Vendor driver. It uses the LoadLibrary winAPI call instead of LoadLibraryEx and as a result it manages to load the DS driver file ok, but then when the dependencies for each DS driver attempt to load it cannot find their location and its search path is restricted because of the MSIX container and how it restricts the DLL search path of LoadLibrary.

    • FYI for someone reading this. Some informational MSIX implementation details that might help others:

      1. If programmatically adding a directory to the default directories solves an issue, then that issue should also be solvable by adding LoaderSearchOrder extension in the AppXManifest file (there is an undocumented limit of 5 directories added this way). 
      2. LoadLibrary API is not really different than LoadLibraryEx, in fact LoadLibrary calls LoadLibraryEx.
      3. Rather than say MSIX restricts the paths checked, I would suggest that it can be that when an app is placed in a container it may have a different search list. 
        • First, the current working directory of the application may be different than before (fixable using a new extension on the application entry of the AppXManifest). 
        • Second, the root directory of the package is added to the list. 
        • Third, any "App Paths" registration of the traditional application installer that adds to the search path list will be ignored (normally fixed by LoaderSearchOrder).
        • Fourth, the traditional installer may have used a change to the Path variable and since that installer is not used, additional paths may be missing (any native installer that has updated the path variable on the system is respected by MSIX container; this only is about when this application installer used to update the path.  Again, LoaderSearchOrder can help).
        • Fifth, there is that LoaderSearchOrder which adds to the list.
  • OwenLanty's avatar
    OwenLanty
    Copper Contributor

    Run the unpackaged application on the same device first and record the TWAIN data source bitness. Check the MSIX manifest, declared capabilities, runtime dependencies, and AppXDeployment-Server events because the package boundary can change device access. Use Microsoft MSIX Packaging Tool diagnostics to determine whether the scanner integration is unsupported rather than granting broad permissions.

  • oconobee I would guess from this that the driver loads in the system process (procId 4) and outside of the container. It is strange for it to get that path, and outside of the container there is nothing to reverse VFS it. Thus the question is how your app passed the path over. I am unfamiliar with the API the app would use to load the driver. If you could provide the procmon trace, at least the part where the driver is getting instantiated, I might be able to craft a PSF intercept to fix up the path when that API is called. You can email to me at tmangan at tmurgent.com.

  • Peer-Atle's avatar
    Peer-Atle
    Brass Contributor

    We have some applications packaged with App-V where scanning stopped working after September 2025. Win11 24H2. It turned out that the September Win11 update was the culprit. Newer updates have the same error. Further troubleshooting with ProcessMonitor showed that if we added twain files from Win10 to the App-V packages, scanning worked again with our App-V packages.


    I have previously not gotten scanning to work when I have packaged these applications as MSIX packages.
    After I got scanning to work with the App-V package, I tried sequencing these again with the MSIX Packaging Tool. In the same way as with the App-V packages, I added the twain files from a Win10 computer and changed the AppXManifest.xml file.

    Added section to AppXMainifest.xml

    Success!
    Now scanning works in our applications packaged as MSIX on Win10 and Win11 24H2 with the latest Microsoft update.

    • Peer-Atle's avatar
      Peer-Atle
      Brass Contributor

      Let me clarify a bit. The only thing that is sequenced and that can be found in the MSIX package is the application itself and the 3 twain files from a Win10 machine. The scanner drivers are installed thick on the machines that use this MSIX package. The problem occurred on Win11 machines now that these received the September -2025 update and then scanning no longer worked. Solutions were to include the 3 twain files from a Win 10 machine in the MSIX package and change the appxmanifest file so that the application used these 3 files instead of the ones that are now found on the Win11 machines. Hope this can help you.

      • oconobee's avatar
        oconobee
        Tin Contributor

        Thank Peer,

         

        I will give that another go and see. I have a Win10 machine here that I can pull the files from. It would be a much more elegant solution.

         

    • oconobee's avatar
      oconobee
      Tin Contributor

      Thank you for posting this information.

      I will give it a go and see how it works out.

      From memory, I was able to load the twain_32.dll library OK from an MSIX container, and it was able to scan the C:\windows\twain_32\ folder for scanner drivers, but when it loaded the scanner *.ds drivers from their subfolders, they were not able to initialize because when it tried to load their own dll dependencies, it could not locate them because the C:\windows\twain_32\MyScannerDriver\ folder wasn't part of the search path.

      So ProcMon would show it looking for its dependencies in the VFS\windows\twain_32\MyScannerDriver\ location instead of C:\Windows\twain_32\MyScannerDriver\

  • oconobee's avatar
    oconobee
    Tin Contributor

    I had written a comprehensive reply to this but it looks like the system deleted it after I posted it because I mentioned the name of a very popular imaging tool that has the same issue? 😡

     

    To Summarise, my MSIX package only contains my application, a simple .NET application that interacts with the TWAIN Data Source manager found in c:\windows\system32\twaindsm.dll

    It doesn't install any drivers or folders to system32, or install any scanning device drivers. All these are installed natively on the Windows OS outside of my MSIX Package

    When the 64-bit version of TWAIN data source manager is loaded, it scans the C:\Windows\twain_64\ folder for scanner drivers installed.

    Each sub folder generally has its own scanner driver within it. There can be any number of sub-folders here depending on how many different scanning devices you have installed.

     

    The TWAINDSM loads the *.ds file contained in this sub-folder that is the scanner driver, with a LoadLibrary call

    This call works fine outside of an MSIX container, but if it tries to do the same thing within an MSIX container, it fails to load the DS file

    I'm not sure what other debugging steps I can take to diagnose why it cannot load the files.

    The Source code is available on github for the TWAINDSM dll and the logs above point to where it fails @ apps.cpp 1557

     

    Hopefully this post is not deleted like the last.

    • oconobee's avatar
      oconobee
      Tin Contributor

      The named program that it's blocking me from posting that has the same issue is a 

      "popular image viewer that starts with an i and ends in view" that has a red icon. It's in the Windows AppStore as an MSIX install

      When you do a File -> Select Scan/TWAIN Source... it has the exact same problem as my application. and the TWAINDSM logs show it cannot load the datasource library either.

      If you run the same application as a standalone application outside of the AppStore distribution, it works fine.

      So it seems there's some quirk here with trying to load Datasource drivers via the TWAIN Data Source Manager in an MSIX container.

  • oconobee's avatar
    oconobee
    Tin Contributor

    Hi Timothy,

     

    Thanks for responding. Yes I believe you are right, the container does have access to the folder. I probably phrased it wrong or I misunderstand some of the limitations when running in an MSIX container.

    I'm not installing any TWAIN drivers here as part of the package. They are already installed on the machine outside of the package by the driver install tools provided by the associated manufacturer for a scanner device, Fujitsu, Canon, Brother, etc.

     

    It is the TWAINDSM.dll loaded from C:\windows\system32 that accesses this system folder in C:\windows\twain_64\ but it attempts to perform LoadLibrary calls for the installed TWAIN drivers. Does an MSIX container allow LoadLibrary calls from this folder and its subfolders or are they restricted to C:\windows\system32\ only and some other limited paths?

    There can be any number of sub folders in this c:\windows\twain_64\ location, each one provided by a different scanning device.

     

    The TWAINDSM.dll allows for debug logging to be enabled.

    When I run the application outside of an MSIX container it is able to scan the folder fine and load the drivers

    [091023184 dsm.cpp   410     0 0000000000000A04] TWAIN Working Group
    [091023184 dsm.cpp   411     0 0000000000000A04] TWAIN 64 Source Manager (Image Acquisition Interface)
    [091023184 dsm.cpp   412     0 0000000000000A04] version: 2, 5, 0, 0
    [091023185 dsm.cpp  2382     0 0000000000000A04] Acme Twain -> DSM
    [091023185 dsm.cpp  2391     0 0000000000000A04] DG_CONTROL/DAT_PARENT/MSG_OPENDSM
    [091023185 apps.cpp  383     0 0000000000000A04]   Application: "Acme Ltd."
    [091023185 apps.cpp  384     0 0000000000000A04]                "Acme Twain"
    [091023185 apps.cpp  385     0 0000000000000A04]                "Acme Twain" version: 1.0
    [091023185 apps.cpp  386     0 0000000000000A04]                TWAIN 2.3
    [091023240 apps.cpp 1648   203 0000000000000A04]   Loaded library: C:\WINDOWS\twain_64\sample2\TWAINDS_Sample64.ds (TWAINDSM_USEAPPID:1)
    [091023244 dsm.cpp  2446   123 0000000000000A04] TWRC_SUCCESS

    When running the application inside an MSIX container, it will fail to load the driver for the scanner

    [171018814 dsm.cpp   410     0 000000000000969C] TWAIN Working Group
    [171018814 dsm.cpp   411     0 000000000000969C] TWAIN 64 Source Manager (Image Acquisition Interface)
    [171018814 dsm.cpp   412     0 000000000000969C] version: 2, 5, 0, 0
    [171018814 dsm.cpp  2382     0 000000000000969C] Acme Twain -> DSM
    [171018814 dsm.cpp  2391     0 000000000000969C] DG_CONTROL/DAT_PARENT/MSG_OPENDSM
    [171018814 apps.cpp  383     0 000000000000969C]   Application: "Acme Ltd."
    [171018814 apps.cpp  384     0 000000000000969C]                "Acme Twain"
    [171018814 apps.cpp  385     0 000000000000969C]                "Acme Twain" version: 1.0
    [171018814 apps.cpp  386     0 000000000000969C]                TWAIN 2.3
    [171018818 apps.cpp 1557   126 000000000000969C]   Could not load library: C:\WINDOWS\twain_64\sample2\TWAINDS_Sample64.ds
    [171018818 apps.cpp  680   126 000000000000969C]   Condition Code: DS or DSM reported error, application shouldn't display an error
    [171018819 dsm.cpp  2446   123 000000000000969C] TWRC_SUCCESS

     

    • Yes, LoadLibrary works just fine in the package, and usually as expected.

      Starting with the simple case of dlls directly in the native System32 folder, and/or the virtual VFS\SystemX64 folder.  The LoadLibrary call will check the folders using the standard search sequence you normally would get, except that the package VFS will be a layer over the native.  This means that it sees both sets of files that have different names, but if the same filename is in both, then it sees the package one.

      There are situations where the list of where to look might be different in an MSIX package.  And that occurs if the installer either alters the PATH variable or adds an "App Paths" registration into the registry.  In either case, we can modify the AppXManifest file to add an extension called "LoaderSearchOrder" to add additional folders.  A maximum of 5 can be added. 

      So if your installer created a "C:\Windows\System32\foobar" folder and dropped dlls into it, and then either changed the PATH variable or added an App Paths registration, you would add a LoaderSearchOrder for "VFS\SystemX64\foobar".  (Assuming an x64 OS in all of this).

  • All this breaks down in an MSIX environment because the container doesn't have access to C:\windows\twain_32\ManfactureDriverN folder

    Well, as written that isn't true.  The app running in the MSIX container would have access to that folder, whether or not it is inside the package or on the native system.

    More likely, you are attempting to install the twain driver as part of the package.  The kernel cannot see your driver as MSIX does not support drivers of any kind being inside the package.  If you treat the driver as an independent dependency, the driver may be installed natively and the application inside the MSIX container may then use it.

    Make sense?