Return value from DLL function - c++

I understand that the DLL typically has its own heap that it stores variables in. My DLL function takes a pointer to a wchar_t variable, and whenever I try to put a value in it, it simply exits the function, leaving the pointer pointing to a bad location which I'm assuming is because the heap gets destroyed.
If my DLL function comes up with some sort of data that needs to be passed back, could someone give me an example of how I could get that data in string format back to the original main function?
Using Visual Studio 2010.
Edit: I can provide some sample code, but I didn't see the point since I'm simply asking for an example/ explanation as to how memory is handled with regards to dll's and their functions. Ask me what information you need and I'll try to deliver.
Well, to give you guys an idea as to what the application does, it's a COM server DLL. The interface is IProperty, the object is called PropertyObject. The DLL was built separately, by me, with the PropertyObject methods. This method, Getproperty, is the one I'm working on.
STDMETHODIMP PropertyObject::GetProperty(int arg1, wchar_t* arg2)
{
arg2 = L"Test";
cout << *arg2 << endl;
return S_OK;
}
int main()
{
CoInitialize(NULL);
IClassFactory * pClassFactory = NULL;
HRESULT hr;
hr = CoGetClassObject(
CLSID_PropertyObject,
CLSCTX_SERVER,
NULL,
IID_IClassFactory,
(void **) &pClassFactory);
if (SUCCEEDED(hr))
{
wchar_t x = NULL;
IProperty *pProperty = NULL;
hr = pClassFactory->CreateInstance(NULL, IID_IProperty, (void **) &pProperty);
hr = pProperty->GetProperty(2, &x);
cout << x << endl;
}
return 0;
}

If you are 100% sure about the fact that all participating programs are compiled with the same version of Visual Studio (which implies they all use the same version of the STL that std::string is part of), you can use the std::string class.
If it needs to be interoperable, your best bet is passing in a char* and a length and write to that supplied buffer. Let the caller handle the memory. That's pretty C-style, but also your safest bet.

Turns out I was still considering a wchar_t pointer like a normal character array. here is my revised code:
STDMETHODIMP PropertyObject::GetProperty(int arg1, wchar_t* arg2)
{
wcscpy(arg2, L"Test"); // This is the function that i needed to be using.
return S_OK;
}
int main()
{
CoInitialize(NULL);
IClassFactory * pClassFactory = NULL;
HRESULT hr;
hr = CoGetClassObject(
CLSID_PropertyObject,
CLSCTX_SERVER,
NULL,
IID_IClassFactory,
(void **) &pClassFactory);
if (SUCCEEDED(hr))
{
wchar_t *x = new wchar_t; // Before, this was a normal variable. Changed it to a pointer.
IProperty *pProperty = NULL;
hr = pClassFactory->CreateInstance(NULL, IID_IProperty, (void **) &pProperty);
hr = pProperty->GetProperty(2, x); // Passed the pointer instead of an address to a normal variable.
wcout << x << endl; // wcout instead of cout. It worked.
}
return 0;
}

Related

How do I run a COM (.ocx) object in a C++ command line interface program. (VS2017)

