File write access in Rhino.compute

I’m trying to export Rhino geometry to .dxf on the server via an RH_OUT Text component.

The only way I’ve found to do that is to export to a file and then read it back in. I’m using a C# script that works perfectly in the Rhino app on the server to write to C:\Users\RhinoComputeUser\Documents.

However, Rhino.compute apparently does not have access to this directory.

Where can I export a file and then read it? (Or is there a better way to do this in memory?)

Thanks,
Bill

Here is the error message:

FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)\r\n   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)\r\n   at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize, Boolean checkHost)\r\n   at System.IO.File.InternalReadAllText(String path, Encoding encoding, Boolean checkHost)\r\n   at Script_Instance.RunScript(List`1 Geo, List`1 Layers, String TempFilename, Boolean DeleteTempFile, Object& DxfTxt) in c:\\Users\\RhinoComputeUser\\AppData\\Local\\Temp\\ypvrftiz.0.cs:line 97\r\n   at Script_Instance.InvokeRunScript(IGH_Component owner, Object rhinoDocument, Int32 iteration, List`1 inputs, IGH_DataAccess DA) in c:\\Users\\RhinoComputeUser\\AppData\\Local\\Temp\\ypvrftiz.0.cs:line 163\r\n   at ScriptComponents.Component_AbstractScript.SolveInstance(IGH_DataAccess access)","Properties":{"Port":"6002"}}
{"Timestamp":"2024-08-04T18:48:52.0786546+00:00","Level":"Error","MessageTemplate":"Could not find file 'C:\\Users\\RhinoComputeUser\\Documents\\D17817851.dxf'. (line: 0): component \"C# Script\" (f5cfc116-d7de-46b2-a020-cca5231e9904)","RenderedMessage":"Could not find file 'C:\\Users\\RhinoComputeUser\\Documents\\D17817851.dxf'. (line: 0): component \"C# Script\" (f5cfc116-d7de-46b2-a020-cca5231e9904)","Properties":{"Port":"6002"}}

Here is the C# script that is doing the file operations.

  private void RunScript(List<GeometryBase> Geo, List<string> Layers, string TempFilename, bool DeleteTempFile, ref object DxfTxt)
  {
    if (Geo.Count != Layers.Count) {
      DxfTxt = "Error in GH model"; // the lists of Geo items and Layers must match up exactly
      return;
    }

    RhinoDoc doc = RhinoDoc.Create(null);

    // static objects used for each item, with appropriate mods
    Rhino.DocObjects.Layer rhinoLayer = new Rhino.DocObjects.Layer(); // layer info is copied when added, so use this one over and over
    Rhino.DocObjects.ObjectAttributes attributes = new Rhino.DocObjects.ObjectAttributes(); // same for assignment to layer

    // loop over Geo and Layers in sync
    for (int i = 0; i < Geo.Count; i++) {
      var item = Geo[i];
      var layerName = Layers[i];
      int layerIndex;
      // create/add the layer
      Rhino.DocObjects.Layer layer = doc.Layers.FindName(layerName);
      if (layer != null) {
        layerIndex = layer.Index;
      }
      else{
        // add it if it doesn't exist
        rhinoLayer.Name = layerName;
        layerIndex = doc.Layers.Add(rhinoLayer);
      }
      attributes.LayerIndex = layerIndex;
      // add the geo item with its layer and then select it
      System.Guid guid = doc.Objects.Add(item, attributes);
      doc.Objects.Select(guid);
    }

    // export to .dxf
    Rhino.RhinoApp.RunScript(String.Format("-Export {0} Scheme Fusion EnterEnd", TempFilename), false);

    // read the file back into GH
    DxfTxt = File.ReadAllText(TempFilename);

    // clean up
    doc.Dispose();
    if (DeleteTempFile) {
      File.Delete(TempFilename);
    }

  }

How are you generating the TempFileName? System.IO has some methods to do that, though I am not sure how it handles permissions. I usually do the following;

var path = System.IO.Path.GetTempPath();
var filePath = System.IO.Path.Join(path, "someFileName.3dm");

That is a better solution, but I get the same error:

{"Timestamp":"2024-08-05T16:42:18.9281374+00:00","Level":"Error","MessageTemplate":"An exception occurred while processing request","RenderedMessage":"An exception occurred while processing request","Exception":"System.IO.FileNotFoundException: Could not find file 'C:\\Users\\RhinoComputeUser\\AppData\\Local\\Temp\\tmp9382406189.dxf'.\r\nFile name: 'C:\\Users\\RhinoComputeUser\\AppData\\Local\\Temp\\tmp9382406189.dxf'\r\n   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)\r\n   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)\r\n   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)\r\n   at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize, Boolean checkHost)\r\n   at System.IO.File.InternalReadAllText(String path, Encoding encoding, Boolean checkHost)\r\n   at Script_Instance.RunScript(List`1 Geo, List`1 Layers, String TempFilename, Boolean DeleteTempFile, Object& DxfTxt) in c:\\Users\\RhinoComputeUser\\AppData\\Local\\Temp\\clsv0fgb.0.cs:line 97\r\n   at Script_Instance.InvokeRunScript(IGH_Component owner, Object rhinoDocument, Int32 iteration, List`1 inputs, IGH_DataAccess DA) in c:\\Users\\RhinoComputeUser\\AppData\\Local\\Temp\\clsv0fgb.0.cs:line 163\r\n   at ScriptComponents.Component_AbstractScript.SolveInstance(IGH_DataAccess access)","Properties":{"Port":"6003"}}
{"Timestamp":"2024-08-05T16:42:19.1469260+00:00","Level":"Error","MessageTemplate":"Could not find file 'C:\\Users\\RhinoComputeUser\\AppData\\Local\\Temp\\tmp9382406189.dxf'. (line: 0): component \"C# Script\" (f5cfc116-d7de-46b2-a020-cca5231e9904)","RenderedMessage":"Could not find file 'C:\\Users\\RhinoComputeUser\\AppData\\Local\\Temp\\tmp9382406189.dxf'. (line: 0): component \"C# Script\" (f5cfc116-d7de-46b2-a020-cca5231e9904)","Properties":{"Port":"6003"}}
{"Timestamp":"2024-08-05T16:42:19.1469260+00:00","Level":"Information","MessageTemplate":"::1 - [2024-08-05T16:42:19.1469260+00:00] \"POST /grasshopper HTTP/1.1\" 500 -","RenderedMessage":"::1 - [2024-08-05T16:42:19.1469260+00:00] \"POST /grasshopper HTTP/1.1\" 500 -","Properties":{"Port":"6003"}}

