Solved

TCHAR[] Problem

Posted on 2006-06-19
19
1,360 Views
Last Modified: 2013-12-14
I have posted below the code for a visual c++ 2005 console app that works with MAPI profiles via MAPI33.DLL (download this if you want to test code below). My problem is when szProfile (which is a TCHAR[]) is streamed in via cin >> and then passed to createProfile, only the first letter in szProfile is used for the name of the profile.

I have marked in the code below the 3 points of interest for this problem. Any ideas?

------------------ Begin Code ------------------

#include <iostream>
#include <string>
#include <windows.h>
#include <stdio.h>
#include <mapix.h>
#include <MAPITAGS.H>
#include <MAPIUTIL.H>
#include "edkmdb.h"
#include <tchar.h>

#pragma comment(lib,"mapi32.lib")

using namespace std;

bool CreateProfileWithIProfAdmin();

istream& operator>> (istream& is, wchar_t wsz[])
{
    string line;
    getline(is, line);
    int i = 0;
    for (i = 0; i < line.length(); ++i)
    {
        wsz[i] = line[i];
    }
    wsz[i] = 0;
    return is;
}

int main() {
    CreateProfileWithIProfAdmin();

    return 0;
}

bool CreateProfileWithIProfAdmin()
{
    HRESULT hRes = S_OK; // Result from MAPI calls.
    LPPROFADMIN lpProfAdmin = NULL; // Profile Admin object.
    LPSERVICEADMIN lpSvcAdmin = NULL; // Service Admin object.
    LPMAPITABLE lpMsgSvcTable = NULL; // Table to hold services.
    LPSRowSet lpSvcRows = NULL; // Rowset to hold results of table query.
    SPropValue rgval[2]; // Property structure to hold values we want to set.
    SRestriction sres; // Restriction structure.
    SPropValue SvcProps; // Property structure for restriction.
    TCHAR szProfile[80] = {0}; // String for profile name. //<<<<<<<<<<<<<<<<<<<<<<< szProfile being declared  <<<<<<<<<<<<<<<<<<<<<<<
    TCHAR szMailbox[80] = {0}; // String for mailbox name.
    TCHAR szServer[80] = {0}; // String for server name.

    // This indicates columns we want returned from HrQueryAllRows.
    enum {iSvcName, iSvcUID, cptaSvc};
    SizedSPropTagArray(cptaSvc,sptCols) = { cptaSvc, PR_SERVICE_NAME, PR_SERVICE_UID };

    // Get configuration info from user.
    cout<<"Enter name for profile: ";
    cin>>szProfile; //<<<<<<<<<<<<<<<<<<<<<<< szProfile being streamed in <<<<<<<<<<<<<<<<<<<<<<<
    cout<<"Enter Exchange mailbox name: ";
    cin>>szMailbox;
    cout<<"Enter Exchange server name: ";
    cin>>szServer;

    // Initialize MAPI.

    if (FAILED(hRes = MAPIInitialize(NULL)))
    {
        cout<<"Error initializing MAPI.";
        goto error;
    }

    // Get an IProfAdmin interface.

    if (FAILED(hRes = MAPIAdminProfiles(0, // Flags.
        &lpProfAdmin))) // Pointer to new IProfAdmin.
    {
        cout<<"Error getting IProfAdmin interface.";
        goto error;
    }

    // Create a new profile.

    if (FAILED(hRes = lpProfAdmin->CreateProfile(szProfile, //<<<<<<<<< szProfile being used to Create a profile in MAPI <<<<<<<<<<<
        NULL, // Password for profile.
        NULL, // Handle to parent window.
        NULL))) // Flags.
    {
        cout<<"Error creating profile.";
        goto error;
    }

    // Get an IMsgServiceAdmin interface off of the IProfAdmin interface.

    if (FAILED(hRes = lpProfAdmin->AdminServices(szProfile, // Profile that we want to modify.
        NULL, // Password for that profile.
        NULL, // Handle to parent window.
        0, // Flags.
        &lpSvcAdmin))) // Pointer to new IMsgServiceAdmin.
    {
        cout<<"Error getting IMsgServiceAdmin interface.";
        goto error;
    }

    // Create the new message service for Exchange.

    if (FAILED(hRes = lpSvcAdmin->CreateMsgService(L"MSEMS", // Name of service from MAPISVC.INF.
        NULL, // Display name of service.
        NULL, // Handle to parent window.
        NULL))) // Flags.
    {
        cout<<"Error creating Exchange message service.";
        goto error;
    }

    // We now need to get the entry id for the new service.
    // This can be done by getting the message service table
    // and getting the entry that corresponds to the new service.

    if (FAILED(hRes = lpSvcAdmin->GetMsgServiceTable(0, // Flags.
        &lpMsgSvcTable))) // Pointer to table.
    {
        cout<<"Error getting Message Service Table.";
        goto error;
    }

    // Set up restriction to query table.

    sres.rt = RES_CONTENT;
    sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
    sres.res.resContent.ulPropTag = PR_SERVICE_NAME;
    sres.res.resContent.lpProp = &SvcProps;

    SvcProps.ulPropTag = PR_SERVICE_NAME;
    SvcProps.Value.lpszA = "MSEMS";

    // Query the table to get the entry for the newly created message service.

    if (FAILED(hRes = HrQueryAllRows(lpMsgSvcTable,
        (LPSPropTagArray)&sptCols,
        &sres,
        NULL,
        0,
        &lpSvcRows)))
    {
        cout<<"Error querying table for new message service.";
        goto error;
    }

    // Setup a SPropValue array for the properties you need to configure.

    // First, the server name.
    ZeroMemory((char*)&rgval[1], sizeof(SPropValue) );
    rgval[1].ulPropTag = PR_PROFILE_UNRESOLVED_SERVER;
    rgval[1].Value.lpszA = (char*)szServer;

    // Next, the mailbox name.
    ZeroMemory((char*)&rgval[0], sizeof(SPropValue) );
    rgval[0].ulPropTag = PR_PROFILE_UNRESOLVED_NAME;
    rgval[0].Value.lpszA = (char*)szMailbox;

    // Configure the message service with the above properties.

    if (FAILED(hRes = lpSvcAdmin->ConfigureMsgService(
        (LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb, // Entry ID of service to configure.
        NULL, // Handle to parent window.
        0, // Flags.
        2, // Number of properties we are setting.
        rgval))) // Pointer to SPropValue array.
    {
        cout<<"Error configuring message service.";
        goto error;
    }

    goto cleanup;

error:
    cout<<" hRes = 0x"<<hex<<hRes<<dec<<endl;
    return FALSE;

cleanup:
    // Clean up.
    if (lpSvcRows) FreeProws(lpSvcRows);
    if (lpMsgSvcTable) lpMsgSvcTable->Release();
    if (lpSvcAdmin) lpSvcAdmin->Release();
    if (lpProfAdmin) lpProfAdmin->Release();

    MAPIUninitialize();
    return TRUE;

}

------------------ End Code ------------------
0
Comment
Question by:inviser
19 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 16936329
Use 'wcin' four unicode input. If you had used the code I posted in your last question, it would have worked 'as is'. Or just add

#ifdef UNICODE
#define cout wcout
#define cin wcin
#endif

right after

using namespace std;

if you do not want to distinguish between these cases manually.
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 16936357
Add MAPI_UNICODE to Flags parameter of CreateProfile function:

    if (FAILED(hRes = lpProfAdmin->CreateProfile(szProfile,
        NULL, // Password for profile.
        NULL, // Handle to parent window.
        MAPI_UNICODE))) // Flags.
0
 
LVL 6

Author Comment

by:inviser
ID: 16936401
AlexFM, when I add the MAPI_UNICODE flag to the function, it does not create profile at all.

jkr, I did as you said and it still only uses the first letter of szProfile.
0
 
LVL 86

Expert Comment

by:jkr
ID: 16936456
>>jkr, I did as you said and it still only uses the first letter of szProfile.

I don't think so - checking that code, you should have gotten a bunch of conpiler errors ;o)

Anway, the code I posted - which you for some reason have chosen not to use - was

#define UNICODE
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <mapix.h>
#include <MAPITAGS.H>
#include <MAPIUTIL.H>
#include "edkmdb.h"
#include <tchar.h>

#pragma comment(lib,"mapi32.lib")

#ifdef UNICODE
#define cout wcout
#define cin wcin
#endif

using namespace std;

bool CreateProfileWithIProfAdmin();

int main() {
    CreateProfileWithIProfAdmin();

    return 0;
}

bool CreateProfileWithIProfAdmin()
{
    HRESULT hRes = S_OK; // Result from MAPI calls.
    LPPROFADMIN lpProfAdmin = NULL; // Profile Admin object.
    LPSERVICEADMIN lpSvcAdmin = NULL; // Service Admin object.
    LPMAPITABLE lpMsgSvcTable = NULL; // Table to hold services.
    LPSRowSet lpSvcRows = NULL; // Rowset to hold results of table query.
    SPropValue rgval[2]; // Property structure to hold values we want to set.
    SRestriction sres; // Restriction structure.
    SPropValue SvcProps; // Property structure for restriction.
    TCHAR szProfile[80] = {0}; // String for profile name.
    TCHAR szMailbox[80] = {0}; // String for mailbox name.
    TCHAR szServer[80] = {0}; // String for server name.

    // This indicates columns we want returned from HrQueryAllRows.
    enum {iSvcName, iSvcUID, cptaSvc};
    SizedSPropTagArray(cptaSvc,sptCols) = { cptaSvc, PR_SERVICE_NAME, PR_SERVICE_UID };

    // Get configuration info from user.
    cout<<_T("Enter name for profile: ");
    cin>>szProfile;
    cout<<_T("Enter Exchange mailbox name: ");
    cin>>szMailbox;
    cout<<_T("Enter Exchange server name: ");
    cin>>szServer;

    // Initialize MAPI.

    if (FAILED(hRes = MAPIInitialize(NULL)))
    {
        cout<<_T("Error initializing MAPI.");
        goto error;
    }

    // Get an IProfAdmin interface.

    if (FAILED(hRes = MAPIAdminProfiles(0, // Flags.
        &lpProfAdmin))) // Pointer to new IProfAdmin.
    {
        cout<<_T("Error getting IProfAdmin interface.");
        goto error;
    }

    // Create a new profile.

    if (FAILED(hRes = lpProfAdmin->CreateProfile(szProfile, // Name of new profile.      <<<<<<<<< ERROR occurs here <<<<<<<<<<<
        NULL, // Password for profile.
        NULL, // Handle to parent window.
        NULL))) // Flags.
    {
        cout<<_T("Error creating profile.");
        goto error;
    }

    // Get an IMsgServiceAdmin interface off of the IProfAdmin interface.

    if (FAILED(hRes = lpProfAdmin->AdminServices(szProfile, // Profile that we want to modify.
        NULL, // Password for that profile.
        NULL, // Handle to parent window.
        0, // Flags.
        &lpSvcAdmin))) // Pointer to new IMsgServiceAdmin.
    {
        cout<<_T("Error getting IMsgServiceAdmin interface.");
        goto error;
    }

    // Create the new message service for Exchange.

    if (FAILED(hRes = lpSvcAdmin->CreateMsgService(TEXT("MSEMS"), // Name of service from MAPISVC.INF.
        NULL, // Display name of service.
        NULL, // Handle to parent window.
        NULL))) // Flags.
    {
        cout<<_T("Error creating Exchange message service.");
        goto error;
    }

    // We now need to get the entry id for the new service.
    // This can be done by getting the message service table
    // and getting the entry that corresponds to the new service.

    if (FAILED(hRes = lpSvcAdmin->GetMsgServiceTable(0, // Flags.
        &lpMsgSvcTable))) // Pointer to table.
    {
        cout<<_T("Error getting Message Service Table.");
        goto error;
    }

    // Set up restriction to query table.

    sres.rt = RES_CONTENT;
    sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
    sres.res.resContent.ulPropTag = PR_SERVICE_NAME;
    sres.res.resContent.lpProp = &SvcProps;

    SvcProps.ulPropTag = PR_SERVICE_NAME;
    SvcProps.Value.lpszA = _T("MSEMS");

    // Query the table to get the entry for the newly created message service.

    if (FAILED(hRes = HrQueryAllRows(lpMsgSvcTable,
        (LPSPropTagArray)&sptCols,
        &sres,
        NULL,
        0,
        &lpSvcRows)))
    {
        cout<<"Error querying table for new message service.";
        goto error;
    }

    // Setup a SPropValue array for the properties you need to configure.

    // First, the server name.
    ZeroMemory(&rgval[1], sizeof(SPropValue) );
    rgval[1].ulPropTag = PR_PROFILE_UNRESOLVED_SERVER;
    rgval[1].Value.lpszW = szServer;

    // Next, the mailbox name.
    ZeroMemory(&rgval[0], sizeof(SPropValue) );
    rgval[0].ulPropTag = PR_PROFILE_UNRESOLVED_NAME;
    rgval[0].Value.lpszW = szMailbox;

    // Configure the message service with the above properties.

    if (FAILED(hRes = lpSvcAdmin->ConfigureMsgService(
        (LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb, // Entry ID of service to configure.
        NULL, // Handle to parent window.
        0, // Flags.
        2, // Number of properties we are setting.
        rgval))) // Pointer to SPropValue array.
    {
        cout<<_T("Error configuring message service.");
        goto error;
    }

    goto cleanup;

error:
    cout<<_T(" hRes = 0x")<<hex<<hRes<<dec<<endl;
    return FALSE;

cleanup:
    // Clean up.
    if (lpSvcRows) FreeProws(lpSvcRows);
    if (lpMsgSvcTable) lpMsgSvcTable->Release();
    if (lpSvcAdmin) lpSvcAdmin->Release();
    if (lpProfAdmin) lpProfAdmin->Release();

    MAPIUninitialize();
    return TRUE;

}

This should work.
0
 
LVL 6

Author Comment

by:inviser
ID: 16936487
well, it pains me to say that it is still not working
0
 
LVL 86

Expert Comment

by:jkr
ID: 16936520
You copied and compiled the whole thing?
0
 
LVL 6

Author Comment

by:inviser
ID: 16936537
yes, maybe our environments differ?
0
 
LVL 86

Expert Comment

by:jkr
ID: 16936700
That's well possible. OK, I've taken AlexFM's suggestion into account and added some output to see what profile is to be created. Could you try that?

#define UNICODE

#include <windows.h>
#include <tchar.h>

//#include <stdio.h>
#include <mapix.h>
#include <MAPITAGS.H>
#include <MAPIUTIL.H>
#include "edkmdb.h"

#include <iostream>
using namespace std;


#pragma comment(lib,"mapi32.lib")

#ifdef UNICODE
#define tcout wcout
#define tcin wcin
#else
#define tcout cout
#define tcin cin
#endif


bool CreateProfileWithIProfAdmin();

int main() {
    CreateProfileWithIProfAdmin();

    return 0;
}

bool CreateProfileWithIProfAdmin()
{
    HRESULT hRes = S_OK; // Result from MAPI calls.
    LPPROFADMIN lpProfAdmin = NULL; // Profile Admin object.
    LPSERVICEADMIN lpSvcAdmin = NULL; // Service Admin object.
    LPMAPITABLE lpMsgSvcTable = NULL; // Table to hold services.
    LPSRowSet lpSvcRows = NULL; // Rowset to hold results of table query.
    SPropValue rgval[2]; // Property structure to hold values we want to set.
    SRestriction sres; // Restriction structure.
    SPropValue SvcProps; // Property structure for restriction.
    TCHAR szProfile[80] = {0}; // String for profile name.
    TCHAR szMailbox[80] = {0}; // String for mailbox name.
    TCHAR szServer[80] = {0}; // String for server name.

    // This indicates columns we want returned from HrQueryAllRows.
    enum {iSvcName, iSvcUID, cptaSvc};
    SizedSPropTagArray(cptaSvc,sptCols) = { cptaSvc, PR_SERVICE_NAME, PR_SERVICE_UID };

    // Get configuration info from user.
    tcout<<TEXT("Enter name for profile: ");
    tcin>>szProfile;
    tcout<<TEXT("Enter Exchange mailbox name: ");
    tcin>>szMailbox;
    tcout<<TEXT("Enter Exchange server name: ");
    tcin>>szServer;

tcout << TEXT("Profile to create: ") << szProfile << endl;

    // Initialize MAPI.

    if (FAILED(hRes = MAPIInitialize(NULL)))
    {
        tcout<<TEXT("Error initializing MAPI.");
        goto error;
    }

    // Get an IProfAdmin interface.

    if (FAILED(hRes = MAPIAdminProfiles(0, // Flags.
        &lpProfAdmin))) // Pointer to new IProfAdmin.
    {
        tcout<<TEXT("Error getting IProfAdmin interface.");
        goto error;
    }

    // Create a new profile.

    if (FAILED(hRes = lpProfAdmin->CreateProfile(szProfile, // Name of new profile.      <<<<<<<<< ERROR occurs here <<<<<<<<<<<
        NULL, // Password for profile.
        NULL, // Handle to parent window.
        MAPI_UNICODE))) // Flags.
    {
        tcout<<TEXT("Error creating profile.");
        goto error;
    }

    // Get an IMsgServiceAdmin interface off of the IProfAdmin interface.

    if (FAILED(hRes = lpProfAdmin->AdminServices(szProfile, // Profile that we want to modify.
        NULL, // Password for that profile.
        NULL, // Handle to parent window.
        0, // Flags.
        &lpSvcAdmin))) // Pointer to new IMsgServiceAdmin.
    {
        tcout<<TEXT("Error getting IMsgServiceAdmin interface.");
        goto error;
    }

    // Create the new message service for Exchange.

    if (FAILED(hRes = lpSvcAdmin->CreateMsgService(TEXT("MSEMS"), // Name of service from MAPISVC.INF.
        NULL, // Display name of service.
        NULL, // Handle to parent window.
        NULL))) // Flags.
    {
        tcout<<TEXT("Error creating Exchange message service.");
        goto error;
    }

    // We now need to get the entry id for the new service.
    // This can be done by getting the message service table
    // and getting the entry that corresponds to the new service.

    if (FAILED(hRes = lpSvcAdmin->GetMsgServiceTable(0, // Flags.
        &lpMsgSvcTable))) // Pointer to table.
    {
        tcout<<TEXT("Error getting Message Service Table.");
        goto error;
    }

    // Set up restriction to query table.

    sres.rt = RES_CONTENT;
    sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
    sres.res.resContent.ulPropTag = PR_SERVICE_NAME;
    sres.res.resContent.lpProp = &SvcProps;

    SvcProps.ulPropTag = PR_SERVICE_NAME;
    SvcProps.Value.lpszW = TEXT("MSEMS");

    // Query the table to get the entry for the newly created message service.

    if (FAILED(hRes = HrQueryAllRows(lpMsgSvcTable,
        (LPSPropTagArray)&sptCols,
        &sres,
        NULL,
        0,
        &lpSvcRows)))
    {
        tcout<<"Error querying table for new message service.";
        goto error;
    }

    // Setup a SPropValue array for the properties you need to configure.

    // First, the server name.
    ZeroMemory(&rgval[1], sizeof(SPropValue) );
    rgval[1].ulPropTag = PR_PROFILE_UNRESOLVED_SERVER;
    rgval[1].Value.lpszW = szServer;

    // Next, the mailbox name.
    ZeroMemory(&rgval[0], sizeof(SPropValue) );
    rgval[0].ulPropTag = PR_PROFILE_UNRESOLVED_NAME;
    rgval[0].Value.lpszW = szMailbox;

    // Configure the message service with the above properties.

    if (FAILED(hRes = lpSvcAdmin->ConfigureMsgService(
        (LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb, // Entry ID of service to configure.
        NULL, // Handle to parent window.
        0, // Flags.
        2, // Number of properties we are setting.
        rgval))) // Pointer to SPropValue array.
    {
        tcout<<TEXT("Error configuring message service.");
        goto error;
    }

    goto cleanup;

error:
    tcout<<TEXT(" hRes = 0x")<<hex<<hRes<<dec<<endl;
    return FALSE;

cleanup:
    // Clean up.
    if (lpSvcRows) FreeProws(lpSvcRows);
    if (lpMsgSvcTable) lpMsgSvcTable->Release();
    if (lpSvcAdmin) lpSvcAdmin->Release();
    if (lpProfAdmin) lpProfAdmin->Release();

    MAPIUninitialize();
    return TRUE;

}
0
 
LVL 6

Author Comment

by:inviser
ID: 16936858
The code compiles fine, but it gives a runtime error when the profile is trying to be created.

When I remove the MAPI_UNICODE flag, the profile creates fine, but only with the first letter (back to the drawing board!)
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 86

Expert Comment

by:jkr
ID: 16937034
What output do you get from

tcout << TEXT("Profile to create: ") << szProfile << endl;

?
0
 
LVL 6

Author Comment

by:inviser
ID: 16937318
It echos the profile back correctly.
0
 
LVL 86

Expert Comment

by:jkr
ID: 16937461
Which runtime error are you getting?
0
 
LVL 6

Author Comment

by:inviser
ID: 16937489
----------- Console Output ---------------

Enter name for profile: new
Enter Exchange mailbox name: blewis
Enter Exchange server name: mail3
Profile to create: new
Error creating profile. hRes = 0x80040103
Press any key to continue . . .
0
 
LVL 86

Expert Comment

by:jkr
ID: 16937743
That is 'MAPI_E_BAD_CHARWIDTH ((SCODE)0x80040103)' aka 'Either the MAPI_UNICODE flag was set and MAPI does not support Unicode, or MAPI_UNICODE was not set and MAPI only supports Unicode.'. Great. OK, can you try to remove the

#define UNICODE

at the top and also 'MAPI_UNICODE'?
0
 
LVL 6

Author Comment

by:inviser
ID: 16937782
Doing what you say gets rid of the error, but again, the name of the profile is only the first letter of szProfile.
0
 
LVL 86

Accepted Solution

by:
jkr earned 500 total points
ID: 16938203
Hm, that's getting *really* odd.
0
 
LVL 6

Author Comment

by:inviser
ID: 16938217
ya, thanks for the help though, ill give your the points and maybe you can email me your visual studio project that you have been using so I can check it out (blewis@shadowmountain.org).
0
 
LVL 86

Expert Comment

by:jkr
ID: 16939211
Well, apart from what I have posted, I haven't use anything but that code above and a command line of

cl.exe code.cpp

so there's not much that I could send you via email (which, by the way, isn't allowed by the EE rules: http://www.experts-exchange.com/Community_Support/help.jsp#hi99).

Also, you might want to rethink if you really wanted to accept a comment that did not solve your problem.
0
 

Expert Comment

by:exchnerd
ID: 22107719
Guys,

I am facing a similar problem but also that the ConfigureMsgService is unable to configure my new service but returns S_OK.

I have a posted a question here --
http://www.experts-exchange.com/Software/Office_Productivity/Groupware/Outlook/Q_23602157.html

Thanks,
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
How to install Selenium IDE and loops for quick automated testing. Get Selenium IDE from http://seleniumhq.org Go to that link and select download selenium in the right hand columnThat will then direct you to their download page.From that page s…
The viewer will learn how to use and create keystrokes in Netbeans IDE 8.0 for Windows.
The viewer will learn how to synchronize PHP projects with a remote server in NetBeans IDE 8.0 for Windows.

895 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now