I am attempting to use RAPI functions in order to put a signed DLL on the
device and call a function on it to return the device type string to the
desktop. Unfortunately I have been unable to actually get this to work with
C#. My DLL is a C++ signed DLL with the correct signature. Currently the
error I am getting when I try to invoke it is 0x8007007E 'The specified
module could not be found'. The DLL code itself works just fine in a test
harness, but when I attempt to call it something fails when I try to write
the output to the output buffer.

I believe the problem lies in the Reflection calls to DllImport rather than
the actual code. The DLL Is copied to the device properly and is in the right
location. Dependency walker shows that the proper externs have been created
and match the required signature for CeRapiInvoke. If, when calling the code
via DllImport I comment out the code that actually writes to the buffer the
function returns successfully so I am reasonably confident that it is my C#
DLLImport statement that is the culprit.

=====
[DllImport("rapi.dll", EntryPoint = "CeRapiInvoke")]// , CharSet =
CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError
= true)]
private extern static Int32 internal_CeRapiInvoke(
[MarshalAs(UnmanagedType.LPWStr)] string path,
[MarshalAs(UnmanagedType.LPWStr)] string functionName,
UInt32 nBytesInInputBuffer,
[MarshalAs(UnmanagedType.LPArray)] byte[] inputBuffer,
ref UInt32 outputBufferLength,
[MarshalAs(UnmanagedType.LPArray)] byte[] outputBuffer,
IntPtr ppIRAPIStream,
UInt32 reserved);

public Int32 RapiInvoke(string path, string function, UInt32
inputBufferSize, byte[] inputBuffer, UInt32 outputLength, byte[] outputBuffer
)
{
// Apparently this only works with Native function

Int32 hResults = internal_CeGetLastError();

string unicodePath =
UnicodeEncoding.Unicode.GetString(UnicodeEncoding.Unicode.GetBytes(path));
string unicodeFunction =
UnicodeEncoding.Unicode.GetString(UnicodeEncoding.Unicode.GetBytes(function));

Int32 results = internal_CeRapiInvoke(unicodePath,
unicodeFunction, inputBufferSize, inputBuffer, ref outputLength,
outputBuffer, IntPtr.Zero, 0);

hResults = internal_CeGetLastError();
if (hResults != 0)
Marshal.ThrowExceptionForHR(hResults);

return results;
}

=====

byte[] outputBuffer = new byte[4];
uint outputLength = (uint)outputBuffer.Length;
int results = rapi.RapiInvoke( "\\TestTickle\\GetOEMInfo.dll",
"GetOEMString", 0, null, outputLength, outputBuffer);


results is currently equal to -2147024770 when the function returns

Does anyone have any idea what might be incorrect? Has anyone actually
gotten this working in C#?

Re: C#2005 using DllImport to call CeRapiInvoke by ctacke/>

ctacke/>
Tue Jun 12 12:52:56 CDT 2007

I'm not sure how the marshaler handles several of these, plus the output
buffer is a double pointer in the CeRapiInvoke declaration, so if it can
handle it, your declaration would still be slightly off.

