Fixing Matlab’s actxserver

Matlab’s built-in actxserver function provides access to COM/DCOM server applications on Microsoft Windows platforms. This enables us, for example, to open a Microsoft Office document programmatically, and modify it from within Matlab. This in turn can be used, for example, to prepare professional PDF reports, relying on Office’s ability to save documents in PDF format. Alternately, we could programmatically update cell formatting (colors, fonts, borders etc.) and embed images and graphs in an Excel workbook – something that the standard xlswrite cannot do (my officedoc utility does this, using the same COM interface).

Note: For some reason, COM servers are called an ActiveX server in Matlab, although the term ActiveX is normally used for clients (i.e., controls), for which we have the actxcontrol* set of built-in functions.

Today I will focus on two changes that I made to Matlab’s built-in actxserver function:

Reusing an active server (no pun intended…)

By default, actxserver starts a new instance of the specified COM server whenever it is called. Sometimes this is indeed useful. However, I find that in the vast majority of cases, I actually want to reuse an existing server instance if it is currently running. For example, it is much faster and more memory-efficient to open an Excel workbook file in an existing Excel process, than to open it in a new dedicated process.

In R2007a, Matlab introduced the actxGetRunningServer function. Unfortunately, it did not see fit to set it as the default behavior for actxserver, nor even as an optional additional parameter (although MathWorks *did* change the actxserver function in that very same release, to accept a custom interface parameter). Users need to change their programs to first call actxGetRunningServer, check whether it works or fails, and then call actxserver if it fails (meaning that a server process is not currently running and a new one needs to be started):

% Try to reuse an existing COM server instance if possible
try
    hServer = actxGetRunningServer('excel.application');
    % no crash so probably succeeded to connect to a running server
catch
    % Never mind - continue normally to start the COM server and connect to it
    hServer = actxserver('excel.application');
end

This is downright silly, I must say.

Moreover, all the existing user and Matlab code that uses actxserver will continue to start a new server instance rather than reuse existing ones. For example, the widely-used xlsread and xlswrite functions.

Instead of fixing Matlab’s installed actxserver.m file, which could be problematic when deploying applications to end-users, I created a copy of actxserver.m somewhere in my user folders that is high on the Matlab path. This way I can modify the file and bundle it with any application that I send to clients. The change to this file is simply to add a variant of the code above at the very top of the actxserver.m file, as follows (the new lines are highlighted):

function h = actxserver(progID, varargin)
%ACTXSERVER Creates a COM Automation server.
...
% Copyright 2006-2007 The MathWorks, Inc.
% $Revision: 1.8.6.12 $ $Date: 2011/08/13 17:30:50 $
 
error(nargchk(1, 5, nargin, 'struct'));
 
% Yair 17/5/2009: Try to reuse an existing COM server instance if possibletry    h = actxGetRunningServer(progID);    return;  % no crash so probably succeeded - returncatch    % Never mind - continue normally to start the COM server and connect to itend 
machinename = '';
interface = 'IDispatch';
...

This simple change means that all exiting code, including Matlab’s built-in xlsread and xlswrite functions, now try to reuse a running COM server process if possible, starting a new one only if this fails. The code is fault-tolerant in that it also works on old Matlab releases where the actxGetRunningServer is not available.

Fix of the progID

The %matlabroot%/toolbox/matlab/winfun/private/newprogid.m function, a private function used by actxserver, normalizes COM program identifiers (progIDs), which is the string used to locate the COM server. Unfortunately, until R2011a (or specifically, when this was fixed by some MathWorker on June 10, 2010), this function had a bug that caused the normalization to fail. In order to correct this, I simply added the fixed function to the bottom of my modified actxserver.m file:

% Taken from: [matlabroot '\toolbox\matlab\winfun\private\newprogid.m']
function convertedProgID = newprogid(progID)
% Copyright 1984-2004 The MathWorks, Inc.
% $Revision: 1.1.8.5 $ $Date: 2004/04/15 00:07:00 $
 
