I want to call an method with invoke. My object input is a string that is address of a file in my computer and type of output is IDispatch. How I should define Disparams as an input of invoke. My code is :
::CLSIDFromProgID(OLESTR("SGNSAutomation.SGNSApplication"), &clsid);
IID iid;
HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL,
IID_IDispatch, (LPVOID*)&pWMPDispatch);
IDispatch * pdisp = (IDispatch *)NULL;
DISPID dispid;
DISPPARAMS params= {NULL,NULL,0,0};
OLECHAR * Name = OLESTR("importCase");
//my problem is here
params.rgvarg[1].pdispVal
params.rgvarg[0].bstrVal="E:\\new library\\university\\final project
documentation\\GPNS project\\net1.gpns";
params.rgvarg[1].scode = DISP_E_PARAMNOTFOUND;
params.cArgs = 2;
params.cNamedArgs = 0;
//
pWMPDispatch->GetIDsOfNames(IID_NULL,
&Name,1,LOCALE_SYSTEM_DEFAULT,&dispid);
HRESULT hresult =pWMPDispatch->Invoke(dispid, IID_NULL,
LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL,
NULL);
please help me to define disparams correctly.
Related
I want to pass list of email address strings from Borland C++ to my C# library. Below my C# side code. I could make call to PrintName() method and it works. Now I want to print email addresses, but if I call PrintEmails() function nothing happens. Could you please suggest how I can pass multiple email addresses to C# lib.
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("4A686AB1-8425-43D9-BD89-B696BB5F6A18")]
public interface ITestConnector
{
void PrintEmails(string[] emails);
void PrintName(string name);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(ITestConnector))]
[Guid("7D818287-298A-41BF-A224-5EAC9C581BD0")]
public class TestConnector : ITestConnector
{
public void PrintEmails(string[] emails)
{
System.IO.File.WriteAllLines(#"c:\temp\emails.txt", emails);
}
public void PrintName(string name)
{
System.IO.File.WriteAllText(#"c:\temp\name.txt", name);
}
}
I have imported TLB file of above C# library into RAD Studio and my C++ side code is as below.
interface ITestConnector : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE PrintEmails(LPSAFEARRAY* emails/*[in,out]*/) = 0; // [-1]
virtual HRESULT STDMETHODCALLTYPE PrintName(WideString name/*[in]*/) = 0; // [-1]
};
TTestConnector *connector = new TTestConnector(this);
SAFEARRAYBOUND bounds[] = {{2, 0}}; //Array Contains 2 Elements starting from Index '0'
LPSAFEARRAY pSA = SafeArrayCreate(VT_VARIANT,1,bounds); //Create a one-dimensional SafeArray of variants
long lIndex[1];
VARIANT var;
lIndex[0] = 0; // index of the element being inserted in the array
var.vt = VT_BSTR; // type of the element being inserted
var.bstrVal = ::SysAllocStringLen( L"abc#xyz.com", 11 ); // the value of the element being inserted
HRESULT hr= SafeArrayPutElement(pSA, lIndex, &var); // insert the element
// repeat the insertion for one more element (at index 1)
lIndex[0] = 1;
var.vt = VT_BSTR;
var.bstrVal = ::SysAllocStringLen( L"pqr#xyz.com", 11 );
hr = SafeArrayPutElement(pSA, lIndex, &var);
connector->PrintEmails(pSA);
delete connector;
Below C++ side code worked in my case.
SAFEARRAYBOUND saBound[1];
saBound[0].cElements = nElements;
saBound[0].lLbound = 0;
SAFEARRAY *pSA = SafeArrayCreate(VT_BSTR, 1, saBound);
if (pSA == NULL)
{
return NULL;
}
for (int ix = 0; ix < nElements; ix++)
{
BSTR pData = SysAllocString(elements[ix]);
long rgIndicies[1];
rgIndicies[0] = saBound[0].lLbound + ix;
HRESULT hr = SafeArrayPutElement(pSA, rgIndicies, pData);
_tprintf(TEXT("%d"), hr);
}
I am working on an application, which connects to a COM object and calls methods and gets properties from this object etc.
I can connect and call the members, that is not the issue. I cannot however figure out, how to call a method, which has an output parameter, for instance (pseudo code):
int GetAppVersion(bsRetMsg [out, optional]).
This functions return int as the version and can also return a string representation of the version via the output parameter.
What I've tried:
(1)
VARIANT result;
DISPPARAMS params = {NULL, NULL, 0, 0};
VARIANTARG args[1];
BSTR str = SysAllocString(L"longerfoostring");
VariantInit(&args[0]);
args[0].vt = VT_BSTR | VT_BYREF;
args[0].bstrVal = str;
params.rgvarg = args;
params.cArgs = 1;
res = dispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &result, NULL,
NULL);
if (SUCCEEDED(res)) { // here it failed: Not enough storage is available to complete this operation.
std::cout << result.intVal << std::endl;
...
(2)
VARIANT result;
DISPPARAMS params = {NULL, NULL, 0, 0};
VARIANTARG args[1];
BSTR str = SysAllocString(L""); // change: empty string
VariantInit(&args[0]);
args[0].vt = VT_BSTR | VT_BYREF;
args[0].bstrVal = str;
params.rgvarg = args;
params.cArgs = 1;
res = dispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &result, NULL,
NULL);
if (SUCCEEDED(res)) { // here it failed: Not enough storage is available to complete this operation.
std::cout << result.intVal << std::endl;
...
(3)
...
BSTR str = SysAllocString(L"longerfoostring");
VariantInit(&args[0]);
args[0].vt = VT_BSTR; // change: no BYREF
args[0].bstrVal = str;
params.rgvarg = args;
params.cArgs = 1;
... invoke is the same...
res = dispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &result, NULL,
NULL);
if (SUCCEEDED(res)) {
std::cout << result.intVal << std::endl; // this number is correct - version in int
printf("'%S'", params.rgvarg[0].bstrVal); // prints 'longerfoostring' instead of version, ie. '2.0.5...'
(4)
BSTR *str;
VariantInit(&args[0]);
args[0].vt = VT_BSTR | VT_PTR; // change: different VT
args[0].pbstrVal = str; //change: different type
params.rgvarg = args;
params.cArgs = 1;
... invoke failes with Bad variable type.
So the question is:
How to pass a string (or any type) as an input/output parameter to a COM method and correctly get the output from this parameter?
Since you want to get back the data, you have to provide the address of a BSTR
BSTR str;
VariantInit(&args[0]);
args[0].vt = VT_BSTR | VT_BYREF; // it's a BSTR and it's by ref
args[0].pbstrVal = &str; // give address of variable
params.rgvarg = args;
params.cArgs = 1;
now call invoke and don't forget to free the returned BSTR.
UPDATE 3-4-15:11IS
As recommended by David modified PInvoke as below, this time I am getting different error "Unhandled exception of type System.ExecutionEngineException occurred in mscorlib.dll"
[DllImport("C:\\Program Files\\DJVULIBRE\\LIBDJVULIBRE.dll", CharSet=CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private unsafe static extern int ddjvu_page_render(IntPtr page, ddjvu_render_mode_t mode, ref ddjvu_rect_t pagerect,
ref ddjvu_rect_t renderrect,
IntPtr pixelformat,
uint rowsize,
[Out][MarshalAs(UnmanagedType.LPArray)]byte[] imagebuffer);
Thanks David for your valuable time, I think is close to a fix.
HISTORY
I know there are many questions in this subject but none of them help to resolve the issue I am currently facing.
Below is the API function in C Language
DDJVUAPI int
ddjvu_page_render(ddjvu_page_t *page,
const ddjvu_render_mode_t mode,
const ddjvu_rect_t *pagerect,
const ddjvu_rect_t *renderrect,
const ddjvu_format_t *pixelformat,
unsigned long rowsize,
char *imagebuffer );
Below is the PInvoke signature of C Function added in .NET code
[DllImport("C:\\Program Files\\DJVULIBRE\\LIBDJVULIBRE.dll", CharSet=CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private unsafe static extern int ddjvu_page_render(IntPtr page, ddjvu_render_mode_t mode, IntPtr pagerect,
IntPtr renderrect,
IntPtr pixelformat,
ulong rowsize,
[Out][MarshalAs(UnmanagedType.LPArray)]byte[] imagebuffer);
Below is how I am calling this function in the c# code
byte* buffer = (byte *)Memory.Alloc(nSize);
try
{
IntPtr ptr1 = (IntPtr)Memory.Alloc(Marshal.SizeOf(prect));
Marshal.StructureToPtr(prect, ptr1, false);
IntPtr ptr2 = (IntPtr)Memory.Alloc(Marshal.SizeOf(rrect));
Marshal.StructureToPtr(rrect, ptr2, false);
byte[] array = new byte[nSize];
fixed (byte* p = array) Memory.Copy(buffer, p, nSize);
ddjvu_page_render(page, ddjvu_render_mode_t.DDJVU_RENDER_MASKONLY, ptr1, ptr2, fmt, (ulong)stride, array);
}
finally
{
Memory.Free(buffer);
}
call to ddjvu_page_render in above code is throwing "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
Prior to this post I must have tried all the option could find in various blogs.
Appreciate any help, is almost a day I am clueless, your timely help could save my job
UPDATE 3-4-15:7:44IS
This code is using DJVULibre
UPDATE 3-4-15:8:35IS
Here is the code I have in Form Load
ctx = ddjvu_context_create(System.AppDomain.CurrentDomain.FriendlyName);
if (ctx != null)
{
string djFile = "C:\\Users\\rammohan.chavakula\\Documents\\eiasample.djvu";
doc = ddjvu_document_create_by_filename(ctx, djFile, 100);
if (doc != null)
{
while (ddjvu_job_status(ddjvu_document_job(doc)) >= ddjvu_status_t.DDJVU_JOB_OK)
SpinDdjvuMessageLoop(ctx, true);
int pageCount = ddjvu_document_get_pagenum(doc);
mediaboxes = new Rectangle[pageCount];
for (int i = 0; i < pageCount; i++)
{
ddjvu_status_t status;
ddjvu_pageinfo_t info = new ddjvu_pageinfo_t();
while ((status = ddjvu_document_get_pageinfo_imp(doc, i, ref info, (uint)System.Runtime.InteropServices.Marshal.SizeOf(info))) < ddjvu_status_t.DDJVU_JOB_OK)
SpinDdjvuMessageLoop(ctx, true);
if (status != ddjvu_status_t.DDJVU_JOB_OK)
continue;
mediaboxes[i] = new Rectangle(0, 0, info.width / info.dpi,
info.height / info.dpi);
}
}
ddjvu_context_release(ctx);
}
In OnPaint function I have this below code
if (doc == null)
{
base.OnPaint(e);
return;
}
Rectangle pageRc = PageMediabox(1);
Rectangle screen = Transform(pageRc, 1, zoom, rotation, false);
Rectangle full = Transform(PageMediabox(1), 1, zoom, rotation, false);
full.Intersect(screen);
IntPtr page = ddjvu_page_create_by_pageno(doc, 1);
if (page == null )
{
base.OnPaint(e);
return;
}
int rotation4 = (((-rotation / 90) % 4) + 4) % 4;
ddjvu_page_set_rotation(page, (ddjvu_page_rotation_t)rotation4);
while (ddjvu_job_status(ddjvu_page_job(page)) >= ddjvu_status_t.DDJVU_JOB_OK)
SpinDdjvuMessageLoop(ctx, true);
if (ddjvu_job_status(ddjvu_page_job(page)) >= ddjvu_status_t.DDJVU_JOB_FAILED)
{
base.OnPaint(e);
return;
}
IntPtr fmt = ddjvu_format_create(ddjvu_format_style_t.DDJVU_FORMAT_BGR24, 0, (UIntPtr)null);
ddjvu_format_set_row_order(fmt, /* top_to_bottom */1);
ddjvu_rect_t prect = new ddjvu_rect_t(full.X, full.Y, (uint)full.Width, (uint)full.Height);
ddjvu_rect_t rrect = new ddjvu_rect_t(screen.X, 2 * full.Y + screen.Y + full.Height - screen.Height, (uint)screen.Width, (uint)screen.Height);
int stride = ((screen.Width * 3 + 3) / 4) * 4;
//byte tmp;
////ScopedMem<char> bmpData(SAZA(char, stride * (screen.dy + 5)));
//for (int y = 0; y < rrect.h; y++) {
// int step_y = y * SCREEN_WIDTH;
// for (int x=0; x < rrect.w; x++) {
// tmp = (byte)((imagebuf[x + step_y] >> 5) << 5);
// }
//}
int rowsize = mediaboxes[0].Width * 3;
int nSize = rowsize * (mediaboxes[0].Height) * 10;
unsafe
{
byte* buffer = (byte *)Memory.Alloc(nSize);
try
{
IntPtr ptr1 = (IntPtr)Memory.Alloc(Marshal.SizeOf(prect));
Marshal.StructureToPtr(prect, ptr1, false);
IntPtr ptr2 = (IntPtr)Memory.Alloc(Marshal.SizeOf(rrect));
Marshal.StructureToPtr(rrect, ptr2, false);
byte[] array = new byte[nSize];
fixed (byte* p = array) Memory.Copy(buffer, p, nSize);
ddjvu_page_render(page, ddjvu_render_mode_t.DDJVU_RENDER_MASKONLY, ptr1, ptr2, fmt, (ulong)stride, array);
}
finally
{
Memory.Free(buffer);
}
ddjvu_page_render should return arbitrary data of page which is to be rendered in a given rectangle area. Once after that I should be able to create image from arbitrary data & display in the screen
Here's how I would write the p/invoke:
[DllImport(dllname, CharSet=CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern int ddjvu_page_render(
[In] IntPtr page,
[In] ddjvu_render_mode_t mode,
[In] ref ddjvu_rect_t pagerect,
[In] ref ddjvu_rect_t renderrect,
[In] ref ddjvu_format_t pixelformat,
[In] uint rowsize,
[Out] byte[] imagebuffer
);
Note that I've stopped using unsafe, and am letting the marshaler handle the structs automatically.
There are quite a few assumptions here that I cannot verify:
The calling convention. It might not be cdecl. The C++ header will would have the definitive answer.
The first parameter is a void* handle type, I presume.
ddjvu_render_mode_t is an enum that you have translated correctly.
The three ref parameters are structs, passed by reference. I cannot check that you have translated them correctly.
Calling this function would be something like this:
byte[] imagebuffer = new byte[nSize];
int retval = ddjvu_page_render(
page,
ddjvu_render_mode_t.DDJVU_RENDER_MASKONLY,
ref prect,
ref rrect,
ref fmt,
(uint)stride,
imagebuffer
);
if (retval != ??)
// handle error
This is quite a broad brush answer. I hope it points you in the right direction. I don't think that all your problems will be solved by this.
I have to get the Features array in vb.net application. How this can be done. This is a function in VC++ .
STDMETHODIMP CclsLicense::FeatureList(VARIANT* Features,BSTR HostName, VARIANT_BOOL *ret){
USES_CONVERSION;
int status = 0;
int iCount = 0;
int nLicenseFeatures = 0;
char **featureList = NULL; // List of features
// Safe Array
SAFEARRAYBOUND bound[1];
SAFEARRAY *safeArray = NULL; // A Safe array for VB
CComVariant *pBstr = NULL; // Array of BSTR Value
// Initialize the return value
*ret = VARIANT_FALSE;
nLicenseFeatures = 0;
featureList = new char*[MAX_FEATURES];
for (i=0;i<4;i++)
{
featureList[nLicenseFeatures]=array[i];
nLicenseFeatures++;
}
// Array starts at 0 and has the number of features as elements
bound[0].lLbound = 0;
bound[0].cElements = nLicenseFeatures;
// Initialize Array
if((safeArray = ::SafeArrayCreate( VT_VARIANT, 1, bound)) == NULL)
return E_FAIL;
::VariantClear(Features);
Features->vt = VT_VARIANT | VT_ARRAY;
Features->parray = safeArray;
//use direct access to data
if(FAILED(hr = ::SafeArrayAccessData(safeArray, (void HUGEP**)&pBstr)) || pBstr == NULL)
return hr;
iCount = 0;
while( featureList[iCount] != NULL )
{
// Add to Array
if(pBstr[iCount].bstrVal != NULL)
{
::SysFreeString(pBstr[iCount].bstrVal);
pBstr[iCount].bstrVal = NULL;
}
if(featureList[iCount] == NULL)
pBstr[iCount].bstrVal = ::SysAllocString(OLESTR("")); //imposible
else
pBstr[iCount].bstrVal = ::SysAllocString(T2OLE(featureList[iCount]));
pBstr[iCount].vt = VT_BSTR;
// Increment counter
iCount++;
}
// Release Array
::SafeArrayUnaccessData(safeArray);
*ret = VARIANT_TRUE;
return S_OK;
}
Vb.Net function to get the list of features
Public Shared Function FeatureList(ByVal strLicensePath As String)
Dim features(10) As String
Try
m_objUTSLicense = CreateObject("dll name")
Call m_objUTSLicense.FeatureList(features, "192.168.1.3")
Catch ex As Exception
End Try
Dim i As Integer
Dim size As Integer = features.Length
For i = 0 To size - 1
MessageBox.Show(features(i))
Next
End Function
When i am trying this code, getting the error "Attempted to read or write protected memory. This is often an indication that other memory is corrupt"
I can't test this without your whole project, but you might try declaring a member variable (rather than a local) so that you can apply the necessary marshalling attribute. Something like:
Imports System.Runtime.InteropServices
<MarshalAs(UnmanagedType.SafeArray, safearraysubtype:=VarEnum.VT_BSTR)>
Private features As System.Array
I'm trying to get a request for the user's information using WinHttp functions. The REST API call for SugarSync is referenced here.
My code is as follows:
DWORD dwBytesWritten = 0;
DWORD dwStatusCode = 0;
DWORD dwSize = sizeof(DWORD);
BOOL bResults = FALSE;
string data;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
WINHTTP_PROXY_INFO proxyInfo;
winhttpintObj.setProxyInfo(proxyInfo);
HINTERNET hSession = WinHttpOpen( L"A WinHTTP Example Program/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
HINTERNET hConnect = WinHttpConnect( hSession, L"api.sugarsync.com",
INTERNET_DEFAULT_HTTPS_PORT, 0);
HINTERNET hRequest = WinHttpOpenRequest( hConnect,
L"GET", L"/user/<userID>",
NULL,
WINHTTP_NO_REFERER,
0,
WINHTTP_FLAG_SECURE);
string header("Authorization: ");
header += (accessToken);
header += "\n";
wstring ws = wstring(header.begin(), header.end());
WinHttpAddRequestHeaders( hRequest, ws.c_str(), (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD );
WinHttpSendRequest( hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
0,
0 );
WinHttpReceiveResponse( hRequest, NULL);
WinHttpQueryHeaders( hRequest,
WINHTTP_QUERY_STATUS_CODE |
WINHTTP_QUERY_FLAG_NUMBER,
NULL,
&dwStatusCode,
&dwSize,
NULL );
cout << "dwStatusCode: " << dwStatusCode << endl;
The accessToken is https://api.sugarsync.com/authorization/....... The status code returned is 401: Authorization required. However, I've already specified it in the header. May I know where has the problem occurred and what is the possible way to resolve it? Thank you.