[IADP Series] 自己寫 SDK 互動的程式太麻煩了,更 Smart 的 SDK 互動作法

在 Intel AppUp SDK Developer's Guide 中,有不少的篇幅都是在講應用程式和 SDK 一些函式的整合,以將 AppUp Center 的機能和應用程式整合在一起,像是 Application Registration, Instrumentation (Events), Upgrade 與 Crash Report 等,除了之前我所發表的使用 Exception 決定 Crash Report 的功能外,我們還可以進一步的將 SDK 的函式包裝起來,讓在整合 SDK 和應用程式的過程能更簡單,簡單到什麼程度呢 ... 只要使用一個類別即可,而且相關的 Exception 都會轉換成自訂的 Crash Report。

在 Intel AppUp SDK Developer's Guide 中,有不少的篇幅都是在講應用程式和 SDK 一些函式的整合,以將 AppUp Center 的機能和應用程式整合在一起,像是 Application Registration, Instrumentation (Events), Upgrade 與 Crash Report 等,除了之前我所發表的使用 Exception 決定 Crash Report 的功能外,我們還可以進一步的將 SDK 的函式包裝起來,讓在整合 SDK 和應用程式的過程能更簡單,簡單到什麼程度呢 ... 只要使用一個類別即可,而且相關的 Exception 都會轉換成自訂的 Crash Report。

下圖為 IADP 應用程式的運行架構,圖中的 Developer Platform 和 Consumer Platform 中的 Application 就是我們要開發的 IADP-Compliant 應用程式所使用的資源與架構。

image

我們以 Windows Forms 來做例子,當產生 Windows Forms 專案時,都會有一個 Program.cs,裡面會放初始化 Windows Forms 應用程式的程式碼,不過最多也只是這樣:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false); 
    Application.Run(new Form1());
}

然後 SDK 的 Developer's Guide 上說,我們要在程式中加入 AdpLibrary 的 Reference,使用 AdpApplication 建構式傳入 Application ID 以註冊應用程式,若有錯誤的話要處理 (捕捉 AdpErrorException 和檢查 ADP_RET_CODE 是屬於哪種例外),而且還要自己維護一個 AdpApplication 的全域變數,再做其他的事 (ex: BeginEvent(), EndEvent(), ...),這些事都要由開發人員來做,老實說是挺煩人的。

所以才會有一個構想:我可以有一個類別可以幫我管這些事,而且盡量的減少要變動的程式碼,那該有多好?我試著把這個構想給實現,不過想要減少變動的程式碼並不容易,因為例外處理的變動比較大,所以我目前可以做到的就是把該處理的例外事件化,而我們更可集中火力在 Program.cs,而不會去改動現有的表單或模組的程式邏輯。例如這樣:

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        IadpApplicationContext context = new IadpApplicationContext(new frmMain(), new com.intel.adp.AdpApplicationId(0x01, 0x01, 0x01, 0x01));

        context.InitializationFailedEvent += (sender, e) =>
        {
            // TODO: implement application logic for SDK initialization error occurred.
        };

        context.RuntimeErrorEvent += (sender, e) =>
        {
            // TODO: implement application logic for SDK runtime error occurred.
        };

        context.UnauthorizedEvent += (sender, e) =>
        {
            MessageBox.Show(
                "This application's key is invalid or unauthorized, please contact to author of this application.",
                "Application Key is invalid or unauthorized.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            Application.ExitThread();
            Application.Exit();
        };

        context.WarningErrorEvent += (sender, e) =>
        {
            // TODO: implement application logic for SDK warning error occurred.
        };

        context.InitAdpApplication();
        context.Run();

    }
}

這段程式碼就已經做完了 SDK 所需的工作,包含註冊 Application ID,處理應該處理的例外 (event-based),以及做一些初始處理 (包含設定 Exception-based Crash Report) 等,應用程式也會自己保有訊息迴圈,以保持應用程式自己的訊息處理與應用程式生命週期等。而一切的核心就是 IadpApplicationContext,這個類別是衍生自 Windows Forms 本身的 ApplicationContext 類別,並且在內部直接使用 Application 類別做應用程式的接續處理,如此開發人員只要把焦點放在怎麼處理 SDK 的例外以及自己本身的應用程式邏輯即可,不需要再對與 SDK 整合這件事傷腦筋了。

下列為 IadpApplicationContext 的完整程式碼,裡面使用到了像是 delegate 與 event 的作法,以及自訂 Exception EventArgs 的作法:

/// <summary>
/// Exception Event Args.
/// </summary>
public class IadpExceptionEventArgs : EventArgs
{
    /// <summary>
    /// Exception Instance for Error Event.
    /// </summary>
    public IadpSDKException Exception { get; set; }

