2010-11-21 66 views
1

我注意到一些很奇怪的東西。我試圖調用CRT函數「putchar」,並且無法使其工作。所以我仔細檢查了我沒有錯過任何東西,並且直接從MSDN上的P/Invoke教程中複製代碼,看看它是否工作。P /調用基本CRT功能(即putchar,puts)的問題

http://msdn.microsoft.com/en-us/library/aa288468%28VS.71%29.aspx

你會發現,他們進口 「看跌期權」。

所以我測試了從MSDN複製的確切代碼。它沒有工作!所以現在我感到沮喪。我以前從未遇到過這個問題。

然後我只是碰巧運行沒有調試(按Ctrl + F5),它的工作!我測試了輸出到控制檯的其他功能,並且它們在調試時都不工作,但在不調試時都可以工作。

然後我寫了一個簡單的C dll,它導出一個名爲「PrintChar(char c)」的函數。當我從C#中調用該函數時,即使我正在調試或沒有調試,它也可以正常工作,沒有任何問題。

這是怎麼回事?

+0

a)「不起作用」是什麼意思? b)PrintChar在做什麼/使用什麼? – 2010-11-21 20:24:57

+0

「不起作用」的意思正是它所說的;該函數不執行它應該執行的任務,即寫入控制檯。它仍然會返回一個值,並且不會使程序崩潰;如果是這樣,我會記下這一點。 PrintChar(char c){putchar(c); } 就是這樣!它可以調試或不調試。 :) – ATC 2010-11-22 12:38:36

回答

2

這是一個糟糕的例子,使用C運行庫DLL來調用puts。請繼續閱讀教程,因爲那裏有很好的信息,但是請嘗試製作Win32 API調用。

這是一個更好的介紹的P/Invoke:http://msdn.microsoft.com/en-us/magazine/cc164123.aspx

這是老了,但信息還是不錯的。

編輯

我的解釋是錯誤的。

我去尋找一個正確的解釋,我發現C運行時放置方法和.NET框架Console.Write方法不同,他們如何寫入控制檯(Console.Write工作,其中p /調用put纔不是)。我想,也許答案是在那裏,所以我颳起了這個演示:

using System; 
using System.Diagnostics; 
using System.IO; 
using System.Runtime.InteropServices; 
using System.Text; 

class Program 
{ 
    public static void Main() 
    { 
     int written; 
     string outputString = "Hello, World!\r\n"; 
     byte[] outputBytes = Encoding.Default.GetBytes(outputString); 

     // 
     // This is the way the C-Runtime Library method puts does it 

     IntPtr conOutHandle = CreateFile("CONOUT$", 0x40000000, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero); 
     WriteConsole(conOutHandle, outputBytes, outputString.Length, out written, IntPtr.Zero); 

     // 
     // This is the way Console.Write does it 

     IntPtr stdOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE); 
     WriteFile(stdOutputHandle, outputBytes, outputBytes.Length, out written, IntPtr.Zero); 


     // Pause if running under debugger 
     if (Debugger.IsAttached) 
     { 
      Console.Write("Press any key to continue . . . "); 
      Console.ReadKey(); 
     } 
    } 

    const int STD_OUTPUT_HANDLE = -11; 

    [DllImport("kernel32.dll", SetLastError = true)] 
    static extern IntPtr GetStdHandle(int nStdHandle); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    static extern int WriteFile(IntPtr handle, [In] byte[] bytes, int numBytesToWrite, out int numBytesWritten, IntPtr mustBeZero); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, FileShare dwShareMode, IntPtr securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); 

    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] 
    static extern bool WriteConsole(IntPtr hConsoleOutput, [In] byte[] lpBuffer, int nNumberOfCharsToWrite, out int lpNumberOfCharsWritten, IntPtr mustBeZero); 

} 

那些下調試成功輸出的兩個,即使啓用宿主進程。所以這是一個死路一條。

我想分享它,以防其導致其他人計算出爲什麼它發生 - Hans?

+0

我正在仔細研究爲什麼他們會選擇這個例子,我只是沒有提出一個好的猜測。 C運行時庫定義了操作系統的標準化接口。 .NET框架爲操作系統定義了[託管]界面。可以這麼說,兩件事正在爭奪同一件事。他們在想什麼? – Tergiver 2010-11-21 20:29:40

+0

我認爲他們只是試圖展示一個互操作如何工作的例子,而不是讓人們在商業產品中使用一段實用的代碼。 – ATC 2010-11-22 12:42:27

2

Visual Studio託管進程能夠將控制檯輸出重定向到輸出窗口。它究竟如何管理這件事,根本沒有記錄,但它在這裏成爲阻礙。它攔截生成puts()輸出的WriteFile()調用。

項目+屬性,調試選項卡,取消「啓用Visual Studio託管過程」。在同一頁面上,啓用非託管調試也可以解決問題。

+0

任何猜測爲什麼主辦過程阻礙了? – Tergiver 2010-11-21 21:21:29

+0

@Ter:不是。有兩種方法可以執行控制檯輸出,將WriteFile()寫入標準輸出並直接通過WriteConsole()。託管過程可以截取其中一個,但不能截取其中一個。雖然這不是一個好的解釋,但是CRT和.NET都使用WriteFile()。另一個是CRT具有可識別調試器的代碼。但是,這不是我在跟蹤puts()時發生的事情。我會在這一點上揮手,這不是很重要。 – 2010-11-21 21:47:45

+0

看到我的編輯,它肯定不是WriteFile/WriteConsole相關的,它們中的兩個使用不同的方法。重要?當然不是。雖然我很好奇自己。 – Tergiver 2010-11-21 22:10:34