Dynamically Load and Activate a dataset (vuforia and unity) - c#

I want to make a system that I can use to download various targets dynamically from my website without using "Cloud" system.
I also want to save the dataset to .xml and .dat formats which I want to activate from my saving device.
There are a lot of methods and pages to doing that with vuforia and unity, but unfortunately when I test it I receive an error for all of them.
It seems that i have made a mistake in my code or a vuforia class was changed.
For instance please look this link:
https://developer.vuforia.com/library/articles/Solution/Unity-Load-DataSet-from-SD-Card
I got Error: Using Vuforia;
I placed the .xml and .dat files in "Application.persistentDataPath + "/" + "Building1.xml"
i used this Script "DataSetLoadBehavior" that attached "AR Camera and placed my code in it. I got an Error:
NullReferenceException: Object reference not set to an instance of an
object DataSetLoadBehaviour.OnInitialized () (at Assets/Qualcomm
Augmented Reality/Scripts/DataSetLoadBehaviour.cs:49)
DataSetLoadBehaviour.Start () (at Assets/Qualcomm Augmented
Reality/Scripts/DataSetLoadBehaviour.cs:80)
My code is this:
unity 4.2 pro and vuforia 2.8.9 or 3.0.9
/*==============================================================================
Copyright (c) 2010-2014 Qualcomm Connected Experiences, Inc.
All Rights Reserved.
Confidential and Proprietary - Qualcomm Connected Experiences, Inc.
==============================================================================*/
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// This behaviour allows to automatically load and activate one or more DataSet on startup
/// </summary>
public class DataSetLoadBehaviour : DataSetLoadAbstractBehaviour
{
[HideInInspector, SerializeField]
public List<string> mDataSetsToActivate2 = new List<string>();
[SerializeField, HideInInspector]
public List<string> mDataSetsToLoad2 = new List<string>();
protected DataSetLoadBehaviour()
{
}
private void OnDestroy()
{
QCARAbstractBehaviour behaviour = (QCARAbstractBehaviour) UnityEngine.Object.FindObjectOfType(typeof(QCARAbstractBehaviour));
if (behaviour != null)
{
}
}
public void OnInitialized()
{
if (QCARRuntimeUtilities.IsQCAREnabled())
{
foreach (string str in this.mDataSetsToLoad2)
{
if (!DataSet.Exists(str, QCARUnity.StorageType.STORAGE_ABSOLUTE))
{
Debug.LogError("Data set " + str + " does not exist.");
}
else
{
ImageTracker tracker = TrackerManager.Instance.GetTracker<ImageTracker>();
DataSet dataSet = tracker.CreateDataSet();
if (!dataSet.Load(str))
{
Debug.LogError("Failed to load data set " + str + ".");
}
else if (this.mDataSetsToActivate2.Contains(str))
{
tracker.ActivateDataSet(dataSet);
}
}
}
}
}
public void OnTrackablesUpdated()
{
}
private void Start()
{
QCARAbstractBehaviour behaviour = (QCARAbstractBehaviour) UnityEngine.Object.FindObjectOfType(typeof(QCARAbstractBehaviour));
if (behaviour != null)
{
mDataSetsToLoad2.Add(Application.persistentDataPath + "/" + "Building1.xml");
OnInitialized();
}
}
public override void AddOSSpecificExternalDatasetSearchDirs()
{
#if UNITY_ANDROID
if (Application.platform == RuntimePlatform.Android)
{
// Get the external storage directory
AndroidJavaClass jclassEnvironment = new AndroidJavaClass("android.os.Environment");
AndroidJavaObject jobjFile = jclassEnvironment.CallStatic<AndroidJavaObject>("getExternalStorageDirectory");
string externalStorageDirectory = jobjFile.Call<string>("getAbsolutePath");
// Get the package name
AndroidJavaObject jobjActivity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
string packageName = jobjActivity.Call<string>("getPackageName");
// Add some best practice search directories
//
// Assumes just Vufroria datasets extracted to the files directory
AddExternalDatasetSearchDir(externalStorageDirectory + "/Android/data/" + packageName + "/files/");
// Assume entire StreamingAssets dir is extracted here and our datasets are in the "QCAR" directory
AddExternalDatasetSearchDir(externalStorageDirectory + "/Android/data/" + packageName + "/files/QCAR/");
}
#endif //UNITY_ANDROID
}
void Update()
{
}
}

