You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
665 lines
18 KiB
665 lines
18 KiB
//
|
|
// stub migration dll for IME Dlls.
|
|
//
|
|
#include "pch.h"
|
|
#include "chs.h"
|
|
#include "cht.h"
|
|
#include "common.h"
|
|
#include "resource.h"
|
|
|
|
typedef struct {
|
|
CHAR CompanyName[256];
|
|
CHAR SupportNumber[256];
|
|
CHAR SupportUrl[256];
|
|
CHAR InstructionsToUser[1024];
|
|
} VENDORINFO, *PVENDORINFO;
|
|
|
|
//IME data
|
|
#define MAX_IME_DATA_FILE_NAME 20
|
|
|
|
TCHAR ChsDataFile[][MAX_IME_DATA_FILE_NAME]={
|
|
"winpy.emb",
|
|
"winsp.emb",
|
|
"winzm.emb",
|
|
"winbx.emb",
|
|
"winxpy.emb",
|
|
"winxsp.emb",
|
|
"winxzm.emb",
|
|
"winxbx.emb",
|
|
"user.rem",
|
|
"tmmr.rem",
|
|
0
|
|
};
|
|
|
|
TCHAR ChtDataFile[][MAX_IME_DATA_FILE_NAME]={
|
|
"lcptr.tbl",
|
|
"lcphrase.tbl",
|
|
0
|
|
};
|
|
|
|
CHAR ImeDataDirectory[MAX_PATH];
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
|
|
#define CP_USASCII 1252
|
|
#define CP_CHINESE_BIG5 950
|
|
#define CP_CHINESE_GB 936
|
|
#define END_OF_CODEPAGES -1
|
|
|
|
//
|
|
// Code page array, add relevant code pages that you support to this list..
|
|
//
|
|
|
|
INT g_CodePageArray[] = {
|
|
CP_USASCII,
|
|
END_OF_CODEPAGES
|
|
};
|
|
|
|
|
|
|
|
|
|
// PCSTR g_MyProductId = "This Must be LOCALIZED";
|
|
//
|
|
// load it from resource
|
|
//
|
|
TCHAR g_MyProductId[MAX_PATH];
|
|
|
|
|
|
VENDORINFO g_MyVendorInfo = {"Localized Company Name","Localized Support Number","Localized Support URL","Localized Instructions"};
|
|
|
|
//
|
|
// Handle to the process heap for allocations. Initialized in DllMain.
|
|
//
|
|
HANDLE g_hHeap;
|
|
|
|
HINSTANCE g_hInstance;
|
|
|
|
BOOL g_bCHSWin98 = FALSE;
|
|
|
|
#ifdef MYDBG
|
|
void Print(LPCTSTR pszFormat,...)
|
|
{
|
|
|
|
TCHAR szBuf[500];
|
|
TCHAR szBuf2[500];
|
|
va_list arglist;
|
|
|
|
va_start(arglist,pszFormat);
|
|
wvsprintf(szBuf,pszFormat,arglist);
|
|
wsprintf(szBuf2,"%s : %s",DBGTITLE,szBuf);
|
|
#ifdef SETUP
|
|
OutputDebugString(szBuf2);
|
|
#else
|
|
SetupLogError(szBuf2,LogSevInformation);
|
|
#endif
|
|
va_end(arglist);
|
|
}
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain (
|
|
IN HANDLE DllInstance,
|
|
IN ULONG ReasonForCall,
|
|
IN LPVOID Reserved
|
|
)
|
|
{
|
|
switch (ReasonForCall) {
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
g_hInstance = DllInstance;
|
|
//
|
|
// We don't need DLL_THREAD_ATTACH or DLL_THREAD_DETACH messages
|
|
//
|
|
DisableThreadLibraryCalls (DllInstance);
|
|
|
|
//
|
|
// Global init
|
|
//
|
|
g_hHeap = GetProcessHeap();
|
|
|
|
if (!MigInf_Initialize()) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Open log; FALSE means do not delete existing log
|
|
SetupOpenLog (FALSE);
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
g_hInstance = NULL;
|
|
MigInf_CleanUp();
|
|
SetupCloseLog();
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LPTSTR CheckSlash (LPTSTR lpDir)
|
|
{
|
|
DWORD dwStrLen;
|
|
LPTSTR lpEnd;
|
|
|
|
lpEnd = lpDir + lstrlen(lpDir);
|
|
|
|
if (*(lpEnd - 1) != TEXT('\\')) {
|
|
*lpEnd = TEXT('\\');
|
|
lpEnd++;
|
|
*lpEnd = TEXT('\0');
|
|
}
|
|
return lpEnd;
|
|
}
|
|
|
|
BOOL
|
|
CheckIfFileExisting(LPCTSTR pszFileName)
|
|
{
|
|
TCHAR szFullPathName[MAX_PATH];
|
|
LONG lResult;
|
|
|
|
//
|
|
// these files are in system directory
|
|
//
|
|
GetSystemDirectory(szFullPathName,MAX_PATH);
|
|
CheckSlash(szFullPathName);
|
|
lstrcat(szFullPathName,pszFileName);
|
|
|
|
|
|
lResult = GetFileAttributes(szFullPathName);
|
|
|
|
if (lResult == 0xFFFFFFFF) { // file does not exist
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
pMyImeInstalled (
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// Add code in this function that determines if your IME is installed on the system.
|
|
//
|
|
int i;
|
|
|
|
UINT uACP;
|
|
|
|
uACP = GetACP();
|
|
|
|
switch(uACP) {
|
|
case CP_CHINESE_GB: // Simplied Chinese
|
|
case CP_CHINESE_BIG5: // Traditional Chinese
|
|
g_CodePageArray[0] = uACP;
|
|
DebugMsg(("pMyImeInstalled OK, CodePage %d is valid\r\n",g_CodePageArray[0]));
|
|
return TRUE;
|
|
}
|
|
DebugMsg(("pMyImeInstalled Failed, CodePage %d is invalid\r\n",g_CodePageArray[0]));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
LONG
|
|
CALLBACK
|
|
QueryVersion (
|
|
OUT LPCSTR * ProductID,
|
|
OUT LPUINT DllVersion,
|
|
OUT LPINT * CodePageArray, OPTIONAL
|
|
OUT LPCSTR * ExeNamesBuf, OPTIONAL
|
|
OUT PVENDORINFO * VendorInfo
|
|
)
|
|
{
|
|
LONG returnCode = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Add code to pMyImeInstalled() to determine wether your IME is installed. If this function
|
|
// returns TRUE, Setup will call this migration dll.
|
|
//
|
|
|
|
if (pMyImeInstalled()) {
|
|
|
|
//
|
|
// We are installed, so tell Setup who we are. ProductID is used
|
|
// for display, so it must be localized. The ProductID string is
|
|
// converted to UNICODE for use on Windows NT via the MultiByteToWideChar
|
|
// Win32 API. The first element of CodePageArray is used to specify
|
|
// the code page of ProductID, and if no elements are returned in
|
|
// CodePageArray, Setup assumes CP_ACP.
|
|
//
|
|
|
|
|
|
LoadString(g_hInstance,IDS_PRODUCTID,g_MyProductId,MAX_PATH);
|
|
|
|
*ProductID = g_MyProductId;
|
|
|
|
//
|
|
// Report our version. Zero is reserved for use by DLLs that
|
|
// ship with Windows NT.
|
|
//
|
|
|
|
*DllVersion = 1;
|
|
|
|
//
|
|
// Because we have English messages, we return an array that has
|
|
// the English language ID. The sublanguage is neutral because
|
|
// we do not have currency, time, or other geographic-specific
|
|
// information in our messages.
|
|
//
|
|
// Tip: If it makes more sense for your DLL to use locales,
|
|
// return ERROR_NOT_INSTALLED if the DLL detects that an appropriate
|
|
// locale is not installed on the machine.
|
|
//
|
|
|
|
//
|
|
// The CODE PAGE INFO is determined in 'pMyImeInstalled'
|
|
//
|
|
*CodePageArray = g_CodePageArray;
|
|
|
|
DebugMsg(("CodePageArray = %d\r\n",g_CodePageArray[0]));
|
|
|
|
//
|
|
// Use system default code page
|
|
//
|
|
|
|
//
|
|
// ExeNamesBuf - we pass a list of file names (the long versions)
|
|
// and let Setup find them for us. Keep this list short because
|
|
// every instance of the file on every hard drive will be reported
|
|
// in migrate.inf.
|
|
//
|
|
// Most applications don't need this behavior, because the registry
|
|
// usually contains full paths to installed components.
|
|
//
|
|
|
|
*ExeNamesBuf = NULL;
|
|
|
|
//
|
|
// VendorInfo is designed to contain support information for a Migration DLL. Since it
|
|
// may be used for UI, it's fields should also be localized.
|
|
//
|
|
LoadString(g_hInstance,MSG_VI_COMPANY_NAME ,g_MyVendorInfo.CompanyName ,256);
|
|
LoadString(g_hInstance,MSG_VI_SUPPORT_NUMBER ,g_MyVendorInfo.SupportNumber ,256);
|
|
LoadString(g_hInstance,MSG_VI_SUPPORT_URL ,g_MyVendorInfo.SupportUrl ,256);
|
|
LoadString(g_hInstance,MSG_VI_INSTRUCTIONS ,g_MyVendorInfo.InstructionsToUser,1024);
|
|
|
|
|
|
*VendorInfo = &g_MyVendorInfo;
|
|
|
|
DebugMsg(("CompanyName = %s\r\n",g_MyVendorInfo.CompanyName));
|
|
DebugMsg(("SupportNumber = %s\r\n",g_MyVendorInfo.SupportNumber));
|
|
DebugMsg(("SupportUrl = %s\r\n",g_MyVendorInfo.SupportUrl));
|
|
DebugMsg(("InstructionsToUser = %s\r\n",g_MyVendorInfo.InstructionsToUser));
|
|
|
|
}
|
|
else {
|
|
//
|
|
// If pMyImeInstalled returns false, we have nothing to do. By returning ERROR_NOT_INSTALLED,
|
|
// we ensure that we will not be called again.
|
|
//
|
|
returnCode = ERROR_NOT_INSTALLED;
|
|
}
|
|
|
|
DebugMsg(("QueryVersion, return value = %d\r\n",returnCode));
|
|
|
|
return returnCode;
|
|
}
|
|
|
|
//Save IME data file to working directory.
|
|
BOOL SaveImeDataFile(LPSTR SourceDirectory, LPSTR TargetDirectory, TCHAR * FileBuf, BOOL CheckAll)
|
|
{
|
|
int lenSource = lstrlen(SourceDirectory);
|
|
int lenTarget = lstrlen(TargetDirectory);
|
|
HANDLE hfile;
|
|
|
|
|
|
while (*FileBuf)
|
|
{
|
|
lstrcat(SourceDirectory, FileBuf);
|
|
lstrcat(TargetDirectory, FileBuf);
|
|
|
|
if ((GetFileAttributes(SourceDirectory) != 0xFFFFFFFF) &&
|
|
(GetFileAttributes(SourceDirectory) != FILE_ATTRIBUTE_DIRECTORY)){
|
|
if (!CopyFile(SourceDirectory, TargetDirectory, FALSE)) {
|
|
DebugMsg(("Copy file %s to %s failed \r\n",SourceDirectory,TargetDirectory));
|
|
} else {
|
|
DebugMsg(("Copy file %s to %s OK \r\n",SourceDirectory,TargetDirectory));
|
|
}
|
|
} else {
|
|
DebugMsg(("File %s doesn't exist, skip it ! \r\n",SourceDirectory));
|
|
}
|
|
FileBuf+=MAX_IME_DATA_FILE_NAME;
|
|
|
|
SourceDirectory[lenSource]=0;
|
|
TargetDirectory[lenTarget]=0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LONG
|
|
CALLBACK
|
|
Initialize9x (
|
|
IN LPCSTR WorkingDirectory,
|
|
IN LPCSTR SourceDirectories,
|
|
LPVOID Reserved
|
|
)
|
|
{
|
|
|
|
LONG returnCode = ERROR_SUCCESS;
|
|
|
|
UINT len;
|
|
TCHAR FilePath[MAX_PATH];
|
|
TCHAR TargetPath[MAX_PATH];
|
|
BOOL bInstall;
|
|
|
|
UINT uACP;
|
|
|
|
//
|
|
// Because we returned ERROR_SUCCESS in QueryVersion, we are being
|
|
// called for initialization. Therefore, we know screen savers are
|
|
// enabled on the machine at this point.
|
|
//
|
|
|
|
//
|
|
// Do any Windows9x side initialization that is necessary here.
|
|
//
|
|
DebugMsg(("Start ..., Initialize9x\r\n"));
|
|
|
|
lstrcpy(TargetPath, WorkingDirectory);
|
|
len=lstrlen(TargetPath);
|
|
if (TargetPath[len-1] != '\\') {
|
|
TargetPath[len] ='\\';
|
|
TargetPath[++len] = 0;
|
|
}
|
|
DebugMsg(("Initialize9x, TargetPath = %s\r\n",TargetPath));
|
|
|
|
len = GetSystemDirectory((LPSTR)FilePath, sizeof(FilePath));
|
|
// Consider root directory
|
|
if (FilePath[len - 1] != '\\') {
|
|
FilePath[len] = '\\';
|
|
FilePath[++len] = 0;
|
|
}
|
|
DebugMsg(("Initialize9x, SystemPath = %s\r\n",FilePath));
|
|
|
|
uACP = GetACP();
|
|
|
|
switch (uACP) {
|
|
|
|
case CP_CHINESE_GB:
|
|
{
|
|
//
|
|
// The Ime tables in CHS Win98 are already unicode format
|
|
//
|
|
// we don't need to do convert tables , just back up them
|
|
//
|
|
UINT CreateNestedDirectory(LPCTSTR, LPSECURITY_ATTRIBUTES);
|
|
TCHAR szWin98Dir[MAX_PATH];
|
|
|
|
OSVERSIONINFO OsVersion;
|
|
|
|
OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
GetVersionEx(&OsVersion);
|
|
|
|
|
|
lstrcpy(szWin98Dir,TargetPath);
|
|
|
|
if ((OsVersion.dwMajorVersion == 4) &&
|
|
(OsVersion.dwMinorVersion == 10)) {
|
|
//
|
|
// this is Windows 98, create a "Win98" subdirectory
|
|
//
|
|
|
|
DebugMsg(("Initialize9x, SaveImeDataFile, GB, Win98 identified !\r\n"));
|
|
lstrcat(szWin98Dir,"Win98");
|
|
DebugMsg(("Initialize9x, SaveImeDataFile, Create %s !\r\n",szWin98Dir));
|
|
CreateNestedDirectory(szWin98Dir,NULL);
|
|
DebugMsg(("Initialize9x, SaveImeDataFile, The target path become %s !\r\n",TargetPath));
|
|
}
|
|
|
|
if (! SaveImeDataFile(FilePath, TargetPath, &ChsDataFile[0][0], FALSE)) {
|
|
DebugMsg(("Initialize9x, SaveImeDataFile, GB, failed !\r\n"));
|
|
returnCode = ERROR_NOT_INSTALLED;
|
|
}
|
|
}
|
|
break;
|
|
case CP_CHINESE_BIG5:
|
|
if (! SaveImeDataFile(FilePath, TargetPath, &ChtDataFile[0][0], TRUE)) {
|
|
DebugMsg(("Initialize9x, SaveImeDataFile, BIG5, failed !\r\n"));
|
|
returnCode = ERROR_NOT_INSTALLED;
|
|
}
|
|
break;
|
|
default:
|
|
DebugMsg(("Initialize9x, Invalid codepage !\r\n"));
|
|
returnCode = ERROR_NOT_INSTALLED;
|
|
}
|
|
DebugMsg(("Initialize9x, SaveImeDataFile OK [%d]!\r\n",returnCode));
|
|
return returnCode;
|
|
}
|
|
|
|
LONG
|
|
CALLBACK
|
|
MigrateUser9x (
|
|
IN HWND ParentWnd,
|
|
IN LPCSTR UnattendFile,
|
|
IN HKEY UserRegKey,
|
|
IN LPCSTR UserName,
|
|
LPVOID Reserved
|
|
)
|
|
{
|
|
DWORD returnCode = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Avoid displaying any user interface when possible.
|
|
//
|
|
// We don't need to use UnattendFile settings because we are not
|
|
// a service (such as a network redirector). Therefore, we do not
|
|
// use the UnattendFile parameter.
|
|
//
|
|
//
|
|
// Note: NO changes allowed on Win9x side, we can only read our
|
|
// settings and save them in a file.
|
|
//
|
|
//
|
|
// UserRegKey should be used instead of HKCU. You will be called once for
|
|
// each user on the system (including logon user and administrator). Each time,
|
|
// the correct user root will have been mapped into HKCU.
|
|
//
|
|
|
|
return returnCode;
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
CALLBACK
|
|
MigrateSystem9x (
|
|
IN HWND ParentWnd,
|
|
IN LPCSTR UnattendFile,
|
|
LPVOID Reserved
|
|
)
|
|
{
|
|
LONG returnCode = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Gather all necessary system wide data in this function.
|
|
//
|
|
|
|
|
|
|
|
return returnCode;
|
|
}
|
|
|
|
|
|
LONG
|
|
CALLBACK
|
|
InitializeNT (
|
|
IN LPCWSTR WorkingDirectory,
|
|
IN LPCWSTR SourceDirectories,
|
|
LPVOID Reserved
|
|
)
|
|
{
|
|
LONG returnCode = ERROR_SUCCESS;
|
|
int len;
|
|
UINT uACP;
|
|
|
|
|
|
//
|
|
// Do any intialization for NT side processing in this function.
|
|
//
|
|
|
|
//Save working directory path
|
|
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
WorkingDirectory,
|
|
-1,
|
|
ImeDataDirectory,
|
|
sizeof(ImeDataDirectory),
|
|
NULL,
|
|
NULL);
|
|
|
|
DebugMsg(("InitializeNT, Save working directory path, ImeDataDirectory = %s\r\n",ImeDataDirectory));
|
|
|
|
//Patch path with '\'
|
|
len = lstrlen(ImeDataDirectory);
|
|
if (ImeDataDirectory[len - 1] != '\\') {
|
|
ImeDataDirectory[len] = '\\';
|
|
ImeDataDirectory[++len] = 0;
|
|
}
|
|
DebugMsg(("InitializeNT, Patch path with '\', ImeDataDirectory = %s\r\n",ImeDataDirectory));
|
|
DebugMsg(("InitializeNT, OK !\r\n"));
|
|
|
|
uACP = GetACP();
|
|
|
|
if (uACP == 936) {
|
|
TCHAR szWin98Dir[MAX_PATH];
|
|
//
|
|
// check if this is CHS Win98
|
|
//
|
|
lstrcpy(szWin98Dir,ImeDataDirectory);
|
|
|
|
//
|
|
// Check if ...\Win98 directory is existing or not
|
|
//
|
|
// If it is, then it means we're migrating Win98
|
|
//
|
|
ConcatenatePaths(szWin98Dir,"Win98",sizeof(szWin98Dir));
|
|
|
|
DebugMsg(("ImeEudcConvert::MigrateImeEUDCTables2 ,Test IME98 directory %s !\r\n",szWin98Dir));
|
|
if (GetFileAttributes(szWin98Dir) == 0xFFFFFFFF || ! (GetFileAttributes(szWin98Dir) & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
g_bCHSWin98 = FALSE;
|
|
} else {
|
|
g_bCHSWin98 = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
return returnCode;
|
|
}
|
|
|
|
|
|
LONG
|
|
CALLBACK
|
|
MigrateUserNT (
|
|
IN HINF UnattendInfHandle,
|
|
IN HKEY UserRegKey,
|
|
IN LPCWSTR UserName,
|
|
LPVOID Reserved
|
|
)
|
|
{
|
|
LONG returnCode = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Migrate all necessary user settings for your IME in this function call. Once again, remember
|
|
// to use UserRegKey in place of HKCU.
|
|
//
|
|
DebugMsg(("MigrateUserNT,Starting ... !\r\n"));
|
|
DebugMsg(("MigrateUserNT,The user is %ws !\r\n",UserName));
|
|
|
|
if (!MigrateImeEUDCTables(UserRegKey)) {
|
|
returnCode = ERROR_NOT_INSTALLED;
|
|
DebugMsg(("MigrateUserNT,MigrateImeEUDCTables failed !\r\n"));
|
|
} else {
|
|
DebugMsg(("MigrateUserNT,MigrateImeEUDCTables OK !\r\n"));
|
|
}
|
|
|
|
if (!MigrateImeEUDCTables2(UserRegKey)) {
|
|
returnCode = ERROR_NOT_INSTALLED;
|
|
DebugMsg(("MigrateUserNT,MigrateImeEUDCTables2 failed !\r\n"));
|
|
} else {
|
|
DebugMsg(("MigrateUserNT,MigrateImeEUDCTables2 OK !\r\n"));
|
|
}
|
|
DebugMsg(("MigrateUserNT,Finished !\r\n"));
|
|
return returnCode;
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
CALLBACK
|
|
MigrateSystemNT (
|
|
IN HINF UnattendInfHandle,
|
|
LPVOID Reserved
|
|
)
|
|
{
|
|
LONG returnCode = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Migrate all necessary system settings for your IME in this function call. Anything relative to
|
|
// a user should have been handled during MigrateUserNT.
|
|
//
|
|
UINT uACP;
|
|
|
|
uACP = GetACP();
|
|
|
|
switch(uACP) {
|
|
|
|
case CP_CHINESE_GB: // Simplied Chinese
|
|
if (ConvertChsImeData()) {
|
|
DebugMsg(("MigrateSystemNT,GB, ConvertChsImeData OK !\r\n"));
|
|
} else {
|
|
DebugMsg(("MigrateSystemNT,GB, ConvertChsImeData OK !\r\n"));
|
|
}
|
|
|
|
MovePerUserIMEData();
|
|
|
|
if (CHSBackupWinABCUserDict(ImeDataDirectory)) {
|
|
DebugMsg(("MigrateSystemNT,GB, CHSBackupWinABCUserDict OK !\r\n"));
|
|
} else {
|
|
DebugMsg(("MigrateSystemNT,GB, CHSBackupWinABCUserDict OK !\r\n"));
|
|
}
|
|
|
|
if (CHSDeleteGBKKbdLayout()) {
|
|
DebugMsg(("MigrateSystemNT,GB, CHSDeleteGBKKbdLayout OK !\r\n"));
|
|
} else {
|
|
DebugMsg(("MigrateSystemNT,GB, CHSDeleteGBKKbdLayout OK !\r\n"));
|
|
}
|
|
|
|
break;
|
|
|
|
case CP_CHINESE_BIG5: // Traditional Chinese
|
|
if (ConvertChtImeData()) {
|
|
DebugMsg(("MigrateSystemNT,BIG5, ConvertChtImeData OK !\r\n"));
|
|
} else {
|
|
DebugMsg(("MigrateSystemNT,BIG5, ConvertChtImeData OK !\r\n"));
|
|
}
|
|
MovePerUserIMEData();
|
|
|
|
break;
|
|
|
|
default:
|
|
returnCode = ERROR_NOT_INSTALLED;
|
|
}
|
|
|
|
return returnCode;
|
|
}
|
|
|
|
|