Csrss.exe初始化源代码分析

Csrss.exe初始化源代码分析

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems]

"Windows"=%SystemRoot%\system32\csrss.exe

ObjectDirectory=\Windows SharedSection=1024,3072,512

Windows=On SubSystemType=Windows

ServerDll=basesrv,1
ServerDll=winsrv:UserServerDllInitialization,3

ServerDll=winsrv:ConServerDllInitialization,2

ProfileControl=Off MaxRequestThreads=16

ServerDll等号后面是动态库的文件名,如basesrv.dll。逗号后面的是这个库注册的序号。如basesrv.dll的就是1

          冒号后面的名字是子系统加载这个动态库时候调用的函数名,如果没有提供,就默认是“ServerDllInitialization”

上面说到注册,子系统csrss.exe用一个数组来保存这些注册的动态库。

这个数组名叫 CsrLoadedServerDll,这个变量是在csrsrv.dll中,一共只有4个元素。

每个元素是一个_CSR_SERVER_DLL结构。包括上面注册表提供的3个,再加上csrss.exe初始化时,提供的一个序号0的元素,一共4个。

其实csrss只是一个外壳,很多处理函数都是在csrsrv.dll中。

csrss.exe在初始化时候,调用csrsrv.dll的导出函数

 int __stdcall CsrServerInitialization(unsigned int argc, char **argv)

{

//这个函数里面会创建那个核心LPC端口"\\Windows\\ApiPort",

并且会创建一个线程CsrApiRequestThread专门来处理来自这个端口的各种LPC请求

  int __stdcall CsrApiPortInitialize();

//这个函数里面会创建LPC端口"\\Windows\\SbApiPort",

并且会创建一个线程CsrSbApiRequestThread专门来处理来自这个端口的各种LPC请求

   int __stdcall CsrSbApiPortInitialize();

int __stdcall CsrParseServerCommandLine(unsigned int argc, char **argv)

}

CsrParseServerCommandLine就会解析上面注册表提供的参数。

逐个加载进来。并为每个dll申请一个_CSR_SERVER_DLL结构。

接着调用dll提供的初始化函数,如上面注册表的就是那些冒号后面的函数。

参数就是_CSR_SERVER_DLL结构。

这样每个dll都可以有一次初始化提供自己私有数据初始化_CSR_SERVER_DLL结构的机会。

最重要的一个私有数据字段是_CSR_SERVER_DLL->ApiDispatchTable。

这个字段指明一个函数分发表。

以后"\\Windows\\ApiPort" 这个LPC有请求的时候,一般客户端发起请求是,是调用下面的函数:CsrClientCallServer()

Status = CsrClientCallServer(

&m,
CaptureBuffer,
CSR_MAKE_API_NUMBER( CSRSRV_SERVERDLL_INDEX,CsrpClientConnect),
sizeof( *a )
);

CsrApiRequestThread会根据其中的APINUMBER来索引对应的_CSR_SERVER_DLL结构。

和_CSR_SERVER_DLL->ApiDispatchTable[xxx]处理函数,所以这个apinumber比较特殊,它能够索引对应的dll和对应的分发处理函数。

所以winsrv.dll里面有两个重要的保存函数的表:ConsoleServerApiDispatchTable(控制台管理),UserServerApiDispatchTable(终端登录之类)。

最后我们发现basesrv.DLL也存在一张保存函数的表:BaseServerApiDispatchTable,

Csrsrv.dll存在CsrServerApiDispatchTable这张表。

        四个分发表序号如下:

        CsrServerApiDispatchTable:0

        BaseServerApiDispatchTable:1

        ConsoleServerApiDispatchTable:2

        UserServerApiDispatchTable:3

第一部分:

base/win32/csrss/csrss.c文件中的main()函数调用

base/win32/server/srvinit.c文件中的CsrServerInitialization()函数

int

_cdecl

main(

    IN ULONG argc,

    IN PCH argv[],

    IN PCH envp[],

    IN ULONG DebugFlag OPTIONAL

    )

