IsCentral, IsLocal

IsCentral, IsLocal

aclarke
Advocate Advocate
1,407 Views
8 Replies
Message 1 of 9

IsCentral, IsLocal

aclarke
Advocate
Advocate

Hi all,

I am struggling with the IsCentral, IsLocal properties

I have read thru some posts and even thought I had it working but no.

 

I understand there is a file path  vs. server path and I believe I am going after the network file path, not a Revit Server Path.

 

public static bool IsLocal(Document doc)
        {
            BasicFileInfo basicFileInfo = BasicFileInfo.Extract(doc.PathName);

            if (basicFileInfo.IsLocal)
                return true;
            else
                return false;

        }


public static bool IsCentral(Document doc)
        {
            BasicFileInfo basicFileInfo = BasicFileInfo.Extract(doc.PathName);

            if (basicFileInfo.IsCentral)
                return true;
            else
                return false;

        }

 

Both return the opposite of what I expect.

If I compare doc.GetWorksharingCentralModelPath

with

doc.PathName, when in a local, they are different, in a central they are the same on home computer

 

but on work computer

I can't compare 'easily' one path with the other because of UNC,

doc.PathName is not UNC and doc.GetWorksharingCentralModelPath is UNC

 

One link I have referenced but did not work for me is this one.

https://forums.autodesk.com/t5/revit-api-forum/basicfileinfo-iscreatedlocal-property-outputting-unex... 

 

 

What is the best way to tell if the file I just opened is a central or a local model?

 

thanks for ant assistance

 

0 Likes
Accepted solutions (1)
1,408 Views
8 Replies
Replies (8)
Message 2 of 9

RPTHOMAS108
Mentor
Mentor

For file based work-sharing the issue with mapped drive vs network drive UNC representation can probably be resolved by converting the mapped drive to the UNC and then comparing that rather than the other way around.

 

pinvoke.net: WNetGetUniversalName (advapi32)

 

The problem with mapped network drive is you can't get the local drive letter really since same location could be mapped to many different drives but you can find the opposite i.e. the actual network location.

 

To establish if it is in UNC form to start with you can use Uri.IsUnc for ModelPathUtils.ConvertModelPathToUserVisiblePath i.e. if it is not on a network it will not have a network location so central file path could exist in other local form.

0 Likes
Message 3 of 9

aclarke
Advocate
Advocate

 @RPThomas, thank you.

 

Is this the best way?  The only way?

Why does doc.PathName return a mapped drive and

BasicFileInfo centralPathName = BasicFileInfo.Extract(
ModelPathUtils.ConvertModelPathToUserVisiblePath(doc.GetWorksharingCentralModelPath()));

return a UNC drive?

 

Why does IsCentral or IsLocal, not return if it is a central or local correctly?, what do I not understand?

 

doc.IsDetached seems to work fine

I wish it was as simple as -> doc.IsCentral or doc.IsLocal

 

this my testing code and it all works except for when the PathName is mapped and the CentralPath is UNC.

 

