How to solve "Cross-thread operation not valid" in Visual C++/CLI - multithreading

I have an issue (Windows Forms, C++, .NET) like this:
When I am trying to modify any WinForms object element (label, button etc) from a manage thread, I get the following error:
Sample Code:
static void myThread(System::Object^ obj, int nSocket) {
while (true) {
MyForm^ ob = (MyForm^)obj;
char buff[256 + 1] = { 0, };
recv(nSocket, buff, 255, 0);
string recvStr(buff);
ob->label3->Text = gcnew String(recvStr.c_str());
}
}
Exception thrown:
'System.InvalidOperationException' in System.Windows.Forms.dll
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
Cross-thread operation not valid: Control 'label3' accessed from a thread other than the thread it was created on.
How to solve that issue in C++/CLI?

Related

_set_se_translator not working for a thread

I want to direct my exceptions raised in thread using _set_se_translator function
Here is the implementation
Thread
_set_se_translator(ExceptionHandler)
Int x = 10;
Int y = x / 0;
Exception handler
Void ExceptionHandler( unsigned code, EXCEPTION_POINTERS*)
{
Throw Exception(code);
}
Exception Class
class Exception
{
Public:
Exception(unsigned codePar) { code =codePar}
Unsigned code;
Const char * getcodestring(void);
Char message[250];
};
getcodestring finction
Const char " Exception::getcodestring()
{
swtich(code)
{
Case EXCEPTION_FLT_DIVIDE_BY_ZERO:
Sprintf(message, "by zero");
Breask;
}
Return message
}
But I never got the divide by zero message infa t I think it's not going through the translator function in my thread
This is what I am getting
Unhandled win32 structured exception
Code : C000094
Where is that I am missing?
PN: there is no try catch block in my thread. Assuming the exception is caught by the translator

Marshalling ActiveX through Global Interface Table

I have been trying to do a basic ActiveX component marshalling for across thread access. I do it through a Global Interface Table (GIT) . I have a simple MFC dialog to which I add an arbitrary AciveX control such as IDC_PDF.
Inserting activeX control to MFC dialog in C++
After that I add the variable representing this control to the code. This effectively adds a pdf1.h and pdf1.cpp to the project. At initialization of the dialog inside OnInitDialog() I try to marshal the interface to this ActiveX component so that it could be used from another thread without STA apartment violation.
bool CLMC_mfcDlg::CreateMarshalledController()
{
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CComPtr<IGlobalInterfaceTable> pGIT;
// get pointer to GIT
CoCreateInstance(CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void **)&pGIT);
// get IID and IUnknown interface pointer
REFIID iid = Pd_OCX_fControl.GetClsid();
IUnknown* active_x_pointer = Pd_OCX_fControl.GetControlUnknown();
// register interface inside the GIT
if (active_x_pointer != NULL) {
HRESULT hr = pGIT->RegisterInterfaceInGlobal(active_x_pointer, iid, &dwCookie);
if (SUCCEEDED(hr))
{
// OK, wir haben das interface im GIT registriert
assert(dwCookie != 0);
}
else
dwCookie = 0;
//pGIT->Release();
}
else
dwCookie = 0;
active_x_pointer->Release();
return dwCookie!=0;
}
As a result the value of the dwCookie is set to 256, which though not 0, still feels like an error value.
When I try to get the marshalled interface from another thread. The marshalled interface received is 0x0000.
bool CLMC_mfcDlg::FetchMarshalledController()
{
HRESULT res = ::OleInitialize(NULL);
switch (res)
{
case S_OK:
break;
case OLE_E_WRONGCOMPOBJ:
case RPC_E_CHANGED_MODE:
return false;
}
CComPtr<IGlobalInterfaceTable> pThreadGIT;
CoCreateInstance(CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void **)&pThreadGIT);
REFIID iid = Pd_OCX_fControl.GetClsid();
pThreadGIT->GetInterfaceFromGlobal(
dwCookie, iid, (void**)&pMarshalledOCX);
pThreadGIT->RevokeInterfaceFromGlobal(dwCookie);
return pMarshalledOCX != nullptr;
}
What am I doing wrong? I am working with a standard ActiveX, using standard marshalling patern. Anybody got this to work?
Aurora was correct. To get a proper IID you need to find the interface in registry using oleview.exe:
you use the finding to define interface IID in your code:
static REFIID const intf_id
= { 0x5CD5C9C3, 0x0CD7, 0x453A,{ 0x8D, 0x27, 0xE3, 0xBB, 0x32, 0xB7, 0xEA, 0xFC } };
you get the interface pointer for it like this:
IUnknown * pUnknown = CBaldorOCXCard.GetControlUnknown();
// get _DMintControllerCtrl interface pointer
void* IMint = NULL;
pUnknown->QueryInterface(intf_id, (void **)&IMint);
The interface pointer and IID can now be used in marshaling.
(The problem how you work with this interface pointer without a wrapper class:) Still looking for answer)

