หลังจากได้รู้จักกับ Intel AppUp Center รวมถึงได้เห็นหน้าค่าตาของ Intel Atom Developer Program SDK สำหรับแอพลิเคชันที่จะเข้าไปทำตลาดกันบ้างแล้ว คราวนี้เราจะมาลองสร้าง/ปรับปรุงแอพลิเคชันเพื่อเตรียมลุยใน AppUp กัน โดยในบทความนี้จะเริ่มจากภาษา C/C++ กันก่อน เพราะตัว SDK รองรับมาตั้งแต่แรก ส่วนภาษาอื่นๆ นั้นแนวคิดจะคงเดิมแต่อาจจะต้องใช้ตัว binding ครับ
เพื่อให้โปรเจกต์ของเราเรียกใช้ SDK ได้ เราจะต้องตั้งค่าเล็กน้อยเพื่อให้โปรเจกต์ของเรารู้จักไฟล์ header และไลบรารีก่อน
สำหรับภาษา C แน่นอนว่าเราต้องเรียกใช้จากฟังก์ชันต่างๆ ที่ SDK เตรียมไว้ให้เท่านั้น แต่กับภาษา C++ เรามีให้เลือกสองวิธีหลักๆ คือ
เนื่องจากที่อื่นจะแสดงวิธีแรกให้เห็นกันเป็นส่วนมาก วันนี้เราจะแนะนำโดยทำวิธีหลังกันครับ
ก่อนอื่นเรามาสร้างคลาสแอพลิเคชันของเรากัน
{syntaxhighlighter brush: cpp}
using namespace HelloAppUp;
// Define your application ID, or use ADP_DEBUG_APPLICATIONID in testing.
const com::intel::adp::ApplicationId adp_app_id = ADP_DEBUG_APPLICATIONID;
class ADPApplication: public com::intel::adp::Application {
public:
// Constructor
ADPApplication(): com::intel::adp::Application(adp_app_id) {
// Don't remove this pair of bracket, to pass the compilation.
}
int run() {
System::Windows::Forms::Application::Run(gcnew MainForm());
return 0;
}
};
{/syntaxhighlighter}
เริ่มต้นจากการสร้างคลาสสำหรับแอพลิเคชันของเรา ซึ่งสืบทอดมาจากคลาส Application ใน SDK เนื่องจากใน Windows Form ก็มีคลาสชื่อ Application เหมือนกัน เพื่อไม่ให้สับสน เราจึงต้องระบุ namespace ของคลาสกำกับไว้ด้วยเมื่อเรียกใช้
ในส่วนของ constructor ของคลาส Application นั้นจะให้เราระบุหมายเลขแอพลิเคชันของเรา (Application ID) เป็นอาร์กิวเมนต์ด้วย ซึ่งเราจะได้จากการลงทะเบียนแอพลิเคชันในระบบ แต่ในช่วงพัฒนาแอพลิเคชัน เราสามารถใช้หมายเลขพิเศษ ADP_DEBUG_APPLICATIONID ไปก่อนได้
ส่วนฟังก์ชัน run ไม่ใช่ฟังก์ชันบังคับจาก SDK นะครับ ผมสร้างเองเพื่อใช้รันแอพลิเคชัน ซึ่งก็คัดลอกเอาซอร์สโค้ดส่วนเปิดหน้าต่างแอพลิเคชันจากโปรเจกต์ที่สร้างขึ้นตอนแรกมาใช้เท่านั้นเอง
แน่นอนว่าเราไม่อยากให้ใครคัดลอกแอพลิเคชันของเราไปใช้ฟรีๆ ดังนั้น SDK จะช่วยเราตรวจสอบผู้ใช้ที่เปิดแอพลิเคชันว่า ซื้อแอพลิเคชันของเรามาถูกต้อง และยังไม่หมดอายุการใช้งาน การทำงานส่วนนี้อยู่ใน constructor ของคลาส Application โดยจะตรวจสอบสิทธิ์ของผู้ใช้สำหรับแอพลิเคชันที่มีหมายเลขตามที่เราระบุลงไปเป็นอาร์กิวเมนต์ใน constructor นั่นเอง ยกเว้นสำหรับหมายเลขพิเศษ ADP_DEBUG_APPLICATIONID นั้น ส่วนของ SDK จะปล่อยผ่านให้ เพื่อให้เราใช้ทดสอบได้
หากผู้ใช้ไม่ผ่านการตรวจสอบ ไม่ว่าจะเกิดจากผู้ใช้ยังไม่ได้ซื้อแอพลิเคชันของเรา, แอพลิเคชันหมดอายุแล้ว, เปิดแอพลิเคชันนี้ไว้แล้ว, หรือไม่สามารถติดต่อบริการ (service) ตรวจสอบสิทธิ์ได้ จะโยน exception ออกมาให้ ดังนั้นเมื่อเรานำคลาสเมื่อสักครู่นี้ไปใช้ เราจะต้องดักจับ exception นี้ด้วย
เราไปดูไฟล์ที่มีฟังก์ชัน main ซึ่งจะเรียกใช้คลาส ADPApplication ที่เราสร้างไว้ครับ
{syntaxhighlighter brush: cpp}
// HelloAppUp.cpp : main project file.
ADPApplication *adp_app = NULL;
using namespace HelloAppUp;
[STAThreadAttribute]
int main(array ^args) {
// Enabling Windows XP visual effects before any controls are created
System::Windows::Forms::Application::EnableVisualStyles();
System::Windows::Forms::Application::SetCompatibleTextRenderingDefault(false);
// Try to authen.
try {
adp_app = new ADPApplication();
}
catch (AdpException& ex) {
// Authentication failed. Show the error message, then exit.
String^ errorMessage = gcnew String(ex.message());
MessageBox::Show(errorMessage, "Authorization Problems",
System::Windows::Forms::MessageBoxButtons::OK,
System::Windows::Forms::MessageBoxIcon::Error);
if (adp_app != NULL) {
delete adp_app;
}
exit(-1);
}
// Authentication passed. We can run the application now.
int returnCode = adp_app->run();
// Don't forget to clear the Application object from the memory.
if (adp_app != NULL) {
delete adp_app;
}
return returnCode;
}
{/syntaxhighlighter}
ส่วนหลักของโปรแกรมก็พยายามสร้างวัตถุจากคลาสแอพลิเคชันของเราขึ้นมา หากไม่มีสิทธิ์รันแอพลิเคชันก็จะแสดงหน้าต่างแสดงข้อผิดพลาดขึ้นมา และจบโปรแกรมทันที
สังเกตว่า อย่าลืมลบวัตถุจากคลาส Application ก่อนจบแอพลิเคชันด้วยนะครับ เพื่อไม่ให้วัตถุค้างอยู่ ซึ่งจะทำให้ตรวจสอบสิทธิ์ผู้ใช้ไม่ผ่านในรอบต่อๆ ไป
ตอนนี้แอพลิเคชันของเราก็รันได้ และพร้อมที่จะทดสอบเบื้องต้นแล้ว โดยในการทดสอบนั้น เราจะต้องเปิดการทำงานของบริการ ATDS เสียก่อน โดยเรียกใช้ได้จาก Start Menu
สำหรับเครื่องที่ติดตั้ง Intel AppUp Center ไว้ด้วย จะต้องปิดโปรเซสบริการของ Intel AppUp Center (serviceManager.exe) ก่อนนะครับ
เมื่อเปิด ATDS แล้วเปิดโปรแกรมของเราขึ้นทดสอบ หน้าต่างคอนโซลของ ATDS จะแสดงข้อมูลการติดต่อกับแอพลิเคชันของเราให้เห็น
อีกส่วนหนึ่งที่ SDK จะช่วยเหลือเราคือการบันทึกสถิติการใช้งานของผู้ใช้ (ในรุ่นเบต้านี้จะบันทึกไว้เฉพาะระยะเวลาที่ผู้ใช้ใช้แอพลิเคชัน) และข้อผิดพลาดต่างๆ ของแอพลิเคชัน โดยข้อมูลเหล่านี้จะถูกบันทึกไว้ในเครื่องของผู้ใช้ก่อน จากนั้นบริการของ AppUp จะจัดการส่งข้อมูลไปที่ Developer Portal เพื่อรวบรวมข้อมูลมาแสดงให้เรารู้ทางแดชบอร์ดต่อไป
เราจะกลับไปที่คลาส ADPApplication ของเราอีกครั้งเพื่อเพิ่มเติมคำสั่งในส่วนนี้กันครับ
{syntaxhighlighter brush: cpp}
using namespace HelloAppUp;
// Define your application ID, or use ADP_DEBUG_APPLICATIONID in testing.
const com::intel::adp::ApplicationId adp_app_id = ADP_DEBUG_APPLICATIONID;
class ADPApplication: public com::intel::adp::Application {
public:
// Constructor
ADPApplication(): com::intel::adp::Application(adp_app_id) {
// Don't remove this pair of bracket, to pass the compilation.
}
int run() {
try {
// Our normal source code of the application goes here.
System::Windows::Forms::Application::Run(gcnew MainForm());
return 0;
}
catch (Exception^ e) {
// Unhandled exception is caughted. We'll report it, and exit the application.
ADP_RET_CODE reportResult = reportException(e);
String^ message;
if (reportResult == ADP_SUCCESS) {
message = "Unexpected error occurs. It'll be reported to the developers.";
}
else {
message = "Unexpected error occurs, and it can't be reported.";
}
MessageBox::Show(message, "Unexpected Error",
System::Windows::Forms::MessageBoxButtons::OK,
System::Windows::Forms::MessageBoxIcon::Error);
return -1;
}
}
private:
ADP_RET_CODE reportException(Exception^ e) {
// Collect general information.
// Sending stack trace as errorData, and useing __LINE__ are not good examples.
wchar_t* moduleName = toWchar(e->Source);
wchar_t* message = toWchar(e->Message);
wchar_t* category = L"Exception";
wchar_t* errorData = toWchar(e->StackTrace);
long line = __LINE__;
// Prepare the custom fields.
ADP_CrashReportField customFields[1];
customFields[0].name = L"ExceptionType";
customFields[0].value = toWchar(e->GetType()->FullName);
// Report.
return pClientProxy->ReportCrash(moduleName, message, category, line,
errorData, customFields, 1);
}
static wchar_t* toWchar(String^ str) {
// Just convert the String^ to wchar_t*
pin_ptr<const wchar_t> result = PtrToStringChars(str);
return (wchar_t*)result;
}
};
{/syntaxhighlighter}
เนื่องจากเราก็ไม่รู้เหมือนกันว่าแอพลิเคชันของเราจะเกิดข้อผิดพลาดตรงไหน ดังนั้นจุดยุทธศาสตร์หลักที่เราจะดักไว้ ก็คือดักข้อผิดพลาดครอบการทำงานของทั้งแอพลิเคชันเลยครับ จากนั้นเมื่อเกิดข้อผิดพลาดก็ใช้เมธอด reportCrash ของ pClientProxy เพื่อรายงาน ก่อนจะปิดแอพลิเคชันลงอย่างสวยงามด้วยหน้าต่างแจ้งผู้ใช้ของเรา
ข้อมูลเบื้องต้นที่เราส่งได้ก็คือ ชื่อโมดูลที่เกิดปัญหา, ข้อความ, หมวดหมู่ของปัญหา, บรรทัดที่เกิดปัญหา รวมถึงใส่ฟิลด์ของเราเองเพิ่มได้ นอกจากนี้ เรายังสามารถส่ง stack trace ไปได้ด้วย โดยต้องลงแรงเพิ่มอีกสักหน่อย แต่เราจะไม่พูดถึงในบทความนี้ และใช้วิธีที่ง่ายๆ คือส่งไปในฟิลด์ errorData แทน
ในส่วนของ ATDS เมื่อได้รับรายงานความผิดพลาดแล้ว ก็จะแจ้งตำแหน่งที่เก็บรายละเอียดไว้ ให้เราไปดูข้อมูลความผิดพลาดที่เรารายงานเข้าไปได้
จากที่ผ่านมาทั้งหมด สังเกตว่าผมไม่ได้พูดถึงตัวแอพลิเคชันเลยแม้แต่น้อย เพราะการพัฒนาแอพลิเคชันสำหรับออกสู่ตลาด Intel AppUp Center นั้น เราไม่จำเป็นต้องแก้ไขหรือเขียนโปรแกรมสำหรับ AppUp โดยเฉพาะ เพียงแต่เติมเต็มส่วนต่างๆ เพื่อเอาประสิทธิภาพในการซื้อขายแอพลิเคชันผ่าน AppUp มาใช้เท่านั้น ซึ่งดูจะง่ายกว่าการเตรียมระบบยืนยันต่างๆ เวลาทำแอพลิเคชันขายเองมากเลยทีเดียว
ข่าวและบทความหมวด - Intel AppUp Center ได้รับการสนับสนุนจากอินเทล เพื่อประชาสัมพันธ์โครงการ Intel Atom Developer Program ให้กับนักพัฒนาในประเทศไทย