Here's what I have from our shared source Communication library
(http://www.opennetcf.org/communication)


[DllImport("rapi.dll", CharSet=CharSet.Unicode, SetLastError=true)]
internal extern static int CeRapiInvoke(string pDllPath, string
pFunctionName, uint cbInput, IntPtr pInput, out uint pcbOutput, out IntPtr
ppOutput, IntPtr ppIRAPIStream, uint dwReserved);

public void Invoke(string DLLPath, string FunctionName, byte[] InputData,
out byte[] OutputData)
{
// RAPI memory management is non-intuitive
// you must allocate the input variable with LocalAlloc and then RAPI will
release them
// you must also call LocalFree on the output buffer though you never call
LocalAlloc

CheckConnection();

uint recvSize = 0;
IntPtr recvData = IntPtr.Zero;

// create a pointer to hold incoming data - RAPI will free this internally
IntPtr sendData = Marshal.AllocHGlobal(InputData.Length);

// copy outgoing data to the pointer - too bad we don't have a memcpy fcn
for(int i = 0 ; i < InputData.Length ; i++)
{
Marshal.WriteByte(sendData, i, InputData[i]);
}

// call the RAPI function
CeRapiInvoke(DLLPath, FunctionName, (uint)InputData.Length, sendData, out
recvSize, out recvData, IntPtr.Zero, 0);

// allocate our managed array
OutputData = new byte[recvSize];

// copy the returned data from unmanaged to managed memory
Marshal.Copy(recvData, OutputData, 0, (int)recvSize);

// RAPI called LocalAlloc on this internally so we must free it
Marshal.FreeHGlobal(recvData);
}



--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Managed Code in an Embedded World
www.OpenNETCF.com



"Ed Kramer" <EdKramer@discussions.microsoft.com> wrote in message
news:FDA184A5-271F-4E33-A88C-FEF000A69551@microsoft.com...
>I am attempting to use RAPI functions in order to put a signed DLL on the
> device and call a function on it to return the device type string to the
> desktop. Unfortunately I have been unable to actually get this to work
> with
> C#. My DLL is a C++ signed DLL with the correct signature. Currently the
> error I am getting when I try to invoke it is 0x8007007E 'The specified
> module could not be found'. The DLL code itself works just fine in a test
> harness, but when I attempt to call it something fails when I try to write
> the output to the output buffer.
>
> I believe the problem lies in the Reflection calls to DllImport rather
> than
> the actual code. The DLL Is copied to the device properly and is in the
> right
> location. Dependency walker shows that the proper externs have been
> created
> and match the required signature for CeRapiInvoke. If, when calling the
> code
> via DllImport I comment out the code that actually writes to the buffer
> the
> function returns successfully so I am reasonably confident that it is my
> C#
> DLLImport statement that is the culprit.
>
> =====
> [DllImport("rapi.dll", EntryPoint = "CeRapiInvoke")]// , CharSet =
> CharSet.Unicode, CallingConvention = CallingConvention.StdCall,
> SetLastError
> = true)]
> private extern static Int32 internal_CeRapiInvoke(
> [MarshalAs(UnmanagedType.LPWStr)] string path,
> [MarshalAs(UnmanagedType.LPWStr)] string functionName,
> UInt32 nBytesInInputBuffer,
> [MarshalAs(UnmanagedType.LPArray)] byte[] inputBuffer,
> ref UInt32 outputBufferLength,
> [MarshalAs(UnmanagedType.LPArray)] byte[] outputBuffer,
> IntPtr ppIRAPIStream,
> UInt32 reserved);
>
> public Int32 RapiInvoke(string path, string function, UInt32
> inputBufferSize, byte[] inputBuffer, UInt32 outputLength, byte[]
> outputBuffer
> )
> {
> // Apparently this only works with Native function
>
> Int32 hResults = internal_CeGetLastError();
>
> string unicodePath =
> UnicodeEncoding.Unicode.GetString(UnicodeEncoding.Unicode.GetBytes(path));
> string unicodeFunction =
> UnicodeEncoding.Unicode.GetString(UnicodeEncoding.Unicode.GetBytes(function));
>
> Int32 results = internal_CeRapiInvoke(unicodePath,
> unicodeFunction, inputBufferSize, inputBuffer, ref outputLength,
> outputBuffer, IntPtr.Zero, 0);
>
> hResults = internal_CeGetLastError();
> if (hResults != 0)
> Marshal.ThrowExceptionForHR(hResults);
>
> return results;
> }
>
> =====
>
> byte[] outputBuffer = new byte[4];
> uint outputLength = (uint)outputBuffer.Length;
> int results = rapi.RapiInvoke( "\\TestTickle\\GetOEMInfo.dll",
> "GetOEMString", 0, null, outputLength, outputBuffer);
>
>
> results is currently equal to -2147024770 when the function returns
>
> Does anyone have any idea what might be incorrect? Has anyone actually
> gotten this working in C#?



Re: C#2005 using DllImport to call CeRapiInvoke by EdKramer

EdKramer
Wed Jun 13 13:51:01 CDT 2007

Using your code as an example I re-wrote my invoke function. But I'm still
getting the same error code. -2147024770

Thinking perhaps I'd made a typo, I went and downloaded your library and
source and tried the same thing with it. But I'm still getting the same error.