Yeah, Vuforia has has changed a lot.
You will now have to include Vuforia as a header in order for it to work
using Vuforia;
Hope this works.
If it says Vuforia hasn't been found it's probably because you haven't imported the Unitypackage for Vuforia. You can follow these instructions.
Also, I believe you haven't followed the steps to Migrating your Unity Project. The new Vuforia doesn't support ImageTracker anymore, hence you will have to change all instances of ImageTracker to ObjectTracker

Related

Unity WebGL Simple JSON

I am trying to import text in my Unity script with WebGL but it seems like in WebGL, it is not able to find the JSON file itself. I have saved the file in StreamingAssets. How do I fix this?
using System.Collections;
using System.Collections.Generic;
using System.IO;
using SimpleJSON;
using UnityEngine;
using UnityEngine.UI;
public class ReadScene : MonoBehaviour {
public string jsonFile;
JSONNode itemsData;
string path;
public Text sceneText;
// Start is called before the first frame update
void Start () {
path = Path.Combine (Application.streamingAssetsPath, "Settings.json");
if (File.Exists (path)) {
jsonFile = File.ReadAllText (path);
DeserializePages ();
} else {
sceneText.gameObject.SetActive(false);
}
}
public void DeserializePages () {
itemsData = JSON.Parse (jsonFile);
var parseJSON = JSON.Parse (jsonFile);
sceneText.text = parseJSON["Scene01"].Value;
}
}
Also I have tried to change the folder location to "Resources" and still facing the same issue.
From Application.streamingAssetsPath
It is not possible to access the StreamingAssets folder on WebGL and Android platforms. No file access is available on WebGL. Android uses a compressed .apk file. These platforms return a URL. Use the UnityWebRequest class to access the Assets.
tbh I don't even know right now whether this No file access is available on WebGL is just misleading or indeed means there is no way to access StreamingAssets. I will assume it is just formulated badly and just means you can not access any file/directory directly but you can still go through UnityWebRequest.Get like
IEnumerator Start ()
{
sceneText.gameObject.SetActive(false);
path = Path.Combine(Application.streamingAssetsPath, "Settings.json");
using(var request = UnityWebRequest.Get(path))
{
yield return request.SendWebRequest();
if(!request.result == UnityWebRequest.Result.Success)
{
Debug.LogError("Error: " + result.error);
yield break;
}
jsonFile = webRequest.downloadHandler.text;
DeserializePages();
}
}
private void DeserializePages()
{
itemsData = JSON.Parse (jsonFile);
sceneText.text = itemsData["Scene01"].Value;
sceneText.gameObject.SetActive(true);
}

Accessing Unity package version at runtime

