VB熟達者というよりは、VBマニアな方々に質問です。

Sub Hoge(A)
A = 6
End Sub

Sub Main()
Dim N As Long
Hoge N
Debug.Print N
End Sub

このコードを実行して "6" が出力されるのは明白です。
Hogeはバリアントの参照を取るにも関わらず、LongのNを渡してそのNに6を入れてくれました。
これは、VBが引数を渡す時点で変数A用のメモリ領域をかってに確保し、Nという変数の参照型を作ったということの証拠になります。
実際、DLLでAPIを用意して、バリアント構造体のVarTypeを調べたところ、未知の16387という数値が現れました。
これは、MSDNライブラリを見たところ、VT_LONG Or VT_BYREFのようです。
このように、VBでも参照型は存在していますが、隠蔽されています。

そこで、参照型をもっと自由に使いたいと思い、参照型を作るAPIを作ってみました。
しかし、=演算子で代入しようとしたところ、参照が解除されて通常のバリアント型に戻ってしまいます。
ただし、もともとある変数の参照であるバリアント変数を別の変数の参照に変えたもののときは、参照先に代入してくれました。
どうやらVBはケチなようで、VBが内部で引数として作った参照変数以外は、参照している先に代入はしてくれないようです。

この障害を乗り越える方法を思いついた方、ぜひ教えてください。

このQ&Aに関連する最新のQ&A

A 回答 (3件)

通常、VBでメソッドを呼び出すときは内部では


DISPPARAMS構造体に引数をセットします。
この構造体の中では引数はVARIANTARGにセットされ、
VARIANTARGの内部で引数のポインタが保持されます。
つまり、VBでメソッドを呼び出すときは
内部的には常にVariant型で呼ばれていると考える事ができます。
Privateの関数を呼ぶときはそんな面倒くさい事をしないで
Cと同じように呼んでいるかと考えていたのですが
もし、ご質問されているような結果が出るのでしたら
DISPPARAMSを使用しているのかもしれませんね。
    • good
    • 0
この回答へのお礼

ということは、その構造体内で宣言された変数しか、=は参照型として扱ってくれないということなのでしょうね。

実際、Variantの参照を取る関数に、Variantの変数を渡したとき、渡した側で調べたときと、関数内で調べたときと、変数のアドレスが違っていました。

DISPPARAMS構造体というのは、初めて聞いたのですが、これなら納得がいきます。

GetWindowsDirectoryAなどのAPIにBSTRを渡しているのに、ちゃんと文字列が得られるというのも、同時に納得がいきました。

お礼日時:2001/06/22 17:20

APIを作るということの解決案にはなりませんが、補足から勝手になぜそのようにしたいかを推測しました。



>Dim A As Variant, B As Long
>MakeRef A, B

とし、Aを変更すればBも勝手に変わる、またはその逆をしたいのでしょうか?
もしそうであるなら、やはりクラスを作成しするのがいいように思います。

Dim obj1 as Object
Dim obj2 as Object

Set obj1 = New ClassA
Set obj2 = obj1

とすれば、大丈夫なような気がします。

勝手な予想で、でしゃばって申し訳ありません。
    • good
    • 0
この回答へのお礼

それは一応私も考えてみました。
しかし、やっぱり、LongやDoubleの参照を作りたいわけです。

LongやDoubleのポインタを取得して、参照/代入を行うクラスも作ってみました。

最後にはやっぱりVariant型を懲らしめてやりたい(?)のです。

お礼日時:2001/06/23 11:25

あまり自信がないのですが・・・



>これは、VBが引数を渡す時点で変数A用のメモリ領域をかってに確保し、Nという変数の参照型を作ったということの証拠になります。
何をさして証拠といわれているのかちょっとわかりませんが、プロシージャの引数は、参照渡しがデフォルトであることはマニュアルに記載があります。(当然、値渡しもあります。)

>参照型をもっと自由に使いたいと思い、参照型を作るAPIを作ってみました。
どのような処理をすることをしたいのかはっきりしませんが、VBの仕様でプロシージャの引数以外に参照値を渡す(代入する)ことはできないような気がします。
当然、=演算子で代入することは仕様上できないと思います。

ところで、「参照型を作るAPI」というのは、言語は何で作られたのでしょうか?

VBで作るのであれば、クラスを作成し、クラス内で参照値を返すように見えるメソッドは作れるような気がしますが、VBは演算子のオーバーライドができないので、=演算子を使って操作できるようにはできないような気がします。
CやC++言語を使い、同じように作ることもできるような気はしますが、VB側で演算子のオーバーライドがやはりできないので、=演算子を使用することはできないと思います。

この回答への補足

関数はバリアント型の参照をほしがっているのに、Longの変数が渡せること自体が、C++から考えるとおかしなことだということです。
要は、VBでは、Cのように関数を呼び出すときに、スタックのpush、pop、callだけを行っているのではなく、mallocを使ったり、Variant構造体の内部をいじったりしているといいたいのです。

