Microsoft.ACE.OLEDB.12.0 bug in multithread scenario - multithreading

I use in my x64 application the Microsoft Access Database Engine 2010 (part of Microsoft Office 2016) for working with mdb files.
But, current version of Microsoft Access Database Engine 2010 (OLEDB provider Microsoft.ACE.OLEDB.12.0) have a bug.
This engine crashes in multithread work.
If I create two OLE DB (or ADO DB) connections with this provider in different threads, then one of them will crashed in Mso40UIwin32client.dll with exception 0xC0000005: Access violation writing location 0x0000000000000000.
Exception stack:
Exception thrown at 0x00007FFB32361F28 in ACEOLEDBTest.exe: Microsoft
C++ exception: std::runtime_error at memory location
0x0000006B771FEAF0.
Exception thrown at 0x00007FFB32361F28 in
ACEOLEDBTest.exe: Microsoft C++ exception: [rethrow] at memory
location 0x0000000000000000.
Exception thrown at 0x00007FFB32361F28
in ACEOLEDBTest.exe: Microsoft C++ exception: std::runtime_error at
memory location 0x0000006B771FEAF0.
Exception thrown at
0x00007FFB32361F28 in ACEOLEDBTest.exe: Microsoft C++ exception:
std::runtime_error at memory location 0x0000006B771FEFB8.
Exception
thrown at 0x00007FFAF9ED1271 (Mso40UIwin32client.dll) in
ACEOLEDBTest.exe: 0xC0000005: Access violation writing location
0x0000000000000000.
C++ code sample with this error:
#include "stdafx.h"
#include <atlcom.h>
#include <atldbcli.h>
#include <conio.h>
typedef UINT(__stdcall* fnThread)(PVOID);
HANDLE hExitEvent = NULL;
UINT __stdcall DbThread1(IN PVOID context)
{
HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
_ASSERTE(SUCCEEDED(hRes));
CDataSource DataSource; // Data source connection object
while (::WaitForSingleObject(hExitEvent, 0) != WAIT_OBJECT_0)
{
// Open DB connection
ATLTRACE2(atlTraceGeneral, 0, L"DbThread1: Create connection...\n");
hRes = DataSource.OpenFromInitializationString(L"Provider=Microsoft.ACE.OLEDB.12.0; Data Source=\"C:\\Temp\\Index_empty1.mdb\"; Persist Security Info=False;");
_ASSERTE(SUCCEEDED(hRes));
// Close DB connection
DataSource.Close();
ATLTRACE2(atlTraceGeneral, 0, L"DbThread1: Close connection...\n");
Sleep(20);
}
::CoUninitialize();
_endthreadex(0);
return 0;
}
UINT __stdcall DbThread2(IN PVOID context)
{
HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
_ASSERTE(SUCCEEDED(hRes));
CDataSource DataSource; // Data source connection object
while (::WaitForSingleObject(hExitEvent, 0) != WAIT_OBJECT_0)
{
// Open DB connection
ATLTRACE2(atlTraceGeneral, 0, L"DbThread2: Create connection...\n");
hRes = DataSource.OpenFromInitializationString(L"Provider=Microsoft.ACE.OLEDB.12.0; Data Source=\"C:\\Temp\\Index_empty2.mdb\"; Persist Security Info=False;");
_ASSERTE(SUCCEEDED(hRes));
// Close DB connection
DataSource.Close();
ATLTRACE2(atlTraceGeneral, 0, L"DbThread2: Close connection...\n");
Sleep(20);
}
::CoUninitialize();
_endthreadex(0);
return 0;
}
int main()
{
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
hExitEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
const fnThread aPtrs[] = { DbThread1, DbThread2 };
HANDLE hDbThread[_countof(aPtrs)] = { NULL };
for (int nIndex = 0; nIndex < _countof(aPtrs); nIndex++) {
if ((hDbThread[nIndex] = (HANDLE)::_beginthreadex(nullptr, 0, aPtrs[nIndex], nullptr, 0, nullptr)) == NULL)
{
return 1;
}
}
CComVariant varData;
printf("Press any key to exit...");
// Loop until any key struck
while (!_kbhit())
{
for (DWORD i = 0; i < 100; i++)
{
// Test for bug of the OLEDB provider for MS ACCESS 2010.
varData.Clear();
}
Sleep(0);
}
// Request threads to exit
SetEvent(hExitEvent);
// Wait for threads to exit
WaitForMultipleObjects(_countof(hDbThread), hDbThread, TRUE, INFINITE);
for (auto& h : hDbThread) {
CloseHandle(h);
}
CloseHandle(hExitEvent);
::CoUninitialize();
return 0;
}
You should build this sample for x64 platform with Visual C++ 2013/2015.
Similar bug I found at MS forum. Can anybody help me?