I'm working on a custom analytics system (which is a custom package) and it would be super helpful to know which version of this package is integrated in the unity app when I'm looking into the data.
Is there any quick solution to retrieve the pacakge version at runtime ?
As said what you could do is using a ScriptableObject just for storing the version for the runtime later.
You could e.g. do it like
// See https://docs.unity3d.com/Manual/PlatformDependentCompilation.html
#if UNITY_EDITOR
using UnityEditor;
#endif;
public class PackageVersion : ScriptableObject
{
// See https://docs.unity3d.com/ScriptReference/HideInInspector.html
[HideInInspector] public string Version;
#if UNITY_EDITOR
// Called automatically after open Unity and each recompilation
// See https://docs.unity3d.com/ScriptReference/InitializeOnLoadMethodAttribute.html
[InitializeOnLoadMethod]
private static void Init()
{
// See https://docs.unity3d.com/ScriptReference/Compilation.CompilationPipeline-compilationFinished.html
// Removing the callback before adding it makes sure it is only added once at a time
CompilationPipeline.compilationFinished -= OnCompilationFinished;
CompilationPipeline.compilationFinished += OnCompilationFinished;
}
private static void OnCompilationFinished()
{
// First get the path of the Package
// This is quite easy since this script itself belongs to your package's assemblies
var assembly = typeof(PackageVersion).Assembly;
// See https://docs.unity3d.com/ScriptReference/PackageManager.PackageInfo.FindForAssembly.html
var packageInfo = PackageManager.PackageInfo.FindForAssembly();
// Finally we have access to the version!
var version = packageInfo.version;
// Now to the ScriptableObject instance
// Try to find the first instance
// See https://docs.unity3d.com/ScriptReference/AssetDatabase.FindAssets.html
// See https://docs.unity3d.com/ScriptReference/PackageManager.PackageInfo-assetPath.html
var guid = AssetDataBase.FindAssets($"t:{nameof(PackageVersion)}", packageInfo.assetPath). FirstOrDefault();
PackageVersion asset;
if(!string.isNullOrWhiteSpace(guid))
{
// See https://docs.unity3d.com/ScriptReference/AssetDatabase.GUIDToAssetPath.html
var path = AssetDatabase.GUIDToAssetPath(guid);
// See https://docs.unity3d.com/ScriptReference/AssetDatabase.LoadAssetAtPath.html
asset = AssetDatabase.LoadAssetAtPath<PackageVersion>(path);
}
else
{
// None found -> create a new one
asset = ScriptableObject.CreateInstance<PackageVersion>();
asset.name = nameof(PackageVersion);
// make it non editable via the Inspector
// See https://docs.unity3d.com/ScriptReference/HideFlags.NotEditable.html
asset.hideFlags = HideFlags.NotEditable;
// Store the asset as actually asset
// See https://docs.unity3d.com/ScriptReference/AssetDatabase.CreateAsset.html
AssetDataBase.CreateAsset(asset, $"{packageInfo.assetPath}/{nameof(PackageVersion)}");
}
asset.Version = version;
// See https://docs.unity3d.com/ScriptReference/EditorUtility.SetDirty.html
EditorUtility.SetDirty(asset);
}
#endif
}
Now wherever needed you can just reference that PackageVersion object and access the version on runtime.
Note: Typed on smartphone but I hope the idea gets clear

Unity Add Default Namespace to Script Template?