参照型をもっと自由に使いたいというのは、

Dim A As Variant, B As Long
MakeRef A, B

とすれば、AがBの参照になるような関数を作りたいと言うことです。
C++では次のようにすれば簡単ですが。

long B;
long& A = B;

ちなみに、APIを作る環境は、C++のWin32 DLLです。

補足日時:2001/06/22 14:31
    • good
    • 0

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aと関連する良く見られている質問

QSubとEnd Subについて

VBScriptのSubとEnd Subについて教えて下さい。

Dim LogonID
Dim Password
Dim URL
Dim objIE
Dim objINPUT
Dim BodyText
Dim AriNashi

LogonID = "XXXXXXXX"
Password = "XXXXXXXXX"
URL = "https://login.yahoo.co.jp/config/login?.src=www&.done=http://www.ya..."

下記のようなプログラムはうまくいくのですがSubで囲むと何故か動作しなくなってしまいます。
使い方が間違っているのでしょうか?


Set objIE = WScript.CreateObject("InternetExplorer.Application")
objIE.Navigate URL
objIE.Visible = True

Do Until objIE.Busy = False
WScript.sleep(250)
Loop

If objIE.document.URL = URL Then
BodyText = objIE.document.body.innerTEXT
AriNashi = InStr(1, BodyText, "Yahoo")
If AriNashi > 0 Then
Set objINPUT = objIE.document.getElementsByTagName("input")
objINPUT(0).value = LoginID
objINPUT(1).value = Password
objINPUT(2).Click
Else
msgbox "NO"
End If
Else
WScript.Echo " ダ メ" & Err.Description
End If
Set objIE = Nothing

VBScriptのSubとEnd Subについて教えて下さい。

Dim LogonID
Dim Password
Dim URL
Dim objIE
Dim objINPUT
Dim BodyText
Dim AriNashi

LogonID = "XXXXXXXX"
Password = "XXXXXXXXX"
URL = "https://login.yahoo.co.jp/config/login?.src=www&.done=http://www.ya..."

下記のようなプログラムはうまくいくのですがSubで囲むと何故か動作しなくなってしまいます。
使い方が間違っているのでしょうか?


Set objIE = WScript.CreateObject("InternetExplorer.Application")
objIE.Navigate ...続きを読む

Aベストアンサー

Subで囲むとプロシージャとなります。
プロシージャは他から呼び出されない限り、勝手に動作はしません。

Sub Hoge
'実体
End Sub

と書いたなら、プロシージャの外で
Call Hoge()
のようにプロシージャを呼び出してあげてください。

QExcel VBA: private sub 内の変数の値を Sub へ渡すには

VBA初心者です。private sub 内で条件に合致した変数の値をSubへ渡すにはどうしたらよいのでしょう。下記を見ていただければやりたいことの意図は伝わるかと思います。private sub内の変数が多分private sub内のみでしか参照出来ないからだとは思いますが、うまくいきません。グローバル変数化みたいなことが出来るのでしょうか。

Sub Find_OK()
MsgBox "条件に合致したのは" & var1 & "です。"
End Sub

Private Sub Worksheet_Calculate()
Dim var1
 For var1 = 1 to 10
 If var1 > 8 Then Call Find_OK
End If
Next
End Sub

Aベストアンサー

Sub Find_OK(var1)
MsgBox "条件に合致したのは" & var1 & "です。"
End Sub

Private Sub Worksheet_Calculate()
Dim var1
 For var1 = 1 to 10
 If var1 > 8 Then Call Find_OK(var1)
End If
Next
End Sub

QVBでOSの環境変数を参照する方法について

下記のようなvbの実行ファイルとアプリケーション構成ファイルがあり、
xmlファイルからvalueの値を取得して処理を行っています。
・vb.exe
・vb.exe.config
・vbConfig.xml

【vbConfig.xml】
---------------------
<?xml version="1.0" encoding="utf-8" ?>
<config>
<params>
<param key="checkFile" value="ここに値を設定" />
</params>
</config>
---------------------

現在はvalueに設定値を明記していますが、設定したい値が環境ごとに異なるため、
各環境ごとにxmlファイルが存在しています。これを、OSのユーザー環境変数から
取得するように変更し、全環境のxmlファイルを統一したいと考えているのですが、
xmlのvalueにOSの環境変数を設定する方法はありますでしょうか?

尚、ユーザー環境変数は各環境のexe実行前にsetxコマンドで定義する想定です。
試しにsetxコマンドでVBTESTというユーザー環境変数を定義し、以下のような
記載にしてみたのですが、正しく動作しませんでした。
<param key="checkFile" value="%VBTEST%" />

書式が間違っているのでしょうか?それとも、上記のような使い方はできない
ものでしょうか?vb初心者で勝手が分からないため、記載内容に不備等も
あるかもしれませんが、アドバイスをお願い致します。