How do I run a COM (.ocx) object in a C++ command line interface program. (VS2017)
After many hours of research I have the following. I think the COM object is loading as trio is populated. But I do not know how to run it successfully. It may need to be attached to a CWND or something.
I have this code, which may be the wrong rabbit hole. It crashes horribly.
HRESULT hr;
hr = CoInitialize(0);
assert(SUCCEEDED(hr));
{
static CLSID const clsid
= { 0xf1933967, 0x74b0, 0x11d3,{ 0x8a, 0x13, 0x0, 0x40, 0x33, 0x93, 0xb2, 0x36 } };
//CLSID ClassID;
//hr = CLSIDFromProgID(OLESTR("TrioPCLib.TrioPC"), &ClassID);
assert(SUCCEEDED(hr));
TrioPCLib::_DTrioPCPtr trio;
IID iid = TrioPCLib::_DTrioPCPtr::GetIID();
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, reinterpret_cast<void**>(&trio));
assert(SUCCEEDED(hr));
trio->Release();
}
CoUninitialize();
The COM object has GUI which I do not need to use it. I just want to call the API.
UPDATE: The ActiveX is loaded and I can call, for example, AboutBox() and it is displayed. It crashes on the CoUninitialize() with an exception...
Unhandled exception at 0x779CA899 (ntdll.dll) in TestVpu.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77A05910).
I'd be willing to bet that the exception is actually happening at the close brace, not the CoUninitialize call. It looks like you might be releasing the COM object twice, once explicitly and once implicitly.
You are explicitly calling trio->Release(). You are also using a TrioPCLib::_DTrioPCPtr, which is probably a COM smart pointer produced by the Visual C++ compiler encountering an #import. This class automatically calls Release on the referenced object when it goes out of scope.
You should either use a TrioPCLib::_DTrioPC* or you should not call trio->Release(). (FWIW: I would prefer to use TrioPCLib::_DTrioPCPtr and not explicitly call trio->Release().)
The following appears to be much more stable.
It uses pointers and calls the Release. The import has some extra params. The import generates code that you can look at. The class is derived from IDispatch and calls are made using _com_dispatch_method(). Up to now I have not needed to worry about a message pump.
#include "stdafx.h"
#include <iostream>
using namespace std;
#import "C:/Users/*****/Documents/Trio Motion Solutions/TrioMCTools/Debug/Win32/TrioPC.ocx" named_guids no_namespace
int main(int, char**)
{
CoInitialize(0);
{
bool inOK = false;
int length = 3;
_DTrioPC* pitd = 0;
HRESULT hr = CoCreateInstance(CLSID_TrioPC, 0, CLSCTX_ALL, DIID__DTrioPC, reinterpret_cast<void**>(&pitd));
if (SUCCEEDED(hr)) cout << "ok" << endl; else cout << "nok" << endl;
pitd->SetHost("192.168.0.250");
bool openOK = pitd->Open(2, 0);
if (openOK) cout << "ok" << endl; else cout << "nok" << endl;
SAFEARRAY *data = SafeArrayCreateVectorEx(VT_R8, 0, length, NULL);
if (data)
{
double *safe_data;
hr = SafeArrayAccessData(data, (void **)&safe_data);
if (SUCCEEDED(hr))
{
int i;
for (i = 0; i < length; i++)
safe_data[i] = i+532;
SafeArrayUnaccessData(data);
VARIANT arg;
VariantInit(&arg);
arg.vt = VT_ARRAY | VT_R8;
arg.parray = data;
inOK = pitd->SetTable(1000, 3, &arg);
}
}
pitd->Release();
}
CoUninitialize();
return 0;
}

Problems understanding and using double pointers

I am not sure how to use double pointers.
The function i need to use looks as following:
HRESULT GetBuffer(
[out] IMediaSample **ppBuffer,
[in] REFERENCE_TIME *pStartTime,
[in] REFERENCE_TIME *pEndTime,
[in] DWORD dwFlags
);
Documentation says:
ppBuffer [out]
Receives a pointer to the buffer's IMediaSample interface. The caller must release the interface.
This is what i tried using it:
HRESULT MCMyOutputPin::Deliver(IMediaSample* sample)
{
HRESULT hr = NO_ERROR;
myLogger->LogDebug("In Outputpin Deliver", L"D:\\TEMP\\yc.log");
if (sample->GetActualDataLength() > 0)
{
IMediaSample **outsample;
m_pAllocator->GetBuffer(outsample, NULL, NULL, NULL); //Access violation here
BYTE** sampleBuffer;
BYTE** newBuffer;
sample->GetPointer(sampleBuffer);
(*outsample)->GetPointer(newBuffer);
memcpy((void *)newBuffer, (void *)sampleBuffer, sizeof(**sampleBuffer));
m_pInputPin->Receive(*outsample);
sample->AddRef();
}
return hr;
//Forward to filter
}
Which gives me an:
Access violation reading location 0xFFFFFFFFFFFFFFFF.
Then i tried using the address operator:
hr = m_pAllocator->GetBuffer(&outsample, NULL, NULL, NULL); //outsample is set to NULL
BYTE* sampleBuffer = NULL;
BYTE* newBuffer = NULL;
sample->GetPointer(&sampleBuffer);
outsample->GetPointer(&newBuffer);
memcpy((void *)newBuffer, (void *)sampleBuffer, sizeof(*sampleBuffer));
m_pInputPin->Receive(outsample);
This sets outsample to NULL.
So what is the correct syntax to handle double pointers?
My first, high-level comment, is that you are not checking the return values of the functions that you call. It's a mistake to neglect error checking. Your first step is to add the necessary error checking.
HRESULT GetBuffer(
[out] IMediaSample **ppBuffer,
[in] REFERENCE_TIME *pStartTime,
[in] REFERENCE_TIME *pEndTime,
[in] DWORD dwFlags
);
The first parameter is used to return a IMediaSample* to the caller. You need to declare a variable of type IMediaSample*, and pass its address:
IMediaSample* sample;
....
hr = m_pAllocator->GetBuffer(&outsample, ...);
// check hr
....
So, outsample is of type IMediaSample*. When you take its address, with &outsample, you now have something of type IMediaSample**, which is what you need.
Remember that when working with interfaces, you always work with a pointer to the interface.
You've made the same mistake with the BYTE** parameters. Again, declare variables of type BYTE*, and pass the address of these variables to the functions that you call.
BYTE* sampleBuffer;
BYTE* newBuffer;
....
hr = sample->GetPointer(&sampleBuffer);
// check hr
hr = outsample->GetPointer(newBuffer);
// check hr
Using sizeof(**sampleBuffer) in your call to memcpy is wrong. In your code, where sampleBuffer is wrongly declared as BYTE**, sizeof(**sampleBuffer) is just sizeof(BYTE) which is always 1.
In fact you can conclude that any use of sizeof is incorrect here because sizeof is evaluated at compile time. You need to find the actual size of the dynamic buffer at runtime using whatever functionality these interfaces provide.
The call to sample->AddRef() looks a little suspect. I don't see any evidence that anything has taken a new reference to the interface.
IMediaSample **outsample;
m_pAllocator->GetBuffer(outsample, NULL, NULL, NULL);
You're passing the function the value of outsample, which is a garbage value since it doesn't point to anything. You want:
IMediaSample *outsample;
m_pAllocator->GetBuffer(&outsample, NULL, NULL, NULL);
This gives the function the address of outsample so that it can stuff the pointer it wants to return in it.
I try to simulate the function as test, which using int** as output . the problem here is the function is suppose *output is is valid, so in the first way ,you have to make sure that, you might want to do something as this
sample = new IMediaSample[1]
this is my sample code, hope it helps
#include <iostream>
using namespace std;
void test(int** output ,int* in)
{
*output = new int[1];
output[0][0] = in[0];
}
int main()
{
int a[] ={ 2 };
int* out1;
test(&out1 ,a);
cout << out1[0] << endl;
int** out2;
out2 = new int*[1];
test(out2 ,a);
cout << out2[0][0] << endl;
return 0;
}