{

    NTSTATUS Status;

    ULONG ErrorResponse;

    KPRIORITY SetBasePriority;

    SetBasePriority = FOREGROUND_BASE_PRIORITY + 4;

    Status = NtSetInformationProcess (NtCurrentProcess(),

                                      ProcessBasePriority,

                                      (PVOID) &SetBasePriority,

                                      sizeof(SetBasePriority));

    ASSERT (NT_SUCCESS (Status));

    Status = CsrServerInitialization( argc, argv );

    if (!NT_SUCCESS( Status )) {

        IF_DEBUG {

    DbgPrint( "CSRSS: Unable to initialize server.  status == %X\n",

      Status

                    );

        }

NtTerminateProcess( NtCurrentProcess(), Status );

    }

    DisableErrorPopups();

    if (NtCurrentPeb()->SessionId == 0) {

        //

        // Make terminating the root csrss fatal

        //

        RtlSetProcessIsCritical(TRUE, NULL, FALSE);

    }

    NtTerminateThread( NtCurrentThread(), Status );

    return( 0 );

}

第二部分:

base/win32/server/srvinit.c

CsrParseServerCommandLine()函数

// Though this function does not seem to cleanup on failure, failure

// will cause CSRSS to exit, so any allocated memory will be freed and

// any open handle will be closed.

NTSTATUS

CsrServerInitialization(

    IN ULONG argc,

    IN PCH argv[]

    )

{

    NTSTATUS Status;

    ULONG i;

    PVOID ProcessDataPtr;

    PCSR_SERVER_DLL LoadedServerDll;

#if DBG

    BOOLEAN bIsRemoteSession =  NtCurrentPeb()->SessionId != 0;

#endif

    //

    // Initialize Wow64 stuffs

    //

#ifdef _WIN64

    InitializeWow64OnBoot(1);

#endif

    //

    // Save away system information in a global variable

    //

    Status = NtQuerySystemInformation( SystemBasicInformation,

                                       &CsrNtSysInfo,

                                       sizeof( CsrNtSysInfo ),

                                       NULL

                                     );

    ASSERT( NT_SUCCESS( Status ) || bIsRemoteSession );

    if (!NT_SUCCESS( Status )) {

        return Status;

    }

    //

    // Use the process heap for memory allocation.

    //

    CsrHeap = RtlProcessHeap();

    CsrBaseTag = RtlCreateTagHeap( CsrHeap,

                                   0,

                                   L"CSRSS!",

                                   L"TMP\0"

                                   L"INIT\0"

                                   L"CAPTURE\0"

                                   L"PROCESS\0"

                                   L"THREAD\0"

                                   L"SECURITY\0"

                                   L"SESSION\0"

                                   L"WAIT\0"

                                 );

    //

    // Set up CSRSS process security

    //

    Status = CsrSetProcessSecurity();

    ASSERT(NT_SUCCESS(Status) || bIsRemoteSession);

    if (!NT_SUCCESS(Status)) {

        return Status;

    }

    //

    // Initialize the Session List

    //

    Status = CsrInitializeNtSessionList();

    ASSERT(NT_SUCCESS(Status) || bIsRemoteSession);

    if (!NT_SUCCESS(Status)) {

        return Status;

    }

    //

    // Initialize the Process List

    //

    Status = CsrInitializeProcessStructure();

    ASSERT(NT_SUCCESS(Status) || bIsRemoteSession);

    if (!NT_SUCCESS(Status)) {

        return Status;

    }

    //

    // Process the command line arguments

    //

    Status = CsrParseServerCommandLine(argc, argv);

    ASSERT(NT_SUCCESS(Status) || bIsRemoteSession);

    if (!NT_SUCCESS(Status)) {

        return Status;

    }

#if DBG

    Status = RtlInitializeCriticalSection(&CsrTrackLpcLock);

    if (!NT_SUCCESS(Status)) {

        return Status;

    }

    LpcTrackIndex = 0;

#endif

    //

    // Fix up per-process data for root process

    //

    ProcessDataPtr = (PCSR_PROCESS)RtlAllocateHeap( CsrHeap,

                                                    MAKE_TAG( PROCESS_TAG ) | HEAP_ZERO_MEMORY,

                                                    CsrTotalPerProcessDataLength

                                                  );

    if (ProcessDataPtr == NULL) {

                return STATUS_NO_MEMORY;

    }

    for (i=0; i<CSR_MAX_SERVER_DLL; i++) {

        LoadedServerDll = CsrLoadedServerDll[ i ];

        if (LoadedServerDll && LoadedServerDll->PerProcessDataLength) {

            CsrRootProcess->ServerDllPerProcessData[i] = ProcessDataPtr;

            ProcessDataPtr = (PVOID)QUAD_ALIGN((PCHAR)ProcessDataPtr + LoadedServerDll->PerProcessDataLength);

        }

        else {

            CsrRootProcess->ServerDllPerProcessData[i] = NULL;

        }

    }

    //

    // Let server dlls know about the root process.

    //

    for (i=0; i<CSR_MAX_SERVER_DLL; i++) {

        LoadedServerDll = CsrLoadedServerDll[ i ];

        if (LoadedServerDll && LoadedServerDll->AddProcessRoutine) {

            (*LoadedServerDll->AddProcessRoutine)( NULL, CsrRootProcess );

        }

    }

    //

    // Initialize the Windows Server API Port, and one or more

    // request threads.

    //

    Status = CsrApiPortInitialize();

    ASSERT( NT_SUCCESS( Status ) || bIsRemoteSession );

    if (!NT_SUCCESS( Status )) {

                return Status;

        }

    //

    // Initialize the Server Session Manager API Port and one

    // request thread.

    //

    Status = CsrSbApiPortInitialize();

    ASSERT( NT_SUCCESS( Status ) || bIsRemoteSession);

    if (!NT_SUCCESS( Status )) {

                return Status;

        }

    //

    // Connect to the session manager so we can start foreign sessions

    //

    Status = SmConnectToSm( &CsrSbApiPortName,

                            CsrSbApiPort,

                            IMAGE_SUBSYSTEM_WINDOWS_GUI,

                            &CsrSmApiPort

                          );

    ASSERT( NT_SUCCESS( Status ) || bIsRemoteSession );

    if (!NT_SUCCESS( Status )) {

                return Status;

        }

    //

    //  Only on Console (HYDRA)

    //

    if (NtCurrentPeb()->SessionId == 0) {

        Status = NtSetDefaultHardErrorPort(CsrApiPort);

    }

    return( Status );

}