I just found Unity's script template for C# scripts. To get the script name you write #SCRIPTNAME# so it looks like this:
using UnityEngine;
using System.Collections;
public class #SCRIPTNAME# : MonoBehaviour
{
void Start ()
{
}
void Update ()
{
}
}
Then it would create the script with the right name, but is there something like #FOLDERNAME# so that I can put it in the right namespace directly when creating the script?
There is no built-in template variables like #FOLDERNAME#.
According to this post, there are only 3 magic variables.
"#NAME#"
"#SCRIPTNAME#"
"#SCRIPTNAME_LOWER#"
But you can always hook into the creation process of a script and append the namespace yourself using AssetModificationProcessor.
Here is an example that adds some custom data to the created script.
//Assets/Editor/KeywordReplace.cs
using UnityEngine;
using UnityEditor;
using System.Collections;
public class KeywordReplace : UnityEditor.AssetModificationProcessor
{
public static void OnWillCreateAsset ( string path )
{
path = path.Replace( ".meta", "" );
int index = path.LastIndexOf( "." );
string file = path.Substring( index );
if ( file != ".cs" && file != ".js" && file != ".boo" ) return;
index = Application.dataPath.LastIndexOf( "Assets" );
path = Application.dataPath.Substring( 0, index ) + path;
file = System.IO.File.ReadAllText( path );
file = file.Replace( "#CREATIONDATE#", System.DateTime.Now + "" );
file = file.Replace( "#PROJECTNAME#", PlayerSettings.productName );
file = file.Replace( "#SMARTDEVELOPERS#", PlayerSettings.companyName );
System.IO.File.WriteAllText( path, file );
AssetDatabase.Refresh();
}
}
I know it's and old question but in newer versions of Unity you can define a root namespace to be used in the project.
You can define the namespace in Edit > Project Settings > Editor > Root Namespace
Doing this will add the defined namespace on newly created scripts.
Using zwcloud's answer and some other resources I was able to generate a namespace on my script files:
First step, navigate:
Unity's default templates can be found under your Unity installation's directory in Editor\Data\Resources\ScriptTemplates for Windows and /Contents/Resources/ScriptTemplates for OSX.
And open the file 81-C# Script-NewBehaviourScript.cs.txt
And make the following change:
namespace #NAMESPACE# {
At the top and
}
At the bottom. Indent the rest so that the whitespace is as desired. Don't save this just yet. If you wish, you can make other changes to the template, such as removing the default comments, making Update() and Start() private, or even removing them entirely.
Again, do not save this file yet or Unity will throw an error on the next step. If you saved, just hit ctrl-Z to undo and then resave, then ctrl-Y to re-apply the changes.
Now create a new script inside an Editor folder inside your Unity Assets directory and call it AddNameSpace. Replace the contents with this:
using UnityEngine;
using UnityEditor;
public class AddNameSpace : UnityEditor.AssetModificationProcessor {
public static void OnWillCreateAsset(string path) {
path = path.Replace(".meta", "");
int index = path.LastIndexOf(".");
if(index < 0) return;
string file = path.Substring(index);
if(file != ".cs" && file != ".js" && file != ".boo") return;
index = Application.dataPath.LastIndexOf("Assets");
path = Application.dataPath.Substring(0, index) + path;
file = System.IO.File.ReadAllText(path);
string lastPart = path.Substring(path.IndexOf("Assets"));
string _namespace = lastPart.Substring(0, lastPart.LastIndexOf('/'));
_namespace = _namespace.Replace('/', '.');
file = file.Replace("#NAMESPACE#", _namespace);
System.IO.File.WriteAllText(path, file);
AssetDatabase.Refresh();
}
}
Save this script as well as saving the changes to 81-C# Script-NewBehaviourScript.cs.txt
And you're done! You can test it by creating a new C# script inside any series of folders inside Assets and it will generate the new namespace definition we created.
I'm well aware that this question has been answered by the awesome people (zwcloud, Darco18 and Alexey).
However, since my namespace organization follows the folder structure in the project, I've put together in a jiffy some minor modification to the code, and I'm sharing it here in case someone needs it and has the same organizational methodology which I'm following.
Please keep in mind that you need to set the root namespace in your project settings in the C# project generation section.
EDIT: I've adjusted the code a bit work with placement in the root folders of Scripts, Editor, etc..
public class NamespaceResolver : UnityEditor.AssetModificationProcessor
{
public static void OnWillCreateAsset(string metaFilePath)
{
var fileName = Path.GetFileNameWithoutExtension(metaFilePath);
if (!fileName.EndsWith(".cs"))
return;
var actualFile = $"{Path.GetDirectoryName(metaFilePath)}\\{fileName}";
var segmentedPath = $"{Path.GetDirectoryName(metaFilePath)}".Split(new[] { '\\' }, StringSplitOptions.None);
var generatedNamespace = "";
var finalNamespace = "";
// In case of placing the class at the root of a folder such as (Editor, Scripts, etc...)
if (segmentedPath.Length <= 2)
finalNamespace = EditorSettings.projectGenerationRootNamespace;
else
{
// Skipping the Assets folder and a single subfolder (i.e. Scripts, Editor, Plugins, etc...)
for (var i = 2; i < segmentedPath.Length; i++)
{
generatedNamespace +=
i == segmentedPath.Length - 1
? segmentedPath[i]
: segmentedPath[i] + "."; // Don't add '.' at the end of the namespace
}
finalNamespace = EditorSettings.projectGenerationRootNamespace + "." + generatedNamespace;
}
var content = File.ReadAllText(actualFile);
var newContent = content.Replace("#NAMESPACE#", finalNamespace);
if (content != newContent)
{
File.WriteAllText(actualFile, newContent);
AssetDatabase.Refresh();
}
}
}
OK, so this question was already answered by two wonderful people, zwcloud and Draco18s, and their solution works, I'm just showing another version of the same code that, I hope, will be a little more clear in terms of what exactly happening.
Quick notes:
Yes, in this method we are getting not the actual file path,
but the path of its meta file as a parameter
No, you can not use AssetModificationProcessor without UnityEditor prefix, it is deprecated
OnWillCreateAsset method is not shown via Ctrl+Shift+M, 'override' typing or base class metadata
_
using UnityEditor;
using System.IO;
public class ScriptTemplateKeywordReplacer : UnityEditor.AssetModificationProcessor
{
//If there would be more than one keyword to replace, add a Dictionary
public static void OnWillCreateAsset(string metaFilePath)
{
string fileName = Path.GetFileNameWithoutExtension(metaFilePath);
if (!fileName.EndsWith(".cs"))
return;
string actualFilePath = $"{Path.GetDirectoryName(metaFilePath)}{Path.DirectorySeparatorChar}{fileName}";
string content = File.ReadAllText(actualFilePath);
string newcontent = content.Replace("#PROJECTNAME#", PlayerSettings.productName);
if (content != newcontent)
{
File.WriteAllText(actualFilePath, newcontent);
AssetDatabase.Refresh();
}
}
}
And this is the contents of my file c:\Program Files\Unity\Editor\Data\Resources\ScriptTemplates\81-C# Script-NewBehaviourScript.cs.txt
using UnityEngine;
namespace #PROJECTNAME#
{
public class #SCRIPTNAME# : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
}
Here is my solution, in my case Unity added the root namespace right before OnWillCreateAsset(), so I had to replace the root namespace in the script with the one I want.
Here is the code
public class AddNameSpace : UnityEditor.AssetModificationProcessor
{
public static void OnWillCreateAsset(string path)
{
var rootNamespace =
string.IsNullOrWhiteSpace(EditorSettings.projectGenerationRootNamespace) ?
string.Empty : $"{EditorSettings.projectGenerationRootNamespace}";
path = path.Replace(".meta", "");
int index = path.LastIndexOf(".");
if (index < 0)
return;
string file = path.Substring(index);
if (file != ".cs" && file != ".js" && file != ".boo")
return;
string formattedNamespace = GetNamespace(path, rootNamespace);
file = System.IO.File.ReadAllText(path);
if (file.Contains(formattedNamespace))
return;
file = file.Replace(rootNamespace, formattedNamespace);
System.IO.File.WriteAllText(path, file);
AssetDatabase.Refresh();
}
private static string GetNamespace(string filePath, string rootNamespace)
{
filePath = filePath.Replace("Assets/Scripts/", "/")
.Replace('/', '.')
.Replace(' '.ToString(), string.Empty);
var splitPath = filePath.Split('.');
string pathWithoutFileName = string.Empty;
for (int i = 1; i < splitPath.Length - 2; i++)
{
if (i == splitPath.Length - 3)
{
pathWithoutFileName += splitPath[i];
}
else
{
pathWithoutFileName += splitPath[i] + '.';
}
}
return $"{rootNamespace}.{pathWithoutFileName}";
}
}

