views:

960

answers:

3

We install Matlab Runtime on a machine, then we restart a .net windows service that invokes methods from the Matlab Runtime.
The problem is that we receive TypeInitializationException errors until we restart windows. We think this happens because Environment Variables are not changed on services until restart and Matlab uses the %Path% variable to reference it's core DLL's.
My question is, do you think I can change the %Path% variable so that Matlab will use it when referencing the core dll's for it's engine?
Or is it possible to add a directory to the runtime DLL loading mechanism of .NET so that those Matlab core dll's would be referenced correctly without restarting the machine?

Here is the exception we get

System.TypeInitializationException: The type initializer for 'MatlabCalculation.Calculation' threw an exception. ---> System.TypeInitializationException: The type initializer for 'MathWorks.MATLAB.NET.Utility.MWMCR' threw an exception. ---> System.DllNotFoundException: Unable to load DLL 'mclmcrrt710.dll': Kan opgegeven module niet vinden. (Exception from HRESULT: 0x8007007E)
   at MathWorks.MATLAB.NET.Utility.MWMCR.mclmcrInitialize()
   at MathWorks.MATLAB.NET.Utility.MWMCR..cctor()
   --- End of inner exception stack trace ---
   at MatlabCalculation.Calculation..cctor()
   --- End of inner exception stack trace ---
   at MatlabCalculation.Calculation.Finalize()

"Kan opgegeven module niet vinden" = "The specified module not found"

A: 

from: http://www.mathworks.com/support/solutions/en/data/1-A1A70V/index.html?product=MN&solution=1-A1A70V

Solution: When the web application calls the CreateEnvironmentBlock function to retrieve the environment variables on a Microsoft Windows Server 2003-based or Microsoft Windows XP-based computer, the returned path environment variable is truncated to 1,024 bytes. This behavior occurs even though the maximum size of an environment variable is 2,048 bytes. This issue prevents the web application from obtaining the correct environment variable.

Specifically, the MATLAB Compiler Runtime directories on the PATH may have been truncated in the returned PATH environment variable.

To Workaround the issue, do one of the following:

1) Add the MATLAB Compiler Runtime directories at the beginning of the existing PATH variable.

2) Obtain a patch for this issue from the following Microsoft website. http://support.microsoft.com/kb/906469

Tzury Bar Yochay
I saw this before. This does not seem to apply to our case. We are far from having 1024 bytes on the %Path% variable. I think the problem is related with the Path variable but not from it being truncated.
Paulo Manuel Santos
this has nothing to do with his problem because after restarting his computer, everything works.
lubos hasko
+1  A: 

If you can rewrite the service, you can use the System.Environment.GetEnvironmentVariable and SetEnvironmentVariable methods within the .NET code and add the Matlab runtime engine path yourself. If you cannot rewrite the service, you might try net stop/net start or installutil, which act on services. Or you might ask over on ServerFault.

OLD ANSWER because I misunderstood the question:

Is the MATLAB component starting and then throwing the exception? If so, the CTFROOT, TOOLBOXDIR, and ADDPATH functions might help. Maybe something like:

if isdeployed
    addpath(ctfroot);
    addpath(toolboxdir('signal'));
    %more addpath(toolboxdir('toolboxname')) statements
end

But if MATLAB isn't starting at all this won't help.

mtrw
From the exception I think matlab code is not invoked, and I would like to find a solution on the .net side, with no changes on the matlab code.
Paulo Manuel Santos
We had to upgrade matlab runtime and we ran into the same problem because the Path is changed. I ended up using System.Environment.SetEnvironmentVariable() successfully. Thanks!
Paulo Manuel Santos
A: 

This might help along your line of thinking "Or is it possible to add a directory to the runtime DLL loading mechanism of .NET so that those Matlab core dll's would be referenced correctly without restarting the machine":

In one app I use the following code to tell .NET where to find assemblies when it's trying to load them dynamically. In my case I need this as my app is loaded as an extension to another program, so my dlls aren't in the same directory as the application exe. Perhaps this also applies to you?

In my case my main application dll is loaded correctly, because it's registered for COM interop. But I need to do the following in order for MS Enterprise Library to load its assemblies, because of the way it does that in some funky dynamic way. The following code tells .NET to look in the currently executing assembly's directory when looking for assemblies to load. You could do the same with any directories you want .NET to look in, e.g. ones based on environment variables.

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.IO;

namespace CommonClasses
{
    /// <summary>
    /// Helper class to ensure the Common Language Runtime can dynamically load our referenced dlls.
    /// Because our components are called from COM via iexplore.exe the executing directory is likely to be something like 
    /// c:\program files\internet explorer\, which obviously doesn't contain our assemblies. This only seems to be a problem
    /// with the Enterprise Library so far, because it dynamically loads the assemblies it needs.
    /// This class helps by directing the CLR to use the directory of this assembly when it can't find the assembly 
    /// normally. The directory of this assembly is likely to be something like c:\program files\my program\
    /// and will contain all the dlls you could ask for.
    /// </summary>
    public static class AssemblyResolveAssistant
    {

        /// <summary>
        /// Records whether the AssemblyResolve event has been wired.
        /// </summary>
        private static bool _isWired = false;

        /// <summary>
        /// Add the handler to enable assemblies to be loaded from this assembly's directory, if it hasn't 
        /// already been added.
        /// </summary>
        public static void AddAssemblyResolveHandler()
        {
            if (!_isWired)
            {
                AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
                _isWired = true;
            }
        }

        /// <summary>
        /// Event handler that's called when the CLR tries to load an assembly and fails.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            Assembly result = null;
            // Get the directory where we think the assembly can be loaded from.
            string dirName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            AssemblyName assemblyName = new AssemblyName(args.Name);
            assemblyName.CodeBase = dirName;
            try
            {
                //Load the assembly from the specified path.
                result = Assembly.Load(assemblyName);
            }
            catch (Exception) { }
            //Return the loaded assembly, or null if assembly resolution failed.
            return result;
        }
    }
}

Then call the method AssemblyResolveAssistant.AddAssemblyResolveHandler() before doing anything that will require loading of assemblies outside the normal folders.

Rory