convertedProgID = lower(progID);  % Yair: in the new version (after 2010/06/10) this line is missing i.e. case-sensitive progID
convertedProgID = regexprep(convertedProgID, '_', '__');  % Yair 17/5/2009: was progId - probably a bug
convertedProgID = regexprep(convertedProgID, '-', '___');
convertedProgID = regexprep(convertedProgID, '\.', '_');
convertedProgID = regexprep(convertedProgID, ' ', '____');
convertedProgID = regexprep(convertedProgID, '&', '_____');

Note that when MathWorks fixed %matlabroot%/toolbox/matlab/winfun/private/newprogid.m in 2010, they removed the conversion of progID to lower-case, making it case-sensitive. In my modified version above, I have kept the older conversion to lowercase for case-insensitivity.

Public-service announcements

Matlab open training day (Israel) - click for details

Matlab open training day (Israel) - click for details

Readers in Israel are invited to attend a free training seminar that I will present on advanced Matlab topics in Herzliya, on Tuesday Jan 8, 2013. The seminar is free, but requires registration. Additional details here. I will speak in Hebrew, but the presentation will be in English and I will be happy to answer questions in English.

I wish to point readers’ attentions to the recent announcement by AccelerEyes and MathWorks, that they will now merge their parallel-processing solutions into Matlab’s product line. I assume this also means an out-of-court settlement of MathWorks’ patent-infringement lawsuit. On the one hand, this is bad news for competition, removing Matlab PCT’s major competitor from the market. On the other hand, it could mean improved PCT/DCS products, merging Jacket’s superior performance and functionality directly within Matlab. If I may venture a guess, 2013 will herald a better, faster, more integrated parallel computing solution for Matlab, but we should probably (and quite sadly) say goodbye to Jacket’s price levels. Such collaborations, generally portrayed as exciting for consumers, are typically much more exciting for the respective shareholders…

This blog will now take a short winter break, and will return following the New-Year. Happy Holidays/New-Year everyone! Let this upcoming year be another year filled with discoveries, innovation and success!

 

Categories: Low risk of breaking in future versions, Undocumented feature

Tags: , , , ,

Bookmark and SharePrint Print

6 Responses to Fixing Matlab’s actxserver

  1. liam says:

    Is there a way to force actxserver to create two separate instances.

    I have been trying to launch to separate COM instance of CQG COM object by calling actxserver(‘CQG.CQGCEL.4’) twice. This will only return one instance of the actxserver.

    Is there a way to force 2 instances?

    Tks

  2. duke says:

    doesn’t work, crashes out.

  3. Sarunas says:

    I had a program that reads two excel files (== two tables) and returns an excel with 10 sheets, 2 – 3 tables each sheet. I used WRITETABLE, I had to use that for every table, making the program a bit slow.

    Even more, when I had to calculate hundreds of input file variation combinations, the run-time started slowing down progressively. This was due to the fact, that EXCEL instances were opened for each call to read/write EXCEL files, and they, not closing before the following loop, started stacking up. I solved this by closing all EXCEL instances after each reference to EXCEL files, side effect being not being able to use EXCEL while running the program (which I was too lazy to solve).

    After implementing the try/catch posted here into actxserver.m, the program runs twice as fast without the side effect. Thank you kind sir.

    • Sarunas says:

      Or maybe I was too joyful too quickly.

      Apparently if there is an excel window open, I get an error and/or the window is closed.

    • Sarunas says:

      Update:

      Since “the one” used instance of EXCEL is closed inside other called functions (like xlsread/xlswrite), I had to hide some of the code there to prevent the programs from closing. I use readtable, which works good with this xlsread correction, but writetable has another Excel.Quit line, which I managed to find in write.m file. I disabled a few lines in writeXLSFile (which is called from within write.m), but not sure if they are of any significance, because my problem was fixed eventually after editing the write.m file. This however means that I will have to manually control the behaviour of how EXCEL workbooks are displayed, but this way my run-time was reduced from ~90 s to ~20 s.

      HOWEVER, once compiled to an .exe, program seems to have a problem using actxGetRunningServer.m, which is the core to this ‘fix’. This meant that I had to change back all the functions to compile a version that’s usable with MCR.

      At least I learnt something in the process. And even though my users get no merits from this, I will definitely use those fixes if I happen read/write hundreds of excel files again.

Leave a Reply

Your email address will not be published. Required fields are marked *