读取内存时出现访问冲突

背景

我正在用Visual Studio 2013 Community Edition编写命令行C ++程序。 它通过LDAP连接到Active Directory服务器,并在一些属性(例如:办公地点,部门)中检索唯一值列表。

问题

该程序编译好,但我遇到一个内存访问问题,当我运行它:

Unhandled exception at 0x74EDC6B1 (Wldap32.dll) in LdapSearchResultTest1.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.

这是我第一次使用C ++与外部库,所以我不知道如何甚至接近debugging(通常我为Android写Java)。 我花了一天的更好的时间来看看这个问题,并根据类似问题的答案尝试一些想法,但是我仍然无法解决这个问题。

确切的问题在于这个函数调用的最后一个参数:

 // Do the search int searchReturnCode = ldap_search_s( ldapSession, &searchBase[0], LDAP_SCOPE_SUBTREE, filter, pAttributes, 0, &pSearchResults); // Error is here 

我的代码

我的代码是基于MSDN网站的一个例子,我已经在代码中复制了这个例子。 这是一个certificate这个问题的SSCCE :

 #include<iostream> #include<Windows.h> #include<Winldap.h> #include<WinBer.h> #include<string> #include<vector> #include<algorithm> using namespace std; // Function headers vector<string> get_unique_departments(LDAP*, char*, string); vector<string> get_unique_office_locations(LDAP*, char*, string); vector<string> get_unique_values_by_property(LDAP*, char*, string, string); void print_list(string, vector<string>); // Main function int main(int argc, char* argv[]) { /** * Take AD connection arguments from Windows command line: * - Server address * - Server port * - Username * - Password * - Search base(s) (space separated if there are multiple) * * Example call from Windows command line: * > program.exe ad-test.example.com 389 joe@ad-test.example.com L3tM31n "OU=Development Team,DC=ad-test,DC=example,DC=com" "OU=Management Team,DC=ad-test,DC=example,DC=com" */ string serverAddress = argv[1]; int serverPort = atoi(argv[2]); string username = argv[3]; string password = argv[4]; vector<string> searchBases; for (int i = 0; i < argc; i++) { searchBases.push_back(argv[i]); } // If debug build, print received parameters #ifdef _DEBUG cout << "Server address: " << serverAddress << endl << "Server port: " << serverPort << endl << "Username: " << username << endl << "Password: " << password << endl; for (size_t i = 5; i < searchBases.size(); i++) { cout << "Search base: " << searchBases.at(i) << endl; } cout << endl; #endif // Initiate LDAP connection to Active Directory int ldapVersion = LDAP_VERSION3; LDAP* ldapSession = ldap_init(&serverAddress[0], serverPort); ldap_set_option(ldapSession, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion); ULONG ldapConnection = ldap_connect(ldapSession, nullptr); // Bind user int ldapBindResult = ldap_simple_bind_s(ldapSession, &username[0], &password[0]); if (ldapBindResult != LDAP_SUCCESS) { ldap_unbind(ldapSession); #ifdef _DEBUG cout << "Unable to connect to LDAP directory" << endl << endl; #endif } else { #ifdef _DEBUG cout << "Connected to LDAP!" << endl << endl; #endif } // The LDAP object filter char* filter = "(&(objectCategory=person)(objectClass=user))"; // Get lists of departments and offices vector<string> departments, offices; for (int i = 0; i < searchBases.size(); i++) { vector<string> tempDepts = get_unique_departments(ldapSession, filter, searchBases.at(i)); vector<string> tempOffices = get_unique_office_locations(ldapSession, filter, searchBases.at(i)); for (int j = 0; j < tempDepts.size(); j++) { departments.push_back(tempDepts.at(j)); } for (int j = 0; j < tempOffices.size(); j++) { offices.push_back(tempOffices.at(j)); } } // Print the lists print_list("Departments", departments); print_list("Offices", offices); // Return return 0; } // Retrieve a list of unique departments vector<string> get_unique_departments(LDAP* session, char* filter, string searchBase) { return get_unique_values_by_property(session, filter, searchBase, "department"); } // Retrieve a list of unique office locations vector<string> get_unique_office_locations(LDAP* session, char* filter, string searchBase) { return get_unique_values_by_property(session, filter, searchBase, "office"); } // Get a list of an attribute's unique values vector<string> get_unique_values_by_property(LDAP* ldapSession, char* filter, string searchBase, string property) { // Initialize some variables vector<string> results; char* pAttributes[1]; pAttributes[0] = &property[0]; LDAPMessage* pSearchResults = NULL; int numResults = 0; // Do the search int searchReturnCode = ldap_search_s( ldapSession, &searchBase[0], LDAP_SCOPE_SUBTREE, filter, pAttributes, 0, &pSearchResults); // Error is here // Process results if (searchReturnCode == LDAP_SUCCESS) { // Initialize some variables LDAPMessage* pEntry = NULL; char* pEntryDN = NULL; char* sMsg; BerElement* pBer = NULL; char* pAttribute = NULL; char** ppValue = NULL; ULONG iValue = 0; // Count the results numResults = ldap_count_entries(ldapSession, pSearchResults); // Loop over results for (ULONG i = 0; i < numResults; i++) { // Get the first/next entry if (!i) { pEntry = ldap_first_entry(ldapSession, pSearchResults); } else { pEntry = ldap_next_entry(ldapSession, pEntry); } // Fail if unable to get entry if (pEntry == NULL) { results.clear(); return results; } // Loop over the attributes pAttribute = ldap_first_attribute(ldapSession, pEntry, &pBer); while (pAttribute != NULL) { // Get and handle the values ppValue = ldap_get_values(ldapSession, pEntry, pAttribute); if (ppValue != NULL) { iValue = ldap_count_values(ppValue); if (find(results.begin(), results.end(), *ppValue) == results.end()) { results.push_back(*ppValue); } // Memory management ldap_value_free(ppValue); ppValue = NULL; ldap_memfree(pAttribute); // Get the next attribute pAttribute = ldap_next_attribute(ldapSession, pEntry, pBer); } } // Memory management if (pBer != NULL) { ber_free(pBer, 0); } pBer = NULL; } } // Free search result memory if (pSearchResults != NULL) { ldap_msgfree(pSearchResults); } // Return return results; } // Print a vector-based list w/ header void print_list(string header, vector<string> items) { if (items.size() > 0) { cout << header << ":" << endl; for (int i = 0; i < items.size(); i++) { cout << items.at(i) << endl; } cout << endl; } } 

