bool FVSAccessorModule::OpenVisualStudioFileAtLineInternal(const FString& FullPath, int32 LineNumber, int32 ColumnNumber)
{
	// Check that the file actually exists first
	if (!FPaths::FileExists(FullPath))
	{
		OpenFileFailed.Broadcast(FullPath);
		return false;
	}

	// Initialize the com library, if not already by this thread
	if (!FWindowsPlatformMisc::CoInitialize())
	{
		UE_LOG(LogVSAccessor, Error, TEXT( "ERROR - Could not initialize COM library!" ));
		return false;
	}
	
	bool bDefer = false, bSuccess = false;
	CComPtr<EnvDTE::_DTE> DTE;
	if (AccessVisualStudio(DTE, SolutionPath, Locations))
	{
		// Set Focus on Visual Studio
		CComPtr<EnvDTE::Window> MainWindow;
		if (SUCCEEDED(DTE->get_MainWindow(&MainWindow)) &&
			SUCCEEDED(MainWindow->Activate()))
		{
			// Get ItemOperations
			CComPtr<EnvDTE::ItemOperations> ItemOperations;
			if (SUCCEEDED(DTE->get_ItemOperations(&ItemOperations)))
			{
				// Open File
				auto ANSIPath = StringCast<ANSICHAR>(*FullPath);
				CComBSTR COMStrFileName(ANSIPath.Get());
				CComBSTR COMStrKind(EnvDTE::vsViewKindTextView);
				CComPtr<EnvDTE::Window> Window;
				if (SUCCEEDED(ItemOperations->OpenFile(COMStrFileName, COMStrKind, &Window)))
				{
					// Scroll to Line Number
					CComPtr<EnvDTE::Document> Document;
					CComPtr<IDispatch> SelectionDispatch;
					CComPtr<EnvDTE::TextSelection> Selection;
					if (SUCCEEDED(DTE->get_ActiveDocument(&Document)) &&
						SUCCEEDED(Document->get_Selection(&SelectionDispatch)) &&
						SUCCEEDED(SelectionDispatch->QueryInterface(&Selection)) &&
						SUCCEEDED(Selection->GotoLine(LineNumber, true)))
					{
						if (ColumnNumber > 0 &&
							SUCCEEDED(Selection->MoveToLineAndOffset(LineNumber, ColumnNumber, false)))
						{
							bSuccess = true;
						}
						else
						{
							UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't goto column number '%i' of line '%i' in '%s'"), ColumnNumber, LineNumber, *FullPath);
						}
					}
					else
					{
						UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't goto line number '%i' in '%s'"), LineNumber, *FullPath);
					}
				}
				else
				{
					UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't open file '%s'."), *FullPath);
				}

				VSLaunchFinished( true );
			}
			else
			{
				UE_LOG(LogVSAccessor, Log, TEXT("Couldn't get item operations. Visual Studio may still be initializing."), *FullPath);
				bDefer = true;
			}
		}
		else
		{
			UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't set focus on Visual Studio."));
		}
	}
	else
	{
		bDefer = true;

		// We can't process until we're in the main thread, if we aren't initially defer until we are
		if ( IsInGameThread() )
		{
			// If we haven't already attempted to launch VS do so now
			if (!IsVSLaunchInProgress())
			{
				// If there's no valid instance of VS running, run one if we have it installed
				if ( !RunVisualStudioAndOpenSolution() )
				{
					bDefer = false;
				}
				else
				{
					VSLaunchStarted();
				}
			}
		}
	}

	// If we have attempted to launch VS, and it's taken too long, timeout so the user can try again
	if ( IsVSLaunchInProgress() && ( FPlatformTime::Seconds() - VSLaunchTime ) > 300 )
	{
		// We need todo this incase the process died or was kill prior to the code gaining focus of it
		bDefer = false;
		VSLaunchFinished( false );

		// We failed to open the solution and file, so lets just use the platforms default opener.
		FPlatformProcess::LaunchFileInDefaultExternalApplication( *FullPath );
	}

	// Defer the request until VS is available to take hold of
	if ( bDefer )
	{
		const FString DeferCommand = FString::Printf( TEXT( "OPEN_VS %s %d %d" ), *FullPath, LineNumber, ColumnNumber);
		LaunchVSDeferred.Broadcast( DeferCommand );
	}
	else
	{
		UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't access Visual Studio"));
	}

	// Uninitialize the com library, if we initialized it above (don't call if S_FALSE)
	FWindowsPlatformMisc::CoUninitialize();

	return bSuccess;
}
bool FVisualStudioSourceCodeAccessor::OpenVisualStudioFilesInternalViaDTE(const TArray<FileOpenRequest>& Requests, bool& bWasDeferred)
{
	ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked<ISourceCodeAccessModule>(TEXT("SourceCodeAccess"));

	// Initialize the com library, if not already by this thread
	if (!FWindowsPlatformMisc::CoInitialize())
	{
		UE_LOG(LogVSAccessor, Error, TEXT( "ERROR - Could not initialize COM library!" ));
		return false;
	}
	
	bool bDefer = false, bSuccess = false;
	TComPtr<EnvDTE::_DTE> DTE;
	const FString SolutionPath = GetSolutionPath();
	switch (AccessVisualStudioViaDTE(DTE, SolutionPath, GetPrioritizedVisualStudioVersions(SolutionPath)))
	{
	case EAccessVisualStudioResult::VSInstanceIsOpen:
		{
			// Set Focus on Visual Studio
			TComPtr<EnvDTE::Window> MainWindow;
			if (SUCCEEDED(DTE->get_MainWindow(&MainWindow)) &&
				SUCCEEDED(MainWindow->Activate()))
			{
				// Get ItemOperations
				TComPtr<EnvDTE::ItemOperations> ItemOperations;
				if (SUCCEEDED(DTE->get_ItemOperations(&ItemOperations)))
				{
					for ( const FileOpenRequest& Request : Requests )
					{
						// Check that the file actually exists first
						if ( !FPaths::FileExists(Request.FullPath) )
						{
							SourceCodeAccessModule.OnOpenFileFailed().Broadcast(Request.FullPath);
							bSuccess |= false;
							continue;
						}

						// Open File
						auto ANSIPath = StringCast<ANSICHAR>(*Request.FullPath);
						FComBSTR COMStrFileName(ANSIPath.Get());
						FComBSTR COMStrKind(EnvDTE::vsViewKindTextView);
						TComPtr<EnvDTE::Window> Window;
						if ( SUCCEEDED(ItemOperations->OpenFile(COMStrFileName, COMStrKind, &Window)) )
						{
							// If we've made it this far - we've opened the file.  it doesn't matter if
							// we successfully get to the line number.  Everything else is gravy.
							bSuccess |= true;

							// Scroll to Line Number
							TComPtr<EnvDTE::Document> Document;
							TComPtr<IDispatch> SelectionDispatch;
							TComPtr<EnvDTE::TextSelection> Selection;
							if ( SUCCEEDED(DTE->get_ActiveDocument(&Document)) &&
								 SUCCEEDED(Document->get_Selection(&SelectionDispatch)) &&
								 SelectionDispatch && SUCCEEDED(SelectionDispatch->QueryInterface(&Selection)) &&
								 SUCCEEDED(Selection->GotoLine(Request.LineNumber, VARIANT_TRUE)) )
							{
								if ( !SUCCEEDED(Selection->MoveToLineAndOffset(Request.LineNumber, Request.ColumnNumber, false)) )
								{
									UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't goto column number '%i' of line '%i' in '%s'"), Request.ColumnNumber, Request.LineNumber, *Request.FullPath);
								}
							}
							else
							{
								UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't goto line number '%i' in '%s'"), Request.LineNumber, *Request.FullPath);
							}
						}
						else
						{
							UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't open file '%s'."), *Request.FullPath);
						}
					}

					VSLaunchFinished( true );
				}
				else
				{
					UE_LOG(LogVSAccessor, Log, TEXT("Couldn't get item operations. Visual Studio may still be initializing."));
					bDefer = true;
				}
			}
			else
			{
				UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't set focus on Visual Studio."));
			}
		}
		break;

	case EAccessVisualStudioResult::VSInstanceIsNotOpen:
		{
			bDefer = true;

			// We can't process until we're in the main thread, if we aren't initially defer until we are
			if ( IsInGameThread() )
			{
				// If we haven't already attempted to launch VS do so now
				if ( !IsVSLaunchInProgress() )
				{
					// If there's no valid instance of VS running, run one if we have it installed
					if ( !RunVisualStudioAndOpenSolution() )
					{
						bDefer = false;
					}
					else
					{
						VSLaunchStarted();
					}
				}
			}
		}
		break;

	case EAccessVisualStudioResult::VSInstanceIsBlocked:
		{
			// VS may be open for the solution we want, but we can't query it right now as it's blocked for some reason
			// Defer this operation so we can try it again later should VS become unblocked
			bDefer = true;
		}
		break;

	default:
		// Do nothing if we failed the VS detection, otherwise we could get stuck in a loop of constantly 
		// trying to open a VS instance since we can't detect that one is already running
		bDefer = false;
		break;
	}
	
	if ( !bSuccess )
	{
		// If we have attempted to launch VS, and it's taken too long, timeout so the user can try again
		if ( IsVSLaunchInProgress() && ( FPlatformTime::Seconds() - VSLaunchTime ) > 300 )
		{
			// We need todo this in case the process died or was kill prior to the code gaining focus of it
			bDefer = false;
			VSLaunchFinished(false);

			// We failed to open the solution and file, so lets just use the platforms default opener.
			for ( const FileOpenRequest& Request : Requests )
			{
				FPlatformProcess::LaunchFileInDefaultExternalApplication(*Request.FullPath);
			}
		}

		// Defer the request until VS is available to take hold of
		if ( bDefer )
		{
			FScopeLock Lock(&DeferredRequestsCriticalSection);
			DeferredRequests.Append(Requests);
		}
		else if ( !bSuccess )
		{
			UE_LOG(LogVSAccessor, Warning, TEXT("Couldn't access Visual Studio"));
		}
	}

	// Uninitialize the com library, if we initialized it above (don't call if S_FALSE)
	FWindowsPlatformMisc::CoUninitialize();

	bWasDeferred = bDefer;
	return bSuccess;
}