J
Josef Meile
Hi,
I'm developing a C# exe addon for visio. I figured out that each time
that you called the RUNADDONWARGS function from a shape's action
cell, the addon will be run again. To cope with this problem, I created
a socket on the main addon application, which uses socket events to
listen for connections. For the shapes I created a small application that
sends the command line arguments to the main application through the
created socket. But I got a problem:
First, when the main application is started, I stored a reference to the
current running visio application on a class attribute called "VisioApp".
Then a socket is created and a callback routine called "receiveCommands"
is set. Finally, I create a shape which calls the small application on an
action cell:
action=RUNADDONWARGS("EVAShape","/action=addLink /element=actor")
menu="Add link"
The small application is called and the command line arguments are received
by the main addon application, but some how, when the callback routine:
"receiveCommands" is called, the reference to the visio application is lost
and when adding it to the debugging watch window, almost all its attributes
are:
"error: cannot obtain value"
Even, when I try to get the "activeDocument" attribute by doing:
Visio.Document activeDoc = VisioApp.ActiveDocument;
I don't get any error and the next line of code isn't excecuted.
The only way I found to solve this problem was getting the running instance
again, but I think it won't work if you have two or more visio instances
running because the "GetActiveObject" from the Marshal class only gets the
first
instance it finds, so, it may get the wrong one.
Does anybody know what the problem could be?
Thanks in advanced,
Josef
Here is my main Application:
using Process = System.Diagnostics.Process;
using System;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Reflection;
using Threading = System.Threading;
using Visio = Microsoft.Office.Interop.Visio;
using Marshal = System.Runtime.InteropServices.Marshal;
namespace EVA
{
/// <summary>
/// This class contains the main subroutine for the Visio add-on.
/// </summary>
public class CExeAddon
{
public Visio.Application VisioApp;
//Socket opened by the application to avoid multiple instances of the addon
private Socket AppSocket;
private System.Net.IPEndPoint mIPConnection;
public System.Net.IPEndPoint IPConnection
{
get
{
return mIPConnection;
}
set
{
mIPConnection = value;
}
}
/// <summary>
/// Class destructor
/// </summary>
~CExeAddon()
{
if (AppSocket != null)
{
AppSocket.Close();
AppSocket = null;
}
VisioApp = null;
}
/// <summary>
/// Here we create/get the visio application instance
/// </summary>
public void Run()
{
object appInstance;
try
{
//Tries to get a running instance
appInstance = Marshal.GetActiveObject("Visio.Application");
}
catch (System.Runtime.InteropServices.COMException)
{
//There isn't a running instance, so, we return null
appInstance = null;
}
if (appInstance != null)
VisioApp = (Visio.Application) appInstance;
else
VisioApp = new Visio.ApplicationClass();
}
/// <summary>
/// This method will be excecuted as soon as a socket connection occures. It
/// will only read the commads sent by a new running instance of the addon,
/// then they will be sent to the parse method
/// </summary>
public void ReceiveCommands(IAsyncResult result)
{
Socket server = (Socket)result.AsyncState;
Socket client = server.EndAccept(result);
byte[] buffer = new byte[CG.BUFFER_SIZE];
int numBytes=client.Receive(buffer);
client.Close();
string command = System.Text.Encoding.ASCII.GetString(buffer,0,numBytes);
//The command line arguments are printed correctly
MessageBox.Show(command);
//When pressing F10, on this line, the next one won't be excecuted
Visio.Document activeDoc = VisioApp.ActiveDocument;
//This line isn't excecuted, and no error is shown
MessageBox.Show(activeDoc.Path);
//Sets again the server socket for waiting connection events
server.BeginAccept(new AsyncCallback(ReceiveCommands),server);
}
/// <summary>
/// In order to avoid that two addon instances run at the same time, I will
/// only allow the first one to set up the visio events, then a socket will
/// be open. The second instance will send the command arguments through
that
/// socket and then it will exit. Finally, the original instance will get
/// the arguments, parse them and excecute the related command.
/// </summary>
public System.Net.IPEndPoint setMainSocket(string[] args)
{
//System.Net.IPAddress [] hostAddresses = null;
//string hostName = System.Net.Dns.GetHostName();
System.Net.IPEndPoint ipEndPoint;
string message = "";
//Gets the associated IPs of the local host
//System.Net.IPHostEntry ipEntry = System.Net.Dns.GetHostByName(hostName);
//hostAddresses = ipEntry.AddressList;
Socket appSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
Process currentProcess = Process.GetCurrentProcess();
string idMessage=string.Format("My id is: {0}",
currentProcess.Id.ToString());
MessageBox.Show(idMessage);
Process [] localByName = Process.GetProcessesByName(
currentProcess.ProcessName);
//There is already an addon running, so, the currentProcess will be
//killed and the command arguments will be send to the other process.
if (localByName.Length>1)
{
try
{
//First I will try with the local network.
ipEndPoint=new System.Net.IPEndPoint(CG.LOCAL_IP,CG.SOCKET_PORT);
appSocket.Connect(ipEndPoint);
}
catch (System.Net.Sockets.SocketException)
{
ipEndPoint=null;
}
if (ipEndPoint==null)
{
//There is another instance running, but it hasn't created a socket
//jet, so, we force the application to quit.
appSocket=null;
MessageBox.Show("Socket error");
Environment.Exit(-1);
}
MessageBox.Show("Building message");
message=string.Join(" ",args,0,args.Length);
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(message);
appSocket.Send(buffer);
appSocket.Close();
Environment.Exit(0);
}
AppSocket=appSocket;
//return new System.Net.IPEndPoint(hostAddresses[0],CG.SOCKET_PORT);
return new System.Net.IPEndPoint(CG.LOCAL_IP,CG.SOCKET_PORT);
}
/// <summary>
/// Assemblies the socket listener in order to receive the commands of other
/// instances and excecute them.
/// </summary>
/// <param name="ipEndPoint">Socket address</param>
public void ListenConnections()
{
AppSocket.Bind(IPConnection);
AppSocket.Listen(CG.SOCKET_CONNECTIONS);
AppSocket.BeginAccept(new AsyncCallback(ReceiveCommands),
AppSocket);
}
static void Main(string[] args)
{
CExeAddon myAddon=new CExeAddon();
myAddon.IPConnection = myAddon.setMainSocket(args);
try
{
myAddon.Run();
}
catch(Exception err)
{
//Here I have to decide what to do
MessageBox.Show(string.Format("Error: {0}\nTraceback:\n{1}",
err.Message,err.StackTrace));
}
myAddon.ListenConnections();
myAddon.VisioApp.Documents.Add("");
//I have to do this in order to keep the addon running
Threading.Thread.Sleep(Threading.Timeout.Infinite);
}
}
And then the small application that sends the command line arguments:
using System;
using System.Net.Sockets;
namespace EVAShape
{
public class CEVAShape
{
static void Main(string[] args)
{
System.Net.IPEndPoint ipEndPoint;
Socket appSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
int SOCKET_PORT = 1234;
System.Net.IPAddress LOCAL_IP =
System.Net.IPAddress.Parse("127.0.0.1");
ipEndPoint=new System.Net.IPEndPoint(LOCAL_IP,SOCKET_PORT);
appSocket.Connect(ipEndPoint);
string message=string.Join(" ",args,0,args.Length);
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(message);
appSocket.Send(buffer);
appSocket.Close();
Environment.Exit(0);
}
}
}
I'm developing a C# exe addon for visio. I figured out that each time
that you called the RUNADDONWARGS function from a shape's action
cell, the addon will be run again. To cope with this problem, I created
a socket on the main addon application, which uses socket events to
listen for connections. For the shapes I created a small application that
sends the command line arguments to the main application through the
created socket. But I got a problem:
First, when the main application is started, I stored a reference to the
current running visio application on a class attribute called "VisioApp".
Then a socket is created and a callback routine called "receiveCommands"
is set. Finally, I create a shape which calls the small application on an
action cell:
action=RUNADDONWARGS("EVAShape","/action=addLink /element=actor")
menu="Add link"
The small application is called and the command line arguments are received
by the main addon application, but some how, when the callback routine:
"receiveCommands" is called, the reference to the visio application is lost
and when adding it to the debugging watch window, almost all its attributes
are:
"error: cannot obtain value"
Even, when I try to get the "activeDocument" attribute by doing:
Visio.Document activeDoc = VisioApp.ActiveDocument;
I don't get any error and the next line of code isn't excecuted.
The only way I found to solve this problem was getting the running instance
again, but I think it won't work if you have two or more visio instances
running because the "GetActiveObject" from the Marshal class only gets the
first
instance it finds, so, it may get the wrong one.
Does anybody know what the problem could be?
Thanks in advanced,
Josef
Here is my main Application:
using Process = System.Diagnostics.Process;
using System;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Reflection;
using Threading = System.Threading;
using Visio = Microsoft.Office.Interop.Visio;
using Marshal = System.Runtime.InteropServices.Marshal;
namespace EVA
{
/// <summary>
/// This class contains the main subroutine for the Visio add-on.
/// </summary>
public class CExeAddon
{
public Visio.Application VisioApp;
//Socket opened by the application to avoid multiple instances of the addon
private Socket AppSocket;
private System.Net.IPEndPoint mIPConnection;
public System.Net.IPEndPoint IPConnection
{
get
{
return mIPConnection;
}
set
{
mIPConnection = value;
}
}
/// <summary>
/// Class destructor
/// </summary>
~CExeAddon()
{
if (AppSocket != null)
{
AppSocket.Close();
AppSocket = null;
}
VisioApp = null;
}
/// <summary>
/// Here we create/get the visio application instance
/// </summary>
public void Run()
{
object appInstance;
try
{
//Tries to get a running instance
appInstance = Marshal.GetActiveObject("Visio.Application");
}
catch (System.Runtime.InteropServices.COMException)
{
//There isn't a running instance, so, we return null
appInstance = null;
}
if (appInstance != null)
VisioApp = (Visio.Application) appInstance;
else
VisioApp = new Visio.ApplicationClass();
}
/// <summary>
/// This method will be excecuted as soon as a socket connection occures. It
/// will only read the commads sent by a new running instance of the addon,
/// then they will be sent to the parse method
/// </summary>
public void ReceiveCommands(IAsyncResult result)
{
Socket server = (Socket)result.AsyncState;
Socket client = server.EndAccept(result);
byte[] buffer = new byte[CG.BUFFER_SIZE];
int numBytes=client.Receive(buffer);
client.Close();
string command = System.Text.Encoding.ASCII.GetString(buffer,0,numBytes);
//The command line arguments are printed correctly
MessageBox.Show(command);
//When pressing F10, on this line, the next one won't be excecuted
Visio.Document activeDoc = VisioApp.ActiveDocument;
//This line isn't excecuted, and no error is shown
MessageBox.Show(activeDoc.Path);
//Sets again the server socket for waiting connection events
server.BeginAccept(new AsyncCallback(ReceiveCommands),server);
}
/// <summary>
/// In order to avoid that two addon instances run at the same time, I will
/// only allow the first one to set up the visio events, then a socket will
/// be open. The second instance will send the command arguments through
that
/// socket and then it will exit. Finally, the original instance will get
/// the arguments, parse them and excecute the related command.
/// </summary>
public System.Net.IPEndPoint setMainSocket(string[] args)
{
//System.Net.IPAddress [] hostAddresses = null;
//string hostName = System.Net.Dns.GetHostName();
System.Net.IPEndPoint ipEndPoint;
string message = "";
//Gets the associated IPs of the local host
//System.Net.IPHostEntry ipEntry = System.Net.Dns.GetHostByName(hostName);
//hostAddresses = ipEntry.AddressList;
Socket appSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
Process currentProcess = Process.GetCurrentProcess();
string idMessage=string.Format("My id is: {0}",
currentProcess.Id.ToString());
MessageBox.Show(idMessage);
Process [] localByName = Process.GetProcessesByName(
currentProcess.ProcessName);
//There is already an addon running, so, the currentProcess will be
//killed and the command arguments will be send to the other process.
if (localByName.Length>1)
{
try
{
//First I will try with the local network.
ipEndPoint=new System.Net.IPEndPoint(CG.LOCAL_IP,CG.SOCKET_PORT);
appSocket.Connect(ipEndPoint);
}
catch (System.Net.Sockets.SocketException)
{
ipEndPoint=null;
}
if (ipEndPoint==null)
{
//There is another instance running, but it hasn't created a socket
//jet, so, we force the application to quit.
appSocket=null;
MessageBox.Show("Socket error");
Environment.Exit(-1);
}
MessageBox.Show("Building message");
message=string.Join(" ",args,0,args.Length);
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(message);
appSocket.Send(buffer);
appSocket.Close();
Environment.Exit(0);
}
AppSocket=appSocket;
//return new System.Net.IPEndPoint(hostAddresses[0],CG.SOCKET_PORT);
return new System.Net.IPEndPoint(CG.LOCAL_IP,CG.SOCKET_PORT);
}
/// <summary>
/// Assemblies the socket listener in order to receive the commands of other
/// instances and excecute them.
/// </summary>
/// <param name="ipEndPoint">Socket address</param>
public void ListenConnections()
{
AppSocket.Bind(IPConnection);
AppSocket.Listen(CG.SOCKET_CONNECTIONS);
AppSocket.BeginAccept(new AsyncCallback(ReceiveCommands),
AppSocket);
}
static void Main(string[] args)
{
CExeAddon myAddon=new CExeAddon();
myAddon.IPConnection = myAddon.setMainSocket(args);
try
{
myAddon.Run();
}
catch(Exception err)
{
//Here I have to decide what to do
MessageBox.Show(string.Format("Error: {0}\nTraceback:\n{1}",
err.Message,err.StackTrace));
}
myAddon.ListenConnections();
myAddon.VisioApp.Documents.Add("");
//I have to do this in order to keep the addon running
Threading.Thread.Sleep(Threading.Timeout.Infinite);
}
}
And then the small application that sends the command line arguments:
using System;
using System.Net.Sockets;
namespace EVAShape
{
public class CEVAShape
{
static void Main(string[] args)
{
System.Net.IPEndPoint ipEndPoint;
Socket appSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
int SOCKET_PORT = 1234;
System.Net.IPAddress LOCAL_IP =
System.Net.IPAddress.Parse("127.0.0.1");
ipEndPoint=new System.Net.IPEndPoint(LOCAL_IP,SOCKET_PORT);
appSocket.Connect(ipEndPoint);
string message=string.Join(" ",args,0,args.Length);
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(message);
appSocket.Send(buffer);
appSocket.Close();
Environment.Exit(0);
}
}
}