tags:

views:

271

answers:

3

I am trying to use a dll in my c# program but I just cant seem to get it to work. I have made a test app shown below. The return value is 0, however it does not actually do what it is supposed to do.

Whereas the following command does work:

rundll32 cmproxy.dll,SetProxy /source_filename proxy-1.txt /backup_filename roxy.bak /DialRasEntry NULL /TunnelRasEntry DSLVPN /Profile "C:\Documents and ettings\Administrator\Application Data\Microsoft\Network\Connections\Cm\dslvpn.cmp"

Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Net;
using WUApiLib;

namespace nac
{
    class Program
    {
        [DllImport("cmproxy.dll", CharSet = CharSet.Unicode)]
        static extern int SetProxy(string cmdLine);

        static void Main(string[] args)
        {
            string cmdLine = @"/source_filename proxy-1.txt /backup_filename proxy.bak /DialRasEntry NULL /TunnelRasEntry DSLVPN /Profile ""C:\Documents and Settings\Administrator\Application Data\Microsoft\Network\Connections\Cm\dslvpn.cmp""";
            Console.WriteLine(SetProxy(cmdLine));
        }
    }
}

Here is the contents of the dumpbin /exports command

File Type: DLL

  Section contains the following exports for cmproxy.dll

    00000000 characteristics
    3E7FEF8C time date stamp Tue Mar 25 05:56:28 2003
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 00001B68 SetProxy

  Summary

        1000 .data
        1000 .reloc
        1000 .rsrc
        2000 .text

When this works it sets the proxy server for a VPN connection.

EDIT:

I wish to avoid running rundll32 from system.process

I can also provide a link to both the dll and my test app. Although the code pasted above is all it contains and the dll is available from the Server resource kit I think.

UPDATE:

I have written a test DLL in c++ that simply echos the args it is given. It is running but it says

The handle is invalid

A: 

Just some food for thought. This may be a possible work around.

Perhaps you could use the System.Diagnostics.Process class to run the command from your code like this:

  System.Diagnostics.Process p = new System.Diagnostics.Process();
  p.StartInfo.Arguments = @"cmproxy.dll,SetProxy /source_filename proxy-1.txt /backup_filename roxy.bak /DialRasEntry NULL /TunnelRasEntry DSLVPN /Profile ""C:\Documents and ettings\Administrator\Application Data\Microsoft\Network\Connections\Cm\dslvpn.cmp\""";
  p.StartInfo.FileName = "rundll32";
  p.Start();

If you need to substitute some of the arguments at runtime then you could use String.Format to format the arguments and substitute whatever you need.

Edit 1:

It doesn't seem to be correctly highlighting the code here. I don't have cmproxy.dll so I cannot test this scenario.

Frank Hale
Sorry I should have mentioned this in the OP, I havent tried it yet, but I assume it does work, however I am trying to avoid using this method.
Charles Gargent
A: 

Perhaps the Microsoft documentation on RUNDLL32.EXE would be helpful? In particular, this section seems useful:

The parameters to the Rundll entry point are as follows:

   hwnd - window handle that should be used as the owner window for
          any windows your DLL creates
   hinst - your DLL's instance handle
   lpszCmdLine - ASCIIZ command line your DLL should parse
   nCmdShow - describes how your DLL's windows should be displayed

In the following example:

     RUNDLL.EXE SETUPX.DLL,InstallHinfSection 132 C:\WINDOWS\INF\SHELL.INF

Rundll would call the InstallHinfSection() entrypoint function in Setupx.dll and pass it the following parameters:

   hwnd = (parent window handle)
   hinst = HINSTANCE of SETUPX.DLL
   lpszCmdLine = "132 C:\WINDOWS\INF\SHELL.INF"
   nCmdShow = (whatever the nCmdShow was passed to CreateProcess)

Update 2:

This should work for you:

[DllImport("cmproxy.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
static extern void SetProxy(IntPtr hwnd, IntPtr hinst, string lpszCmdLine, int nCmdShow);

and then call it like:

const int SW_SHOWNORMAL = 1;
IntPtr hWnd = (this as Form).Handle;
IntPtr hInstance = Marshal.GetHINSTANCE(this.GetType().Module);
SetProxy(hWnd, hInstance, cmdLine, SW_SHOWNORMAL);

I tested the same code with the Control_RunDLL entrypoint in shell32.dll and I can confirm that it works fine for me. The trick is getting the correct HWND and HINSTANCE pointers to pass in as the first two arguments. Also, I was previously passing 0 for the nCmdShow, but according to pinvoke.net, SW_SHOWNORMAL has value 1, which is probably what you want.

Daniel Pryden
Ive already read this, unfortunately it doesnt shed any light for me. I wonder if I need to change the type of the commandline argument?
Charles Gargent
@Charles Gargent: I just edited my answer. Does the P/Invoke code sample help?
Daniel Pryden
We are obviously typing at the same time :) I have tried your edit, but still no change. I have also tried it by setting the EntryPoint = SetProxy. Still no difference. Perhaps someone from MS will see this and give us the answer!
Charles Gargent
@Charles Gargent: OK, it looks like it was an issue with the hWnd and hInstance arguments. I was previously just passing NULL for those, but it looks like that doesn't work for some DLLs, so I've added code to show how to get valid window and instance handles. With this change, I can get it to call RunDll entrypoints in shell32.dll, so I'm pretty confident this should work for you now.
Daniel Pryden
I am afraid that doesnt make any difference.
Charles Gargent
@Charles Gargent: Well, the HINSTANCE should technically be the HINSTANCE of cmproxy.dll, not the HINSTANCE of the current assembly (which is what the code is currently doing). I think the correct solution is to P/Invoke `LoadLibrary()` and pass the returned HINSTANCE to the function. To do that correctly would require P/Invoking `GetProcAddress()` and then calling `Marshal.GetDelegateForFunctionPointer()`. When I have time I'll update my answer with a code example.
Daniel Pryden
That would be much appreciated, I dont think I have progressed that far in my knowledge.
Charles Gargent
Thanks for your help, I posted a new question http://stackoverflow.com/questions/2621066/calling-unmanaged-dll-from-c-take-2 and it was as simple as getting the right character encoding.
Charles Gargent
A: 

The answer is CharSet = CharSet.Unicode should be CharSet = CharSet.Ansi

Charles Gargent
Could you enlighten us? How did you know that?
Dave