第三部分:

base/win32/server/srvinit.c

NTSTATUS

CsrParseServerCommandLine(

    IN ULONG argc,

    IN PCH argv[]

    )

{

    NTSTATUS Status;

    OBJECT_ATTRIBUTES ObjectAttributes;

    ULONG i, ServerDllIndex;

    PCH KeyName, KeyValue, s;

    PCH InitRoutine;

    CsrTotalPerProcessDataLength = 0;

    CsrObjectDirectory = NULL;

    CsrMaxApiRequestThreads = CSR_MAX_THREADS;

    SessionId = NtCurrentPeb()->SessionId;

    //

    // Create session specific object directories

    //

    Status = CsrCreateSessionObjectDirectory ( SessionId );

    if (!NT_SUCCESS(Status)) {

       if (SessionId == 0) {

           ASSERT( NT_SUCCESS( Status ) );

           DbgPrint("CSRSS: CsrCreateSessionObjectDirectory failed status = %lx\n", Status);

       } else {

           DbgPrint("CSRSS: CsrCreateSessionObjectDirectory failed status = %lx\n", Status);

           return Status;

       }

    }

    for (i=1; i<argc ; i++) {

        KeyName = argv[ i ];

        KeyValue = NULL;

        while (*KeyName) {

            if (*KeyName == '=') {

                *KeyName++ = '\0';

                KeyValue = KeyName;

                break;

                }

            KeyName++;

            }

        KeyName = argv[ i ];

        if (!_stricmp( KeyName, "ObjectDirectory" )) {

            ANSI_STRING AnsiString;

            ULONG attributes;

            CHAR SessionDirectory[MAX_SESSION_PATH];

            if (SessionId != 0) {

                //

                // Non-Console session

                //

                _snprintf(SessionDirectory, sizeof (SessionDirectory), "%ws\\%ld%s", SESSION_ROOT, SessionId, KeyValue);

                SessionDirectory[MAX_SESSION_PATH-1] = '\0';

            }

            //

            // Create an object directory in the object name space with the

            // name specified.   It will be the root for all object names

            // created by the Server side of the Client Server Runtime

            // SubSystem.

            //

            attributes =  OBJ_OPENIF | OBJ_CASE_INSENSITIVE;

            if (SessionId == 0) {

               attributes |= OBJ_PERMANENT;

               RtlInitString( &AnsiString, KeyValue );

            } else {

               RtlInitString( &AnsiString, SessionDirectory );

            }

            Status = RtlAnsiStringToUnicodeString( &CsrDirectoryName, &AnsiString, TRUE );

            ASSERT(NT_SUCCESS(Status) || SessionId != 0);

            if (!NT_SUCCESS( Status )) {

                break;

            }

            InitializeObjectAttributes( &ObjectAttributes,

                                        &CsrDirectoryName,

                                        attributes,

                                        NULL,

                                        NULL

                                      );

            Status = NtCreateDirectoryObject( &CsrObjectDirectory,

                                              DIRECTORY_ALL_ACCESS,

                                              &ObjectAttributes

                                            );

            if (!NT_SUCCESS( Status )) {

                break;

                }

            Status = CsrSetDirectorySecurity( CsrObjectDirectory );

            if (!NT_SUCCESS( Status )) {

                break;

                }

            }

        else

        if (!_stricmp( KeyName, "SubSystemType" )) {

            }

        else

        if (!_stricmp( KeyName, "MaxRequestThreads" )) {

            Status = RtlCharToInteger( KeyValue,

                                       0,

                                       &CsrMaxApiRequestThreads

                                     );

            }

        else

        if (!_stricmp( KeyName, "RequestThreads" )) {

#if 0

            Status = RtlCharToInteger( KeyValue,

                                       0,

                                       &CsrNumberApiRequestThreads

                                     );

#else

            //

            // wait until hive change !

            //

            Status = STATUS_SUCCESS;

#endif

            }

        else

        if (!_stricmp( KeyName, "ProfileControl" )) {

            }

        else

        if (!_stricmp( KeyName, "SharedSection" )) {

            Status = CsrSrvCreateSharedSection( KeyValue );

            if (!NT_SUCCESS( Status )) {

                IF_DEBUG {

                    DbgPrint( "CSRSS: *** Invalid syntax for %s=%s (Status == %X)\n",

                              KeyName,

                              KeyValue,

                              Status

                            );

                    }

                                return Status;

                }

            Status = CsrLoadServerDll( "CSRSS", NULL, CSRSRV_SERVERDLL_INDEX );

            }

        else

        if (!_stricmp( KeyName, "ServerDLL" )) {

            s = KeyValue;

            InitRoutine = NULL;

            Status = STATUS_INVALID_PARAMETER;

            while (*s) {

                if ((*s == ':') && (InitRoutine == NULL)) {

                    *s++ = '\0';

                    InitRoutine = s;

                }

                if (*s++ == ',') {

                    Status = RtlCharToInteger ( s, 10, &ServerDllIndex );

                    if (NT_SUCCESS( Status )) {

                        s[ -1 ] = '\0';

                        }

                    break;

                    }

                }

            if (!NT_SUCCESS( Status )) {

                IF_DEBUG {

                    DbgPrint( "CSRSS: *** Invalid syntax for ServerDll=%s (Status == %X)\n",

                              KeyValue,

                              Status

                            );

                    }

                }

            else {

                IF_CSR_DEBUG( INIT) {

                    DbgPrint( "CSRSS: Loading ServerDll=%s:%s\n", KeyValue, InitRoutine );

                    }

                Status = CsrLoadServerDll( KeyValue, InitRoutine, ServerDllIndex);

                if (!NT_SUCCESS( Status )) {

                    IF_DEBUG {

                        DbgPrint( "CSRSS: *** Failed loading ServerDll=%s (Status == 0x%x)\n",

                                  KeyValue,

                                  Status

                                );

                        }

                    return Status;

                    }

                }

            }

        else

        //

        // This is a temporary hack until Windows & Console are friends.

        //

        if (!_stricmp( KeyName, "Windows" )) {

            }

        else {

            Status = STATUS_INVALID_PARAMETER;

            }

        }

    return( Status );

}