Unity Cloud Build: post export method

Problem:
I can't seem to figure out the right signature for Unity cloud build's post export method. According to the documentation:
The fully-qualified name of a public static method you want us to call
after we finish the Unity build process (but before Xcode). For
example: ClassName.CoolMethod or NameSpace.ClassName.CoolMethod. No
trailing parenthesis, and it can't have the same name as your
Pre-Export method! This method must accept a string parameter, which
will receive the path to the exported Unity player (or Xcode project
in the case of iOS).
Here is my code:
public static void OnPostprocessDevBuildIOS(string ExportPath)
{
var projPath = ExportPath + "/Unity-iPhone.xcodeproj/project.pbxproj";
var proj = new PBXProject();
var nativeTarget =
proj.TargetGuidByName(PBXProject.GetUnityTargetName());
var testTarget =
proj.TargetGuidByName(PBXProject.GetUnityTestTargetName());
string[] buildTargets = {nativeTarget, testTarget};
proj.ReadFromString(File.ReadAllText(projPath));
proj.SetBuildProperty(buildTargets, "ENABLE_BITCODE", "NO");
File.WriteAllText(projPath, proj.WriteToString());
}
and here is the error:
I've tried multiple test method signatures and can't seem to get anything to work. I've even tried just a method that logs out the path.
Additional Information:
Unity Version: 5.3.1f
Unity Cloud Build: 5.3.1f
Target: iOS 8.0+
Also, my cloud build settings script is located in the editor folder as required.
Ok so I got the the bitCode disabling post process to work with the following code, but only when I build manually. When I build from cloud build, with no error the app freezes at the splash screen. When I build from my local machine, the app runs just fine.
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string nativeTarget = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
string testTarget = proj.TargetGuidByName(PBXProject.GetUnityTestTargetName());
string[] buildTargets = new string[]{nativeTarget, testTarget};
proj.SetBuildProperty(buildTargets, "ENABLE_BITCODE", "NO");
File.WriteAllText(projPath, proj.WriteToString());
}
}
I too had the same issue "splash screen stuck" right after launch....
I solved this issue. Please use the below code.
Tested in Unity 5.4.1p2 and Xcode 7.3.
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using UnityEditor.Callbacks;
#if UNITY_IOS
using UnityEditor.iOS.Xcode;
#endif
public class Postprocessor : AssetPostprocessor
{
#if UNITY_IOS
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string target = proj.TargetGuidByName("Unity-iPhone");
proj.SetBuildProperty(target, "ENABLE_BITCODE", "false");
File.WriteAllText(projPath, proj.WriteToString());
// Add url schema to plist file
string plistPath = path + "/Info.plist";
PlistDocument plist = new PlistDocument();
plist.ReadFromString(File.ReadAllText(plistPath));
// Get root
PlistElementDict rootDict = plist.root;
rootDict.SetBoolean("UIRequiresFullScreen",true);
plist.WriteToFile(plistPath);
}
}
#endif
}
In fact OnPostprocessBuild is always called, so you don't have to put anything in post export method field, which is designed for more specific methods.