I don't suppose you have a test DLL to execute that you know works with the
Invoke that I could try it with? Though I'm starting to suspect it's not my
DLL at all. The error code I'm getting back is "The specified module could
not be found". I'm wondering if this is referring to something being wrong
with rapi.dll or something like that. Looking at my DLL in Dependency Walker
I can see my functions just fine.

I think in the meantime I"m doing to reinstall active sync. Maybe something
got gorped up or something.

Re: C#2005 using DllImport to call CeRapiInvoke by EdKramer

EdKramer
Wed Jun 13 14:50:02 CDT 2007

Reinstalling Active Sync had no effect either. =(

Re: C#2005 using DllImport to call CeRapiInvoke by ctacke/>

ctacke/>
Wed Jun 13 15:19:36 CDT 2007

Your DLL is in the \Windows folder of the device?

-Chris

"Ed Kramer" <EdKramer@discussions.microsoft.com> wrote in message
news:A1F4F789-A396-473D-8C12-C9B2B2138980@microsoft.com...
> Using your code as an example I re-wrote my invoke function. But I'm still
> getting the same error code. -2147024770
>
> Thinking perhaps I'd made a typo, I went and downloaded your library and
> source and tried the same thing with it. But I'm still getting the same
> error.
>
> I don't suppose you have a test DLL to execute that you know works with
> the
> Invoke that I could try it with? Though I'm starting to suspect it's not
> my
> DLL at all. The error code I'm getting back is "The specified module could
> not be found". I'm wondering if this is referring to something being wrong
> with rapi.dll or something like that. Looking at my DLL in Dependency
> Walker
> I can see my functions just fine.
>
> I think in the meantime I"m doing to reinstall active sync. Maybe
> something
> got gorped up or something.



Re: C#2005 using DllImport to call CeRapiInvoke by EdKramer

EdKramer
Wed Jun 13 15:23:02 CDT 2007

I am now getting -2147024883 "The data is invalid"

But if I comment out the code in my DLL that is actually modifying the
output buffer or output buffer size that I'm passing in. Thing is the code
works just fine if I use my test harness to call it rather than trying to do
it over RAPI.

"Ed Kramer" wrote:

> Reinstalling Active Sync had no effect either. =(

Re: C#2005 using DllImport to call CeRapiInvoke by EdKramer

EdKramer
Wed Jun 13 15:32:01 CDT 2007

No, it is in a directory that I am creating and copying it to. However I am
specifying the path to the DLL in the invoke parameters.

Incidentally, as long as I don't write to the out parameters, I don't get an
error I get a successful return I even tried to write a hardcoded '10' to the
pointer and that caused the same error.

This is what the code currently looks like...

GETOEMINFO_API DWORD GetOEMStringSize( DWORD cbInput, BYTE *pInput, DWORD
*pcbOutput, BYTE **ppOutput, IRAPIStream *pIRAPIstream)
{
// *pcbOutput = size;
*pcbOutput = 10;



"<ctacke/>" wrote:

> Your DLL is in the \Windows folder of the device?
>
> -Chris
>

Re: C#2005 using DllImport to call CeRapiInvoke by Scott

Scott
Wed Jun 13 17:32:22 CDT 2007

<EdKramer@discussions.microsoft.com> wrote:
>
>No, it is in a directory that I am creating and copying it to. However I am
>specifying the path to the DLL in the invoke parameters.
>
>Incidentally, as long as I don't write to the out parameters, I don't get an
>error I get a successful return I even tried to write a hardcoded '10' to the
>pointer and that caused the same error.
>
>This is what the code currently looks like...
>
>GETOEMINFO_API DWORD GetOEMStringSize( DWORD cbInput, BYTE *pInput, DWORD
>*pcbOutput, BYTE **ppOutput, IRAPIStream *pIRAPIstream)
>{
> // *pcbOutput = size;
> *pcbOutput = 10;

Are you allocating something and storing it into the ppOutput buffer
that's at least 10 bytes long?

--
Scott Seligman [MSFT]
This posting is provided AS IS with no warranties, and confers
no rights.

Re: C#2005 using DllImport to call CeRapiInvoke by ctacke/>

ctacke/>
Wed Jun 13 18:34:13 CDT 2007

Also check your allocations. RapiInvoke is weird in that you have to
allocate and not release in the callee, and release without allocating in
the caller.


--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Managed Code in an Embedded World
www.OpenNETCF.com


"Ed Kramer" <EdKramer@discussions.microsoft.com> wrote in message
news:F3F520BA-AA3D-4E5C-899E-90122A96077A@microsoft.com...
> No, it is in a directory that I am creating and copying it to. However I
> am
> specifying the path to the DLL in the invoke parameters.
>
> Incidentally, as long as I don't write to the out parameters, I don't get
> an
> error I get a successful return I even tried to write a hardcoded '10' to
> the
> pointer and that caused the same error.
>
> This is what the code currently looks like...
>
> GETOEMINFO_API DWORD GetOEMStringSize( DWORD cbInput, BYTE *pInput, DWORD
> *pcbOutput, BYTE **ppOutput, IRAPIStream *pIRAPIstream)
> {
> // *pcbOutput = size;
> *pcbOutput = 10;
>
>
>
> "<ctacke/>" wrote:
>
>> Your DLL is in the \Windows folder of the device?
>>
>> -Chris
>>



RE: C#2005 using DllImport to call CeRapiInvoke by EdKramer

EdKramer
Thu Jun 14 13:31:01 CDT 2007

Finally got it. Thanks for the assist. Was a matter of not writing to the
output buffer correctly and not setting the output buffer length to the
correct length.

Re: C#2005 using DllImport to call CeRapiInvoke by ctacke/>

ctacke/>
Thu Jun 14 13:45:20 CDT 2007

Out of curiosity, was my implementation of Invoke right (I wrote it probably
5 years ago and I don't recall if I even tested it)?

-Chris

"Ed Kramer" <EdKramer@discussions.microsoft.com> wrote in message
news:933523E8-CB74-4C3C-A515-9EEFE592A017@microsoft.com...
> Finally got it. Thanks for the assist. Was a matter of not writing to the
> output buffer correctly and not setting the output buffer length to the
> correct length.



Re: C#2005 using DllImport to call CeRapiInvoke by EdKramer

EdKramer
Fri Jun 15 16:43:00 CDT 2007

I would guess so. It seemed to be working. The problem turned out to be the
way I was writing to the pcbOutput and ppOutput variables. I had a coworker
who's a C++ guru help me out and got it working.

Now I'm having another problem though....

The thing works just fine on the first device I tried, a Treo 700w running
Mobile 5.0. Works beautifully. I was about to have a party =)


Then I tried it on a i-mate k-jam running mobile 5.0 and things went
completely wonky.

I started getting 'access denied' messages, so I farted around with my
authenticode signing and such. Then I finally got it partially working.
Unfortunately the first call is supposed to return the size of a buffer
needed to hold a unicode string. It's generally a small string, just the
device's hardware string that you see in the About screen. I use
SystemParametersInfo with the (SPI_GETOEMINFO) parameter.

It's probably never going to be greater than 100 characters. I hate being
wasteful with memory so my size function strats at a size of 1 WCHAR and
keeps adding to the size until it finds a number that is big enough to hold
the string and then returns that number.

The second call is one where you supply a buffer of the required length in
C# and it makes the SystemParametersInfo call and writes the result to your
buffer.

On the first device everything is hunky dory. I get a result that is 4 bytes
long and contains a value of 30 bytes ( includes the \0 at the end ).

On the second device I assume I am overflowing the DWORD value I am using as
my size because I get back -1945063044. So basically I'm never getting a size
that doesn't return the error code I'm using to figure out when I get a valid
value.

When I try setting the size manually on the IMate I can go no less than 130
characters or the call will fail... yet I get back a buffer that contains my
data string then a \0 and a whole bunch of crap I have to truncate off at the
end.

I've gone so far as to wipe the device and provision it with wide open
security so I can just get this working and it's having no effect at all. I'm
a server side programmer, this device stuff is new to me. It's rather
aggravating since the devices both have the same OS yet I'm getting differing
results.

I have seen mention that you can get around those settings by using an XML
file to provision the device. But this has to be inserted into a cab file. I
don't want to deal with cabs. I'm already copying the file to the device just
fine using RAPI why should I bother with the extra work to generate a cab
file? Ya know?

I'll probably have to make another posting on monday about this to see if
anyone knows what's going on. I really wish I could debug the dll's call in
the environment through Invoke. I'm sure that'd help.