1

I have the following 2 sets of code, both of which produce the same results:

using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ResetIE
{
    class Program
    {
        [DllImport("InetCpl.cpl", SetLastError=true, CharSet=CharSet.Unicode)]
        public static extern long ClearMyTracksByProcessW(IntPtr hwnd, IntPtr hinst, ref TargetHistory lpszCmdLine, FormWindowState nCmdShow);

    static void Main(string[] args)
    {
        TargetHistory th = TargetHistory.CLEAR_TEMPORARY_INTERNET_FILES;
        ClearMyTracksByProcessW(Process.GetCurrentProcess().Handle, Marshal.GetHINSTANCE(typeof(Program).Module), ref th, FormWindowState.Maximized);
        Console.WriteLine("Done.");
    }
}

and ...

static class NativeMethods
{
    [DllImport("kernel32.dll")]
    public static extern IntPtr LoadLibrary(string dllToLoad);

    [DllImport("kernel32.dll")]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

    [DllImport("kernel32.dll")]
    public static extern bool FreeLibrary(IntPtr hModule);
}

public class CallExternalDLL
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate long ClearMyTracksByProcessW(IntPtr hwnd, IntPtr hinst, ref TargetHistory lpszCmdLine, FormWindowState nCmdShow);

    public static void Clear_IE_Cache()
    {
        IntPtr pDll = NativeMethods.LoadLibrary(@"C:\Windows\System32\inetcpl.cpl");
        if (pDll == IntPtr.Zero)
        {
           Console.WriteLine("An Error has Occurred.");
        }

        IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "ClearMyTracksByProcessW");
        if (pAddressOfFunctionToCall == IntPtr.Zero)
        {
            Console.WriteLine("Function Not Found.");
        }

        ClearMyTracksByProcessW cmtbp = (ClearMyTracksByProcessW)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(ClearMyTracksByProcessW));
        TargetHistory q = TargetHistory.CLEAR_TEMPORARY_INTERNET_FILES;
        long result = cmtbp(Process.GetCurrentProcess().Handle, Marshal.GetHINSTANCE(typeof(ClearMyTracksByProcessW).Module), ref q, FormWindowState.Normal);
    }
}

both use the following Enum:

public enum TargetHistory
{
    CLEAR_ALL = 0xFF,
    CLEAR_ALL_WITH_ADDONS = 0x10FF,
    CLEAR_HISTORY = 0x1,
    CLEAR_COOKIES = 0x2,
    CLEAR_TEMPORARY_INTERNET_FILES = 0x8,
    CLEAR_FORM_DATA = 0x10,
    CLEAR_PASSWORDS = 0x20
}

Both methods of doing this compile and run just fine, offering no errors, but both churn endlessly never returning from their work. The PInvoke code was ported from the following VB, which was fairly difficult to track down:

 Option Explicit

Private Enum TargetHistory
    CLEAR_ALL = &HFF&
    CLEAR_ALL_WITH_ADDONS = &H10FF&
    CLEAR_HISTORY = &H1&
    CLEAR_COOKIES = &H2&
    CLEAR_TEMPORARY_INTERNET_FILES = &H8&
    CLEAR_FORM_DATA = &H10&
    CLEAR_PASSWORDS = &H20&
End Enum

Private Declare Function ClearMyTracksByProcessW Lib "InetCpl.cpl" _
   (ByVal hwnd As OLE_HANDLE, _
    ByVal hinst As OLE_HANDLE, _
    ByRef lpszCmdLine As Byte, _
    ByVal nCmdShow As VbAppWinStyle) As Long

Private Sub Command1_Click()
    Dim b() As Byte
    Dim o As OptionButton
    For Each o In Option1
        If o.Value Then
            b = o.Tag
            ClearMyTracksByProcessW Me.hwnd, App.hInstance, b(0), vbNormalFocus
            Exit For
        End If
    Next
End Sub