How can I open AutoCAD 2015 through the .NET API

I've been browsing for a good hour and have yet to find something that would help with this. I'm working on opening AutoCAD from the .NET API in VS2013 using C#, but for some reason, I can never get AutoCAD to actually launch. I'm using the following code:
using System;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
namespace IOAutoCADHandler
{
public static class ACADDocumentManagement
{
[CommandMethod("ConnectToAcad")]
public static void ConnectToAcad()
{
AcadApplication acAppComObj = null;
// no version number so it will run with any version
const string strProgId = "AutoCAD.Application";
// Get a running instance of AutoCAD
try
{
acAppComObj = (AcadApplication)Marshal.GetActiveObject(strProgId);
}
catch // An error occurs if no instance is running
{
try
{
// Create a new instance of AutoCAD
acAppComObj = (AcadApplication)Activator.CreateInstance(Type.GetTypeFromProgID(strProgId), true);
}
catch //// STOPS HERE
{
// If an instance of AutoCAD is not created then message and exit
// NOTE: always shows this box and never opens AutoCAD
System.Windows.Forms.MessageBox.Show("Instance of 'AutoCAD.Application'" +
" could not be created.");
return;
}
}
// Display the application and return the name and version
acAppComObj.Visible = true;
System.Windows.Forms.MessageBox.Show("Now running " + acAppComObj.Name +
" version " + acAppComObj.Version);
// Get the active document
AcadDocument acDocComObj;
acDocComObj = acAppComObj.ActiveDocument;
// Optionally, load your assembly and start your command or if your assembly
// is demandloaded, simply start the command of your in-process assembly.
acDocComObj.SendCommand("(command " + (char)34 + "NETLOAD" + (char)34 + " " +
(char)34 + #"C:\Users\Administrator\Documents\All Code\main-libraries\IOAutoCADHandler\bin\Debug\IOAutoCADHandler.dll" + (char)34 + ") ");
acDocComObj.SendCommand("DRAWCOMPONENT");
}
}
Unfortunately, it always stops at the nested catch statement and always displays the popup box without opening AutoCAD. Any suggestions on how to at least make AutoCAD open for me?
EDIT: Error message
The issue is you're coding (correctly) to the AutoCAD interop interface. I recommend against that (due to potential version changes).
The other issue is that the documentation for AutoCAD plugins using the newer .net api is for plugins when AutoCAD is already running.
Final issue could be that the program Id of AutCAD is a mystery. I have resorted to making that a configurable setting, but default to "AutoCAD.Application", which will take the currently registered AutoCAD.Application on the production machine. If there are multiple versions installed on the machine and you want to be specific, then you could append the version number (which you'll need to research) to the ProgID like: "AutoCAD.Application.19", or "AutoCAD.Application.20" for 2015.
For the first issue, one technique is to use dynamics for the autoCad objects, particularly for creating instances. I have used the ObjectARX api for creating my application in a dummy project, and then switching to dynamics when I'm happy with the properties and method names.
In a standalone .Net application that starts AutoCAD you could use something like:
// I comment these out in production
//using Autodesk.AutoCAD.Interop;
//using Autodesk.AutoCAD.Interop.Common;
//...
//private static AcadApplication _application;
private static dynamic _application;
static string _autocadClassId = "AutoCAD.Application";
private static void GetAutoCAD()
{
_application = Marshal.GetActiveObject(_autocadClassId);
}
private static void StartAutoCad()
{
var t = Type.GetTypeFromProgID(_autocadClassId, true);
// Create a new instance Autocad.
var obj = Activator.CreateInstance(t, true);
// No need for casting with dynamics
_application = obj;
}
public static void EnsureAutoCadIsRunning(string classId)
{
if (!string.IsNullOrEmpty(classId) && classId != _autocadClassId)
_autocadClassId = classId;
Log.Activity("Loading Autocad: {0}", _autocadClassId);
if (_application == null)
{
try
{
GetAutoCAD();
}
catch (COMException ex)
{
try
{
StartAutoCad();
}
catch (Exception e2x)
{
Log.Error(e2x);
ThrowComException(ex);
}
}
catch (Exception ex)
{
ThrowComException(ex);
}
}
}
When there are several versions of AutoCAD installed on a computer, creating an instance with the ProgID "AutoCAD.Application" will run the latest version started on this computer by the current user. If the version of the Interop assemblies used does not match the version that is starting, you'll get a System.InvalidCastException with an HRESULT 0x80004002 (E_NOINTERFACE).
In your specific case, the {070AA05D-DFC1-4E64-8379-432269B48B07} IID in your error message is the GUID for the AcadApplicationinterface in R19 64-bit (AutoCAD 2013 & 2014). So there is an AutoCAD 2013 or 2014 that is starting, and you cannot cast this COM object to a 2015 type because 2015 is R20 (not binary compatible).
To avoid that, you can add a specific version to your ProgID (like "AutoCAD.Application.20" for AutoCAD 2015 (R20.0) to 2016 (R20.1)) to start the version matching your Interop assemblies or you can use late binding (eg. remove your references to Autodesk.AutoCAD.Interop* and use the dynamic keyword instead of the AutoCAD types).
In the last case, you will lost autocompletion, but your program will work with all the versions of AutoCAD.
Check also 32-bit vs 64-bit because TypeLib/Interop assemblies are not the same.
I open the application in a much straight-forward way. First, be sure to reference the correct type library. The one I am using is AutoCAD 2014 Type Library, located at:
c:\program files\common files\autodesk shared\acax19enu.tlb
To initialize the application:
using AutoCAD;
namespace test
{
class Program
{
static void Main(string[] args)
{
AutoCAD.AcadApplication app;
app = new AcadApplication();
app.Visible = true;
Console.Read();
}
}
}
Try this:
"sourcefile" is the original file
"newfile" is the new file
[CommandMethod("ModifyAndSaveas", CommandFlags.Redraw | CommandFlags.Session)]
public void ModifyAndSaveAs()
{
Document acDoc = Application.DocumentManager.Open(sourcefile);
Database acDB = acDoc.Database;
Transaction AcTran = acDoc.Database.TransactionManager.StartTransaction();
using (DocumentLock acLckDoc = acDoc.LockDocument())
{
using (AcTran)
{
BlockTable acBLT = (BlockTable)AcTran.GetObject(acDB.BlockTableId, OpenMode.ForRead);
BlockTableRecord acBLTR = (BlockTableRecord)AcTran.GetObject(acBLT[BlockTableRecord.ModelSpace], OpenMode.ForRead);
var editor = acDoc.Editor;
var SelectionSet = editor.SelectAll().Value;
foreach (ObjectId id in SelectionSet.GetObjectIds())
{
Entity ent = AcTran.GetObject(id, OpenMode.ForRead) as Entity;
//modify entities
}
AcTran.Commit();
}
}
acDB.SaveAs(newfile, DwgVersion.AC1021);
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Tekkit
{
class Program
{
static void Main(string[] args)
{
//make sure to add last 2 using statements
ProcessStartInfo start = new ProcessStartInfo("calc.exe");
Process.Start(start);//starts the process
}
}
}

Categories