Thread Program in visual C++/CLI giving errors

I am trying to follow the tutorial at http://www.drdobbs.com/cpp/ccli-threading-part-i/184402018 to do thread programming in winform in visual c++. I opened a win32 console project and added an empty cpp file to it inside which i placed the code as follows:
using namespace System;
using namespace System::Threading;
public class ThreadX{
int loopStart;
int loopEnd;
int dispFrequency;
public:
ThreadX(int startValue, int endValue, int frequency)
{
loopStart = startValue;
loopEnd = endValue;
dispFrequency = frequency;
}
void ThreadEntryPoint()
{
String^ threadName = Thread::CurrentThread->Name;
for (int i = loopStart; i <= loopEnd; ++i)
{
if ( i % dispFrequency == 0)
{
Console::WriteLine("{0} : i = {1,10}", threadName, i);
}
}
Console::WriteLine("{0} thread terminating", threadName);
}
};
int main()
{
ThreadX o1 = gcnew ThreadX(0, 1000000,200000);
Thread^ t1 = gcnew Thread(gcnew ThreadStart(o1, &ThreadX::ThreadEntryPoint));
t1->Name = "t1";
ThreadX o2 = gcnew ThreadX(-1000000, 0, 200000);
Thread^ t2 = gcnew Thread(gcnew ThreadStart(o2, &ThreadX::ThreadEntryPoint));
t1->Name = "t2";
t1->Start();
t2->Start();
Console::WriteLine("Primary Thread Terminating");
}
However this gives me errors such as :
error C2726: 'gcnew' may only be used to create an object with
managed type
error C2440: 'initializing' : cannot
convert from 'ThreadX *' to 'ThreadX' No constructor could take the
source type, or constructor overload resolution was ambiguous
error C3364: 'System::Threading::ThreadStart' : invalid argument for delegate constructor; delegate target needs to be a
pointer to a member function
You are mixing C++ and C++/CLI which is a different thing. Replace
public class ThreadX
with
public ref class ThreadX

Visual C++/CLI, ThreadStart with a function pointer

I am working on managed C++ or C++/CLI. I am trying to start a CLI thread to execute a function. However when I try to build I get the error "Microsoft (R) C/C++ optimizing compiler has stopped working." In the output window. "Foo.cpp(8): fatal error C1001: An internal error has occurred in the compiler."
//the class which holds the function to run
ref class Foo
{
void handleEvent();
void (*func)(void);
};
void Foo::handleEvent()
{
ThreadStart ^ param = gcnew ThreadStart(func); //line 8
Thread ^ thread = gcnew Thread(param);
thread.Start();
}
Is ThreadStart not capable of handling native function pointers? If not, is there is another way to run a regluar C function pointer from C++/CLI?
Try substituting line 8 with
ThreadStart^ param = (ThreadStart^) System::Runtime::InteropServices::Marshal::GetDelegateForFunctionPointer((IntPtr)func, ThreadStart::typeid);

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.

Resources