第四部分:

base/subsys/csr/server/srvloadr.c:122:CsrLoadServerDll(

NTSTATUS

CsrLoadServerDll(

    IN PCH ModuleName,

    IN PCH InitRoutineString,

    IN ULONG ServerDllIndex

    )

{

    NTSTATUS Status;

    ANSI_STRING ModuleNameString;

    UNICODE_STRING ModuleNameString_U;

    HANDLE ModuleHandle;

    PCSR_SERVER_DLL LoadedServerDll;

    STRING ProcedureNameString;

    PCSR_SERVER_DLL_INIT_ROUTINE ServerDllInitialization;

    ULONG n;

    if (ServerDllIndex >= CSR_MAX_SERVER_DLL) {

        return( STATUS_TOO_MANY_NAMES );

        }

    if (CsrLoadedServerDll[ ServerDllIndex ] != NULL) {

        return( STATUS_INVALID_PARAMETER );

        }

    ASSERT( ModuleName != NULL );

    RtlInitAnsiString( &ModuleNameString, ModuleName );

    if (ServerDllIndex != CSRSRV_SERVERDLL_INDEX) {

        Status = RtlAnsiStringToUnicodeString(&ModuleNameString_U, &ModuleNameString, TRUE);

        if (!NT_SUCCESS(Status)) {

            return Status;

            }

        Status = LdrLoadDll( UNICODE_NULL, NULL, &ModuleNameString_U, &ModuleHandle );

        if ( !NT_SUCCESS(Status) ) {

            PUNICODE_STRING ErrorStrings[2];

            UNICODE_STRING ErrorDllPath;

            ULONG ErrorResponse;

            NTSTATUS ErrorStatus;

            ErrorStrings[0] = &ModuleNameString_U;

            ErrorStrings[1] = &ErrorDllPath;

            RtlInitUnicodeString(&ErrorDllPath,L"Default Load Path");

            //

            // need to get image name

            //

            ErrorStatus = NtRaiseHardError(

                            (NTSTATUS)STATUS_DLL_NOT_FOUND,

                            2,

                            0x00000003,

                            (PULONG_PTR)ErrorStrings,

                            OptionOk,

                            &ErrorResponse

                            );

            }

        RtlFreeUnicodeString(&ModuleNameString_U);

        if (!NT_SUCCESS( Status )) {

            return( Status );

            }

        }

    else {

        ModuleHandle = NULL;

        }

    n = sizeof( *LoadedServerDll ) + ModuleNameString.MaximumLength;

    LoadedServerDll = RtlAllocateHeap( CsrHeap, MAKE_TAG( INIT_TAG ), n );

    if (LoadedServerDll == NULL) {

        if (ModuleHandle != NULL) {

            LdrUnloadDll( ModuleHandle );

            }

        return( STATUS_NO_MEMORY );

        }

    RtlZeroMemory( LoadedServerDll, n );

    LoadedServerDll->SharedStaticServerData = CsrSrvSharedSectionHeap;

    LoadedServerDll->Length = n;

    LoadedServerDll->ModuleName.Length = ModuleNameString.Length;

    LoadedServerDll->ModuleName.MaximumLength = ModuleNameString.MaximumLength;

    LoadedServerDll->ModuleName.Buffer = (PCH)(LoadedServerDll+1);

    if (ModuleNameString.Length != 0) {

        strncpy( LoadedServerDll->ModuleName.Buffer,

                 ModuleNameString.Buffer,

                 ModuleNameString.Length

               );

        }

    LoadedServerDll->ServerDllIndex = ServerDllIndex;

    LoadedServerDll->ModuleHandle = ModuleHandle;

    if (ModuleHandle != NULL) {

        RtlInitString(

            &ProcedureNameString,

            (InitRoutineString == NULL) ? "ServerDllInitialization" : InitRoutineString);

        Status = LdrGetProcedureAddress( ModuleHandle,

                                         &ProcedureNameString,

                                         0,

                                         (PVOID *) &ServerDllInitialization

                                       );

        }

    else {

        ServerDllInitialization = CsrServerDllInitialization;

        Status = STATUS_SUCCESS;

        }

    if (NT_SUCCESS( Status )) {

        try {

            Status = (*ServerDllInitialization)( LoadedServerDll );

            }

        except ( CsrUnhandledExceptionFilter( GetExceptionInformation() ) ){

            Status = GetExceptionCode();

            }

        if (NT_SUCCESS( Status )) {

            CsrTotalPerProcessDataLength += (ULONG)QUAD_ALIGN(LoadedServerDll->PerProcessDataLength);

            CsrLoadedServerDll[ LoadedServerDll->ServerDllIndex ] =

                LoadedServerDll;

            if ( LoadedServerDll->SharedStaticServerData != CsrSrvSharedSectionHeap ) {

                CsrSrvSharedStaticServerData[LoadedServerDll->ServerDllIndex] = LoadedServerDll->SharedStaticServerData;

                }

            }

        else {

            if (ModuleHandle != NULL) {

                LdrUnloadDll( ModuleHandle );

                }

            RtlFreeHeap( CsrHeap, 0, LoadedServerDll );

            }

        }

    else {

        if (ModuleHandle != NULL) {

            LdrUnloadDll( ModuleHandle );

            }

        RtlFreeHeap( CsrHeap, 0, LoadedServerDll );

        }

    return( Status );

}

第五部分:

base/subsys/csr/server/sbinit.c:123:CsrSbApiPortInitialize( VOID )

NTSTATUS

CsrSbApiPortInitialize( VOID )

{

    NTSTATUS Status;

    OBJECT_ATTRIBUTES ObjectAttributes;

    HANDLE Thread;

    CLIENT_ID ClientId;

    ULONG n;

    PSECURITY_DESCRIPTOR pCsrSbApiPortSD;

    n = CsrDirectoryName.Length +

        sizeof( CSR_SBAPI_PORT_NAME ) +

        sizeof( OBJ_NAME_PATH_SEPARATOR );

    CsrSbApiPortName.Buffer = RtlAllocateHeap( CsrHeap, MAKE_TAG( INIT_TAG ), n );

    if (CsrSbApiPortName.Buffer == NULL) {

        return( STATUS_NO_MEMORY );

        }

    CsrSbApiPortName.Length = 0;

    CsrSbApiPortName.MaximumLength = (USHORT)n;

    RtlAppendUnicodeStringToString( &CsrSbApiPortName, &CsrDirectoryName );

    RtlAppendUnicodeToString( &CsrSbApiPortName, L"\\" );

    RtlAppendUnicodeToString( &CsrSbApiPortName, CSR_SBAPI_PORT_NAME );

    IF_CSR_DEBUG( LPC ) {

        DbgPrint( "CSRSS: Creating %wZ port and associated thread\n",

                  &CsrSbApiPortName );

        }

    Status = CsrCreateLocalSystemSD( &pCsrSbApiPortSD );

    if (!NT_SUCCESS(Status)) {

        return Status;

    }

    InitializeObjectAttributes( &ObjectAttributes, &CsrSbApiPortName, 0,

                                NULL, pCsrSbApiPortSD );

    Status = NtCreatePort( &CsrSbApiPort,

                           &ObjectAttributes,

                           sizeof( SBCONNECTINFO ),

                           sizeof( SBAPIMSG ),

                           sizeof( SBAPIMSG ) * 32

                         );

    if (pCsrSbApiPortSD != NULL) {

        RtlFreeHeap( CsrHeap, 0, pCsrSbApiPortSD );

    }

    if (!NT_SUCCESS(Status)) {

        return Status;

    }

    Status = RtlCreateUserThread( NtCurrentProcess(),

                                  NULL,

                                  TRUE,

                                  0,

                                  0,

                                  0,

                                  CsrSbApiRequestThread,

                                  NULL,

                                  &Thread,

                                  &ClientId

                                );

    if (!NT_SUCCESS(Status)) {

        return Status;

    }

    CsrSbApiRequestThreadPtr = CsrAddStaticServerThread(Thread,&ClientId,0);

    Status = NtResumeThread( Thread, NULL );

    return( Status );

}

第六部分:

base/win32/server/srvinit.c

NTSTATUS

CsrServerDllInitialization(

    IN PCSR_SERVER_DLL LoadedServerDll

    )

{

    LoadedServerDll->ApiNumberBase = CSRSRV_FIRST_API_NUMBER;

    LoadedServerDll->MaxApiNumber = CsrpMaxApiNumber;

    LoadedServerDll->ApiDispatchTable = CsrServerApiDispatchTable;

    LoadedServerDll->ApiServerValidTable = CsrServerApiServerValidTable;

#if DBG

    LoadedServerDll->ApiNameTable = CsrServerApiNameTable;

#endif

    LoadedServerDll->PerProcessDataLength = 0;

    LoadedServerDll->ConnectRoutine = NULL;

    LoadedServerDll->DisconnectRoutine = NULL;

    return( STATUS_SUCCESS );

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值