Problems accessing a COM interface in C++

What I want to do is access a COM interface and then call the "Open" method of that interface.
I have a sample code in Visual Basic which works fine, but I need to write it in C++ and I can't seem to get it to work.
First of all, this is the working VB code:
Dim CANapeApplication As CANAPELib.Application
CANapeApplication = CreateObject("CANape.Application")
Call CANapeApplication.Open("C:\Users\Public\Documents\Vector\CANape\12\Project", 0)
CANape.Application is the ProgID which selects the interface I need.
After reading some docs at msdn.microsoft.com and this question, I wrote this code:
void ErrorDescription(HRESULT hr); //Function to output a readable hr error
int InitCOM();
int OpenCANape();
// Declarations of variables used.
HRESULT hresult;
void **canApeAppPtr;
IDispatch *pdisp;
CLSID ClassID;
DISPID FAR dispid;
UINT nArgErr;
OLECHAR FAR* canApeWorkingDirectory = L"C:\\Users\\Public\\Documents\\Vector\\CANape\\12\\Project";
int main(){
// Instantiate CANape COM interface
if (InitCOM() != 0) {
std::cout << "init error";
return 1;
}
// Open CANape
if (OpenCANape() != 0) {
std::cout << "Failed to open CANape Project" << std::endl;
return 1;
}
CoUninitialize();
return 0;
}
void ErrorDescription(HRESULT hr) {
if(FACILITY_WINDOWS == HRESULT_FACILITY(hr))
hr = HRESULT_CODE(hr);
TCHAR* szErrMsg;
if(FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&szErrMsg, 0, NULL) != 0)
{
_tprintf(TEXT("%s"), szErrMsg);
LocalFree(szErrMsg);
} else
_tprintf( TEXT("[Could not find a description for error # %#x.]\n"), hr);
}
int InitCOM() {
// Initialize OLE DLLs.
hresult = OleInitialize(NULL);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
// Get CLSID from ProgID
//hresult = CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID);
hresult = CLSIDFromProgID(OLESTR("CanapeCom.CanapeCom"), &ClassID);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
// OLE function CoCreateInstance starts application using GUID/CLSID
hresult = CoCreateInstance(ClassID, NULL, CLSCTX_LOCAL_SERVER,
IID_IDispatch, (void **)&pdisp);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
// Call QueryInterface to see if object supports IDispatch
hresult = pdisp->QueryInterface(IID_IDispatch, (void **)&pdisp);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
std::cout << "success" << std::endl;
return 0;
}
int OpenCANape() {
//Method name
OLECHAR *szMember = L"Open";
// Retrieve the dispatch identifier for the Open method
// Use defaults where possible
DISPID idFileExists;
hresult = pdisp->GetIDsOfNames(
IID_NULL,
&szMember,
1,
LOCALE_SYSTEM_DEFAULT,
&idFileExists);
if (!SUCCEEDED(hresult)) {
std::cout << "GetIDsOfNames: ";
ErrorDescription(hresult);
return 1;
}
unsigned int puArgErr = 0;
VARIANT VarResult;
VariantInit(&VarResult);
DISPPARAMS pParams;
memset(&pParams, 0, sizeof(DISPPARAMS));
pParams.cArgs = 2;
VARIANT Arguments[2];
VariantInit(&Arguments[0]);
pParams.rgvarg = Arguments;
pParams.cNamedArgs = 0;
pParams.rgvarg[0].vt = VT_BSTR;
pParams.rgvarg[0].bstrVal = SysAllocString(canApeWorkingDirectory);
pParams.rgvarg[1].vt = VT_INT;
pParams.rgvarg[1].intVal = 0; // debug mode
// Invoke the method. Use defaults where possible.
hresult = pdisp->Invoke(
dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
&pParams,
&VarResult,
NULL,
&puArgErr
);
SysFreeString(pParams.rgvarg[0].bstrVal);
if (!SUCCEEDED(hresult)) {
ErrorDescription(hresult);
return 1;
}
return 0;
}
There are several problems with this.
Using the ClassID received from CLSIDFromProgID as the first parameter of CoCreateInstance does not work, it returns the error: class not registered
If i use the ProgID CanapeCom.CanapeCom (I found it by looking in the Registry), CoCreateInstance works. However, when I use pdisp->GetIDsOfNames I get the error message: Unkown name. Which I think means that the method was not found. That seems logical because I've used a different ProgID, but I just can't figure out how to get to the interface I'm looking for.
I have also tried to use the resulting CLSID from CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID); as the 4th argument of CoCreateInstance but that resulted in a "No such interface supported" error.
Do I need the dll file of the software? In the VB example the dll file is used to get the interface and then create a new object using the ProgID. I'm not sure if I need to do the same in C++ or how this should work.
I'm really stuck here and hope that someone can help me.
Thanks for your comments.
I've fixed the problem, although the solution is kind of embarrassing...
In my defense, I'm still a student and new to this kind of stuff.
I've used the Process Monitor to check what happens when I execute the VB script.
I saw that the CLSID used there is the ID returned by CLSIDFromProgID(OLESTR("CANape.Application"), &ClassID);, which meant that this had to be the right one and the problem had to be somewhere else. I've looked again at the CoCreateInstance and then took a look at the other parameters. Turns out that the context CLSCTX_LOCAL_SERVER was wrong, it has to be CLSCTX_INPROC_SERVER. I don't know why I've set it to local_server in the first place or why I've never questioned it. I wrote that part of the code a few days ago and then focused too much on the CLSID and IID rather than on the other parameters.
I've also taken the first comment from Alex into account and created a tlb file.
This is a simplified version of the code that works:
#import "CANape.tlb"
int _tmain(int argc, _TCHAR* argv[])
{
_bstr_t path = "C:\\Users\\Public\\Documents\\Vector\\CANape\\12\\Project";
CLSID idbpnt;
CoInitialize(NULL);
HRESULT hr = CLSIDFromProgID (L"CANape.Application", &idbpnt);
CANAPELib::IApplication *app;
hr = CoCreateInstance(idbpnt,NULL,CLSCTX_INPROC_SERVER,__uuidof(CANAPELib::IApplication),(LPVOID*)&app );
app->Open(path,0);
CoUninitialize();
return 0;
}