[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class Cmd_IsCentral : IExternalCommand
{
    public Result Execute(ExternalCommandData commandData, ref string 
    message, ElementSet elements)
            {
                UIApplication uiapp = commandData.Application;
                UIDocument uidoc = uiapp.ActiveUIDocument;
                Application app = uiapp.Application;
                Document doc = uidoc.Document;

                thisApp = app;
                thisDoc = doc;

                if (doc.IsWorkshared)
                {
                    if (doc.IsDetached)
                    {
                        TaskDialog.Show("task", "Is Detached");
                    }
                    else
                    {

                        BasicFileInfo centralPathName = BasicFileInfo.Extract(
                            ModelPathUtils.ConvertModelPathToUserVisiblePath(doc.GetWorksharingCentralModelPath()));

                        if (doc.PathName == centralPathName.CentralPath)
                        {
                            TaskDialog.Show("task", "Is Central");
                        }
                        else
                        {
                            TaskDialog.Show("task", "Is Local");

                        }

                    }

                }

                return Result.Succeeded;
            }
        }

If someone can show me some code on how they handle this, I would be greatful.

 

Thanks

 

0 Likes
Message 4 of 9

aclarke
Advocate
Advocate

Ok, the IsCentral and IsLocal do work, except when creating a new local from the central model.

Before saving, it reads as a central model

After saving, it reads as a local model

 

Is a Revit model truely not a local until after the first save from an opened document?

When opening Revit and prompted to overwrite the local file, an overwrite assumes a save has accured, is this file not yet a local file until the next save from the opened document?

 

How can I easily tell upon model opened and before the first save, if a model is central or local?

 

I am looking at the UNC to Mapped Path idea, this seems a bit complex for a simple task.  Is this the proper way to discover Central vs Local?

 

I am thinking, can access the 'local save' button to see if it is Enabled or not, to tell if I am in a central file if not Enabled.

 

My fingers are crossed for an easy solution, or an explination.  🙂

 

Thanks

 

 

 

 

0 Likes
Message 5 of 9

aclarke
Advocate
Advocate

my current solution.  I would like to find a better solution but...

 

if (doc.IsWorkshared)
{
    if (doc.IsDetached)
    {
        TaskDialog.Show("task", "Is Detached");
    }
    else
    {
        string unc1 = @"\\UNCPath1\DriveLetter\".ToLower();
        string unc2 = @"\\UNCPath2\DriveLetter\".ToLower();
        string mapLetter = @"F:\".ToLower();

        ModelPath centralModelPath = doc.GetWorksharingCentralModelPath();
        string uncCentralPath = ModelPathUtils.ConvertModelPathToUserVisiblePath(centralModelPath).ToLower();
        string mapCentralPath = string.Empty;

        if (uncCentralPath.StartsWith(unc1))
        {
            mapCentralPath = uncCentralPath.Replace(unc1, mapLetter);
        }

        if (uncCentralPath.StartsWith(unc2))
        {
            mapCentralPath = uncCentralPath.Replace(unc2, mapLetter);
        }

        if (string.Equals(doc.PathName.ToLower(), mapCentralPath))
        {
            TaskDialog.Show("task", "Is Central");
        }
        else
        {
            TaskDialog.Show("task", "Is Local");
        }

    }

}

 

 

0 Likes
Message 6 of 9

RPTHOMAS108
Mentor
Mentor

Can't speak for BasicFileInfo, I assume the properties work similar to the Document properties. Sounds like .IsLocal should be the perfect answer. Older generation Revit users would copy the central to another location manually and then open that where they get a notification that the file will become local of the central. Since then there is now the tick button option to open as local which automatically copies the file to a local location. So perhaps basic file info can be updated at the time for one form of creating a local but not the other. Would I as a Revit user opt for the tick box (open as local overwriting an existing)? No. When you do this any changes that you forget to save back to central with the old local (last it was opened) are lost. By opening old local and synchronising  you get a second chance. Also if Revit crashes you may have saved local just prior but that is also lost (although recovery files often work quite well if you get that option).

 

When a project is workshared i.e. has Worksets the IsDetached property is only transitional in nature (being true immediately after detaching and just before saving). As soon as the detached model is first saved it is it's own central file and you can see this noted in the synchronise to central dialogue. Prior to saving a detached model the local path is empty since the project has not been saved. So any method that compares the path of the central against file path of document should be able to determine it's status. It's unfortunate that one path is UNC whilst the other is not.

0 Likes
Message 7 of 9

aclarke
Advocate
Advocate

Hi Thomas,

Thanks for discussing this with me.

I understand your work flow, but we don't ever copy a central to a new location, nor do we use the splash screen due to server mirroring.  At least this is how my last IT guy explained it to me that any given day the main server may change while other are backing up.  I took that as fact but I am really not sure, I just know sometimes opening from the splash screen then saving will not communicate to the central, sometimes, because the UNC path changed or something.

 

We have users double clicking the central and opening it, then working and others can't sync.  So to keep it simple and standard, we go to the central and create new local and override.  If someone forgets to sync to central before closing, well oops.  also, we want all locals saved so the central doesn't become 'unreconcilable' 

 

It is very unfortunate that the central path and the file path are different, UNC and Mapped,  I am sure there is a reason as I am almost sure there is a simpler way.

 

some how the software knows if it is central, detached, or local because ribbon buttons change, like the local save button is greyed out when in central.  From what I can find, it seems comparing file paths is the common answer, or IsLocal but this doesn't work until after the first save.

 

I am wondering how the 'factory' handles this, I am hoping someone chime in.

 

Thanks

 

--Edit--

I also just found out that we use two active UNC for the same drive letter, containing the same file structure and files.  That's why I have to compare unc1 and unc2.

0 Likes
Message 8 of 9

RPTHOMAS108
Mentor
Mentor
Accepted solution

Actually to correct myself; when is detached the path is not null it is in the form Central_Local_Detached.rvt. So would use .IsDetached instead of path.

 

Not sure you'll get the answer you are looking for. Probably something mundane to do with Windows/OS file paths being ok for local files but UNC being required for remote central files where Revit Sever is used i.e. generic implementation for both file based and Revit sever based central files. The reason the status of BasicFileInfo is wrong before saving is probably due to reading the information in file header not yet udated (it was a central last time it was saved).

 

I've tidied up the code from the pinvoke link, some of it was a bit out of date and some not required. I've also included the various code paths for Detached/Central/Local, in second bit below. Seems to work for all permutations of Local/Central/UNC/Not UNC combinations.

 

 

 

public class UniversalPathConverter
	{
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
		private class UNIVERSAL_NAME_INFOW
		{
			//only string reqd now and global character set sttribute used so MarshalAs not reqd.
			public string UniversalName;
		}

		[DllImport("mpr.dll", EntryPoint = "WNetGetUniversalName", CharSet = CharSet.Unicode, SetLastError = false)]
		private static extern int GetUName(string Path, INFO_LEVEL outName, IntPtr bObj, ref int bSize);

		private static int bufSize = 1;

		private static IntPtr lpBuffer;
		private enum INFO_LEVEL : int
		{
			UNIVERSAL_NAME_INFO_LEVEL = 1
			//REMOTE_NAME_INFO_LEVEL = 2 (not used)
		}
		const int ERROR_MORE_DATA = 234;
		const int ERROR_NOT_CONNECTED = 2250;
		public static string GetUniversalName(string LocalPath)
		{

			bufSize = 1;
			lpBuffer = Marshal.AllocHGlobal(bufSize);

			UNIVERSAL_NAME_INFOW UnName = new UNIVERSAL_NAME_INFOW();
			string Out = "";

			int Res = GetUName(LocalPath, INFO_LEVEL.UNIVERSAL_NAME_INFO_LEVEL, lpBuffer, ref bufSize);

			if (Res == ERROR_MORE_DATA)
			{
				//Get actual buffer size required with first call
				lpBuffer = Marshal.ReAllocHGlobal(lpBuffer, new IntPtr(bufSize));
			}
			else if (Res == ERROR_NOT_CONNECTED)
			{
				//e.g. c:\temp not a network path or a path at all (return original)
				Out = LocalPath;
				goto end1;
			}
			else
			{
				goto end1;
			}

			if (GetUName(LocalPath, INFO_LEVEL.UNIVERSAL_NAME_INFO_LEVEL, lpBuffer, ref bufSize) == 0)
			{
				Marshal.PtrToStructure(lpBuffer, UnName);
				Out = UnName.UniversalName;
			}
		end1:
			Marshal.FreeHGlobal(lpBuffer);
			return Out;
		}
	}

 

 

 

 

 

Private Function TObj148(ByVal commandData As Autodesk.Revit.UI.ExternalCommandData,
ByRef message As String, ByVal elements As Autodesk.Revit.DB.ElementSet) As Result

        Dim UIDoc As UIDocument = commandData.Application.ActiveUIDocument
        If UIDoc Is Nothing Then Return Result.Cancelled Else
        Dim IntDoc As Document = UIDoc.Document

        Dim Res As String = ""
        If IntDoc.IsWorkshared Then
            If IntDoc.IsDetached Then
                Res = "Detached"
            Else
                Dim S As String() = New String(1) {}
                S(0) = ModelPathUtils.ConvertModelPathToUserVisiblePath(IntDoc.GetWorksharingCentralModelPath)
                S(1) = IntDoc.PathName
                For i = 0 To 1
                    If String.IsNullOrEmpty(S(i)) = False Then 'can't use null for Uri
                        Dim Uri As New Uri(S(i))
                        If Uri.IsUnc = False Then 'if in form X:\...\... then try find UNC.
                            S(i) = UniversalPathConverter.GetUniversalName(S(i))
                        End If
                    End If
                    S(i) = S(i).ToLower
                Next
                If S(0) = S(1) Then
                    Res = "Central"
                Else
                    Res = "Local"
                End If
            End If
        Else
            Res = "Not workshared"
        End If

        TaskDialog.Show("State", Res)

        Return Result.Succeeded
    End Function

 

 

 

Message 9 of 9

aclarke
Advocate
Advocate

Hi Thomas.

 

Thank you for sharing your code and knowledge.  You have given me a path to follow and I appreciate that.

 

thanks

0 Likes