// Leaves the link in the same state as when it started. The out param leafExprType will be Function if // the array is empty in the first bottom-level subarray: {{}, {1,2,3}}. internal static int determineIncomingArrayDepth(IMathLink ml /*, out ExpressionType leafExprType*/) { int actualDepth = 0; ILinkMark mark = ml.CreateMark(); // // Initial value just to satisfy compiler; if depth of 0 is returned, then the leafExprType will be ignored by callers. // leafExprType = ExpressionType.Function; try { int len; string head = ml.GetFunction(out len); actualDepth = 1; while (len > 0) { ExpressionType leafExprType = ml.GetNextExpressionType(); if (leafExprType == ExpressionType.Function) { head = ml.GetFunction(out len); actualDepth++; } else { break; } } // // If the last sublist is empty (len == 0), we will walk off the end of the above loop with // // leafExprType == ExpressionType.Function, which is what we want. } catch (MathLinkException) { // Do nothing but clear it. Returning 0 is enough. ml.ClearError(); } finally { ml.SeekMark(mark); ml.DestroyMark(mark); } return(actualDepth); }
public static object readArbitraryResult(IKernelLink ml, GH_Component component) { Object exprOrObjectResult = null; ILinkMark mark = ml.CreateMark(); try { exprOrObjectResult = ml.GetObject(); } catch (MathLinkException) { ml.ClearError(); ml.SeekMark(mark); // Technically, GetExpr() should never throw MathLinkException. But there was at least one bug I found and fixed where it did. // So to be safe we leave this try/catch here, otherwise the link will get in a bad state. try { Expr ex = ml.GetExpr(); exprOrObjectResult = new ExprType(ex); } catch (MathLinkException) { ml.ClearError(); component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Error reading result as an Expr. This is a bug in RhinoLink, not the user program."); } } finally { ml.DestroyMark(mark); ml.NewPacket(); } return(exprOrObjectResult); }
public Expr PeekExpr() { ILinkMark mark = CreateMark(); try { return(Expr.CreateFromLink(this)); } finally { SeekMark(mark); DestroyMark(mark); } }
// Tests whether symbol waiting on link is a valid object reference or the symbol Null. // Called by GetNextExpressionType() and GetExpressionType(), after it has already been verified that the type is MLTKSYM. protected bool isObject() { ILinkMark mark = CreateMark(); try { getObj(); return(true); } catch (MathLinkException) { ClearError(); return(false); } finally { SeekMark(mark); DestroyMark(mark); } }
/****************************** GetObject ******************************/ public virtual object GetObject() { // Note not GetNextExpressionType(), as that will likely already have been called. ExpressionType leafExprType = GetExpressionType(); switch (leafExprType) { case ExpressionType.Integer: return(GetInteger()); case ExpressionType.Real: return(GetDouble()); case ExpressionType.String: return(GetString()); case ExpressionType.Object: return(getObj()); case ExpressionType.Boolean: return(GetBoolean()); case ExpressionType.Complex: return(GetComplex()); case ExpressionType.Symbol: { ILinkMark mark = CreateMark(); try { string s = GetSymbol(); if (s == "Null") { return(null); } SeekMark(mark); throw new MathLinkException(MathLinkException.MLE_SYMBOL); } finally { DestroyMark(mark); } } case ExpressionType.Function: return(Utils.readArbitraryArray(this, typeof(Array))); default: Debug.Fail("Unhandled ExpressionType value in MathLinkImpl.GetObject()"); return(null); } }
public void CheckFunctionWithArgCount(string f, int argCount) { ILinkMark mark = CreateMark(); try { int argc; string s = GetFunction(out argc); if (s != f || argc != argCount) { SeekMark(mark); throw new MathLinkException(MathLinkException.MLE_CHECKFUNCTION); } } finally { DestroyMark(mark); } }
public int CheckFunction(string f) { ILinkMark mark = CreateMark(); try { int argCount; string s = GetFunction(out argCount); if (s != f) { SeekMark(mark); throw new MathLinkException(MathLinkException.MLE_CHECKFUNCTION); } return(argCount); } finally { DestroyMark(mark); } }
// Special method for reading results typed as "Any". We want native objects for simple types like int, double, // arbitrary object refs as objects (let Grasshopper internals worry about wrapping in IGH_Goo), and Expr for anything else. // Note that we don't have to worry here about list/tree access type, because this is only called either for // leaf elements, or to get whole nested lists as a single item for DataAccess.item slots. private static object ReadAsAny(IKernelLink link) { ILinkMark mark = link.CreateMark(); try { ExpressionType leafExprType = link.GetNextExpressionType(); link.SeekMark(mark); switch (leafExprType) { case ExpressionType.Integer: return(link.GetInteger()); case ExpressionType.Real: return(link.GetDouble()); case ExpressionType.String: case ExpressionType.Symbol: return(link.GetString()); case ExpressionType.Object: return(link.GetObject()); case ExpressionType.Boolean: return(link.GetBoolean()); case ExpressionType.Complex: // TODO: Don't have any idea how this is handled elsewhere return(link.GetComplex()); case ExpressionType.Function: return(new ExprType(link.GetExpr())); default: // Just to satisfy the compiler. return(null); } } finally { link.DestroyMark(mark); } }
public virtual bool OnPacketArrived(PacketType pkt) { if (PacketArrived == null) { return(true); } bool continueProcessing = true; Delegate[] pktHandlers = PacketArrived.GetInvocationList(); if (pktHandlers.Length > 0) { object[] args = new object[] { this, pkt }; ILinkMark mark = CreateMark(); try { foreach (Delegate d in pktHandlers) { try { continueProcessing = (bool)d.DynamicInvoke(args); } catch (Exception) { // Don't want an exception thrown by a PacketHandler method to // prevent all handlers from being notified. } ClearError(); SeekMark(mark); if (!continueProcessing) { break; } } } finally { DestroyMark(mark); } } return(continueProcessing); }
public override void SeekMark(ILinkMark mark) { impl.SeekMark(mark); }
public abstract void DestroyMark(ILinkMark mark);
public abstract void SeekMark(ILinkMark mark);
protected override void SolveInstance(IGH_DataAccess DA) { ` GetData ` // Final, optional link input param is always present. LinkType linkType = null; DA.GetData(` InputArgCount `, ref linkType); IKernelLink link = linkType != null ? linkType.Value : Utils.GetLink(); // Send initialization code and saved defs. try { if (` UseInit `) { link.Evaluate("ReleaseHold[\"" + ` Initialization ` + "\"]"); link.WaitAndDiscardAnswer(); } if (` UseDefs `) { link.Evaluate(` Definitions `); link.WaitAndDiscardAnswer(); } } catch (MathLinkException exc) { link.ClearError(); AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "WSTPException communicating with Wolfram Engine: " + exc); return; } // Now begin sending the actual computation. try { link.PutFunction("EvaluatePacket", 1); link.PutNext(ExpressionType.Function); link.PutArgCount(` InputArgCount `); if (` HeadIsSymbol `) { link.PutSymbol("`Func`"); } else { // pure function link.PutFunction("ToExpression", 1); link.Put("`Func`"); } ` SendInputParams ` link.EndPacket(); link.WaitForAnswer(); } catch (MathLinkException exc) { link.ClearError(); link.NewPacket(); AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "WSTPException communicating with Wolfram Engine: " + exc); return; } // This is one part of a (likely) temporary feature that puts the Expr result on the second output. Expr debuggingExpr = link.PeekExpr(); // Read the result(s) and store them in the DA object. ILinkMark mark = link.CreateMark(); // TODO: ghResultCount is not used, but if we are going to allow multiple outputs, then we have to protect against // users specifying more than one, but only one arrives. This will hang reading the second one. int ghResultCount = 0; bool isGHResult = false; try { ghResultCount = link.CheckFunction("GHResult") - 1; isGHResult = true; } catch (MathLinkException) { link.SeekMark(mark); } finally { link.DestroyMark(mark); } try { if (isGHResult) { ` ReadAndStoreResults ` } else { ` ReadAndStoreResults ` } // This is the second part of the temporary feature that puts the entire result as an Expr on a second output port. DA.SetData(++ghResultCount, new ExprType(debuggingExpr)); // Last output item is always the link. DA.SetData(ghResultCount + 1, new LinkType(link)); } catch (MathLinkException exc) { link.ClearError(); AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "WSTPException communicating with Wolfram Engine while reading results: " + exc); } finally { link.NewPacket(); } }
// Can read any kind of array, except for jagged arrays that are multidimensional at the beginning, like [][,] // (jagged at start like [,][] or [,...][][] is OK, but only if we know the type, not for an Array slot). Recall // that mixed array types are read backwards, so [][,] is a 2-deep array of element type [] (i.e., it is multidimensional // at the start, not the end). One more limitation: For an Array argument slot, if the incoming array is 3-deep or deeper, // it must be rectangular, not jagged. // We know the incoming expression has head List. internal static Array readArbitraryArray(IMathLink ml, Type t) { int len; if (t == typeof(Array)) { ExpressionType leafExprType; Type leafType; // For the Array type, we have no clue about how the leaf elements will be read. Thus we need to peek into // the incoming data to decide what type it is. int depth = determineIncomingArrayDepth(ml); ILinkMark mark = ml.CreateMark(); ILoopbackLink loop = null; try { if (depth == 1) { len = ml.CheckFunction("List"); if (len == 0) { throw new MathLinkException(MathLinkException.MLE_EMPTY_ARRAY); } leafExprType = ml.GetNextExpressionType(); ml.SeekMark(mark); leafType = netTypeFromExpressionType(leafExprType, ml); // Fail if data could only be read as Expr. This means that we cannot pass a list of arbitrary expressions // to an arg slot typed as Array. It would have to be typed as Expr[]. We make this choice to provide // more meaningful error reporting for the cases where the array has bogus data in it. We assume that // this convenience outweighs the very rare cases where a programmer would want to pass an array of exprs // for an Array slot (they could always create the array separately and pass it as an object reference). if (leafType == typeof(Expr)) { if (leafExprType == ExpressionType.Complex) { throw new MathLinkException(MathLinkException.MLE_NO_COMPLEX); } else { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY); } } return((Array)ml.GetArray(leafType, 1)); } else if (depth == 2) { // The loopback link is just a utility for quickly reading expressions off the link (via TransferExpression). // Nothing is ever read back off the loopback link. loop = MathLinkFactory.CreateLoopbackLink(); // Determine if the array is rectangular or jagged. len = ml.CheckFunction("List"); bool isJagged = false; bool foundLeafType = false; // This next assignment is strictly for the compiler. The value will never be used // unless it is set to an actual value below. We have an existing function that will get // the leafExprType of the incoming array (getLeafExprType()), but we also need to check if the array // is jagged here, so we will do both tasks at once and save a little time. leafExprType = ExpressionType.Integer; int lenAtLevel2 = ml.CheckFunction("List"); if (lenAtLevel2 > 0) { leafExprType = ml.GetNextExpressionType(); foundLeafType = true; } // peel off all the elements in the first sublist. for (int j = 0; j < lenAtLevel2; j++) { loop.TransferExpression(ml); } // For each remaining sublist, check its length and peel off its members. At this point, though, we // cannot be guaranteed that all elements are lists (or, at least, guaranteed that it is an error // if they are not). They could be Null if the array is jagged: {{1, 2}, Null}. for (int i = 1; i < len; i++) { ExpressionType nextExprType = ml.GetNextExpressionType(); if (nextExprType == ExpressionType.Object) { isJagged = true; // OK to have null as an element in a jagged array. if (ml.GetObject() != null) { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE); } } else if (nextExprType == ExpressionType.Function) { int thisLength = ml.CheckFunction("List"); if (!foundLeafType && thisLength > 0) { leafExprType = ml.GetNextExpressionType(); foundLeafType = true; } if (thisLength != lenAtLevel2) { isJagged = true; break; } else { for (int j = 0; j < thisLength; j++) { loop.TransferExpression(ml); } } } else { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_DEPTH); } } // If the array is empty we cannot create a .NET array for it, as there is no type info for the array we are creating. if (!foundLeafType) { throw new MathLinkException(MathLinkException.MLE_EMPTY_ARRAY); } leafType = netTypeFromExpressionType(leafExprType, ml); ml.SeekMark(mark); if (isJagged) { ml.CheckFunction("List"); Array result = Array.CreateInstance(Array.CreateInstance(leafType, 0).GetType(), len); for (int i = 0; i < len; i++) { // Have to check if elements are lists or Null. ExpressionType nextExprType = ml.GetNextExpressionType(); if (nextExprType == ExpressionType.Function) { result.SetValue(ml.GetArray(leafType, 1), i); } else { string sym = ml.GetSymbol(); if (sym != "Null") { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE); } result.SetValue(null, i); } } return(result); } else { return(ml.GetArray(leafType, 2)); } } else { // For an Array argument slot, we only support passing >2-deep rectangular arrays, not jagged. for (int i = 0; i < depth; i++) { ml.CheckFunction("List"); } leafExprType = ml.GetNextExpressionType(); leafType = netTypeFromExpressionType(leafExprType, ml); ml.SeekMark(mark); return(ml.GetArray(leafType, depth)); } } finally { if (loop != null) { loop.Close(); } ml.DestroyMark(mark); } } else { // We have the actual array shape encoded in the type. Either [], [,..], or array-of-arrays: [][].... int arrayRank = t.GetArrayRank(); Type elementType = t.GetElementType(); if (elementType.IsArray) { if (arrayRank > 1) { // Don't support multidimensional array at start of jagged array: [][,]. Recall that mixed array types // are read backwards, so [][,] is a 2-deep array of element type []. throw new MathLinkException(MathLinkException.MLE_MULTIDIM_ARRAY_OF_ARRAY); } len = ml.CheckFunction("List"); Array result = Array.CreateInstance(elementType, len); for (int i = 0; i < len; i++) { ExpressionType nextExprType = ml.GetNextExpressionType(); if (nextExprType == ExpressionType.Function) { result.SetValue(readArbitraryArray(ml, elementType), i); } else { string sym = ml.GetSymbol(); if (sym != "Null") { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE); } result.SetValue(null, i); } } return(result); } else if (elementType == typeof(Array)) { // Don't support Array[]. throw new MathLinkException(MathLinkException.MLE_ARRAY_OF_ARRAYCLASS); } else { return(ml.GetArray(elementType, arrayRank)); } } }
// M code must ensure that the only types that can arrive in this function are ones that are currently handled here. public static bool ReadAndStoreResult(string type, int index, IKernelLink link, IGH_DataAccess DA, GH_ParamAccess accessType, GH_Component component) { try { switch (accessType) { case GH_ParamAccess.item: { object res = ReadSingleItemAsType(type, link); DA.SetData(index, res); break; } case GH_ParamAccess.list: { ILinkMark mark = link.CreateMark(); int length = 1; bool isList = false; try { length = link.CheckFunction("List"); isList = true; } catch (MathLinkException) { link.ClearError(); } finally { link.SeekMark(mark); link.DestroyMark(mark); } if (isList) { IList list = ReadListOfItemsAsType(type, link); DA.SetDataList(index, list); } else { object item = ReadSingleItemAsType(type, link); DA.SetData(index, item); } break; } case GH_ParamAccess.tree: { // Quick and dirty method. Only support numbers. Read as Expr, then use Dimensions, etc. // TODO: Handle objects. See how it is done in ReadSingleItemAsType(). // This read-as-expr method shows problems for tree type. It would be better, for tree, to not use // the expr, but rather re-read the data from the link (as is done for item, list). In the curremt // method, I don't see any good way to handle "Any". // WAIT, IT'S WORSE. Expr.AsArray() only does Integer, Real, and only up to depth 2. ILinkMark mark = link.CreateMark(); try { Expr e = link.GetExpr(); int[] dims = e.Dimensions; if (dims.Length == 0) { link.SeekMark(mark); object res = ReadSingleItemAsType(type, link); DA.SetData(index, res); } else if (dims.Length == 1) { link.SeekMark(mark); IList list = ReadListOfItemsAsType(type, link); DA.SetDataList(index, list); } else if (dims.Length == 2) { // This code could be cleaner with GH_Structure, but I dont quite understand that class... switch (type) { case "Number": { DataTree <double> tree = new DataTree <double>(); Array dataArray = e.AsArray(ExpressionType.Real, 2); for (int i = 0; i < dims[0]; i++) { GH_Path pth = new GH_Path(i); for (int j = 0; j < dims[1]; j++) { tree.Add((double)dataArray.GetValue(i, j), pth); } } DA.SetDataTree(index, tree); break; } case "Integer": { DataTree <int> tree = new DataTree <int>(); Array dataArray = e.AsArray(ExpressionType.Integer, 2); for (int i = 0; i < dims[0]; i++) { GH_Path pth = new GH_Path(i); for (int j = 0; j < dims[1]; j++) { tree.Add((int)dataArray.GetValue(i, j), pth); } } DA.SetDataTree(index, tree); break; } default: // Can't handle this. return(false); } } else { // TODO. At least set a RuntimeMessage before returning false. return(false); } } finally { link.DestroyMark(mark); } break; // Problem with the read-and-fill-element-by-element approach is that this is a GH_Structure<IGH_Goo>, // so I need to create an IGH_Goo pbjectout of every piece of data. But there are zillions of IGH_Goo // objects out there. I don't want to map every object to its IGH_Goo type (e.g., Circle --> GH_Circle). // For lists, I could simply create a List<anything> and apparently it gets converted to the appopriate // IGH_Goo object later on. But with trees, I don't see the corresponding technique. Compare the signature // of DA.SetDataList() and DA.SetDataTree(). /* * ILinkMark mark3 = link.CreateMark(); * int length = 1; * try { * length = link.CheckFunction("List"); * // Don't catch; allow to fail. Must be a list arriving for tree results. * } finally { * link.DestroyMark(mark3); * } * GH_Structure<IGH_Goo> structure = new GH_Structure<IGH_Goo>(); * GH_Path path = new GH_Path(0); * int pathLen = 1; * int pathIndex = 0; * for (int i = 0; i < length; i++) { * if (isListWaitingOnLink(link)) { * path. * } else { * structure.Append( * } * } */ } } } catch (Exception) { component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Unexpected type of result from Wolfram Engine"); link.ClearError(); link.NewPacket(); return(false); } return(true); }
public override void DestroyMark(ILinkMark mark) { impl.DestroyMark(mark); }
public override PacketType NextPacket() { // Code here is not just a simple call to impl.nextPacket(). For a KernelLink, nextPacket() returns a // wider set of packet constants than the MathLink C API itself. We want nextPacket() to work on the // non-packet types of heads the kernel and FE send back and forth. // Because for some branches below we seek back to the start before returning, it is not guaranteed // that when nextPacket() returns, the current packet has been "opened". Thus it is not safe to write // a J/Link program that loops calling nextPacket()/newPacket(). You need to call handlePacket() after // nextPacket() to ensure that newPacket() will throw away the curent "packet". // createMark will fail on an unconnected link, and the user might call this // before any reading that would connect the link. if (!linkConnected) { Connect(); linkConnected = true; } PacketType pkt; ILinkMark mark = CreateMark(); try { pkt = impl.NextPacket(); } catch (MathLinkException e) { if (e.ErrCode == 23 /* MLEUNKNOWNPACKET */) { ClearError(); SeekMark(mark); int argCount; string f = GetFunction(out argCount); if (f == "ExpressionPacket") { pkt = PacketType.Expression; } else if (f == "BoxData") { // Probably more than just BoxData will need to join this group. I thought that kernel // always sent cell contents (e.g. Print output) wrapped in ExpressionPacket, but not so. // "Un-wrapped" BoxData is also possible, perhaps others. We need to treat this case // specially, since there is no packet wrapper. SeekMark(mark); pkt = PacketType.Expression; } else { // Note that all other non-recognized functions get labelled as PacketType.FrontEnd. I could perhaps be // more discriminating, but then I risk not recognizing a legitimate communication // intended for the front end. This means that PacketType.FrontEnd is not a specific indication that // a front-end-related packet is on the link. Because there is no diagnostic packet head, // we need to seek back to before the function was read. That way, when programs call // methods to read the "contents" of the packet, they will in fact get the whole thing. SeekMark(mark); pkt = PacketType.FrontEnd; } } else { // The other MathLink error from MLNextPacket is MLENEXTPACKET, which is a common one saying // that you have called MLNextPacket when more data is in the current packet. throw e; } } finally { DestroyMark(mark); } return(pkt); }
public virtual void HandlePacket(PacketType pkt) { switch (pkt) { // If you ever change the default behavior on the 4 "answer" packets to read off the link, // you'll need to add a seekMark in NativeKernelLink.waitForAnswer... case PacketType.Return: case PacketType.InputName: case PacketType.ReturnText: case PacketType.ReturnExpression: case PacketType.Menu: case PacketType.Message: break; // From here on, the cases do actual work. case PacketType.Call: { ExpressionType type = GetExpressionType(); if (type == ExpressionType.Integer) { // A normal CallPacket representing a call to .NET via nCall. callPktHandler.handleCallPacket(this); } else { // A CallPacket destined for the FE via MathLink`CallFrontEnd[] and routed through // .NET due to ShareFrontEnd[]. This would only be in a 5.1 FE, as earlier // versions do not use CallPacket and later versions would use the FE's Service Link. IMathLink feServerLink = callPktHandler.FEServerLink; if (feServerLink != null) { feServerLink.PutFunction("CallPacket", 1); feServerLink.TransferExpression(this); // FE will always reply to a CallPacket. Note that it is technically possible for // the FE to send back an EvaluatePacket, which means that we really need to run a // little loop here, not just write the result back to the kernel. But this branch // is only for a 5.1 FE, and I don't think that the 5.1 FE ever does that. TransferExpression(feServerLink); } } break; } case PacketType.Display: case PacketType.DisplayEnd: { IMathLink feServerLink = callPktHandler.FEServerLink; if (feServerLink != null) { if (accumulatingPS == null) { accumulatingPS = new System.Text.StringBuilder(34000); // 34K is large enough to hold an entire packet } accumulatingPS.Append(GetString()); if (pkt == PacketType.DisplayEnd) { // XXXPacket[stuff] ---> Cell[GraphicsData["PostScript", stuff], "Graphics"] feServerLink.PutFunction("FrontEnd`FrontEndExecute", 1); feServerLink.PutFunction("FrontEnd`NotebookWrite", 2); feServerLink.PutFunction("FrontEnd`SelectedNotebook", 0); feServerLink.PutFunction("Cell", 2); feServerLink.PutFunction("GraphicsData", 2); feServerLink.Put("PostScript"); feServerLink.Put(accumulatingPS.ToString()); feServerLink.Put("Graphics"); feServerLink.Flush(); accumulatingPS = null; } } else { Debug.WriteLine("Got PacketType.Display in handlePacket, but no FE link"); } break; } case PacketType.Input: case PacketType.InputString: { IMathLink feServerLink = callPktHandler.FEServerLink; if (feServerLink != null) { feServerLink.PutFunction(pkt == PacketType.InputString ? "InputStringPacket" : "InputPacket", 1); feServerLink.Put(GetString()); feServerLink.Flush(); NewPacket(); Put(feServerLink.GetString()); Flush(); } break; } case PacketType.Text: case PacketType.Expression: { // Print output, or message text. IMathLink feServerLink = callPktHandler.FEServerLink; if (feServerLink != null) { // XXXPacket[stuff] ---> Cell[stuff, "Print"] feServerLink.PutFunction("FrontEnd`FrontEndExecute", 1); feServerLink.PutFunction("FrontEnd`NotebookWrite", 2); feServerLink.PutFunction("FrontEnd`SelectedNotebook", 0); feServerLink.PutFunction("Cell", 2); feServerLink.TransferExpression(this); feServerLink.Put((lastPktWasMsg) ? "Message" : "Print"); feServerLink.Flush(); } else { // For one type of PacketType.Expression, no part of it has been read yet. Thus we must "open" the // packet so that later calls to newPacket() throw it away. if (pkt == PacketType.Expression) { int ignore; GetFunction(out ignore); } } break; } case PacketType.FrontEnd: { // This case is different from the others. At the point of entry, the link is at the point // _before_ the "packet" has been read. As a result, we must at least open the packet. // Note that PacketType.FrontEnd is really just a fall-through for unrecognized packets. We don't have any // checks that it is truly intended for the FE. IMathLink feServerLink = callPktHandler.FEServerLink; if (feServerLink != null) { ILinkMark mark = CreateMark(); try { // Wrap FrontEndExecute around it if not already there. int ignore; string wrapper = GetFunction(out ignore); if (wrapper != "FrontEnd`FrontEndExecute") { feServerLink.PutFunction("FrontEnd`FrontEndExecute", 1); } } finally { SeekMark(mark); DestroyMark(mark); } feServerLink.TransferExpression(this); feServerLink.Flush(); // Wait until either the fe is ready (because what we just sent causes a return value) // or kernel is ready (the computation is continuing because the kernel is not waiting // for a return value). do { System.Threading.Thread.Sleep(50); } while (!feServerLink.Ready && !Ready); if (feServerLink.Ready) { // fe link has something to return to kernel from last PacketType.FrontEnd we sent it. TransferExpression(feServerLink); Flush(); } } else { // It's OK to get here. For example, this happens if you don't share the fe, but have a // button that calls NotebookCreate[]. This isn't a very good example, because that // function expects the fe to return something, so Java will hang. you will get into // trouble if you make calls on the fe that expect a return. Everything is OK for calls // that don't expect a return, though. int ignore; GetFunction(out ignore); // Must at least open the packet, so newPacket (back in caller) will get rid of it. } break; } default: break; } lastPktWasMsg = pkt == PacketType.Message; }