COM server as windows service, cannot pass array as function argument

I'm implementing COM server (using ATL) as Windows service. I have the following method defined in service header:
STDMETHOD(SetBytes)(long lenSource, const BYTE* pSource, VARIANT_BOOL *pResult);
This method is declared in the IDL file:
[
object,
uuid(351C5A5F-3EB8-4CC5-AB79-6DCD27C2F7E0),
dual,
pointer_default(unique)
]
interface ISampleInterface: IUnknown {
HRESULT SetBytes([in] long lenSource, [in,ref,size_is(lenSource)] const BYTE* pSource, [out,retval] VARIANT_BOOL *pResult);
};
I'm calling it from my test application like this:
CoInitialize(NULL);
IUnknownPtr unknown_ptr;
HRESULT hr = unknown_ptr.CreateInstance(__uuidof(MyLib::SampleManager));
if (FAILED(hr)) {
...
};
MyLib::ISampleInterfacePtr sample_ptr;
sample_ptr = unknown_ptr; // no check here, assume sample_ptr is not null
VARIANT_BOOL function_result = VARIANT_FALSE;
vector<uint8_t> flash_data(1000, 2);
function_result = sample_ptr->SetBytes(flash_data.size(), &flash_data[0]);
I'm registering service by performing:
MyService.exe /regserver
MyService.exe -service
Then I'm executing test code step by step. When I'm going to tli file where we can see the following
HRESULT _hr = raw_SetBytes(lenSource, pSource, &_result);
pSource is absolutely ok and points to the area of memory where my data is contained. But when I'm going further (I'm attached to the service with debugger) and I'm in service's function SetBytes, only one byte from this array is contained in memory area and this pointer points to the different address.
I have tried implementing server via dll (it's registered in the system with regsvr32 [dllname]) and the pointer was absolutely ok in it in this case and all the length was passed, not only one byte.
I'm new to COM technology and wondering where I am wrong.
you could maybe wrap your BYTE array it into a SAFEARRAY.
STDMETHODIMP MyClass::getVariantFromCharArray( char *inputCharArray, UINT inputCharArrayLength, VARIANT *outputVariant)
{
SAFEARRAYBOUND saBound;
char *pData = NULL;
saBound.cElements = inputCharArrayLength;
saBound.lLbound = 0;
VariantInit( outputVariant);
(*outputVariant).vt = VT_UI1 | VT_ARRAY;
(*outputVariant).parray = SafeArrayCreate( VT_UI1, 1, &saBound);
if ( (*outputVariant).parray)
{
SafeArrayAccessData( (*outputVariant).parray, (void **)&pData);
memcpy( pData, inputCharArray, inputCharArrayLength);
SafeArrayUnaccessData( (*outputVariant).parray);
return S_OK;
}
else
{
return E_OUTOFMEMORY;
}
}
You need to use a SAFEARRAY to pass byte arrays around in COM.

Why COM doesn't work in a new thread?

My problems started after converting my VS2003 project to VS2008. Solution contains 3 projects. Projects are DLL's. There were A LOT of compilation errors, then some linker errors... Well, I fought them off. Now it just simply doesn't work ;)
So, one of this DLL's is suppoused to communicate with Word by COM.
Word::_ApplicationPtr d_pApp;
Word::_DocumentPtr d_pDoc;
void MSWord2003::init()
{
free();
HRESULT hr;
CLSID clsid;
CLSIDFromProgID(L"Word.Application", &clsid);
// Get an interface to the running instance, if any..
IUnknown *pUnk;
hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk);
if(hr!=S_OK)
throw MSWord::MSWordException("Nie znaleziono działającej aplikacji MSWord.");
IDispatch* d_pDispApp;
hr = pUnk->QueryInterface(IID_IDispatch, (void**)&d_pDispApp);
if(hr!=S_OK)
throw MSWord::MSWordException("Nie udało się połączyć z aplikacją MSWord.");
pUnk->Release();
pUnk = 0;
d_pApp = d_pDispApp;
d_pDoc = d_pApp->ActiveDocument;
d_pDispApp->AddRef();
d_currIdx = -1;
paragraphsCount = d_pDoc->GetParagraphs()->Count;
footnotesCount = d_pDoc->GetFootnotes()->Count;
endnotesCount = d_pDoc->GetEndnotes()->Count;
}
void MSWord2003::free()
{
if(d_pApp!=0)
{
d_pApp->Release();
d_pApp=0;
}
}
This code works on VS2003 (and different machine, I don't have VS2003 on my computer) while in VS2008 it works only if it is called by main thread.
When called by a new thread (wich is initialized by CoInitialize) d_pApp is not initialized properly - its ptr shows 0.
While debugging I reached code in comip.h:
template<typename _InterfacePtr> HRESULT _QueryInterface(_InterfacePtr p) throw()
{
HRESULT hr;
// Can't QI NULL
//
if (p != NULL) {
// Query for this interface
//
Interface* pInterface;
hr = p->QueryInterface(GetIID(), reinterpret_cast<void**>(&pInterface));
// Save the interface without AddRef()ing.
//
Attach(SUCCEEDED(hr)? pInterface: NULL);
}
else {
operator=(static_cast<Interface*>(NULL));
hr = E_NOINTERFACE;
}
return hr;
}
In a new thread, QueryInterface returns E_NOINTERFACE, although GetIID() returns the same thing for both threads. And that is where I got stuck - I have no idea, what causes this behaviour...
IMO you should initialize COM not with CoInitialize, but with CoInitializeEx, specifying COINIT_MULTITHREADED. Otherwise you'll have separate single-threaded COM apartment for every thread.