    /// <summary>
    /// Constructor.
    /// </summary>
    /// <param name="e">Exception Instance for Error Event.</param>
    public IadpExceptionEventArgs(IadpSDKException e)
    {
        this.Exception = e;
    }
}

/// <summary>
/// Fire this event when Unauthorized exception of IADP SDK occurred.
/// </summary>
/// <param name="sender">Event Firer, mean Application Context Class</param>
/// <param name="e">Unauthorized Exception Data.</param>
public delegate void IadpAppUnauthorizedEvent(object sender, IadpExceptionEventArgs e);
/// <summary>
/// Fire this event when Initialization Failed exception of IADP SDK occurred.
/// </summary>
/// <param name="sender">Event Firer, mean Application Context Class</param>
/// <param name="e">Initialization Failed Exception Data.</param>
public delegate void IadpAppInitializationFailedEvent(object sender, IadpExceptionEventArgs e);
/// <summary>
/// Fire this event when Runtime exception of IADP SDK occurred.
/// </summary>
/// <param name="sender">Event Firer, mean Application Context Class</param>
/// <param name="e">Runtime Exception Data.</param>
public delegate void IadpAppRuntimeErrorEvent(object sender, IadpExceptionEventArgs e);
/// <summary>
/// Fire this event when Warning exception of IADP SDK occurred.
/// </summary>
/// <param name="sender">Event Firer, mean Application Context Class</param>
/// <param name="e">Warning Exception Data.</param>
public delegate void IadpWarningErrorEvent(object sender, IadpExceptionEventArgs e);

/// <summary>
/// EasyIADP Application Context Wrapper.
/// </summary>
public class IadpApplicationContext : ApplicationContext
{
    private AdpApplication _adpApplication = null;
    private AdpApplicationId _adpApplicationID;
    private bool _initCompleted = false;

    /// <summary>
    /// Fire this event when Runtime exception of IADP SDK occurred.
    /// </summary>
    public event IadpAppInitializationFailedEvent InitializationFailedEvent;
    /// <summary>
    /// Fire this event when Unauthorized exception of IADP SDK occurred.
    /// </summary>
    public event IadpAppUnauthorizedEvent UnauthorizedEvent;
    /// <summary>
    /// Fire this event when Runtime exception of IADP SDK occurred.
    /// </summary>
    public event IadpAppRuntimeErrorEvent RuntimeErrorEvent;
    /// <summary>
    /// Fire this event when Warning exception of IADP SDK occurred.
    /// </summary>
    public event IadpWarningErrorEvent WarningErrorEvent;

    /// <summary>
    /// Constructor.
    /// </summary>
    public IadpApplicationContext()
        : base()
    {
        this._adpApplicationID = AdpConstants.ADP_DEBUG_APPLICATIONID;
    }

    /// <summary>
    /// Constructor for Specified application ID.
    /// </summary>
    /// <param name="ApplicationID">Application ID.</param>
    /// <remarks>
    /// If your application is under debugging, please use ADP_DEBUG_APPLICATIONID, otherwise use the ID provided by Intel.
    /// </remarks>
    public IadpApplicationContext(AdpApplicationId ApplicationID)
        : base()
    {
        this._adpApplicationID = ApplicationID;
    }

    /// <summary>
    /// Constructor for Startup Form.
    /// </summary>
    /// <param name="MainForm">Startup Form.</param>
    public IadpApplicationContext(Form MainForm)
        : base(MainForm)
    {
        this._adpApplicationID = AdpConstants.ADP_DEBUG_APPLICATIONID;
    }

    /// <summary>
    /// Constructor for Startup Form and Specified Application ID.
    /// </summary>
    /// <param name="MainForm">Startup Form.</param>
    /// <param name="ApplicationID">Application ID.</param>
    /// <remarks>
    /// If your application will submit to Intel, please use this constructor to initialize application.
    /// </remarks>
    public IadpApplicationContext(Form MainForm, AdpApplicationId ApplicationID)
        : base(MainForm)
    {
        this._adpApplicationID = ApplicationID;
    }

    /// <summary>
    /// Initialize Application Context, include register application and register Crash Report.
    /// </summary>
    public void InitAdpApplication()
    {
        try
        {
            this._adpApplication = new com.intel.adp.AdpApplication(this._adpApplicationID, false);
            this._adpApplication.SetCrashReport(new IadpCrashReport());
            this._initCompleted = true;
        }
        catch (UnauthorizedException unauthorizedEx)
        {
            HandleSDKException(unauthorizedEx);
        }
        catch (InitializationException initEx)
        {
            HandleSDKException(initEx);
        }
        catch (AdpRuntimeException rtEx)
        {
            HandleSDKException(rtEx);
        }
        catch (AdpWarningException warnEx)
        {
            HandleSDKException(warnEx);
        }
    }

