COM function that return BSTR** does not work as it seems to me it should , but same function not from COM work like expected

huangapple go评论95阅读模式
英文:

COM function that return BSTR** does not work as it seems to me it should , but same function not from COM work like expected

问题

  1. 我正在尝试编写一个具有Windows服务管理功能的COM对象。为此,我想要一个返回所有服务名称的函数。我已经熟悉Windows Api几天了,所以我不太明白我在做什么(错误)以及如何更好地完成它。
  2. 在另一个程序中,我以以下方式调用这个函数:
  3. ```c
  4. ...
  5. BSTR* pServiceNames = (BSTR*)CoTaskMemAlloc(sizeof(BSTR));;
  6. DWORD dwServicesReturned = 0;
  7. hr = pIService->GetServices(&pServiceNames, &dwServicesReturned);
  8. ...

然后我尝试类似这样:

  1. std::wcout << (pServiceNames[0]); // 结果: AdobeARMservice
  2. std::wcout << (pServiceNames[1]); // 结果: (进程 8844) 以代码 -1073741819 退出。

如果使用 "printf" 也是一样的。

  1. std::cout << (pServiceNames[1]); // 结果: 000000084D454D4C

当我将相同的函数粘贴到我的主程序中时,一切正常,即所有服务名称都会显示出来。

附加信息:对于COM,我使用了ATL,也许这很重要。

  1. <details>
  2. <summary>英文:</summary>
  3. I am trying to write a COM object with Windows Service Managment functionality. For this, I want a function that returns the names of all services. I&#39;ve been familiar with Windows Api for a few days, so I don&#39;t really understand what I&#39;m doing (wrong) and how I can do it better.

STDMETHODIMP CServiceHandler::GetServices(BSTR** pOut, LPDWORD dwServicesReturned)
{
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if (!hSCManager)
{

  1. return HRESULT_FROM_WIN32(GetLastError());
  2. }
  3. DWORD dwBytesNeeded = 0;
  4. DWORD dwResumeHandle = 0;
  5. EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &amp;dwBytesNeeded, dwServicesReturned, &amp;dwResumeHandle);
  6. if (GetLastError() != ERROR_MORE_DATA)
  7. {
  8. CloseServiceHandle(hSCManager);
  9. return HRESULT_FROM_WIN32(GetLastError());
  10. }
  11. LPENUM_SERVICE_STATUS lpServices = (LPENUM_SERVICE_STATUS)malloc(dwBytesNeeded);
  12. if (!EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, lpServices, dwBytesNeeded, &amp;dwBytesNeeded, dwServicesReturned, &amp;dwResumeHandle))
  13. {
  14. free(lpServices);
  15. CloseServiceHandle(hSCManager);
  16. return HRESULT_FROM_WIN32(GetLastError());
  17. }
  18. BSTR* pServiceNames = (BSTR*)malloc(*dwServicesReturned * sizeof(BSTR));
  19. if (!pServiceNames)
  20. {
  21. free(lpServices);
  22. CloseServiceHandle(hSCManager);
  23. return E_OUTOFMEMORY;
  24. }
  25. ZeroMemory(pServiceNames, *dwServicesReturned * sizeof(BSTR));
  26. for (DWORD i = 0; i &lt; *dwServicesReturned; i++)
  27. {
  28. pServiceNames[i] = SysAllocString(lpServices[i].lpServiceName);
  29. }
  30. *pOut = pServiceNames;

// //return S_OK;
free(lpServices);

  1. CloseServiceHandle(hSCManager);
  2. return S_OK;
  1. In another program where I call this function this way:

...
BSTR* pServiceNames = (BSTR*)CoTaskMemAlloc(sizeof(BSTR));;
DWORD dwServicesReturned = 0;
hr = pIService->GetServices(&pServiceNames, &dwServicesReturned);
...

  1. Then I try something like this:
  2. `std::wcout &lt;&lt; (pServiceNames[0]); // result: AdobeARMservice`
  3. `std::wcout &lt;&lt; (pServiceNames[1]); // result: (process 8844) exited with code -1073741819.`
  4. Same if use &quot;printf&quot;. And
  5. `std::cout &lt;&lt; (pServiceNames[1]); //result: 000000084D454D4C`
  6. When I paste the same function into my main program, everything is fine, i.e. all the service names are displayed.
  7. Additional information: For COM I used ATL, maybe it&#39;s important.
  8. </details>
  9. # 答案1
  10. **得分**: 1
  11. 你对`EnumServiceStatus()`的处理不完整,因为你没有考虑`ERROR_INSUFFICIENT_BUFFER`。对`EnumServiceStatus()`的每次调用可能需要更多内存,因此可能报告`ERROR_INSUFFICIENT_BUFFER`,或者可能只返回部分数据,报告`ERROR_MORE_DATA`。你需要处理这两种情况,以及当报告`ERROR_MORE_DATA`时,可能已返回一些有效数据,需要进行处理。因此,在枚举过程中,可能需要多次重新分配你的`BSTR`数组。
  12. 此外,在COM中传递的所有内存都需要使用COM的内存管理器分配,但`GetServices()`正在使用`malloc()`分配输出的`BSTR`数组。你的调用代码在调用`GetServices()`之前本地调用了`CoTaskMemAlloc()`,然后`GetServices()`覆盖了调用者的指针,从而泄漏了调用者分配的内存。调用者不应该在本地分配任何内存,只需接管`GetServices()`输出的内存。
  13. 有了这些信息,你可以尝试像这样改进代码:
  14. ```cpp
  15. STDMETHODIMP CServiceHandler::GetServices(BSTR** pServices, LPDWORD pdwServicesReturned)
  16. {
  17. if (!(pServices && pdwServicesReturned))
  18. return E_POINTER;
  19. *pServices = NULL;
  20. *pdwServicesReturned = 0;
  21. SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
  22. if (!hSCManager)
  23. return HRESULT_FROM_WIN32(GetLastError());
  24. LPENUM_SERVICE_STATUS lpStatuses = NULL;
  25. DWORD dwBytesAllocated = 0;
  26. DWORD dwBytesNeeded = 0;
  27. DWORD dwNumStatuses = 0;
  28. DWORD dwResumeHandle = 0;
  29. BSTR* pServiceNames = NULL;
  30. DWORD dwNumServiceNames = 0;
  31. BOOL bSuccess;
  32. DWORD dwError = 0;
  33. HRESULT hRes = S_OK;
  34. do
  35. {
  36. bSuccess = EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, lpStatuses, dwBytesAllocated, &dwBytesNeeded, &dwNumStatuses, &dwResumeHandle);
  37. if (!bSuccess)
  38. {
  39. dwError = GetLastError();
  40. if ((dwError != ERROR_INSUFFICIENT_BUFFER) &&
  41. (dwError != ERROR_MORE_DATA))
  42. {
  43. hRes = HRESULT_FROM_WIN32(dwError);
  44. goto Failed;
  45. }
  46. }
  47. if ((bSuccess || (dwError == ERROR_MORE_DATA)) &&
  48. (dwNumStatuses > 0))
  49. {
  50. BSTR* pTmpServiceNames = (BSTR*)CoTaskMemRealloc(pServiceNames, sizeof(BSTR) * (dwNumServiceNames + dwNumStatuses));
  51. if (!pTmpServiceNames)
  52. {
  53. hRes = E_OUTOFMEMORY;
  54. goto Failed;
  55. }
  56. pServiceNames = pTmpServiceNames;
  57. for (DWORD i = 0; i < dwNumStatuses; ++i)
  58. {
  59. pServiceNames[dwNumServiceNames] = SysAllocString(lpStatuses[i].lpServiceName);
  60. if (!pServiceNames[dwNumServiceNames])
  61. {
  62. hRes = E_OUTOFMEMORY;
  63. goto Failed;
  64. }
  65. ++dwNumServiceNames;
  66. }
  67. }
  68. if (!bSuccess)
  69. {
  70. LPENUM_SERVICE_STATUS lpTmpStatuses = (LPENUM_SERVICE_STATUS)realloc(lpStatuses, dwBytesNeeded);
  71. if (!lpTmpStatuses)
  72. {
  73. hRes = E_OUTOFMEMORY;
  74. goto Failed;
  75. }
  76. lpStatuses = lpTmpStatuses;
  77. }
  78. } while (!bSuccess);
  79. goto Finished;
  80. Failed:
  81. if (pServiceNames)
  82. {
  83. for (DWORD i = 0; i < dwNumServiceNames; ++i)
  84. SysFreeString(pServiceNames[i]);
  85. CoTaskMemFree(pServiceNames);
  86. pServiceNames = NULL;
  87. }
  88. dwNumServiceNames = 0;
  89. Finished:
  90. free(lpStatuses);
  91. CloseServiceHandle(hSCManager);
  92. *pServices = pServiceNames;
  93. *pdwServicesReturned = dwNumServiceNames;
  94. return hRes;
  95. }
  1. BSTR* pServiceNames = NULL;
  2. DWORD dwServicesReturned = 0;
  3. hr = pIService->GetServices(&pServiceNames, &dwServicesReturned);
  4. if (hr == S_OK)
  5. {
  6. ...
  7. for (DWORD i = 0; i < dwServicesReturned; ++i)
  8. SysFreeString(pServiceNames[i]);
  9. CoTaskMemFree(pServiceNames);
  10. }

或者,既然你将问题标记为C++,你应该使用C++习惯来更有效地管理内存,例如:

  1. struct SCHandle
  2. {
  3. SC_HANDLE m_SC;
  4. SCHandle(SC_HANDLE hSC) : m_SC(hSC) {}
  5. ~SCHandle() { if (m_SC) CloseServiceHandle(m_SC); }
  6. bool operator !() const { return !m_SC; }
  7. operator SC_HANDLE() { return m_SC; }
  8. };
  9. STDMETHODIMP CServiceHandler::GetServices(BSTR** pServices, LPDWORD pdwServicesReturned)
  10. {
  11. if (!(pServices && pdwServicesReturned))
  12. return E_POINTER;
  13. try
  14. {
  15. *pServices = NULL;
  16. *pdwServicesReturned = 0;
  17. SCHandle hSCManager(OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
  18. if (!hSCManager)
  19. return HRESULT_FROM_WIN32(GetLastError());
  20. std::vector<BYTE> statusBuffer;
  21. std::vector<std::wstring> serviceNames;
  22. LPENUM_SERVICE_STATUS lpStatuses = NULL;
  23. DWORD dwBytesNeeded = 0;
  24. DWORD dwNumStatuses = 0;
  25. DWORD dwResumeHandle = 0;
  26. DWORD dwError = 0;
  27. do
  28. {
  29. BOOL bSuccess = EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, lpStatuses, statusBuffer.size(), &dwBytesNeeded, &dwNumStatuses, &dwResumeHandle);
  30. if (!bSuccess)
  31. {
  32. dwError = GetLastError();
  33. if ((dwError != ERROR_INSUFFICIENT_BUFFER) &&
  34. (dwError != ERROR_MORE_DATA))
  35. return HRESULT_FROM_WIN32(dwError);
  36. }
  37. if (bSuccess || (dwError == ERROR_MORE_DATA))
  38. {
  39. serviceNames.reserve(serviceNames.size() + dwNumStatuses);
  40. for (DWORD i = 0; i < dwNumStatuses; ++i)
  41. serviceNames.push_back(lpStatuses[i].lpServiceName);
  42. }
  43. if (!bSuccess)
  44. {
  45. statusBuffer.resize(dwBytesNeeded);
  46. lpStatuses = reinterpret_cast<LPENUM_SERVICE_STATUS>(statusBuffer.data());
  47. }
  48. } while (!bSuccess);
  49. DWORD dwNumServiceNames =
  50. <details>
  51. <summary>英文:</summary>
  52. Your handling of `EnumServiceStatus()` is incomplete, as you are not taking `ERROR_INSUFFICIENT_BUFFER` into account. Any given call to `EnumServiceStatus()` may require more memory, thus reporting `ERROR_INSUFFICIENT_BUFFER`, or it may return only a subset of data, reporting `ERROR_MORE_DATA`. You need to handle both cases, as well as the fact that when `ERROR_MORE_DATA` is reported, some valid data may have been returned which you need to process. So, you may end up having to reallocate your `BSTR` array more than once during the course of your enumeration.
  53. Also, all memory passed around in COM needs to be allocated with COM&#39;s memory manager, but `GetServices()` is allocating the output `BSTR` array using `malloc()`. Your calling code is calling `CoTaskMemAlloc()` locally before calling `GetServices()`, and then `GetServices()` is overwriting the caller&#39;s pointer, thus leaking the memory that the caller allocated. The caller should not be allocating any memory locally at all, only taking ownership of the memory that `GetServices()` outputs.
  54. With that said, try something more like this:

STDMETHODIMP CServiceHandler::GetServices(BSTR** pServices, LPDWORD pdwServicesReturned)
{
if (!(pServices && pdwServicesReturned))
return E_POINTER;

  1. *pServices = NULL;
  2. *pdwServicesReturned = 0;
  3. SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
  4. if (!hSCManager)
  5. return HRESULT_FROM_WIN32(GetLastError());
  6. LPENUM_SERVICE_STATUS lpStatuses = NULL;
  7. DWORD dwBytesAllocated = 0;
  8. DWORD dwBytesNeeded = 0;
  9. DWORD dwNumStatuses = 0;
  10. DWORD dwResumeHandle = 0;
  11. BSTR* pServiceNames = NULL;
  12. DWORD dwNumServiceNames = 0;
  13. BOOL bSuccess;
  14. DWORD dwError = 0;
  15. HRESULT hRes = S_OK;
  16. do
  17. {
  18. bSuccess = EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, lpStatuses, dwBytesAllocated, &amp;dwBytesNeeded, &amp;dwNumStatuses, &amp;dwResumeHandle);
  19. if (!bSuccess)
  20. {
  21. dwError = GetLastError();
  22. if ((dwError != ERROR_INSUFFICIENT_BUFFER) &amp;&amp;
  23. (dwError != ERROR_MORE_DATA))
  24. {
  25. hRes = HRESULT_FROM_WIN32(dwError);
  26. goto Failed;
  27. }
  28. }
  29. if ((bSuccess || (dwError == ERROR_MORE_DATA)) &amp;&amp;
  30. (dwNumStatuses &gt; 0))
  31. {
  32. BSTR *pTmpServiceNames = (BSTR*) CoTaskMemRealloc(pServiceNames, sizeof(BSTR) * (dwNumServiceNames + dwNumStatuses));
  33. if (!pTmpServiceNames)
  34. {
  35. hRes = E_OUTOFMEMORY;
  36. goto Failed;
  37. }
  38. pServiceNames = pTmpServiceNames;
  39. for (DWORD i = 0; i &lt; dwNumStatuses; ++i)
  40. {
  41. pServiceNames[dwNumServiceNames] = SysAllocString(lpStatuses[i].lpServiceName);
  42. if (!pServiceNames[dwNumServiceNames])
  43. {
  44. hRes = E_OUTOFMEMORY;
  45. goto Failed;
  46. }
  47. ++dwNumServiceNames;
  48. }
  49. }
  50. if (!bSuccess)
  51. {
  52. LPENUM_SERVICE_STATUS lpTmpStatuses = (LPENUM_SERVICE_STATUS) realloc(lpStatuses, dwBytesNeeded);
  53. if (!lpTmpStatuses)
  54. {
  55. hRes = E_OUTOFMEMORY;
  56. goto Failed;
  57. }
  58. lpStatuses = lpTmpStatuses;
  59. }
  60. }
  61. while (!bSuccess);
  62. goto Finished;

Failed:
if (pServiceNames)
{
for (DWORD i = 0; i < dwNumServiceNames; ++i)
SysFreeString(pServiceNames[i]);
CoTaskMemFree(pServiceNames);
pServiceNames = NULL;
}
dwNumServiceNames = 0;

Finished:
free(lpStatuses);
CloseServiceHandle(hSCManager);

  1. *pServices = pServiceNames;
  2. *pdwServicesReturned = dwNumServiceNames;
  3. return hRes;

}

BSTR* pServiceNames = NULL;
DWORD dwServicesReturned = 0;
hr = pIService->GetServices(&pServiceNames, &dwServicesReturned);
if (hr == S_OK)
{
...
for(DWORD i = 0; i < dwServicesReturned; ++i)
SysFreeString(pServiceNames[i]);
CoTaskMemFree(pServiceNames);
}

  1. Alternatively, since you did tag the question as C++, you should use C++ idioms to help manage memory more effectively, eg:

struct SCHandle
{
SC_HANDLE m_SC;

  1. SCHandle(SC_HANDLE hSC) : m_SC(hSC) {}
  2. ~SCHandle() { if (m_SC) CloseServiceHandle(m_SC); }
  3. bool operator !() const { return !m_SC; }
  4. operator SC_HANDLE() { return m_SC; }

};

STDMETHODIMP CServiceHandler::GetServices(BSTR** pServices, LPDWORD pdwServicesReturned)
{
if (!(pServices && pdwServicesReturned))
return E_POINTER;

  1. try
  2. {
  3. *pServices = NULL;
  4. *pdwServicesReturned = 0;
  5. SCHandle hSCManager(OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
  6. if (!hSCManager)
  7. return HRESULT_FROM_WIN32(GetLastError());
  8. std::vector&lt;BYTE&gt; statusBuffer;
  9. std::vector&lt;std::wstring&gt; serviceNames;
  10. LPENUM_SERVICE_STATUS lpStatuses = NULL;
  11. DWORD dwBytesNeeded = 0;
  12. DWORD dwNumStatuses = 0;
  13. DWORD dwResumeHandle = 0;
  14. DWORD dwError = 0;
  15. do
  16. {
  17. BOOL bSuccess = EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, lpStatuses, statusBuffer.size(), &amp;dwBytesNeeded, &amp;dwNumStatuses, &amp;dwResumeHandle);
  18. if (!bSuccess)
  19. {
  20. dwError = GetLastError();
  21. if ((dwError != ERROR_INSUFFICIENT_BUFFER) &amp;&amp;
  22. (dwError != ERROR_MORE_DATA))
  23. return HRESULT_FROM_WIN32(dwError);
  24. }
  25. if (bSuccess || (dwError == ERROR_MORE_DATA))
  26. {
  27. serviceNames.reserve(serviceNames.size() + dwNumStatuses);
  28. for (DWORD i = 0; i &lt; dwNumStatuses; ++i)
  29. serviceNames.push_back(lpStatuses[i].lpServiceName);
  30. }
  31. if (!bSuccess)
  32. {
  33. statusBuffer.resize(dwBytesNeeded);
  34. lpStatuses = reinterpret_cast&lt;LPENUM_SERVICE_STATUS&gt;(statusBuffer.data());
  35. }
  36. }
  37. while (!bSuccess);
  38. DWORD dwNumServiceNames = serviceNames.size();
  39. BSTR *pServiceNames = static_cast&lt;BSTR*&gt;(CoTaskMemAlloc(sizeof(BSTR) * dwNumServiceNames));
  40. if (!pServiceNames)
  41. return E_OUTOFMEMORY;
  42. for (DWORD i = 0; i &lt; dwNumServiceNames; ++i)
  43. {
  44. pServiceNames[i] = SysAllocString(serviceNames[i].c_str());
  45. if (!pServiceNames[i])
  46. {
  47. for(DWORD j = 0; j &lt; i; ++j)
  48. SysFreeString(pServiceNames[j]);
  49. CoTaskMemFree(pServiceNames);
  50. return E_OUTOFMEMORY;
  51. }
  52. }
  53. *pServices = pServiceNames;
  54. *pdwServicesReturned = dwNumServiceNames;
  55. return S_OK;
  56. }
  57. catch (const std::bad_alloc &amp;)
  58. {
  59. return E_OUTOFMEMORY;
  60. }
  61. catch (...)
  62. {
  63. return E_UNEXPECTED;
  64. }

}

  1. </details>

huangapple
  • 本文由 发表于 2023年2月23日 21:44:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/75545659.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定