Private Sub Form_Load()
    Command1.Caption = "削除"

    Option1(0).Caption = "インターネット一時ファイル"
    Option1(0).Tag = CStr(CLEAR_TEMPORARY_INTERNET_FILES)

    Option1(1).Caption = "Cookie"
    Option1(1).Tag = CStr(CLEAR_COOKIES)

    Option1(2).Caption = "履歴"
    Option1(2).Tag = CStr(CLEAR_HISTORY)

    Option1(3).Caption = "フォーム データ"
    Option1(3).Tag = CStr(CLEAR_HISTORY)

    Option1(4).Caption = "パスワード"
    Option1(4).Tag = CStr(CLEAR_PASSWORDS)

    Option1(5).Caption = "すべて削除"
    Option1(5).Tag = CStr(CLEAR_ALL)

    Option1(2).Value = True
End Sub

The question is simply what am I doing wrong? I need to clear the internet cache, and would prefer to use this method as I know it does what I want it to when it works (rundll32 inetcpl.cpl,ClearMyTracksByProcess 8 works fine). I've tried running both as normal user and admin to no avail. This project is written using C# in VS2012 and compiled against .NET3.5 (must remain at 3.5 due to client restrictions)

Kyt
  • 11
  • 3

1 Answers1

1

The signature for a rundll32 function is:

void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);

So the first issue is that your third parameter is an enum, which becomes an integer type, but you need to pass a string. Also note that this is an LPSTR, not an LPTSTR, so the character set is ANSI, not Unicode.

Second, the calling convention is stdcall rather than cdecl.

So perhaps something more along the lines of this:

[DllImport("InetCpl.cpl", SetLastError = true, CharSet = CharSet.Ansi)]
private static extern void ClearMyTracksByProcess(IntPtr hwnd, IntPtr hinst, string lpszCmdLine, int nCmdShow);

Test which works here, at least as far as getting the function to return:

class Program
{
    [DllImport("InetCpl.cpl", SetLastError = true, CharSet = CharSet.Ansi)]
    private static extern void ClearMyTracksByProcessW(IntPtr hwnd, IntPtr hinst, string lpszCmdLine, int nCmdShow);

    static void Main(string[] args)
    {
        ClearMyTracksByProcessW(IntPtr.Zero, IntPtr.Zero, "2", 5);
    }
}
Dark Falcon
  • 43,592
  • 5
  • 83
  • 98
  • Unfortunately, making these changes does not seem to have had the desired effect. It still just sits and churns. (Tried it in both code blocks.) – Kyt Jun 28 '13 at 16:47
  • You are also passing a process handle for the first argument, but a window handle is expected. Try passing `IntPtr.Zero`. – Dark Falcon Jun 28 '13 at 16:56
  • Unfortunately, same result. it's maddening to know that I've got the entrypoint (It has to be ClearMyTracksByProcessW, by the way. I'm not sure why, but ClearMyTracksByProcess isn't found, and W uses Unicode not ANSI) - but can't seem to find the right syntax to invoke. – Kyt Jun 28 '13 at 17:02
  • But it does not use Unicode because the string is a `LPSTR`. `LPSTR` is always ANSI. Unicode needs either `LPWSTR` or `LPTSTR` with a `W` variant of the function, so even though this is a Unicode variant, the string is not Unicode. I do not have any other ideas beyond what I have given you. – Dark Falcon Jun 28 '13 at 17:03
  • Fair enough! Happy to be educated on the nuances, I'm fairly new to this :) - I tried it again with Ansi Charset just to be certain, same result. – Kyt Jun 28 '13 at 17:06
  • One more thing: The second parameter is to be a handle to `InetCpl.cpl`. Pretty sure both your examples are passing a handle to your program's assembly. You could try correcting this. – Dark Falcon Jun 28 '13 at 17:11
  • Unless I'm mistaken the unmanaged DLL code in my second example does that with Marshal.GetHINSTANCE(typeof(ClearMyTracksByProcessW).Module) ? If this is incorrect I'd be interested to know how to go about doing so. – Kyt Jun 28 '13 at 17:21
  • `ClearMyTracksByProcessW` is defined in your managed module. I would presume for the second example you would just pass `pDll`? – Dark Falcon Jun 28 '13 at 17:26
  • Still no dice. Thanks for trying :( – Kyt Jun 28 '13 at 17:42