    /// <summary>
    /// Register customized Crash Report.
    /// </summary>
    /// <param name="CrashReport">Customized Crash Report object instance.</param>
    /// <remarks>Your Crash Report class must inherit from IadpCrashReport class.</remarks>
    public void RegisterCrashReport(IadpCrashReport CrashReport)
    {
        this._adpApplication.SetCrashReport(CrashReport);
    }

    /// <summary>
    /// Run application.
    /// </summary>
    public void Run()
    {
        if (this._initCompleted)
            Application.Run(this);
    }

    /// <summary>
    /// Exit this application.
    /// </summary>
    public void Exit()
    {
        Application.Exit();
    }

    /// <summary>
    /// Begin SDK event instrumentation.
    /// </summary>
    public void BeginEvent()
    {
        try
        {
            this._adpApplication.BeginEvent();
        }
        catch (UnauthorizedException unauthorizedEx)
        {
            HandleSDKException(unauthorizedEx);
        }
        catch (InitializationException initEx)
        {
            HandleSDKException(initEx);
        }
        catch (AdpRuntimeException rtEx)
        {
            HandleSDKException(rtEx);
        }
        catch (AdpWarningException warnEx)
        {
            HandleSDKException(warnEx);
        }
    }

    /// <summary>
    /// End SDK event instrumentation.
    /// </summary>
    public void EndEvent()
    {
        try
        {
            this._adpApplication.EndEvent();
        }
        catch (UnauthorizedException unauthorizedEx)
        {
            HandleSDKException(unauthorizedEx);
        }
        catch (InitializationException initEx)
        {
            HandleSDKException(initEx);
        }
        catch (AdpRuntimeException rtEx)
        {
            HandleSDKException(rtEx);
        }
        catch (AdpWarningException warnEx)
        {
            HandleSDKException(warnEx);
        }
    }

    /// <summary>
    /// Handling Application ID upgrade authorization.
    /// </summary>
    /// <param name="IdapCode0">Application ID Code Part 1.</param>
    /// <param name="IdapCode1">Application ID Code Part 2.</param>
    /// <param name="IdapCode2">Application ID Code Part 3.</param>
    /// <param name="IdapCode3">Application ID Code Part 4.</param>
    public void Upgrade(uint IdapCode0, uint IdapCode1, uint IdapCode2, uint IdapCode3)
    {
        try
        {
            this._adpApplication.Upgrade(new AdpApplicationId(IdapCode0, IdapCode1, IdapCode2, IdapCode3));
        }
        catch (UnauthorizedException unauthorizedEx)
        {
            HandleSDKException(unauthorizedEx);
        }
        catch (InitializationException initEx)
        {
            HandleSDKException(initEx);
        }
        catch (AdpRuntimeException rtEx)
        {
            HandleSDKException(rtEx);
        }
        catch (AdpWarningException warnEx)
        {
            HandleSDKException(warnEx);
        }
    }

    private void HandleSDKException(AdpException SDKException)
    {
        IadpSDKException sdkException = null;

        if (SDKException is UnauthorizedException)
        {
            sdkException = new IadpSDKException("SDK_APPID_UNAUTHORIZED_EXCEPTION", SDKException.Code, SDKException.Message);

            if (this.UnauthorizedEvent != null)
                this.UnauthorizedEvent(this, new IadpExceptionEventArgs(sdkException));
            else
                throw sdkException;
        }
        else if (SDKException is InitializationException)
        {
            sdkException = new IadpSDKException("SDK_INITIALIZE_EXCEPTION", SDKException.Code, SDKException.Message);

            if (this.InitializationFailedEvent != null)
                this.InitializationFailedEvent(this, new IadpExceptionEventArgs(sdkException));
            else
                throw sdkException;
        }
        else if (SDKException is AdpRuntimeException)
        {
            sdkException = new IadpSDKException("SDK_RUNTIME_EXCEPTION", SDKException.Code, SDKException.Message);

            if (this.RuntimeErrorEvent != null)
                this.RuntimeErrorEvent(this, new IadpExceptionEventArgs(sdkException));
            else
                throw sdkException;
        }
        else if (SDKException is AdpWarningException)
        {
            sdkException = new IadpSDKException("SDK_WARNING_EXCEPTION", SDKException.Code, SDKException.Message);

            if (this.WarningErrorEvent != null)
                this.WarningErrorEvent(this, new IadpExceptionEventArgs(sdkException));
            else
                throw sdkException;
        }
        else
        {
            sdkException = new IadpSDKException("SDK_UNKNOWN_EXCEPTION", ADP_RET_CODE.ADP_FAILURE, "Unknown SDK Exception");
            throw sdkException;
        }
    }
}

Reference:

Intel AppUp SDK Developer's Guide.