2012-12-14 123 views
2

我需要寫入匿名管道,例如double (*fun)(double),但以下WriteFile(pipe, fun, 4, written_bytes, 0)會在管道接收器中造成錯誤,而ReadFile(read_pipe, fun, 4, written_bytes, 0)。有沒有辦法做到這一點?如何將函數傳遞給匿名管道WinAPI?

我有個主意。我可以創建同一類型的字段一個struct:

struct Foo 
{ 
    double (*f)(double); 
}; 

然後我把它寫WriteFile(hWritePipe_StdIN, &to_process, sizeof(Foo), &bytes, 0); 但我有問題,該管道接收器永遠不會結束讀取數據: ReadFile(hReadPipe, &to_process, sizeof(Foo), &bytes, 0);

+0

只是可以肯定的。你想發送函數本身嗎?或者你想調用函數並傳遞結果? – StackedCrooked

+0

@StackedCrooked第一個版本。我想發送函數本身。 – zerospiel

回答

2

有一些問題吧:

首先,你應該知道函數的大小。 如果這樣做,您只需致電WriteFile(pipe, funcPtr, funcSize, ...)即可轉移。

其次,函數應該只包含position-independent code,並且不處理任何數據。 例如這樣的功能將無法正常工作:

double fun(double x) 
{ 
    int arr[10000]; // implicit function call (alloca or something like this) 
    printf("some"); 
    static int some = 1; 
    return globalVal + (++some); 
} 

因爲功能printf都會有不同的地址,就會出現在另一個過程中沒有靜態變量和字符串。
(好吧,也許你可以傳輸數據爲好,但也沒有辦法,你會產生PI碼)

因此,與所有的限制,您可以發送功能:

__declspec(naked) double fun(double x) { __asm ret } 

const auto funcSize = 1; 
WriteFile(pipe, &fun, funcSize, ...); 
+0

如何在接收後使用此功能?我需要像'fun = cos'這樣的東西。 – zerospiel

1

你到底在這裏實現?你真的試圖寫函數本身?爲什麼?這不是在C++中可以輕鬆完成的事情,例如因爲函數的大小沒有很好的定義。

你或許應該寫數據,即由fun()而不是返回的數字:

const double value = fun(input); 
DWORD numberOfBytesWritten; 
WriteFile(pipe, &value, sizeof value, &numberOfBytesWritten, NULL); 

你當然應該添加代碼來檢查輸出。請注意,像這樣寫入二進制數據可能很脆弱。

+0

我真的需要自己編寫_function_,因爲這個函數將被用在像fun(input)這樣的子進程中。我在父進程中沒有任何輸入來寫入數據。 – zerospiel

0

看看這是如何過於複雜和容易出錯的C++(並且只適用於一套非常有限的函數),我建議你爲此使用一種腳本語言。除了已經提到的指令緩存和DEP外,還有兩件事你不得不考慮。

真的。將函數作爲腳本發送,並在另一端運行。節省自己的痛苦。

Angelscript的外觀和感覺與C++差不多,所以這可能是一個可能的候選人。

現在,如果你反對這個,因爲你需要腳本不能輕易做的事情,那麼知道:在這種情況下,C++也無法做到這一點。

除了上面提到的PIC代碼問題(@Abyx)以及您無法安全地或可移植地知道函數大小的事實之外,您可以想象通過管道發送並且以有意義的方式執行的唯一C++函數是嚴格的const函數。這裏,const是在例如GCC的__attribute__((const)),而不是C++ const關鍵字。

也就是說,任何這樣的函數可能不會檢查除參數之外的任何值,並且除返回值外沒有任何影響。原因很明顯:不同的進程存在於不同的地址空間,所以你引用的任何東西都是沒有意義的。你改變的任何東西都沒有意義。
現在,這正是腳本能以安全,直接的方式,可靠地完成的任務。考慮到你已經通過管道發送代碼,開銷太大了,可以忽略不計。

1

由於您使用的是WinAPI,所以發送函數的本地方式是通過COM。特別是,將該函數作爲COM對象上的方法公開,獲得COM名字對象併發送名字對象。 Monikers可以序列化並通過管道發送。另一方可以反​​序列化名稱並訪問您的對象。

在水中,這是通過在COM 運行對象表

2

在本機代碼中查找對象,你不能發送功能(代碼)本身,既不相同也不是不同的過程。 (你可以嘗試像@Abyx這樣的低級黑客攻擊,但是它會嚴重限制代碼的功能,並且可能會使你用手工編寫彙編代碼。)

你也可以'將函數的地址發送給另一個進程,因爲每個進程都有自己的獨立地址空間;在另一個進程中,該地址將包含不同的數據。

解決方案將創建一個共享庫(最好是動態的),它將包含可能以這種方式發送的所有函數。爲每個函數分配一些標籤(例如數字或名稱),讓DLL維護標籤和地址之間的映射。然後發送標籤。

+0

+1使用DLL併發送符號名稱(洞穴搗毀!)是一個巧妙的想法。 – Damon

相關問題