I am working on catching a simple login exception (in the event of an incorrect password entry) for an application I am building for internal use. All of my p4api code works fine for connection, login, client creation, syncing, etc but I cannot seem to catch exceptions and display them no matter what I try.
The exceptions are caught no problem by the debugger
Perforce.P4.P4Exception: 'Authentication failed.'
Catch doesn't seem to work whether I use catch (P4Exception ex)
, catch (Exception ex)
, or catch
with no types.
Current code block:
public void PerforceLogin(){
var P4USER = CurrentUserName; //established as current logged in user globally
P4PORT = locationSelectText.Split('(')[1].TrimEnd(')');
var P4PASSWD = p4PasswordBox.Password;
var srv = new Perforce.P4.Server(new ServerAddress(P4PORT));
var p4 = new Perforce.P4.Repository(srv);
var p4Options = new Perforce.P4.Options();
p4Options["-a"] = null;
try
{
p4.Connection.Login(P4PASSWD, p4Options);
}
catch (P4Exception ex)
{
Debug.WriteLine("Exception caught: " + ex.Message);
//DisplayDialogBox(ex.Message, "Login Failed");
return;
}
}
private void p4LoginButton_Click(object sender, RoutedEventArgs e)
{
PerforceLogin();
}
Currently have my DisplayDialogBox method noted out to rule out any issues stemming from that but still nothing in my Debug output either unfortunately.
I've also messed with setting P4Exception.MinThrowLevel = ErrorSeverity.LEVEL
to various levels with no real success.
I don't have a ton of experience with catching exceptions, especially from non-.Net sources but it seems like it should be pretty straight forward?
EDIT to add Call Stack:
p4api.net.dll!Perforce.P4.P4Exception.Throw(string cmd, string[] args, Perforce.P4.P4ClientErrorList errors, Perforce.P4.P4ClientInfoMessageList details)
p4api.net.dll!Perforce.P4.P4Server.RunCommand(string cmd, uint cmdId, bool tagged, string[] args, int argc)
p4api.net.dll!Perforce.P4.P4Command.RunInt(Perforce.P4.StringList flags)
p4api.net.dll!Perforce.P4.P4CommandResult.P4CommandResult(Perforce.P4.P4Command cmd, Perforce.P4.StringList flags)
p4api.net.dll!Perforce.P4.P4Command.Run(Perforce.P4.StringList flags)
p4api.net.dll!Perforce.P4.Connection.Login(string password, Perforce.P4.Options options, string user)
p4api.net.dll!Perforce.P4.Connection.Login(string password, Perforce.P4.Options options)
OMIT.dll!OMIT.Views.MainPage.PerforceLogin() Line 419
at OMIT\Views\MainPage.xaml.cs(419)
OMIT.dll!OMIT.Views.MainPage.p4LoginButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) Line 971
at OMIT\Views\MainPage.xaml.cs(971)
Microsoft.WinUI.dll!WinRT._EventSource_global__Microsoft_UI_Xaml_RoutedEventHandler.EventState.GetEventInvoke.AnonymousMethod__1_0(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) Line 8184
at C:\__w\1\s\BuildOutput\obj\x86fre\src\projection\generated\WinRTEventHelpers.cs(8184)
Microsoft.WinUI.dll!ABI.Microsoft.UI.Xaml.RoutedEventHandler.Do_Abi_Invoke(nint thisPtr, nint sender, nint e) Line 26137
at C:\__w\1\s\BuildOutput\obj\x86fre\src\projection\generated\Microsoft.UI.Xaml.cs(26137)
[Native to Managed Transition]
[Managed to Native Transition]
Microsoft.WinUI.dll!ABI.Microsoft.UI.Xaml.IApplicationStaticsMethods.Start(WinRT.IObjectReference _obj, Microsoft.UI.Xaml.ApplicationInitializationCallback callback) Line 13655
at C:\__w\1\s\BuildOutput\obj\x86fre\src\projection\generated\Microsoft.UI.Xaml.cs(13655)
Microsoft.WinUI.dll!Microsoft.UI.Xaml.Application.Start(Microsoft.UI.Xaml.ApplicationInitializationCallback callback) Line 312
at C:\__w\1\s\BuildOutput\obj\x86fre\src\projection\generated\Microsoft.UI.Xaml.cs(312)
OMIT.dll!OMIT.Program.Main(string[] args) Line 31
OMIT\obj\x64\Release\net7.0-windows10.0.19041.0\win10-x64\App.g.i.cs(31)
Edit to add: Username is auto-pulled from Windows login as our Perforce instance is LDAP authenticated and password is pulled from a password box on the mainform. There are no issues running this with the right password but I'd love to be able to detect bad passwords!
Edit to add Usings:
using COD_Workspace_Setup.ViewModels;
using Monitor.Core.Utilities;
using Microsoft.UI.Xaml.Controls;
using Perforce.P4;
using System;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Management;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;
using Microsoft.UI.Xaml;
using Windows.UI.Popups;
Edit to add new snippet. I went ahead and built a standalone project in an attempt to get this working. It's only a PasswordBox and a Button for login. All of the p4 info is hard coded.
using Microsoft.UI.Xaml.Controls;
using Perforce.P4;
using System;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;
using Microsoft.UI.Xaml;
using Windows.UI.Popups;
using P4LoginTest.ViewModels;
using Microsoft.UI.Xaml.Media;
namespace P4LoginTest.Views;
public sealed partial class MainPage : Page
{
public MainViewModel ViewModel
{
get;
}
public MainPage()
{
ViewModel = App.GetService<MainViewModel>();
InitializeComponent();
}
private async void DisplayDialogBox(string message, string title)
{
if (!string.IsNullOrWhiteSpace(message))
{
ContentDialog dialog = new ContentDialog();
dialog.XamlRoot = this.XamlRoot;
dialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style;
dialog.Title = title;
dialog.PrimaryButtonText = "Ok";
dialog.DefaultButton = ContentDialogButton.Primary;
dialog.Content = message;
var result = await dialog.ShowAsync();
}
}
public void PerforceLogin()
{
if (p4PasswordBox.Password.Length > 0)
{
Debug.WriteLine("Starting Login");
p4PasswordBox.IsEnabled = false;
var P4USER = "HARDCODEDUSERNAME";
var P4PORT = "HARDCODEDSERVERFQDN";
var P4PASSWD = p4PasswordBox.Password;
Debug.WriteLine("P4PORT = " + P4PORT);
var srv = new Perforce.P4.Server(new ServerAddress(P4PORT));
var p4 = new Perforce.P4.Repository(srv);
p4.Connection.UserName = P4USER;
Debug.WriteLine("Starting connection...");
p4.Connection.Connect(new Perforce.P4.Options());
Debug.WriteLine("Connection passed");
var p4Options = new Perforce.P4.Options();
p4Options["-a"] = null;
Debug.WriteLine("Starting Login try block");
try
{
p4.Connection.Login(P4PASSWD, p4Options);
}
catch (Perforce.P4.P4Exception)
{
Debug.WriteLine("Exception CAUGHT, P4Exception");
DisplayDialogBox("Incorrect password entered, please try again.", "Login Failed");
System.Diagnostics.Debugger.Break();
return;
}
catch
{
Debug.WriteLine("Exception CAUGHT, ALL");
System.Diagnostics.Debugger.Break();
return;
}
Debug.WriteLine("Try block passed");
}
}
void p4LoginButton_Click(object sender, RoutedEventArgs e)
{
PerforceLogin();
}
}
And the Output from the above:
Starting Login
P4PORT = HARDCODEDSERVERFQDN
Starting connection...
Connection passed
Starting Login try block
Exception thrown: 'Perforce.P4.P4Exception' in p4api.net.dll
Exception thrown: 'Perforce.P4.P4Exception' in p4api.net.dll
Exception thrown: 'Perforce.P4.P4Exception' in p4api.net.dll
Try block passed
EDIT: I spun up a WinForms project and copy pasted my login method and the catch still doesn't work.
I did, however, seem to resolve it in a roundabout way. I wrapped the p4 command I run immediately after login in a try/catch statement:
// Get list of top level folders in Depot to build branch select menu
var opts = new GetDepotDirsCmdOptions(GetDepotDirsCmdFlags.None, null);
var dirs = new List<String>();
dirs.Add("//" + projectID + "/*");
IList<String> target = new List<String>();
try
{
target = p4.GetDepotDirs(dirs, opts);
}
catch (P4Exception e)
{
DisplayDialogBox(e.Message + "\nPlease retry password entry.", "Login Error.");
return;
}
The command fails if the password is bad and it returns a P4Exception referencing an error in P4PSSWD which I can use to toss up a dialog window asking to reenter the password. So while I can't catch it at login time I can still catch it in follow up commands that fall back on P4PSSWD for their operation. I plan to read the e.Message
string to look for a P4PSSWD error to differentiate the error messages but as I've never seen any other error with this command it's a low priority. Hope this helps others!