We too have encountered this issue in a multi-threaded VB.Net (Framework v4.5.2) service application. After much testing, the only way we found to resolve this was to either use a single thread or turn off connection pooling (using OLE DB Services=-2). In the end we have gone with the latter as we needed the system to be able to process requests in parallel.
FYI, the version of Office 2016 I have installed does not include this driver (hence why our program passed any kind of basic testing) but it was included with the version of Office 2016 that our customer installed. So far I have checked "Microsoft Office Professional Plus 2016" (from both MSDN and MS Partner Network) and "Microsoft Office 365 ProPlus" and none of them appear to come with the Microsoft.ACE.OLEDB.12.0 OLEDB provider. Also, as far as I can see, the only version of the Microsoft Access Database Engine 2010 Redistributable available to download is SP2 (released 22/07/2013) so it is hard to get this tested in a development environment!

Related

CreateFile on Mailslot fails with Error 53 ERROR_BAD_NETPATH after 2018-05 Windows 10 Feature Update 1803

commands such as CreateFile("\\mycomputer\mailslot\this_fails",...) fail with last error = 53 ERROR_BAD_NETPATH
That fails if used with any valid or non-existing computer name including the same computer on which the test is running. On computers where this works, it succeeds and returns a mailslot handle even if the referenced computer does not exist or does not have a mailslot created with that name. Note that if an non-existing computer name or mailslot is used, subsequent WriteFiles on the handle will fail, but the CreateFile does succeed.
However, the CreateFile above will succeed if the Mailslot reference is explicitly local: "\\.\mailslot\always_works"
This worked on all versions of Windows previously until the 2018-05 cumulative updates were installed. Specifically KB4103721 (Windows 10 home) seemed to be the culprit. [Edit: as noted in answers below, it is actually Feature Update Build 1803 that causes this issue.]
Test Client: (works with no parameter or "." but fails with any computername).
Based on msdn sample
Syntax: testclient [server computername]
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
LPTSTR SlotName = TEXT("\\\\%hs\\mailslot\\sample_mailslot");
BOOL WriteSlot(HANDLE hSlot, LPTSTR lpszMessage)
{
BOOL fResult;
DWORD cbWritten;
fResult = WriteFile(hSlot,
lpszMessage,
(DWORD) (lstrlen(lpszMessage)+1)*sizeof(TCHAR),
&cbWritten,
(LPOVERLAPPED) NULL);
if (!fResult)
{
// this failure is valid if computername is not valid
printf("WriteFile failed with %d.\n", GetLastError());
return FALSE;
}
printf("Slot written to successfully.\n");
return TRUE;
}
int main(int nArgs,char * arg[])
{
HANDLE hFile;
TCHAR szSlot[256];
_stprintf (szSlot,SlotName,nArgs > 1 ? arg[1] : ".");
_tprintf(TEXT("Writing to slot %s\n"),szSlot);
hFile = CreateFile(szSlot,
GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// this is the failure I'm trying to debug
printf("CreateFile failed with %d.\n", GetLastError());
return FALSE;
}
WriteSlot(hFile, TEXT("Message one for mailslot."));
WriteSlot(hFile, TEXT("Message two for mailslot."));
Sleep(5000);
WriteSlot(hFile, TEXT("Message three for mailslot."));
CloseHandle(hFile);
return TRUE;
}
Test Server: (reads a displays sent messages)
Note that duplicate messages may be received because Mailslot messages are transmitted over all possible protocols. Based on msdn sample.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
HANDLE hSlot;
LPTSTR SlotName = TEXT("\\\\.\\mailslot\\sample_mailslot");
BOOL ReadSlot()
{
DWORD cbMessage, cMessage, cbRead;
BOOL fResult;
LPTSTR lpszBuffer;
TCHAR achID[80];
DWORD cAllMessages;
HANDLE hEvent;
OVERLAPPED ov;
cbMessage = cMessage = cbRead = 0;
hEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("ExampleSlot"));
if( NULL == hEvent )
return FALSE;
ov.Offset = 0;
ov.OffsetHigh = 0;
ov.hEvent = hEvent;
fResult = GetMailslotInfo( hSlot, // mailslot handle
(LPDWORD) NULL, // no maximum message size
&cbMessage, // size of next message
&cMessage, // number of messages
(LPDWORD) NULL); // no read time-out
if (!fResult)
{
printf("GetMailslotInfo failed with %d.\n", GetLastError());
return FALSE;
}
if (cbMessage == MAILSLOT_NO_MESSAGE)
{
printf("Waiting for a message...\n");
return TRUE;
}
cAllMessages = cMessage;
while (cMessage != 0) // retrieve all messages
{
// Create a message-number string.
StringCchPrintf((LPTSTR) achID,
80,
TEXT("\nMessage #%d of %d\n"),
cAllMessages - cMessage + 1,
cAllMessages);
// Allocate memory for the message.
lpszBuffer = (LPTSTR) GlobalAlloc(GPTR,
lstrlen((LPTSTR) achID)*sizeof(TCHAR) + cbMessage);
if( NULL == lpszBuffer )
return FALSE;
lpszBuffer[0] = '\0';
fResult = ReadFile(hSlot,
lpszBuffer,
cbMessage,
&cbRead,
&ov);
if (!fResult)
{
printf("ReadFile failed with %d.\n", GetLastError());
GlobalFree((HGLOBAL) lpszBuffer);
return FALSE;
}
// Concatenate the message and the message-number string.
StringCbCat(lpszBuffer,
lstrlen((LPTSTR) achID)*sizeof(TCHAR)+cbMessage,
(LPTSTR) achID);
// Display the message.
_tprintf(TEXT("Contents of the mailslot: %s\n"), lpszBuffer);
GlobalFree((HGLOBAL) lpszBuffer);
fResult = GetMailslotInfo(hSlot, // mailslot handle
(LPDWORD) NULL, // no maximum message size
&cbMessage, // size of next message
&cMessage, // number of messages
(LPDWORD) NULL); // no read time-out
if (!fResult)
{
printf("GetMailslotInfo failed (%d)\n", GetLastError());
return FALSE;
}
}
CloseHandle(hEvent);
return TRUE;
}
BOOL WINAPI MakeSlot(LPTSTR lpszSlotName)
{
hSlot = CreateMailslot(lpszSlotName,
0, // no maximum message size
MAILSLOT_WAIT_FOREVER, // no time-out for operations
(LPSECURITY_ATTRIBUTES) NULL); // default security
if (hSlot == INVALID_HANDLE_VALUE)
{
printf("CreateMailslot failed with %d\n", GetLastError());
return FALSE;
}
return TRUE;
}
void main()
{
MakeSlot(SlotName);
while(TRUE)
{
ReadSlot();
Sleep(3000);
}
}
The test server to read messages, and the test client to send messages can be run in different cmd shells on the same computer, or run on different computers. When it fails, it fails immediately and seems to be a problem trying to resolve the network path name. On the same computer, file shares such as \\ThisComputer\share work properly from the same computer or a different one.
NetBIOS is enabled over TCP/IP for the network adapters in use. The network adapters are designated as Private. Firewall was disabled for testing. File and Printer sharing are enabled. Computers are in same workgroup. Computer name resolution works, and this fails even if IP addresses are used (even 127.0.0.1).
The issue is already fixed since last Year
September 26, 2018—KB4458469 (OS Build 17134.320)
Addresses an issue that causes NTLTEST, DCLOCATOR, or joining an
Active Directory and SAMBA domain to fail when using the NetBIOS
domain name. The error is “An Active Directory domain Controller (AD
DC) for the domain %domain% could not be contacted”. This also
addresses connection issues for applications that use mailslots to
communicate.
This seems to be a problem with the latest Feature Update from Windows 10 (1803), not a patch via Windows Update.
Please check if you are using build 17134.48 (Also known as 1803)
Try a downgrade to 1709.
01/09/2019:
With the latests 1809 Build Mailslots are working again
I didn`t find any information that mailslot communication is not longer supported in that way you do this.
I think it is a bug.
But the only way to find out is to open a support ticket via support.microsoft.com.
Or you could post here https://social.technet.microsoft.com/Forums
Until we get any new information from Microsoft everybody who needs mailslots should block the feature upgrade 1803.

Desktop duplication screen capturing - DuplicateOutput returns E_ACCESSDENIED error

I'm capturing the screens using desktop duplication APIs (DirectX11). The DuplicateOutput API returns the access denied error and that too happens very rare(may be 10% of the time) on a windows 8.1 machine on logon screen though my application is running with SYSTEM level privileges and the SetThreadDesktop being called properly. I used to reset and call SetThreadDesktop after every error I got but the application couldn't recover from the error after that even after multiple device resets and inits. I had to fallback to GDI (application works fine after being switched from directx to GDI) based approach after multiple retries or restart the application but that idea seems terrible.
Note: I did come across the same problem on Windows 10/ Windows 8 machines but not too often compared to that particular windows 8.1 machine.
here is the description of the E_ACCESSDENIED error that tells only possible case (not having system level privileges or the SetThreadDesktop not being called properly) for this error. I tried all the possible ways to find out the problem but couldn't.
Any help would be appreciated, Thanks in advance.
Here is the code to init the device:
//
// Initialize duplication interfaces
//
HRESULT cDuplicationManager::InitDupl(_In_ ID3D11Device* Device, _In_ IDXGIAdapter *_pAdapter, _In_ IDXGIOutput *_pOutput, _In_ UINT Output)
{
HRESULT hr = E_FAIL;
if(!_pOutput || !_pAdapter || !Device)
{
return hr;
}
m_OutputNumber = Output;
// Take a reference on the device
m_Device = Device;
m_Device->AddRef();
_pOutput->GetDesc(&m_OutputDesc);
// QI for Output 1
IDXGIOutput1* DxgiOutput1 = nullptr;
hr = _pOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER"), _T("Error"), hr);
}
// Create desktop duplication
hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl);
DxgiOutput1->Release();
DxgiOutput1 = nullptr;
if (FAILED(hr) || !m_DeskDupl)
{
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
return ProcessFailure(nullptr, _T("Maximum number of applications using Desktop Duplication API"), _T("Error"), hr);
}
return ProcessFailure(m_Device, _T("Failed to get duplicate output in DUPLICATIONMANAGER"), _T("Error"), hr);//, CreateDuplicationExpectedErrors);
}
return S_OK;
}
The code to set the desktop to current thread:
DWORD setCurrentInputDesktop()
{
DWORD errorCode = ERROR_ACCESS_DENIED;
HDESK currentInputDesktop = OpenInputDesktop(0, false, GENERIC_ALL);
if(currentInputDesktop != NULL)
{
if(!SetThreadDesktop(currentInputDesktop))
{
errorCode = GetLastError();
cout << ("setCurrentInputDesktop: SetThreadDesktop failed. Error code: ") << errorCode;
}
else
{
errorCode = ERROR_SUCCESS;
cout << ("setCurrentInputDesktop: SetThreadDesktop succeeded.");
}
CloseDesktop(currentInputDesktop);
}
else
{
errorCode = GetLastError();
cout << "setCurrentInputDesktop: OpenInputDesktop failed. Error code: " << errorCode;
}
return errorCode;
}
Here is the error message returned after processing the error code:
Id3d11DuplicationManager::ProcessFailure - Error: Failed to get
duplicate output in DUPLICATIONMANAGER, Detail: Access is denied.

CLR 2.0 memory leak: Failing to release COM object on unload

I have come across a bug in CLR 2.0 that is resolved in CLR 4.0. It occurs when passing arrays across .NET COM interop and a COM exception is generated (E_FAIL). The details of how to reproduce this bug are below.
My problem is that it will be very difficult to force our clients to upgrade to .NET 4.0, so I'd like to implement a workaround. I can do so by calling obj->Release if I know that the bug has occured, but clearly this is dangerous if there's any chance of a false positive.
And so the question: What is the specification of this bug, and is it possible for me to identify it precisely?
I found .NET release notes for 4.0.1, 4.0.2, and 4.0.3, but the bug is not mentioned. There must be a significant changelist in the CLR transition from 2.0 to 4.0, and I guess this is not publicly available?
Obviously the code below makes little sense on its own, but it's the simplest reproduction of the issue that I could distill based on quite a large, complicated solution.
Thanks in advance for taking a look,
R
Important Edit
Unfortunately, I came back to try and investigate a little further, and it's possible that the code below does not actually reproduce the bug, which would be disappointing. However, in the actual application, the memory leak is clear. If someone is interested and I have time, I'll try to produce a valid example.
Code Overview
I have a .NET application, ConsoleApp.exe, here reproduced in C# although the original is F#. ConsoleApp.exe calls a managed assembly, managed.AComObject.dll, that exposes a COM object, AComObject. AComObject.get_TheObject() returns a VARIANT* pointing to a smart pointer, ASmartPtr, which allows me to override the AddRef and Release methods to observe the references held against the object.
When running ConsoleApp.exe with unmanaged code debugging enabled, I can see the reference counts on the SmartPtr. I change the CLR by adjusting the supportedRuntime property in ConsoleApp.exe.config with the following results:
v4.0 shows "DEBUGMSG::ASmartPtr::Release:0", at which point the SmartPtr is deleted.
v2.0.50727 shows "DEBUGMSG::ASmartPtr::Release:1" before it exits, a leak.
I include the bits of code I believe are relevant, but please shout if more is required; COM needs a lot of boilerplate code...!
ConsoleApp.exe
using managed.AComObject;
using System;
public static class Program
{
public static void Main()
{
AComObject an_obj = new AComObject();
object[] pData = new object[] { 1 };
object a_val = an_obj.get_TheObject(0, pData);
object[] pData2 = new object[] { a_val };
try
{
object obj3 = an_obj.get_TheObject(1, pData2);
}
catch (System.Exception)
{
// Makes no diff whether it's caught - still does not clean
}
}
}
AComObject.dll
AComObject.idl
interface IAComObject : IDispatch
{
[propget, id(1), helpstring("")] HRESULT DllName([out, retval] BSTR* pName);
[propget, id(2), helpstring("")] HRESULT TheObject([in] LONG count, [in, size_is(count)] VARIANT* pData, [out, retval] VARIANT* pObject);
};
[...]
library AComObjectLib
{
importlib("stdole2.tlb");
// Class information
[...]
coclass AComObject
{
[default] interface IAComObject;
};
};
AComObject.h
[...]
class ATL_NO_VTABLE CAComObject :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CAComObject, &CLSID_AComObject>,
public IDispatchImpl<IAComObject, &IID_IAComObject, &LIBID_AComObjectLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
DECLARE_REGISTRY_RESOURCEID(IDR_ACOMOBJECT)
BEGIN_COM_MAP(CAComObject)
COM_INTERFACE_ENTRY2(IDispatch, IAComObject)
COM_INTERFACE_ENTRY(IAComObject)
END_COM_MAP()
public:
CAComObject();
virtual /* [helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_DllName(
/* [retval][out] */ BSTR* pName);
virtual /* [helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_TheObject(
/* [in] */ LONG count,
/* [in, size_is(count)] */ VARIANT* pData,
/* [retval][out] */ VARIANT* pObject);
};
OBJECT_ENTRY_AUTO(CLSID_AComObject, CAComObject)
AComObject.cpp
class ASmartPtr : public IUnknown
{
int m_RC;
void DebugMsg(std::string msg)
{
std::stringstream _msg;
_msg << ".\nDEBUGMSG::ASmartPtr::" << msg << "\n";
OutputDebugStringA(_msg.str().c_str());
}
public:
ASmartPtr()
: m_RC(1)
{
DebugMsg(std::string("Created"));
}
virtual ULONG STDMETHODCALLTYPE AddRef()
{
ULONG refcnt = ++m_RC;
std::stringstream msg;
msg << "AddRef:" << refcnt;
DebugMsg(msg.str());
return refcnt;
}
virtual ULONG STDMETHODCALLTYPE Release()
{
ULONG refcnt = --m_RC;
std::stringstream msg;
msg << "Release:" << refcnt;
DebugMsg(msg.str());
if (m_RC == 0)
delete this;
return refcnt;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObj)
{
if (!ppvObj) return E_POINTER;
if (iid == IID_IUnknown)
{
*ppvObj = this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
};
[...]
STDMETHODIMP CAComObject::get_TheObject(LONG count, VARIANT* pData, VARIANT* pObject)
{
if (count == 1)
return E_FAIL;
CComVariant res;
res.punkVal = new ASmartPtr();
res.vt = VT_UNKNOWN;
res.Detach(pObject);
return S_OK;
}
managed.AComObject.dll
This is assembled from the COM object with the following post-build events to enable passing of arrays to get_TheObject() rather than references.
Batch File
call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Tools\vsvars32.bat"
echo "f" | xcopy /L/D/Y ..\Debug\AComObject.dll managed.AComObject.dll | find "AComObject" > nul
if not errorlevel 1 (
tlbimp ..\Debug\AComObject.dll /primary /keyfile:..\piakey.snk /out:managed.AComObject.dll
ildasm managed.AComObject.dll /out:managed.AComObject.raw.il
perl -p oneliner.pl < managed.AComObject.raw.il > managed.AComObject.il
ilasm managed.AComObject.il /dll /key=..\piakey.snk
)
set errorlevel=0
exit 0
oneliner.pl
$a = 1 if (/TheObject\(/);if ($a){s/object&/object\[\]/; s/marshal\( struct\) pData/marshal\( \[\]\) pData/; $a++; $a&=3;}
This simply changes the IL:
[in] object& marshal( struct) pData) runtime managed internalcall
to
[in] object[] marshal( []) pData) runtime managed internalcall
Some additional information
In considering my response to Hans's comment, I realised some relevant information is missing.
If no exception is thrown (i.e. E_FAIL is changed to S_OK), there is no leak. In the S_OK case, we can see the object reference count returning to 1 as we cross the .NET COM interop back into ConsoleApp.exe. In the E_FAIL case, the refcount remains at 2. In both cases, we can observe the finalizer reducing the refcount again as the application terminates (and observe the object destructor in the S_OK case), but in the E_FAIL case, this still leaves the refcount at 1 so the object is leaked. In CLR 4.0, all behaves as expected (i.e. refcount returns to 1 on passing back to ConsoleApp.exe even in the E_FAIL case).
We are considering upgrading to CLR 4.0 to resolve this leak, but it is not entirely trivial since it treats COM wrapped managed DLLs in a different way and this is a breaking change for some of our clients. If there was a way for me to precisely identify this bug, we could avoid the upgrade pain for a little longer.
In the end, the solution was rather simple, and we were able to proceed without upgrading. It was the old trick of adding a supportedRuntime and the additional attribute to the application.exe.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
</startup>
</configuration>
Without the attribute, the .NET2 code loads side-by-side into CLR2, and so we suffer the leak. The attribute allows the .NET2 code to be loaded directly into the CLR4, hence avoiding the leak. There is a detailed review of that attribute here: http://www.marklio.com/marklio/PermaLink,guid,ecc34c3c-be44-4422-86b7-900900e451f9.aspx.
This unfortunately leaves the memory leak extant for anyone using an application with such a config, but this is adequate for the time being.

What is the limitation with calling CoInitializeEx/CoUninitialize multiple times - RPCSS service leaking memory

One of our apps has a long loop which occasionally causing the Windows XP (SP3) RPCSS service to leak memory when our app is accessing another shared app/service providing COM access as 'local server'.
I've worked it back to the raw problem now (I think) and can show the same leak if the app is accessing other local servers like IExplore.exe quite quickly
Using Process Explorer to monitor RPCSS service, having launched IE and then run the following, the RPCSS service will start to show some steady IO usage after a few seconds and then the Private Bytes will start to steadily increase. Stopping the app frees a large portion of this memory, but not always all of it.
#include <exdisp.h>
while(true){
HRESULT hrIni = CoInitializeEx(NULL, COINIT_MULTITHREADED);
{
HRESULT hr;
CComPtr<IWebBrowser2> pBrowser2;
hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, (void**)&pBrowser2);
}
if( hrIni == S_OK || hrIni == S_FALSE ) CoUninitialize();
}
This does not cause RPCSS service to leak - in process server
#include <activeds.h>
while(true){
HRESULT hrIni = CoInitializeEx(NULL, COINIT_MULTITHREADED);
{
HRESULT hr;
CComPtr<IADsADSystemInfo> pSys;
hr = CoCreateInstance(CLSID_ADSystemInfo, NULL, CLSCTX_INPROC_SERVER, IID_IADsADSystemInfo, (void**)&pSys);
}
if( hrIni == S_OK || hrIni == S_FALSE ) CoUninitialize();
}
This adjustment to ensure CoInit and uninit at a broader scope appears to avoid the leak, or maybe just make it very slow
HRESULT hrIni = CoInitializeEx(NULL, COINIT_MULTITHREADED);
while(true){
{
HRESULT hr;
CComPtr<IWebBrowser2> pBrowser2;
hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, (void**)&pBrowser2);
}
}
if( hrIni == S_OK || hrIni == S_FALSE ) CoUninitialize();
Have I made some stupid mistake I'm just not seeing as to why this wouldn't be clean?
or, Is there some limitation on init/uninit and calling local service?

Starting .exe from Visual C++ 2005 dll

Does anyone know the code or have ideas on how to kick off an .exe using Visual C++ 2005?
The environment the dll is on if Windows Mobile. The C# to do this using P/Invoke is
[DllImport("coredll.Dll")]
private static extern int CreateProcess(string strImageName, string strCmdLine, IntPtr pProcessAttributes, IntPtr pThreadAttributes , int bInheritsHandle, int dwCreationFlags, IntPtr pEnvironment, IntPtr pCurrentDir, Byte[] bArray, ProcessInfo oProc);
c# Code to start .exe
CreateProcess("\\Program Files\\myprogram\\myprogram.exe.exe", "", IntPtr.Zero, IntPtr.Zero, 0, 0, IntPtr.Zero, IntPtr.Zero, new Byte[128], pi);
The reason I need it in C++ is because I am forced to use a native dll to carry out pre and post intit checks etc when running a custom cab installer.
Your thoughts are much appreciated.
Tony
PROCESS_INFORMATION ProcessInfo = { 0 };
if (CreateProcess(ImagePath,
NULL,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
NULL,
&ProcessInfo))
{
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
}
else
{
return GetLastError();
}
Try this:
BOOL RunExe(CString strFile)
{
WIN32_FIND_DATA fd;
HANDLE hFind;
BOOL bFind;
hFind = FindFirstFile(strFile, &fd);
bFind = (hFind != INVALID_HANDLE_VALUE);
if(bFind)
{
if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
SHELLEXECUTEINFO info;
ZeroMemory(&info, sizeof(info));
info.cbSize = sizeof(info);
info.fMask = SEE_MASK_NOCLOSEPROCESS;
info.hwnd = 0;
info.lpVerb = _T("open");
info.lpFile = strFile;
info.lpParameters = NULL;
info.lpDirectory = NULL;
info.nShow = SW_SHOW;
info.hInstApp = NULL;
ShellExecuteEx(&info);
}
else
bFind = FALSE;
}
FindClose(hFind);
return bFind;
}
If you mean running an exe on the device, then no visual studio can't do it directly. You need to setup a custom build step or pre/post build steps to run a application that will do it for you. You can use the WM5 SDK code example prun (or create your own). PRun uses RAPI to run the application on the device, so the device needs to be connected through ActiveSync for this to work.
If you are trying to make stuff "automatically" happen on the device (e.g. unit tests), you may like to look into running the device emulator. This may get you more control than trying to use a physical device.

Resources