MSDN示例:searchLDAP目录

从https://msdn.microsoft.com/en-us/library/aa367016%28v=vs.85%29.aspx

 //---------------------------------------------- // Performing an LDAP Synchronous Search. // // Be aware that you must set the command prompt screen buffer // height to 350 and the width to 90. //------------------------------------------------------------- #include <windows.h> #include <winldap.h> #include <winber.h> #include <rpc.h> #include <rpcdce.h> #include <stdio.h> //----------------------------------------------------------- // This subroutine must have validated credentials (name and // password) passed to it. //----------------------------------------------------------- int MyLDAPSearch(PCHAR pUserName, PCHAR pPassword) { //--------------------------------------------------------- // Initialize a session. LDAP_PORT is the default port, 389. //--------------------------------------------------------- PCHAR hostName = "fabrikam.com"; LDAP* pLdapConnection = NULL; pLdapConnection = ldap_init(hostName, LDAP_PORT); if (pLdapConnection == NULL) { printf("ldap_init failed with 0x%x.\n",LdapGetLastError()); ldap_unbind(pLdapConnection); return -1; } else printf("ldap_init succeeded \n"); //------------------------------------------------------- // Set session options. //------------------------------------------------------- ULONG version = LDAP_VERSION3; ULONG numReturns = 10; ULONG lRtn = 0; // Set the version to 3.0 (default is 2.0). lRtn = ldap_set_option( pLdapConnection, // Session handle LDAP_OPT_PROTOCOL_VERSION, // Option (void*) &version); // Option value if(lRtn == LDAP_SUCCESS) printf("ldap version set to 3.0 \n"); else { printf("SetOption Error:%0lX\n", lRtn); ldap_unbind(pLdapConnection); return -1; } // Set the limit on the number of entries returned to 10. lRtn = ldap_set_option( pLdapConnection, // Session handle LDAP_OPT_SIZELIMIT, // Option (void*) &numReturns); // Option value if(lRtn == LDAP_SUCCESS) printf("Max return entries set to 10 \n"); else { printf("SetOption Error:%0lX\n", lRtn); ldap_unbind(pLdapConnection); return -1; } //-------------------------------------------------------- // Connect to the server. //-------------------------------------------------------- lRtn = ldap_connect(pLdapConnection, NULL); if(lRtn == LDAP_SUCCESS) printf("ldap_connect succeeded \n"); else { printf("ldap_connect failed with 0x%lx.\n",lRtn); ldap_unbind(pLdapConnection); return -1; } //-------------------------------------------------------- // Bind with credentials. //-------------------------------------------------------- PCHAR pMyDN = "DC=fabrikam,DC=com"; SEC_WINNT_AUTH_IDENTITY secIdent; secIdent.User = (unsigned char*)pUserName; secIdent.UserLength = strlen(pUserName); secIdent.Password = (unsigned char*)pPassword; secIdent.PasswordLength = strlen(pPassword); secIdent.Domain = (unsigned char*)hostName; secIdent.DomainLength = strlen(hostName); secIdent.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; lRtn = ldap_bind_s( pLdapConnection, // Session Handle pMyDN, // Domain DN (PCHAR)&secIdent, // Credential structure LDAP_AUTH_NEGOTIATE); // Auth mode if(lRtn == LDAP_SUCCESS) { printf("ldap_bind_s succeeded \n"); secIdent.Password = NULL; // Remove password pointer pPassword = NULL; // Remove password pointer } else { printf("ldap_bind_s failed with 0x%lx.\n",lRtn); ldap_unbind(pLdapConnection); return -1; } //---------------------------------------------------------- // Perform a synchronous search of fabrikam.com for // all user objects that have a "person" category. //---------------------------------------------------------- ULONG errorCode = LDAP_SUCCESS; LDAPMessage* pSearchResult; PCHAR pMyFilter = "(&(objectCategory=person)(objectClass=user))"; PCHAR pMyAttributes[6]; pMyAttributes[0] = "cn"; pMyAttributes[1] = "company"; pMyAttributes[2] = "department"; pMyAttributes[3] = "telephoneNumber"; pMyAttributes[4] = "memberOf"; pMyAttributes[5] = NULL; errorCode = ldap_search_s( pLdapConnection, // Session handle pMyDN, // DN to start search LDAP_SCOPE_SUBTREE, // Scope pMyFilter, // Filter pMyAttributes, // Retrieve list of attributes 0, // Get both attributes and values &pSearchResult); // [out] Search results if (errorCode != LDAP_SUCCESS) { printf("ldap_search_s failed with 0x%0lx \n",errorCode); ldap_unbind_s(pLdapConnection); if(pSearchResult != NULL) ldap_msgfree(pSearchResult); return -1; } else printf("ldap_search succeeded \n"); //---------------------------------------------------------- // Get the number of entries returned. //---------------------------------------------------------- ULONG numberOfEntries; numberOfEntries = ldap_count_entries( pLdapConnection, // Session handle pSearchResult); // Search result if(numberOfEntries == NULL) { printf("ldap_count_entries failed with 0x%0lx \n",errorCode); ldap_unbind_s(pLdapConnection); if(pSearchResult != NULL) ldap_msgfree(pSearchResult); return -1; } else printf("ldap_count_entries succeeded \n"); printf("The number of entries is: %d \n", numberOfEntries); //---------------------------------------------------------- // Loop through the search entries, get, and output the // requested list of attributes and values. //---------------------------------------------------------- LDAPMessage* pEntry = NULL; PCHAR pEntryDN = NULL; ULONG iCnt = 0; char* sMsg; BerElement* pBer = NULL; PCHAR pAttribute = NULL; PCHAR* ppValue = NULL; ULONG iValue = 0; for( iCnt=0; iCnt < numberOfEntries; iCnt++ ) { // Get the first/next entry. if( !iCnt ) pEntry = ldap_first_entry(pLdapConnection, pSearchResult); else pEntry = ldap_next_entry(pLdapConnection, pEntry); // Output a status message. sMsg = (!iCnt ? "ldap_first_entry" : "ldap_next_entry"); if( pEntry == NULL ) { printf("%s failed with 0x%0lx \n", sMsg, LdapGetLastError()); ldap_unbind_s(pLdapConnection); ldap_msgfree(pSearchResult); return -1; } else printf("%s succeeded\n",sMsg); // Output the entry number. printf("ENTRY NUMBER %i \n", iCnt); // Get the first attribute name. pAttribute = ldap_first_attribute( pLdapConnection, // Session handle pEntry, // Current entry &pBer); // [out] Current BerElement // Output the attribute names for the current object // and output values. while(pAttribute != NULL) { // Output the attribute name. printf(" ATTR: %s",pAttribute); // Get the string values. ppValue = ldap_get_values( pLdapConnection, // Session Handle pEntry, // Current entry pAttribute); // Current attribute // Print status if no values are returned (NULL ptr) if(ppValue == NULL) { printf(": [NO ATTRIBUTE VALUE RETURNED]"); } // Output the attribute values else { iValue = ldap_count_values(ppValue); if(!iValue) { printf(": [BAD VALUE LIST]"); } else { // Output the first attribute value printf(": %s", *ppValue); // Output more values if available ULONG z; for(z=1; z<iValue; z++) { printf(", %s", ppValue[z]); } } } // Free memory. if(ppValue != NULL) ldap_value_free(ppValue); ppValue = NULL; ldap_memfree(pAttribute); // Get next attribute name. pAttribute = ldap_next_attribute( pLdapConnection, // Session Handle pEntry, // Current entry pBer); // Current BerElement printf("\n"); } if( pBer != NULL ) ber_free(pBer,0); pBer = NULL; } //---------------------------------------------------------- // Normal cleanup and exit. //---------------------------------------------------------- ldap_unbind(pLdapConnection); ldap_msgfree(pSearchResult); ldap_value_free(ppValue); return 0; } 

pAttributes数组必须以null结尾。

代码应该如下所示:

 char* pAttributes[2]; pAttributes[0] = &property[0]; pAttributes[1] = NULL; 

阅读ldap_search_s的文档,我看到:

基地[在]

指向包含开始搜索条目的可分辨名称的以空字符结尾的字符串。

然而, &searchBase[0]只会得到一个指向存储std::string使用 – 没有要求这是null终止。 你应该使用searchBase.c_str() ,因为这是保证给你一个空终止的c字符串。