下記のようなvbの実行ファイルとアプリケーション構成ファイルがあり、
xmlファイルからvalueの値を取得して処理を行っています。
・vb.exe
・vb.exe.config
・vbConfig.xml

【vbConfig.xml】
---------------------
<?xml version="1.0" encoding="utf-8" ?>
<config>
<params>
<param key="checkFile" value="ここに値を設定" />
</params>
</config>
---------------------

現在はvalueに設定値を明記していますが、設定したい値が環境ごとに異なるため、
各環境ごとにxmlファイルが...続きを読む

Aベストアンサー

xmlファイルをどのようにして読んでいるかが不明なので(どのようなライブラリを使用しているか不明なので)
なんとも言えませんが、たぶん、
>書式が間違っているのでしょうか?それとも、上記のような使い方はできない
>ものでしょうか?
の回答は、「上記のような使い方はできない」になると思います。
もし、仮に使用しているxmlファイルを読み込むためのライブラリに%XXX%の文字を環境変数XXXから取得する
と明記されていれば、話はべつですが・・・・

ですので、vb.exeを改造するのが最も手っ取り早い解決策になるかと思います。
改造の内容は、「読み込んだ内容の該当箇所が"%XXX%"であれば、環境変数XXXの値で読み替える」ということになります。

Q変数の参照でエラーが出てしまいます。(VB.NET)

プロシージャの外に記した変数があるのですが、
(Dim IniFileName as string = "myapp.ini")など
Public Shared Sub 内でIniFileNameを使おうとすると
---------
クラスの明示的なインスタンスを指定しないで、共有メソッドまたは共有メンバ初期化子内からクラスのインスタンス メンバへ参照することはできません。
---------
というエラーが出てしまいます。
クラスの明示的なインスタンスを指定したいのですが、
意味がさっぱり分かりません。
class.IniFileNameとかForm1.IniFileNameなどとしてみたのですが違うようです。
一つ正解をご教示頂けたらと思います。
宜しくお願い致します。

Aベストアンサー

参考URLの「インスタンスを作成せずに呼び出せるメソッド 」が参考になるかもしれません。

Sharedをつけると、インスタンス生成しなくても呼び出せるメソッドになるので、インスタンスに属するメンバ変数などにアクセスできません。

Sharedメソッドから参照したいのであれば、
そのSharedメソッドと同じクラス内でShared変数とするか、Sharedメソッドの引数にするかなどで対応できます。

参考URL:http://www.atmarkit.co.jp/fdotnet/vb6tonet2/vbnet2_05/vbnet2_05_02.html

QVB6変数の宣言dim j,k,p,m,n as Integerは良くない?

お世話になります。

VB暦1年です。

汎用の変数宣言でタイトルのように
dim j,k,p,m,n as Integer
dim ssa,ssb as String

など、カンマ区切りで変数宣言を使っていたのですが
最近、知人にasの手前の変数は型どおり宣言されるが
その手前の変数はVariant型で宣言されてしまうと指摘されました。

指摘されるまで気にはしていませんでしたが
ウォッチで確認すると変数に代入されるまでは
型がVariant/Emptyとなってます。

以後、気をつければいいのですが
過去にコーディングしたプログラムにも多少、使用していて客先に納品してしまっているものもあります。
後々、問題になるのかな?

Aベストアンサー

おっしゃる通り、カンマで区切る場合は、
Dim j As Integer, k As Integer, p As Integer
という風にすべてAsで型を明示しないと、
Variant型になってしまいます。
つまり、
Dim j
Dim k
Dim p As Integer
とわけて書いた場合と同じです。
(VB.NETは、
Dim j, k, p As Integer
で全部Integerになるようなので、ややこしいですね)

すでにご存知とも思いますが、一般的にVariant型を多用しているコードは、宣言を見ても、どういう種類の値を使うかわかりづらいですし、比較対象の型を誤るというようなバグの原因になりやすいので、型を明示できるような状況で敢えてVariant型を使うべきではないです。

しかし、すでに納品してしまったコードについては、問題はメモリを余計に使ってしまうことくらいではないでしょうか?
他の人が気づかなかったことから考えるに、多分狭いスコープで使っていますよね? 比較対象や代入するべき型を誤るというようなロジックの誤りがない限り、少なくとも動作上の問題は発生しないと思います。ただ、直せる機会があるのであれば、直した方がいいとは思いますが。

おっしゃる通り、カンマで区切る場合は、
Dim j As Integer, k As Integer, p As Integer
という風にすべてAsで型を明示しないと、
Variant型になってしまいます。
つまり、
Dim j
Dim k
Dim p As Integer
とわけて書いた場合と同じです。
(VB.NETは、
Dim j, k, p As Integer
で全部Integerになるようなので、ややこしいですね)

すでにご存知とも思いますが、一般的にVariant型を多用しているコードは、宣言を見ても、どういう種類の値を使うかわかりづらいですし、比較対象の型を誤るというよう...続きを読む


人気Q&Aランキング

おすすめ情報