I’ve given ‘Everyone’ full access to this folder: No difference.

I don’t see any other permissions in IIS itself that would inhibit file creation.

I’ve verified that RhinoCompute can Read a file from the file system.

The failure is in the export:

// export to .dxf
Rhino.RhinoApp.RunScript(String.Format("-Export {0} Scheme Fusion EnterEnd", TempFilename), false);

where TempFilename is:

C:\Users\RhinoComputeUser\Documents\tmp0871489346.dxf

I have given ‘Everyone’ Full permissions to this folder.

I am curious if this has to do with the fact you are using RunScript. Have you tried any of the Rhino.FileIO.File* classes for writing the file, like Rhino.FileIO.FileDwg? I understand you are trying to get DXF, but I wonder if the Rhino.FileIO.File* methods show the same or different behavior?

Probably a stupid question, but I can’t find FileDwg. I get

2. Error (CS0234): The type or namespace name 'FileDwg' does not exist in the namespace 'Rhino.FileIO' (are you missing an assembly reference?) (line 96)

I tried:

 new Rhino.FileIO.File3dm().Write(TempFilename, new Rhino.FileIO.File3dmWriteOptions());

That worked! Although that isn’t useful for me.

So maybe it is related to RunScript.

In a separate test, I also added just before the RunScript:

    RhinoDoc.ActiveDoc = doc;

No difference.

So, what now?

RunScript doesn’t work very well in compute. I would use the code driven file I/O technique like Luis suggested. This article shows how to do this for dwg files.

It appears this might be a good solution, but for Rhino8 only.

I’ll try it when we upgrade.