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: 220.127.116.11 $ $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: 18.104.22.168 $ $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.
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!